Categories for Front-end



A single data store called Redux

ReduxRedux is a data store container for JavaScript apps. I’m currently on a team, that’s building an application for the government in three months. We use React and Redux in the front-end. This combination works well for us. In this blog I’ll explain a little bit about what we have done so far and how we implemented Redux.

We’re building a Content Management System (CMS) for editors (users) to enter information and later disclose it through an API. In order to show and edit the data in a clear way for the users in the front-end, we want to have the display of the data (rendered UI components) linked to the data itself. The displayed data should be re-rendered when the data changes. Redux controlled React components come in handy for this!

Questions overview

We store the data retrieved from the server through asynchronous calls in the data store container of Redux. When the user uses the user interface by going to different pages, tabs or saving forms, a call is triggered to fetch the latest data from the server. This data (partially) overwrites the old data in the store and brings the store to a new state.. When data is changed in the store the parts (components) of the user interface that display the data are re-rendered.

Content block form

The actions of the user trigger (actually dispatch) Redux Actions. Responses from the server also dispatch Redux Actions. These Actions contain a type and a payload. The type specifies which kind of action is triggered. The payload contains the new data, which can be the response (body) of the server. This data needs to be put in the data store. This is done by a Reducer. The Reducer is responsible of changing the state of the store. Actually it takes (part of) the previous state and switches over the type of Action, manipulates the data from it accordingly and returns the new state. It uses the payload of the Action to create a new state. It can do anything more you want too, like sorting arrays or execute other functions on the state. This way you have a time line of the state and its changes due to Actions.

// actions.js
export const createResource = formData => ({
    type: 'CREATE_RESOURCE_REQUEST_ACTION',
    payload: formData
});

export const createResourceSuccess = response => ({
    type: 'CREATE_RESOURCE_RESPONSE_SUCCESS_ACTION',
    payload: response
});

export const createResourceFailed = err => ({
    type: 'CREATE_RESOURCE_RESPONSE_FAILED_ACTION',
    payload: err
});
// reducer.js
const initialState = {
    isFetching: false,
    createdResources: [],
};

export const reducer = (state = initialState, action) => {
    switch (action.type) {
        case 'CREATE_RESOURCE_REQUEST_ACTION':
            return {
                ...state, // copies the previous state
                isFetching: true // overwrites this field in the copied state
            };
        case 'CREATE_RESOURCE_RESPONSE_SUCCESS_ACTION':
            return {
                isFetching: false,
                createdResources: [...state.createdResources, action.payload]
            };
        default:
            return state;
    }
};

In a complex, big application all these can be split, for example by a feature. So you can make Actions, a Reducer and React components for a particular feature. This way you can easily order and maintain your application. There are also Redux DevTools and extensions for multiple browsers to look into the data store and debug while you code.

In our application we use Saga’s. When a user triggers an Action that needs to make a request to the server this is handled by a worker Saga. The worker Saga sends an asynchronous request to the server. Depending on the response of the server, i.e. a success or a failure, the Saga dispatches a new Action specifically for a success or failure response of the specific request. Then the Reducer catches this to handle the data from the response.

// saga.js
import { call, put, takeLatest } from 'redux-saga/effects'

export function* createResource(action) {
    const { formData } = action.payload;
    try {
        const response = yield call(axios.post, `https://amsterdam.luminis.eu/api/resources`, formData);
        yield put(actions.createResourceSuccess(response.data));
    } catch (e) {
        yield put(actions.createresourceFailed(e));
    }
}

export function* saga() {
    yield takeLatest('CREATE_RESOURCE_REQUEST_ACTION', createResource);
}

The great advantage of this is that all the data is easily accessible from a single data store. Also the data can be reused elsewhere in the user interface. For example, a user fills in a form field, then this data can be dynamically used to show data elsewhere (after calculations). All this is done automatically as long the React components are connected to the Redux store.

Preview form with questions and content blocks


Developing Kibana Plugins

For quite some time I’ve been meaning to rewrite an old Elasticsearch plugin to a new Kibana plugin. It’s quite different than you were used to. The Kibana plugins are quite new and were released in version 4.2.0. There are quite a few warnings on the Kibana Github issues regarding not having a public API yet or not making plugins at all. Essentially this means you need to keep up with the Kibana releases if you’d like to proceed anyway.

Starting

First you would need to set-up a Kibana development environment. In order to do this you can follow these steps: https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#development-environment-setup. I would recommend not to pick the master branch.

Kibana Plugin Yeoman Generator

Next up is generating the boilerplate for your Kibana plugin using the Kibana Plugin Yeoman Generator. When following the instructions of the readme please notice there is an instruction to put the Kibana development environment at the same folder level as your plugin named kibana.

What did you get?

After we generated the project there is in the root of the project a file called index.js listed below, which ties up the whole project together. In this file we see the configuration of a Kibana plugin. The uiExports object configures your frontend of the plugin. There are more variants other than ‘app’ that can be found here. The properties an app can have are listed in the source code here. The ‘main’ property lists the path to your angular code, which is in fact the public folder of your project.

import exampleRoute from './server/routes/example';

export default function (kibana) {
  return new kibana.Plugin({
    require: ['elasticsearch'],
    
    // Your frontend app
    uiExports: {
      app: {
        title: 'Example',
        description: 'An awesome Kibana plugin',
        main: 'plugins/example/app'
      }
    },

    config(Joi) {
      return Joi.object({
        enabled: Joi.boolean().default(true),
      }).default();
    },

    init(server, options) {
      // Add server routes and initalize the plugin here
      exampleRoute(server);
    }

  });
};

