This documentation supports the 20.08 version of BMC Helix Platform.  To view an earlier version, select 20.02 from the Product version menu.

Creating a simple view component


Another powerful capability of BMC Helix Platform is the ability to code a custom visual component that can be dragged into a View using View Designer, as described in Creating-a-custom-user-interface-with-AngularJS. A quick note here is not to confuse the AngularJS framework with another framework that is somewhat confusingly called just plain Angular, which is not supported at this time. Currently, BMC Helix Innovation Studio's View Designer supports Angular JS.

There is a very simple example of this in the Meal Program Library project, that you may have noticed appearing in the Palette of View Designer, after you deployed the Library and refreshed your BMC Helix Innovation Studio browser. It's only function is to take a parameter and format it into a heading (style H3) followed by the word "Restaurant:". Of course this example is very trivial and in fact you would simply use the Rich Text Component to do this, but using this component will help to understand how the code for any custom View Component works.

Overview

Let's check out how the View Component looks at design-time and run-time, after which we will study the code that implements it. Open the View Designer for the Restaurants View you created earlier. Note the Meal Program component called Restaurant Label. This comes from the custom code in the Meal Program Library.  Drag it into the View - a good spot would be just above the + New Dish Button. In the Property Inspector, use the Expression Editor dialog to bind the First Selected Row's Name field as its input parameter. 

Restaurant Label in View Designer.png

Now when you Preview this View (or refresh the Application) you will see the currently-selected Restaurant Name appearing as the content of the View Component.

Restaurant Label in Runtime.PNG

Let's examine the code, which resides within the meal-program-lib project - you can find it under the bundle\src\main\webapp folder. 

First we'll notice the overall structure. Like most View Components, the project has at least these source files. 

View Compoent Code Structure Diagram.PNG

As you will discover here, a View Component is really nothing more than a pair of AngularJS Directives with some additional declarations so it will work with the View Designer in BMC Helix Innovation Studio.  At any point during this explanation, remember that you can always reference documentation from the AngularJS website.

For this tutorial we will just note a few things about the sample code. If you are not very familiar with the AngularJS framework, this code may be difficult to understand.

The AngularJS Module

A great place to start is the AngularJS Module that is declared as a container for all of this code. Note that we name-space it with both the bundle-id and the component name; this will minimize conflicts with other JavaScript that may be deployed in the same space. We also declare dependencies on some  functionality in the standard library.

Hint: if you are working with retrieving Records and Associations some component of your own, you will need to add additional dependencies here.

restaurant-label-module.js
(function () {
   'use strict';
    angular.module('com.example.meal-program-lib.view-components.restaurant-label', [
       'com.bmc.arsys.rx.standardlib.security',
       'com.bmc.arsys.rx.standardlib.view-component'
    ]);
})();

The Meal Program Library already has an AngularJS Module that is generated by the Maven Archetype when this project was created. However, you are adding new code that must be compiled and loaded, so this built-in Module must have a dependency on your new Module. For Libraries, this is where all custom View Components need to be listed so they can be compiled and loaded correctly.

com.example.meal-program-lib.module.js
(function
() {
   'use strict';
    angular.module('com.example.meal-program-lib', [
       'ngSanitize',
'com.bmc.arsys.rx.standardlib.error-handling'
    ]);
})();

The BMC Helix Platform View Configuration

The configuration of the Directive is placed in restaurant-label.config.js. This is a key big of logic that binds together the component's Module with the various ways it is rendered (design-time and run-time), and declares the Properties of the component that will be exposed to the View Designer once it is deployed. Consider the source code:


restaurant-label.config.js
(function () {
   'use strict';
    angular.module('com.example.meal-program-lib.view-components.restaurant-label')
    .config(function (rxViewComponentProvider) {
        rxViewComponentProvider.registerComponent([
            {
                name: 'Restaurant Label',
        group: 'Meal Program',
                icon: 'area_text',
                type: 'com-example-meal-program-lib-restaurant-label.directive',  
                designType: 'com-example-meal-program-lib-restaurant-label-design',
                bundleId: 'com.example.meal-program-lib',
                propertiesByName: [
                   {
                      name: 'label',
                      type: 'string',
                      isConfig: true,
                      isProperty: true,
                      isRequired: true,
          enableExpressionEvaluation: true   
                   }
               ]
            }
        ]);
    });
})();

There are some important things to note here so we will cover them one by one:

  1. The component is registered with the rxViewComponentProvider resource, so it can show up in the Palette of View Designer.
  2. Basic attributes are declared here, such as:
    • name - text that appears in the Palette
    • group - will add to this section of the Palette or create a new section
    • icon - short name of a built-in icon from the Standard Library
    • type - this binds the component to a run-time rendering Directive, determined by converting the "hypenated" name to "camelCase". 

      In this case, the directive referred to here as com-example-meal-program-lib-restaurant-label.directive must be actually named comExampleMealProgramLibRestaurantLabel.  It will be declared, by convention, in the file restaurant-label.directive.js and we will be examining this code a bit later on. This transformation is required due to one of the quirks of AngularJS.
    • designType - similar to the "type" attribute, but refers to the design-time rendering Directive that will be used in the View Designer canvas itself. 

      Similarly to the type attribute, com-example-meal-program-lib-restaurant-label-design implies the actual Directive is named comExampleMealProgramLibRestaurantLabelDesign, and it will be placed by convention in the file restaurant-label-design.directive.js.
    • bundle-id is self-explanatory
  3. The propertiesByName attribute declares Properties (in this case, there is only one needed) that will be accessible to the View Designer. The attributes are used as follows:
    • name: how this will appear in the Property Inspector and/or Edit Expression dialog's Available Values tree.
    • type: this is the data type. You can plug in custom design-time experiences but string is the simplest one to use.
    • isConfig - because this is set to true, it will appear in the Property Inspector, so it's value can be bound to other components (such as the First Selected Row of a Record Grid).
    • isProperty - because this is set to true, it will appear in the Edit Expression dialog'Available Values tree, so it's value can be used by other components.
    • isRequired - the View Designer will not allow the value to not be set.
    • enableExpressionEvaluation - because this is set to true, the Edit Expression dialog can be used to build an expression for the value. Otherwise, it has to be a constant value set by the user of the View Designer.

