(1) Creating a simple custom widget

Overview

The first chapter describes the necessary steps to create and configure a simple custom widget. It also gives a first insight into the work with AngularJS and data binding.

This chapter is recommended for everyone new to custom widgets and AngularJS.

Widget Preview

The first custom widget “Demo Widget 1” is supposed to display a simple text based view with a message – "Hello world!" 
The result will look like this:

Resulting files

"Demo Widget 1" consists of the following folders and files:

\---customWidgets
     \---demoWidget1
          |     demoWidget1Config.js
          |     demoWidget1Module.js
          | 
          +---assets
          |     +---css
          |     |        demoWidget1Styles.css
          |     | 
          |     \---images
          |              demoWidget1MenuIcon_32x32.png
          | 
          +---js (optional)
          |        demoWidget1Ctrl.js (optional)
          | 
          \---partials
                   demoWidget1.html

Explanation: This is the recommended file structure for a custom widget, that will be created in this chapter. Each custom widget has to have a module file, a config file and folders for assets, Javascript files, and partials.
Hint: The created files and source code accompanying this article can be downloaded from here.


Creation steps

Step 1: Create the main folder for the new custom widget

Create a folder demoWidget1 in the root folder "custom widgets". This is the main folder for all files of the new custom widget.
Hint: For further explanations on the custom widget root folder, see: Where are custom widgets located at?

Step 2: Create the custom widget menu icon

Create a folder assets and sub-folder images in the main folder of the new custom widget. 
Create a 32x32 px png-image file demoWidget1MenuIcon_32x32.png inside the folder images.

Explanation: The menu icon will appear in the widget selection control of the dashboard.

Step 3: Create the custom widget view

Create a folder partials in the main folder of the new custom widget.

Open a new file and save it as demoWidget1.html. Enter the following code:

demoWidget1.html
<!--This is the template for the main widget-->
<div class="demoWidget1">
    <h3>Hello World!</h3>
</div>

Explanation: This file represents the custom widget user interface. It will be also mentioned as "view" or "view template" in the further course.

Step 4: Create the custom widget module

MashZone NEXTGEN is based on AngularJS. Each AngularJS app has to have a module to specify the parts of the application and how they are working together. This is also true for the custom widget framework and each custom widget has to add its AngularJS components, for example controllers, services, filters or directives, to its module.
In order to create a module, please open a new file in the main folder of the custom widget and save it as demoWidget1Module.js. Enter the following code:

demoWidget1Module.js
angular.module('demoWidget1Module', ['prestoDashboard.dashboardProvider']);

Explanation: The custom widget module has the name "demoWidget1Module". For further explanations on custom widget modules see: What is a Module?

Step 5: Create the custom widget configuration

The configuration file contains all the configurable properties of a custom widget. For example the the title, the view template or the menu icon.
In order to be able to configure the custom widget, please create a file demoWidget1Config.js in the main folder of the custom widget and add the following content:

demoWidget1Config.js
angular.module('demoWidget1Module')
.config([<span style="color:#008000;">'dashboardProviderProvider'</span>,
   <span style="color:#FF0000;"> function</span> (dashboardProviderProvider) {
        dashboardProviderProvider.widget(<span style="color:#008000;">'demoWidget1'</span>, {
            title: <span style="color:#008000;">'Demo Widget 1'</span>,
            category:<span style="color:#008000;"> 'customWidget'</span>,
            description:<span style="color:#008000;"> 'Demo Widget 1'</span>,
            templateUrl: <span style="color:#008000;">'widgets/customWidgets/demoWidget1/partials/demoWidget1.html'</span>,
            iconUrl: <span style="color:#008000;">'widgets/customWidgets/demoWidget1/assets/images/demoWidget1MenuIcon_32x32.png'</span>,
            container: {
                hideHeader: <span style="color:#FF0000;">false</span>,
                hideBorder: <span style="color:#FF0000;">false</span>,
                width: 200,
                height: 150,
                contentMinHeight: 60,
                contentMinWidth: 60
            }
        });
}]);</code></pre>