The latter part of the sample above contains configuration for a hapijs server. With this you’re able to write your custom backend API. More of this you can find in the server/routes folder.

The frontend

In the public/app.js file we can find two important parts of code. One of which is the part with uiRoutes. This object allows you to embed your routing within Kibana. The syntax of the routing is according angular’s ngRoute module. When you don’t need routing remove it.

uiRoutes.enable();
uiRoutes
    .when('/', {
        template,
        resolve: {
            currentTime($http) {
                return $http.get('../api/example/example').then(function (resp) {
                    return resp.data.time;
                });
            }
        }
    });

The latter part has the uiModules object which is in charge of generating dynamic modules on the fly and coupling them. You can see the uiModules.get() function as replacement for angular.module().

uiModules
    .get('app/example', [])
    .controller('exampleHelloWorld', function ($scope, $route, $interval) {
        $scope.title = 'Example';
        $scope.description = 'An awesome Kibana plugin';

        var currentTime = moment($route.current.locals.currentTime);
        $scope.currentTime = currentTime.format('HH:mm:ss');
        var unsubscribe = $interval(function () {
            $scope.currentTime = currentTime.add(1, 'second').format('HH:mm:ss');
        }, 1000);
        $scope.$watch('$destroy', unsubscribe);
    });

When writing the templates and styles you should keep in mind that Kibana uses Twitter Bootstrap. Another note is that in the previous file mentioned there is also a chrome object, which you can ignore it will be deprecated in Kibana 5.0.0. It was used to control the navbar.

Result

When all went well this should be the result.
Kibana Plugin Example Screenshot

Useful links

Source Sense
Source Timelion
Kibana styleguide
The plugin I’m converting
More information about plugins


Directives for graphs with c3js library

Since the beginning of this year I am working on an AngularJS library with directives to make it easier to create graphs using the excellent c3js library. I started with some basic graphs, but by now a lot has changed. There now is better documentation, more examples, more graphs and finally we now have support for events. Time to give an update.

Better documentation

There are now two things you can reach out for when in need for documentation. There is a documentation page containing a lot of samples, contact information, gettings started, etc. This page can be found here:

http://jettro.github.io/c3-angular-directive/

If you are more into source code, you can checkout the project from github and use grunt to run the sample. If you do not like grunt you can also copy the examples folder to a webserver of your choice and it should work as well.

# git clone https://github.com/jettro/c3-angular-directive.git
# cd c3-angular-directive
# grunt devserver

Point your browser to localhost:8888 and you should see the tutorial page.

Some new graphs

Below some examples of new graphs that are now available. Most op the options available through the c3js api are now also available through the directive.

Scatter

scatter-graph

Stacked bar

stackedbar-graph

Gauge

gauge-graph

Clickable Donut

Before I show the graph I want to make it clear that the clickable part can also be combined with all the other graphs. For I know a lot of you want this functionality I’ll give it a bit more attention. First below the source code in html:

<c3chart bindto-id="donut-plot1-chart">
  <chart-column column-id="Data 1"
              column-values="70"
              column-type="donut"/>
  <chart-column column-id="Data 2"
              column-values="35"
              column-type="donut"/>
  <chart-column column-id="Data 3"
              column-values="60"
              column-type="donut"/>
  <chart-donut title="Donut" width="60"/>
  <chart-events on-click-data="showClick(data)"/>
</c3chart>

Notice the last directive chart-events. In here we refer to a function called showClick. Be sure to give the name of the parameter to pass called data. This is the correct way of passing a callback function to a directive. The next code block shows the controller function.

graphApp.controller('GraphCtrl', function ($scope) {
  $scope.clicked = {};
  $scope.showClick = function(data) {
    $scope.clicked = data;
  }
});

Clickable-donut-graph

Next steps

Before making version 1.0 available there are still a few things to do. I want to make the library more flexible in relation to missing properties by providing a hook to add some configurations yourself. Now that we have events for the complete graph like mouseover and mouseout as well as events for the data points in the graph, I also want to make the events for the legenda available. Finally I want to have a look at making things like the columns dynamic as well and maybe provide a hook to redraw the graph.

If you have other ideas you would like to have included, feel free to create an issue in Github for the project.

https://github.com/jettro/c3-angular-directive/issues


c3js directives for AngularJS

Some time a go I started experimenting with c3js, a nice library around the very popular D3 graphics library. It is all about creating nice looking charts with lots of options. When integrating this in an AngularJS application it is logical to create some reusable directives. The next step is to make these directives easily available to integrate in other projects. Nowadays the easiest way to do this in front-end projects is to use Bower. During development GruntJS helps with some tasks you just need to do.

In this blog post I am going to introduce you to the library, show you how I used it in my elasticsearch plugin and show some of the GruntJS functionality that help me during development.

(more…)


Improve my AngularJS project with grunt

Some time a go I created issue 63 for my elasticsearch gui plugin. This issue was about improving the plugin with tools like grunt and bower. The plugin is an angularjs based library and it makes use of a number of other libraries like: d3, c3js, elasticsearch.js. In this blog post I am describing the steps I took to improve my web application using grunt and bower.

(more…)