This guide will lead you through the process of integrating Airship with Vue.
Airship styles need to be globally included within our Vue application, so that we can use styles everywhere instead of including them inside any component scope. We are going to import them in the application’s entry point via npm.
1
npm i @carto/airship-style
To import the styles, we need to include airship.css
in the file:
1
import '@carto/airship-style/dist/airship.css';
If you are not using Vue CLI, you will need to include css
webpack loader to be able to parse and include styles within your bundle as long as you have not had them installed yet.
To install CSS loader:
1
npm i css-loader style-loader -D
You will need to tell Webpack to use these loaders by changing the configuration file:
1
2
3
4
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
}
On top of adding Airship styles, we need to load our components into your application. You can do it below our styles import in the main entry point of the application.
First, we need to tell Vue to ignore elements starting with as-
, which are our Airship components. We do not want Vue to raise an error everytime an Airship component is included in a template.
1
2
3
import Vue from 'vue';
Vue.config.ignoredElements = [/as-\w+/];
Second, we need to import and define our Airship components. As we did before with Airship styles, we import them from npm, and then we need to execute defineCustomElements
function.
1
2
3
import { defineCustomElements } from '@carto/airship-components/dist/loader';
defineCustomElements(window);
To add a Web Component to a Vue template you just need to use the HTML syntax that is provided in the reference.
Although Web Components are supported in Vue, there is one caveat when it comes to injecting data into the component. The only properties that can be passed via attributes are text and number properties, whether via direct text or injecting the property in the template.
1
2
3
4
<as-category-widget
heading="Business Volume"
description={widgetDescription}>
</as-category-widget>
To pass complex properties such as Objects and Arrays or listen to component events, you need to do it via Vue element reference or creating a mirror component.
There are some properties that we cannot pass or events that we cannot listen to in the example above, such as category property or categoriesSelected event.
To get the element reference, we need to set ref
property in the component template node.
1
2
3
4
5
6
7
<template>
<as-category-widget
ref="categoryWidget"
heading="Business Volume"
description={widgetDescription}>
</as-category-widget>
</template>
Element’s reference will be in our Vue component’s refs
property, hence we will have full access to our Airship component.
The best way to listen to component events is in mounted
function in your desired component.
1
2
3
4
5
6
7
8
9
10
export default {
name: 'MyComponent',
mounted: function () {
this.categoryWidget = this.$refs.categoryWidget;
this.categoryWidget.addEventListener('categoriesSelected', (event) => {
console.log('Selected Categories', event.detail);
});
}
}
We create an easy-to-use reference to the widget so that we can reuse it in other parts of our component. Then, we listen to categoriesSelected
event where you can put your own custom code to perform the action that you want.
It will only listen once to component event and it will not create multiple unneeded event listeners by doing it in mounted
callback.
The way to pass complex properties to the component is very similar to listening to events.
To make the component react to property changes we need to create a watch over the property that holds our data. Once watch
is ready, we need to set the new value of the property to the desired property in the component, like:
1
2
3
4
5
6
7
8
export default {
name: 'MyComponent',
watch: {
categories: function (newValue) {
this.categoryWidget.categories = newValue;
}
}
}
To invoke a component method, we need to create a mirror method in our Vue component that executes the original method.
1
2
3
4
5
6
7
8
9
10
11
export default {
name: 'MyComponent',
methods: {
async getSelectedCategories: function () {
return await this.categoryWidget.getSelectedCategories();
},
async clearSelection: function () {
return await this.categoryWidget.clearSelection();
}
}
}
Mirroring one Airship Component with a Vue component is pretty much like doing the same things as above but encapsulated within a single component.
To mirror the Airship Component, we need to create a new Vue component like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<template>
<as-category-widget
heading={heading}
description={description}
show-clear-button={showClearButton}
defaultBarColor={defaultBarColor}>
</as-category-widget>
</template>
<script>
export default {
name: 'CategoryWidget',
properties: {
heading: String,
description: String,
showClearButton: Boolean,
defaultBarColor: String,
categories: Object
},
mounted: function () {
this.categoryWidget = this.$refs.categoryWidget;
this.categoryWidget.categories = this.categories;
this.categoryWidget.addEventListener('categoriesSelected', (event) => {
console.log('Selected Categories', event.detail);
});
},
methods: {
async getSelectedCategories: function () {
return await this.categoryWidget.getSelectedCategories();
},
async clearSelection: function () {
return await this.categoryWidget.clearSelection();
}
},
watch: {
categories: function (newValue) {
this.categoryWidget.categories = newValue;
}
}
}
</script>
It includes all the logic management encapsulated in one component. You do not have to mimick all component’s properties or events either, only the ones that you are going to use.
And then, you will use that component as you would do with your other Vue components.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<template>
<CategoryWidget
heading={heading}
description={description}
categories={categories} />
</template>
<script>
import CategoryWidget from './components/CategoryWidget.vue';
export default {
name: 'app',
components: {
CategoryWidget
},
data: function () {
return {
heading: 'Business Volume',
description: 'Description',
categories: [
{ name: 'Category 1', value: 1000 }
{ name: 'Category 2', value: 500 }
]
};
}
}
</script>