Frontend#

A plugin performs all of its modifications to the heedy frontend through the use of a single javascript module. Heedy is made aware of this module in the plugin’s heedy.conf, by setting the frontend variable in the plugin’s block. The variable is set to the module’s path relative to the public/static directory in the plugin’s folder.

plugin "myplugin" {
    frontend = "myplugin/main.mjs"
}

While not strictly required, it is recommended that you prefix the module path with your plugin name (as was done here). In this case, heedy’s frontend code will look for a javascript module file served at /static/myplugin/main.mjs. Then, for heedy’s backend to serve the file at the correct location, you need to create a main.mjs file, and place it at public/static/myplugin/main.mjs in your plugin’s folder. The minimal content of your main.mjs is the following:

function setup(frontend) {
  // Initialize your plugin's frontend here
}
export default setup;

The frontend object#

Using the frontend object passed into your module’s setup function, you can tell heedy’s UI how to accommodate your plugin. The UI is built with Vue, so the frontend object allows you to directly deal with Vue components:

function setup(frontend) {
  // Register a vuex store for your plugin (optional)
  frontend.store.registerModule("myplugin", vuexModule);
  // Add a route to the ui, which will be accessibe from `/#/myplugin/myroute`
  frontend.addRoute({
    path: "/myplugin/myroute",
    component: MyComponent,
  });
  // Add an item to the main menu that will redirect to the registered route
  frontend.addMenuItem({
    key: "mypluginMenuItem",
    text: "My Plugin",
    icon: "home",
    route: "/myplugin/myroute",
    location: "primary",
  });
}
export default setup;

Each of these can be accessed as a property of the frontend object (e.g. frontend.websocket, frontend.worker, frontend.timeseries).

Injected Functionality#

Each heedy plugin can attach additional functionality to the frontend object (i.e. inject their own objects into the frontend). In a bare heedy install, the following extensions are injected into the raw frontend object, giving specialized APIs for specific portions of the UI:

Frontend API#

class frontend(Vue, appinfo, store)#

The frontend object is passed to the setup functions of each plugin, and exposes the APIs necessary to augment heedy’s UI.

Examples:

function setup(frontend) {
   frontend.addRoute({
       path: "/myplugin/myroute",
       component: MyComponent
   });
}
frontend.info#

This property contains information passed in from the server, and is used to set up the session.

Examples:

frontend.info = {
 // Whether the current session is of an admin user
 admin: true,
 // The list of plugins with frontend code,
 // as well as paths to their modules
 plugins: [
     {name: "heedy", path: "heedy/main.mjs"},
     ...
     {name: "timeseries", path: "timeseries/main.mjs"},
     {name: "myplugin", path: "myplugin/main.mjs"}
 ],
 // The currently logged in user. If no user is logged in,
 // this field is null. Checking if frontend.info.user
 // is null is the recommended way to modify plugin features
 // for logged in users.
 user: {
     username: "myuser",
     name: "My User",
     icon: "base64:...",
     public_read: false,
     users_read: false
 }
}
frontend.notFound#

The vue component to display when linked to a route that was not registered. For example, the notFound component will be displayed when the path /#/blahblah is used.

Examples:

frontend.notFound = MyNotFoundComponent
frontend.router#

The vue router used for the frontend. This value is only initialized after the initial setup, so it is not available when initializing plugins.

frontend.store#

The vuex store used for the frontend. A plugin can set up its own vuex modules to manage its state.

Examples:

frontend.store.registerModule("myplugin",{
 state: {count: 0},
 mutations: {
     increment(state) {state.count++}
 }
});
frontend.theme#

The vue component to use as the main theme for the frontend. The theme renders the main menu, and holds the router that shows individual pages.

Examples:

frontend.theme = MyThemeComponent
frontend.vue#

The Vue instance. This is mainly to be used to register components and plugins.

Examples:

frontend.vue.use(MyVuePlugin);
frontend.vue.component("mycomponent",MyComponent);
frontend.addMenuItem(m)#

Add an item to the main app menu.

Arguments
  • m (object) – Menu item to add.

  • m.key (string) – a unique ID for the menu item

  • m.text (string) – the text to display

  • m.icon (string) – The icon to show

  • m.route (string) – the route to navigate to on clicking the menu item

  • m.location (string) – optional, hints at where the user might want the menu (primary,secondary,spaced_primary).

  • m.component (vue.Component) – an optional vue component to display instead of icon. Be aware that the component must have a “state” prop, where it is told how to behave i.e. whether the menu is small, on bottom, etc.

Examples:

frontend.addMenuItem({
   key: "myMenuItem",
   text: "My Menu Item",
   icon: "fas fa-handshake",
   route: "/myplugin/mypath",
   location: "primary"
});
frontend.addRoute(r)#

Routes sets up the app’s browser bar routing (portion of the path after /#/)

To set up different routes for logged in users and the public, the plugin can check if info.user is null.

Arguments
  • r.path (string) – The path at which to define the route

  • r.component (vue.Component) – The vue component object to show as the page at that route.

Examples:

frontend.addRoute({
 path: "myplugin/mypath", // This means /#/myplugin/mypath
 component: MyComponent
});
frontend.inject(name, toInject)#

A plugin can inject its own API into the frontend object, so that all plugins loaded after it have access to it.

Arguments
  • name (string) – the name at which to inject the object

  • toInject (*) – the object to inject into the frontend

Examples:

class MyAPI {
   constructor() {}
   myfunction() {}
}
frontend.inject("myapi", new MyAPI());
frontend.myapi.myfunction();
frontend.rest(method, uri, data=null, opt)#

An async helper function that allows querying the REST API explicitly. It explicitly returns the decoded json object, or returns the error response.

Arguments
  • method (string) – HTTP verb to use (GET/POST/…)

  • uri (string) – uri to query (api/users/…)

  • data (object) – optional object to send as a json payload. If the method is GET, the data is sent as url params.

  • opt (object) – request options

Returns

object – an object with two fields, response and data. response gives a fetch query response object, and data contains the response content decoded from json.

Examples:

let username = "myuser"
let res = await frontend.rest("GET",`api/users/{encodeURIComponent(username)}`,{
 icon: true // urlparam to include icon with request
})
if (!res.response.ok) {
 console.log("Failed:",res.data.error_description);
} else {
 console.log("User:",res.data);
}