Run-Time Rendering

Since we have seen that the config calls for rendering Directives let's look at the code for them. As mentioned above, the run-time rendering has to be done by comExampleMealProgramLibRestaurantLabel in the file restaurant-label.directive.js.

restaurant-label.directive.js
(function () {
   'use strict';
    angular.module('com.example.meal-program-lib.view-components.restaurant-label')
    .directive('comExampleMealProgramLibRestaurantLabel',

       function ($q,
                  $filter,
                  rxSession) {
           return {
                restrict: 'E',
                templateUrl: 'scripts/view-components/restaurant-label/com-example-meal-program-lib-restaurant-label.directive.html',

                scope: {
                    rxConfiguration: '='
                },

                link: function ($scope) {
                   var _config;

                   var init = function () {
                        _config = $scope.rxConfiguration.propertiesByName;
                        $scope.restaurantLabel = _config.label;
                    };
                    $scope.$watch('rxConfiguration.propertiesByName.label', init);
                }
            };
    });
})();

Much of this code is very standard for creating a Directive in AngularJS and won't be explained here. Some important points particular to BMC Helix Platform and this component are: 

  1. The camelCase Directive name of comExampleMealProgramLibRestaurantLabel was already explained above, as a transformation of com-example-meal-program-lib-restaurant-label.directive. It is not an aribtrary name, and it's worth noting again that this naming convention is crucial to avoiding conflicts with unknown JavaScript that may be deployed in the same environment. 
  2. The templateUrl attribute binds the html template that will be evaluated using the scope information - in this case the scope object restaurantLabel - that is maintained by this Directive's code. It needs to have the fully-qualified file name of com-example-meal-program-lib-restaurant-label.directive.html to avoid collisions because this resource will be compiled together with all View Components deployed to BMC Helix Platform.

    com-example-meal-program-lib-restaurant-label.html
    <div>
       <h3><b>Restaurant: {{restaurantLabel}}</h3>
    </div>
  3. The link function populates and maintains the scope. When the Directive is initialized it pulls the label expression from the _config, which is populated by the Standard Library through its binding to the run-time of the View. By assigning it to the scope's restaurantLabel, this enables the use of the expression {{restaurantLabel}} in the AngularJS template above, so it can render information coming from other components at run-time.
  4. Finally, because we did configure this component with enableExpressionEvaluation set to true, it is not enough to evaluate the expression just once at initialization time. We need to set up a listener so that there will be a call-back of the init() function whenever the expression on which it is based is re-evaluated, so that the component can be re-rendered. That is exactly the purpose of the $scope.$watch() call.

Design-Time Rendering

We have looked at all the code that configures and renders the component at run-time. However, recall that the config specified a different Directive would be used at design-time. This is needed for the restaurant-label component because it has dynamic data (namely, the label expression) which is only known at run-time. In fact, most components will have a different design-time rendering than that which is used at run-time.

The design-time Directive is pretty simple, especially since it is static content. Although you can get very fance with the design-time rendering, we are simply going to emit the label "Restaurant:". Remember that the name must be this particular name in camelCase, comExampleMealProgramLibRestaurantLabelDesign, in order to be mapped back to the hyphenated name com-example-meal-program-lib-restaurant-label-design in the config.

restaurant-label-design.directive.js
(function () {
   'use strict';
    angular.module('com.example.meal-program-lib.view-components.restaurant-label')
    .directive('comExampleMealProgramLibRestaurantLabelDesign', function () {

       return {
            restrict: 'E',
            templateUrl: 'scripts/view-components/restaurant-label/com-example-meal-program-lib-restaurant-label-design.directive.html',

            scope: {
                rxConfiguration: '='
            }
        };
    });
})();

There is not much else to note here, other than the html template snippet, which does not contain any references to the scope or anything else that is dynamic.

com-example-meal-program-lib-restaurant-label-design.directive.html
<h3>Restaurant:</h3>

Challenge

  • Modify the design-time and run-time templates. Rebuild and deploy the project using the following command, refresh the browser, and try out your changes.
Updating the Meal Program Library
projects\meal-program-lib> mvn clean install -Pexport -Pdeploy
  • If you have some experience with building JavaScript with AngularJS, and some time on your hands, this project is completely open for you to change any aspect of this custom code. Create your own custom component for Meal Program Library and deploy and test it.  As always, to learn more, please consult the documentation; this particular topic is found at Creating a custom UI with AngularJS, and more information about the framework is available in many places. A good starting place is from the AngularJS website.

  • Create some useful, common component in a brand-new library that you build using the Maven Archetype, as described in Creating a project using Maven and the Archetype.

What Have you Learned

  • Custom View Components are created using standard AngularJS technology. This is not the same framework as the one known simply as Angular.
  • Certain naming conventions are important for scoping.
  • Registration of your Module is required.
  • You can create custom design-time as well as run-time experiences in your own code, and users can bind values from other components in the View by declaring Properties.

 

Tip: For faster searching, add an asterisk to the end of your partial query. Example: cert*