Explanation: In line #6 the custom widget title is defined, followed by category and description. In line #9 the templateUrl is set. This URL points to the custom widget view. In line #10 the URL for the just created menu icon is configured. The container property starting in line #11 configures the widget container properties, like width and height.
For further explanations on the custom widget configuration, see: How to configure a custom widget?

Step 6: Do some view styling by adding CSS


Following the implementation up until this point, creates a custom widget looking like that:

Adding some CSS makes it look like this:

Therefore please create a sub-folder in the assets folder called CSS.
Create a new file and save it as demoWidget1Styles.css. Add the following content to change the widget look as shown above:

demoWidget1Styles.css
.panel-dashboard-default[widget-type='demoWidget1'] h3{
    text-decoration: underline;
}
.demoWidget1 {
    overflow: auto;
    text-align: center;
    height: 100%;
}

Hint: For further explanations on custom widget styling, see: How to style a custom widget?

Step 7: Activate the just created custom widget

Now it is time to get things working. Therefore please follow the explanations on: How can custom widgets be activated?


Summary and sources


At the end of this chapter you have created a simple custom widget, showing static text.
The created files and source code for the simple custom widget with static content can be downloaded from here.
 
Appendix 1-3 will give an introduction to the usage of AngularJS and data binding in custom widgets. The widget will be extended further to deal with dynamic content.
Anybody familiar with AngularJS can stop reading here and continue directly with Chapter 2 - Setting up data assignment.
The created files and source code for the simple custom widget with dynamic content can be downloaded from here.


Appendix 1: AngularJS Basics - Adding dynamic content to the view

As of now, the content of the widget is displaying just static content. In order to work with dynamic content, the HTML needs to be enriched with placeholders or custom HTML tags displaying the data coming from a data model. With the help of AngularJS, this can be done quite easily. AngularJS follows the Model View Controller (MVC) pattern and so does every custom widget. As a result, all custom widgets are divided into three parts.The data-model maintaining the data. The view displaying the data to the user. And the controller managing the interactions between the data-model and the view. The view has been created in step #3 already. Now let's implement the controller.
Create a folder js in the main folder of the new custom widget. Create the file demoWidget1Ctrl.js under the folder and add the following content:

demoWidget1Ctrl.js
angular.module('demoWidget1Module')
    .controller('demoWidget1Ctrl',['$scope',
        function($scope){
            $scope.message = "Hello controller!";
        }
    ])

Explanation: Line #1 names the custom widget module "demoWidget1Module" to which the controller will be added. For further explanations see: What is a Module?

Line #2 creates the controller "demoWidget1Ctrl" and injects it with the dependency '$scope'. In other words, the '$scope' is said to be required by the controller and will be automatically made available by AngularJS. The '$scope' is a JavaScript object and can be seen as the owner of the widget properties and functions. These properties and functions are defined and added to the '$scope' in the controller.
In line #4 the widget property "message" is defined and added to the scope object. The property is also called a "scope variable". The value of the scope variable is set to "Hello controller!".
 
The scope object and all defined widget properties and functions are accessible by the view as well. Therefore a special HTML element must be added to the view.
Edit the file demoWidget1.html, remove the static text and add the new element <span ng-bind="message"/> as in the example below. 

demoWidget1.html
<div class="demoWidget1">
    <h3><span ng-bind="message"/></h3>
</div>

Explanation: In line #2 the <span> element was enriched by an attribute "ng-bind". This attribute is called an AngularJS directive. Directives add extra functionalities to HTML elements. Is this case the HTML element <span> can access the scope variable "message" and will display the string "Hello Controller!", that has been defined in the controller.
But, if we look at the custom widget now, it is not showing any text at all:

Why is the data not showing up?
In the last step, it is necessary to activate the data binding between model, view, and controller. Therefore the controller needs to be added to the custom widget configuration file!
Please edit the file demoWidget1Config.js and add line #20 from the example code below:

