Vue.js

Genelet APIs follows a unique URL format, which allows one to serve the whole website as a single page using JavaScript. Here we show how to do in Vue.js. For Angular 1.2, check here.

If you are using Tabilet, the layout of initial Vue.js files will be generated automatically.

 

URI Mapping

Genelet API use this URL:
http://WEBSITE/HANDLER/role/json/component?action=string&query…     (1)

where role is the name of login group,  component the database table one is dealing with, and action the verb on the component.

With Ajax, the API can make the following dynamical pages at the single app.html:

http://WEBSITE/app.html#/role/component?action=string&query…                       (2)

 

File Layout

The layout of the HTML5 files is organized in the document root as:

/app.html
/genelet.js
/role1/header1.vue
/role1/footer1.vue
/role1/component1/action1.vue
/role1/component1/action2.vue
/role1/component2/action1.vue
/role1/component2/action2.vue

/role2/header2.vue
/role2/footer2.vue
/role2/login2.vue
/role2/component1/action1.vue
/role2/component1/action2.vue
/role2/component2/action1.vue
/role2/component2/action2.vue

Here is app.html:

<!doctype html>
<html lang="en">
<head>
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/http-vue-loader"></script>
<script src="/genelet.js"></script>
</head>
<body>
<div id="app">
<component v-bind:is="headerComponent"></component>
<component v-bind:is="currentComponent" v-bind:names="names"></component>
<component v-bind:is="footerComponent"></component>
</div>
<script>
var $scope = new VueGenelet({
  siteUrl: "/app.php", html: "vue", json: "json",
  logins: "login", header: "header", footer: "footer",
  role: "p", comp: "question", action: "topics",
});
$scope.start();

var vm = new Vue({
  el: '#app',
  data : $scope,
  components: {
    'role1-header': httpVueLoader('./role1/header.vue'),
    'role1-footer': httpVueLoader('./role1/footer.vue'),
    'role1-login': httpVueLoader('./role1/login.vue'),
    'role1-component1-action1': httpVueLoader('./role1/component1/action1.vue'),
    ...
    'role2-header': httpVueLoader('./role2/header.vue'),
    ...
  }
});
vm.$scope = $scope;
</script>
</body>
</html>

Here is app.php that calls API:

<?php
declare (strict_types = 1);
require __DIR__ . '/../vendor/autoload.php';
$c = json_decode(file_get_contents( __DIR__ . "/../conf/config.json"));
$pdo = new PDO(...$c->{"Db"});
$jsons = array();
$storage = array();
foreach (["component1", "component2"] as $item) {
    $jsons[$item] = json_decode(file_get_contents(__DIR__ . "/../src/$item/component.json"));
    $class = '\\'."Mypackage".'\\'.ucfirst($item).'\\'."Model";
    $storage[$item]  = new $class($pdo, $jsons[$item]);
}
$logger = new \Genelet\Logger($c->{"Log"}->{"Filename"}, $c->{"Log"}->{"Level"});
$controller = new \Genelet\Controller($c, $pdo, $jsons, $storage, $logger);
$ret = $controller->Run();

http_response_code($ret->http_status());
if ($ret->http_status()==200) {
    echo $ret->http_report();
} elseif ($ret->http_status()==303 || $ret->http_status()==301) {
    header("Location: ".$ret->http_report());
}

The program genelet.js (download here) will call the serve API in (1) via app.php and fill in retrieved data into Vue.js templates.  The JSON data are saved in global variable called $scope which one can use to access in Vue. 

 

Login

Here is example login1.vue:

<template>
<div>
<h3>{{ names.error_code }}</h3>
<FORM id="role1_login" @submit.prevent="sendit">
<pre>
Username :<INPUT TYPE="TEXT"     v-model="f0.login" />
Password : <INPUT TYPE="PASSWORD" v-model="f0.passwd" />
<button TYPE="SUBMIT"> Login Now </button>
</pre>
</FORM>
</div>
</template>
<script>
module.exports = {
  props: ['names'],
  data: function() {
    return { f0: {} }
  },
  methods: {
    sendit: function(e) {
      this.f0["role"] = "role1";
      this.f0["tag"] = "json";
      $scope.login('role1', 'component1', 'action1', this.f0);
    }
  }
}
</script>

 

