laxar-vue-adapter

Implement LaxarJS widgets and controls with Vue.js.

Installation

The recommended installation is through NPM:

npm install --save laxar-vue-adapter

If using the source module instead, you need to ensure that the .js file is loaded with ES2015 support, plus support for Object spread.

Getting started

You can either use the familiar directory structure for LaxarJS widgets, with separate .js, .html and .css files, or you can use the vue-loader and put the whole widget into a single .vue file.

In any case, your widget module will have to export an object of Vue.js options. These will be passed to Vue.extend() by the adapter (together with some extras to enable widget service injections) so make sure you use options that are supported in component definitions.

Separate files

When using separate files for HTML template and controller logic, the template will be compiled on-the-fly in the user's browser, incurring a slight performance penalty. You will also have to make sure that vue resolves to the so-called "standalone build" of Vue.js.

<!-- default.theme/my-widget.html -->
<b>{{counter}}</b>
// my-widget.js
export default {
   data() {
      return { counter: 0 };
   },
   created() {
      this.interval = setInterval( () => {
         this.counter++;
      }, 1000 );
   },
   destroyed() {
      clearInterval( this.interval );
   }
}

You have to make sure laxar-vue-adapter has access to Vue.compile to compile the template HTML. This method is not part of Vue.js' default NPM package. Using webpack, you can use the standalone build of Vue.js by defining an alias in your resolve configuration:

// webpack.config.js
module.exports = {
   resolve: {
      alias: {
         'vue$': 'vue/dist/vue.js'
      }
   }
};

Single file components

In this example the vue-loader will preprocess the .vue file and compile the template to a JavaScript function.

<!-- my-widget.vue -->
<template>
   <b class="counter">{{counter}}</b>
</template>

<script>
export default {
   data() {
      return { counter: 0 };
   },
   created() {
      this.interval = setInterval( () => {
         this.counter++;
      }, 1000 );
   },
   destroyed() {
      clearInterval( this.interval );
   }
}
</script>

<style>
.counter {
   color: red;
}
</style>

Make sure vue-loader is installed and set your webpack.config.js accordingly.

// webpack.config.js
module.exports = {
   resolve: {
      extensions: [ '', '.js', '.vue' ]
   },
   module: {
      loaders: [ {
         test: /\.vue$/,
         loader: 'vue-loader'
      } ]
   }
};

LaxarJS integration

With the basic setup described above, simple Vue components should just work. If you are developing anything useful you probably need to interact with the LaxarJS runtime in some way. The following sections will describe how the laxar-vue-adapter provides a (mostly) non-invasive integration with LaxarJS.

Injections

LaxarJS widget services can be injected into your Vue.js component by using the injections mixin provided by the laxar-vue-adapter. An list of corresponding injection values is then available to the component instance as this.$injections.

import { injections } from 'laxar-vue-adapter';
export default {
   mixins: [ injections( 'axEventBus', 'axGlobalLog' ) ],
   created() {
      const [ eventBus, log ] = this.$injections;
      eventBus.subscribe( 'beginLifecycleRequest', () => {
         log.debug( 'So it has begun!' );
      } );
   }
};

axContext

The axContext service is automatically injected. It can be accessed via the $data property. The context does not appear among this.$injections unless you explicitly specify it by using the injections mixin. Its presence in the component's $data allows you to easily access the id() generator, the event bus and the features configured for your particular widget instance.

export default {
   template: '<b :id="id(\'some-suffix\')">{{ features.mytext }}</b>',
   created() {
      this.eventBus.subscribe( 'some-event', this.methods.eventHandler );
   },
   methods: {
      eventHandler( payload, meta ) {
         // ...
      }
   }
};

Controls

You can specify controls in your widget.json. Just list a module that can resolved by the module loader. The control will then be registered locally as a component with the name specified in its control.json.