demoWidget1Config.js
angular.module('demoWidget1Module')
    .config(['dashboardProviderProvider',
        function (dashboardProviderProvider) {
            dashboardProviderProvider.widget('demoWidget1', {
                title: 'Demo Widget 1',
                category: 'customWidget',
                toolTip : {
                    "de":"Demo Widget 1",
                    "en":"Demo Widget 1"
                },
                actions: ['editName',
                    'hLine',
                    'copy', 'paste', 'cut', 'delete',
                    'toTop', 'bringForward', 'sendBackward', 'toBack',
                    'hideHeader','hideBorder'],
                description: 'Demo Widget 1',
                templateUrl: 'widgets/customWidgets/demoWidget1/partials/demoWidget1.html',
                iconUrl: 'widgets/customWidgets/demoWidget1/assets/images/demoWidget1MenuIcon_32x32.png',
            controller: <span style="color:#008000;">'demoWidget1Ctrl'</span>,
             
            container: {
                hideHeader: <span style="color:#B22222;">false</span>,
                hideBorder: <span style="color:#B22222;">false</span>,
                width: 200,
                height: 150,
                contentMinHeight: 60,
                contentMinWidth: 60
            }
        });
    }]);</code></pre>

Explanation: In line #20 the custom widget main controller was defined.
The custom widget works as expected now and shows dynamic content - the defined message. 

Appendix 2: AngularJS Basics - Understanding two-way data binding

Showing dynamic content in the widget is nice, but often some user input or interaction with the UI is needed. This is realized by AngularJS data binding, an automatic way of updating the view whenever the data-model changes and vice versa. In the example below, this is shown for the message property. The initial value was set as "Hello controller!" in the controller (see Appendix 1), but is now to be changeable by the user, typing a message into an input field.
Therefore please edit the file demoWidget1.html, and add the lines #4 and #5 as shown in the example below:

demoWidget1.html
<div class="demoWidget1">
    <h3><span ng-bind="message"></span></h3>
Please enter a message:
&lt;span&gt;&lt;input type=<span style="color:#008000;">"text"</span> ng-model=<span style="color:#008000;">"message"</span>&gt;&lt;/span&gt;

</div>

Explanation: The custom widget can now interact with the user. Whatever is typed into the input field will be stored in the scope variable "message" and hence will be recognized and displayed in the heading part of the view, that is bound to the data model via ng-bind.

Due to the changes in the view template, the initial size of the custom widget needed to be changed as well. The container height was changed from 150 to 200 px. This was done in the custom widget configuration file (see step 5 - demoWidget1Config.js - line #15).

Appendix 3: AngularJS Basics - Adding Application Logic to the controller

The two-way data binding used in the above example keeps the data model and view synchronized. But in order to realize more complex use cases, some application logic needs to be added to the controller. The following example will add a clear function to the widget controller that sets the message property to an empty string and therefore clears the shown message in the view. In order to add this new functionality, the custom widget controller and view template need to be modified.
Therefore please edit the controller file demoWidget1Ctrl.js and add the lines #6-8 as shown in the example below:

demoWidget1Ctrl.js
angular.module('demoWidget1Module')
    .controller('demoWidget1Ctrl',['$scope',
        function($scope){
            $scope.message = "Hello controller!";
        $scope.clear =<span style="color:#B22222;"> function</span>() {
            $scope.message = "";
        }

    }
])</code></pre>

Explanation: The clear function was defined as a scope function and can be accessed from all parts of the widget that have access to $scope.
Additionally the view file demoWidget1.html must be adapted to add the new functionality to the user interface.
Therefore please edit the file demoWidget1.html and add line #6 as shown in the example below:

demoWidget1.html
<div class="demoWidget1">
    <h3><span ng-bind="message"></span></h3>
    Please enter a message:
    <span><input type="text" ng-model="message"></span>
&lt;span ng-click=<span style="color:#008000;">"clear()"</span> class=<span style="color:#008000;">"glyphicon glyphicon-remove"</span>&gt;&lt;/span&gt;

</div>

Explanation: This change will extend the view by a clickable icon that calls the scope function clear() on a mouse click. The logic for listening to the click action and performing the function call is again coming from an AngularJS directive: "ng-click".
As a result of the implementation changes the custom widget will now look like that:

    

More on AngularJS directives can be found here:

Read in this series:

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png