List of All Items

The List All in REST has the action name topics in Genelet. Here it is

<template>
<div>
<table>
  <thead>
<tr> <th>Column1</th> <th>Column2</th> ...
<th> </th> </tr> </thead> <tbody>
<tr v-for="item in names.data"> <td> <role1-component1-edit v-if="showModal && currentID===item.column1_id" v-bind:single="currentData" v-bind:id="currentID" @close="showModal=false"> </role1-component1-edit> <button id="show-modal" @click="openModal(item.column1_id)">{{ item.column1_id }}</button> </td> <td>{{ item.column1 }}</td> <td>{{ item.column2 }}</td>
... <td><button @click="deleteItem(item.column1_id)">Delete</button></td> </tr> </tbody> </table> <p> <role1-component1-startnew v-if="newModal" @close="newModal=false"> </role1-component1-startnew> <button id="new-modal" @click="newModal=true">Add</button> </p> </div> </template> <script> module.exports = { name: 'role1-component1-topics', components: { 'role1-component1-edit': httpVueLoader('./edit.vue'), 'role1-component1-startnew': httpVueLoader('./startnew.vue'), }, props: ['names'], data: function() { return { newModal: false, showModal: false, currentID: 0, currentData: null, }; }, methods: { openModal: function(id) { that = this; var mylanding = function(x) { that.currentData = JSON.parse(JSON.stringify(x.data[0])); }; $scope.ajaxPage("role1", "component1", {action:"edit", column1_id:id}, "GET", mylanding); this.currentID = id; this.showModal = true; }, deleteItem: function(id) { if (confirm("Please confirm that you want to delete: " + id + "?")) { $scope.ajaxPage("role1", "component1", {action:"delete", column1_id:id}, "GET", {operator:"delete", "id_name":"column1_id"}); } } } } </script>

 

Other REST Files

Please check Tabilet-generated files for implementation.

 

JavaScipt Functions

You can call the following methods on the global object $scope in Vue.

 

Anchor Links

Anchor link uses the format:

<a href=”” onclick=”$scope.go(role, component, action, query, landing)”></a>     (3)

Clicking on the link will result in the browser to navigate, depending on how landing is passed in:

  • landing is null, it simulates the traditional anchor, i.e. jump to new page:
    http://WEBSITE/app.html#/role/component?action=string&query
  • landing is string, it retrieves the API data only. No anchor jump nor page refresh. The returned data are saved in object string, instead of overriding the existing data array names. A variety of the function called optional_go is also available. If the name object string is already existing, the API call will simply be ignored. The use case of this landing is when you want two or more API data on one page.
  • landing == {operator: stringid_name: string} where operator string is one of insert, update or delete. No anchor jump. This landing is used to change the existing lists on spot, without refreshing the page. The optional variable  id_name defines the primary key in delete or update. The use case is when you want to insert, delete or update dynamic data without refreshing the current page.
  • landing == {role: stringcomponent: string, action: string, query: object}. After running the original API, it will redirect to the new URL while running the following, second API:
    /#/landing{role}/landing{component}?action=string&landing{query}
    For example, after a user updates his credit card, you lead her back to the original payment page. You need two API calls for this click: (1) update her credit card and (2) refresh the payment page, presumably with the updated card information on it.

 

POST a Form

To make a POST request, e.g. to submit a form, we use:

send(role, component, action, query, landing)

All the parameters and use cases are the same as the above GET anchor.

 

Login

While the login process is also a POST, we’d like an easier API:

login(role, component, action, query,  provider)

where provider is the name of the authentication agent. After the successful login to role, the URL will be redirected to what defined in role, componentaction and landing.