{
   "name": "my-widget",
   "integration": {
      "technology": "vue",
      "type": "widget"
   },
   "controls": [ "my-vue-control-module" ]
}
{
   "name": "my-vue-control",
   "integration": {
      "technology": "vue",
      "type": "control"
   }
}
<!-- my-widget.vue -->
<template>
<div>This is my widget</div>
<my-vue-control>It's using a control</my-vue-control>
</template>

<script>
export default {
   components: {
      // this part is automatically supplied by the runtime:
      'my-vue-control': ConstructorCreatedByTheVueAdapter
   }
};
</script>

Injections in controls

Controls can access the following global services via injections:

axWidgetServices

The services of the widget instance that uses the control can be injected as axWidgetServices.

import { injections } from 'laxar-vue-adapter';
// my-vue-control.js
export default {
   mixins: [ injections( 'axWidgetServices' ) ],
   created() {
      const [ { axLog } ] = this.$injections;
      axLog.info( 'I\'m a control using my widget\'s logger!' );
    }
  };

Widget Areas

All Vue.js components loaded by this adapter have access to an additional component ax-widget-area. This component can be used to provide containers for nested widgets.

<!-- my-widget.vue -->

<template>
   <div>
      <h1>Here are two areas:</h1>
      <ax-widget-area name="first" />
      <ax-widget-area name="second" />
   </div>
</template>

<script>
   <!-- export default { ... }; -->
</script>

No modifications to the component JavaScript code are needed. Behind the scenes, the widget area component uses the axAreaHelper widget service injection, to provide additional widget areas:

   // my-page.json, areas:
   "content": [
      {
         "widget": "my-widget",
         "id": "widgetX"
      }
   ],
   "widgetX.first": [ /* ...more widgets... */ ],
   "widgetX.second": [ /* ... */ ]

Note that once mounted, widget areas should not be destroyed until their containing page is destroyed. Use styling (display: none) to hide and show areas as needed. This is a necessary evil for now, as some integration technologies (such as AngularJS v1) do not support temporary removal of their DOM. For this reason, it is also not recommended to use a mutable expression for the name prop.

Testing

LaxarJS widgets and activities can be tested using LaxarJS Mocks just like when using other LaxarJS integration technologies. Note that the laxar-vue-adapter makes available the special property axMocks.widget.vueComponent when axMocks.widget.load is called, referencing your widget's Vue.js component instance. You can use this property to inspect your widget component data as well as its eventBus, and to simulate method calls.

Theming

Theme directories are fully supported. Additionally, if you are using the vue-loader, the default HTML and CSS can be embedded in the main .vue file. However, the HTML and CSS corresponding to the default theme will be present in the bundled application regardless of the theme used for bundling. If that is an issue, we recommend using separate files for the HTML template and CSS.

Precompiling themed assets

Using external (S)CSS is supported, just like with other integration technologies.

You can also use the vue-loader to precompile your themed HTML and CSS. Just specify a Vue.js component file as your templateSource and make sure it gets processed by the vue-loader.

{
   "name": "my-widget",
   "templateSource": "my-widget.vue"
}
<!-- any.theme/my-widget.vue -->
<template>
   <b class="counter">{{counter}}</b>
</template>

<style>
.counter {
   color: red;
}
</style>

In this case, the component scripting logic could be kept in a my-widget.js at the widget's top-level.

Hacking the Adapter

First, clone the repository and fetch the dependencies:

git clone https://github.com/LaxarJS/laxar-vue-adapter.git
cd laxar-vue-adapter
npm install

To rebuild the pre-compiled bundle, use:

npm run dist

Otherwise, you may need to adjust your project to load the adapter with ES2015 support. To pick up clone from within a LaxarJS application, you may need to modify the project's webpack resolve configuration.

To test the adapter, run:

npm test

For interactive tests that can be inspected in the browser, run:

npm start

Now you can access the spec-tests at http://localhost:8080/dist/spec/laxar-vue-adapter.spec.html