Creating UI view (v22 and above)

Follow

Now let's discuss how you can build a UI for your plugin by adding new Countly views to the dashboard.

We know that some files are loaded automatically into Countly dashboard, and now using these files (basically, countly.models.js and countly.views.js) we can both get data from API and display them.

So what we need to achieve is:

  • Create a model that can fetch data from API.
  • Create a VueJS view which would get the data from the model load it into the template and append it to a page.
  • Add this view to the App Router.
  • Inject some Javascript to modify the page, like creating a menu item for our plugin.

Setting up a model

Now let's define a simple model that fetched data from API on path /o?method=ourplugin

File: countly.models.js

1. Define your Vuex store

If your view needs to manage the state, you can define a Vuex store for it. This store will allow you to manage, update, and retrieve state in a reactive way.

(function(countlyOurPlugin) {

countlyOurPlugin.getVuexModule = function(){
var getEmptyState = function() {
return {
data: null // we will store the data from api as this variable in state
}
} var getters = {
.... // we will add these in next steps
};
var mutations = {
.... // we will add these in next steps
};
var actions = {
.... // we will add these in next steps
};

var store = CV.vuex.Module("countlyOurPlugin", { // define the store
state: getEmptyState,
getters: getters,
mutations: mutations,
actions: actions,
});
}
})(window.countlyOurPlugin = window.countlyOurPlugin || {});

2. Define actions to fetch and populate data in the model.

In the Vuex store defined above, add a new key to the actions object that will fetch the data from the API on path /o?method=ourplugin. 

var actions = {
initialize = function(){
return new Promise(function(resolve, reject) {
CV.$.ajax({ type:"GET", url:"/o", data:{ //providing current user's api key "api_key":countlyGlobal.member.api_key, //providing current app's id "app_id":countlyCommon.ACTIVE_APP_ID, //specifying method param "method":"ourplugin" }, success:function (json) { //got our data, let's process it, we use the countly helper method to process the data
var precessed = CountlyHelpers.createMetricModel(json)
// lets return the processed data resolve(processed);
}
}); });
}
}

3. Define mutation to set the data to state.

In the Vuex store defined above, add a new key to the mutations object that will set the data from the action to the state.

var mutations = {
setData = function(state, payload){
state.data = payload; // state.data is set to null as default in the 1st step in the getEmptyState method
}
}

4. Define a getter to get the data from the state.

In the Vuex store defined above, add a new key to the mutations object that will set the data from the action to the state.

var getters = {
data = function(state){
return state.data;
}
}

Creating the view

Now let's create a basic Countly view, which will use our model to fetch data and display components on the page.

File: countly.views.js

1. Create a component

Create a new countly Vue component using CV.views.create method. This component will represent the main content of your view.

var MainView = CV.views.create({
template: CV.T('/our-plugin/templates/main.html'), // html template
mixins: [],
data: function() {
return {
appId: countlyCommon.ACTIVE_APP_ID,
}
},
computed: {
data: function() {
return this.$store.getters["countlyOurPlugin/data"];
}
},
mounted: function(){ // vue lifecycle methods are supported
this.$store.dispatch("countlyOurPlugin/initialize"); // vuex action
},
refresh: function(){
this.$store.dispatch("countlyOurPlugin/initialize") // refresh data
}
});

2. Adding HTML & localization

We can add the HTML for the component by using the filename we defined while creating the component.

In our case, we defined the HTML template as templates/main.html
So we add the main.html file in the templates directory and add the component HTML in that file.
Here we can display the computed data defined in the component and add localization by using the i18n method.
The localizations can be added to the .properties files.

File: localization/outPlugin.properties

ourPlugin.title = Our Plugin
ourPlugin.placeholder = Data from API placeholder

File: templates/main.html

<div :class="[componentId]">
<h3>{{ i18n("ourPlugin.title") }}</h3>
<div class="data">
<span>{{ i18n("ourPlugin.placeholder") }}</span>
<p>{{ data }}</p>
</div>
</div>

3. Register view with Countly

Once you've defined your Vue component, you'll need to register it with Countly so that it can be displayed in the UI.

app.route('ourplugin', 'ourplugin', function() { // register route
this.renderWhenReady(new CV.views.BackboneWrapper({
component: MainView, // component to display
vuex: [{ clyModel: countlyOurPlugin }], // we reference the vuex model here
}));
});

After that, you should be able to see the view at: http://yourdomain.com/dashboard#/ourplugin

Injecting scripts

But we do not want to type in our URL; rather, we want to access it by selecting the menu button. To accomplish that, we need to add a menu item.

This is a one-time task that should be accomplished when the webpage has loaded, so here we can simply wait for the document to be ready and add our plugin item to the menu (here is more information about addMenu method).

$( document ).ready(function() {
app.addMenu("understand", {
code: "ourplugin",
url: "#/ourplugin",
text: "ourplugin.title",
icon: '<div class="logo ion-pricetags"></div>',
priority: 50
}); });

But what if we need to inject HTML on a specific view, which might not be available when the page is loaded? How can we know if the user accessed this specific view, so we can modify it?

We can add a page script that will be executed on a specified route. So, for example, this script will be executed every time the user visits the page "/dashboard#/analytics/sessions"

app.addPageScript("/analytics/sessions", function(){
   //You can perform any dom manipulations here
   alert("You are viewing Sessions Analytics");
});

But what if we need to do something on every page view? Easy, we use # as a page script route.

app.addPageScript("#", function(){
   //You can perform any dom manipulations here
   console.log("new page view loaded");
});

How about dynamic URLs that you don't know upfront? Easy!

// All pages which URL starts with '/users/' and follows some string, for example: 
// /users/c49ebdad8f39519af9e0bfbf79332f4ec50b6d0f
app.addPageScript("/users/#", function(){
   console.log("new user profile view loaded");
});

Finally, we have added our new metric to Countly installation and additionally to API part example, we can share the plugin with everybody else.

Now we only need to modify your Countly SDK installation to report our custom metrics to our server and we are done.

Looking for help?