Wednesday, April 27, 2016

[AngularJS] Create directive for reusable elements and behavior

 AngularJS   Directive  


▌Introduction


What are directive?

Directives are markers on a DOM element (such as an attribute, element name, comment or CSS class) that tell AngularJS's HTML compiler ($compile) to attach a specified behavior to that DOM element (e.g. via event listeners), or even to transform the DOM element and its children. - from docs.angularjs.org



▌Background


There are two or more pages that contain the same elements and behavior on them. For example, the following 2 textboxes and checkbox appear in multiple pages, and we would like to make the HTML and javascript reusable.








▌Implement : Create reusable directive



▋HTML

<div class="form-horizontal" ng-app="app" ng-controller="CreateCtrl">
<div class="form-group">
    <label class="control-label col-md-2" for="EnabledOn">啟用日期</label>
    <div class="col-md-10">
        <input type="datetime" enabledon ng-model="EnabledOn"/>
    </div>
</div>

<div class="form-group">
    <label class="control-label col-md-2">停用日期</label>
    <div  class="col-md-10">
        <table width="100%">
            <tr>
                <td>
                    <input type="datetime" disabledon ng-disabled="disabledDisabledOn" ng-model="DisabledOn" />
                </td>
            </tr>
            <tr>
                <td>
                    <input type="checkbox" ng-model="Permanent" ng-checked="checkedPermanent" />&nbsp;永久有效
                </td>
            </tr>

        </table>
    </div>
</div>
</div>



▋AngularJS - Directive

l   directive-enabled-on.js

angular.module('enabledonapp', [])
.directive('enabledon', function () {
    return {
        scope: true,
        link: function ($scope) {

        },
        controller: function ($scope, $element) {
            //Initialize
            var date = new Date();
            $scope.EnabledOn = moment(date).format('YYYY/MM/DD');
        }
    }
});

l   directive-disable-on.js

angular.module('disabledonapp', [])
.directive('disabledon', function () {
    return {
        scope: true,
        link: function ($scope) {
           
            //當勾選/取消 永久有效 (Checkbox)
            $scope.$watch("Permanent", function (newValue, oldValue) {

                if (newValue == true) {
                    $scope.DisabledOn = '9999/12/31'
                    $scope.disabledDisabledOn = true;
                }
                else {
                    var date = new Date();
                    $scope.DisabledOn = moment(date).format('YYYY/MM/DD');
                    $scope.disabledDisabledOn = false;
                }
            });
        },
        controller: function($scope, $element) {
            //The watch can put here, it will execute before link function.

            $scope.checkedPermanent = false; //ng-check for Checkbox : 永久有效
            $scope.disabledDisabledOn = false; //ng-disable for Textbox : 停用日期

            //Initialize
            var date = new Date();
            $scope.DisabledOn = moment(date).format('YYYY/MM/DD');
        }
    }
});

Important!!
While creating directive, the parameters are defined as following.

scope
u  “false” : Use the parent scope.
u  “true” : Use the child scope inherit from the parent and which aren’t relevant to other directives.
u  “{}” : Isolated scope.
link
A function that contains the core logic of the directive.
controller
Controller for the directive.

For the difference between link and controller, you can take a look at this article on Stackflow.


▋AngularJS - Controller

var app =
angular.module('app', ['disabledonapp', 'enabledonapp'])
.controller('CreateCtrl', function ($scope, $http, $q) {

});



▌Implement : Create reusable HTML


Since we have the directive, it’s time to make the HTML reusable.
Fortunately, we can reach goal by Directive as well.

In the following sample, I will create only one directive for the input block which contain 2 textboxes and a checkbox.


▋HTML

l   Main Html

<div ng-app="app" ng-controller="EnabledDurationCtrl">
        <b>This is a test page for EnabledDuration directive</b>
        <hr />
        <div enabledduration>
        </div>
    </div>


l   EnabledDurationPartialView.html : reusable HTML (input block inside)

<div class="form-group">
    <label class="control-label col-md-2" for="EnabledOn">啟用日期</label>
    <div class="col-md-10">
        <input type="datetime" ng-model="EnabledOn"/>
    </div>
</div>

<div class="form-group">
    <label class="control-label col-md-2">停用日期</label>
    <div  class="col-md-10">
        <table width="100%">
            <tr>
                <td>
                    <input type="datetime" ng-disabled="disabledDisabledOn" ng-model="DisabledOn" />
                </td>
            </tr>
            <tr>
                <td>
                    <input type="checkbox" ng-model="Permanent" ng-checked="checkedPermanent" />&nbsp;永久有效
                </td>
            </tr>

        </table>
    </div>
</div>


▋AngularJS : Directive

angular.module('enableddurationapp', [])
.directive('enabledduration', function () {
    return {
        scope: true,
        templateUrl: '/HTML/EnabledDurationPartialView.html',
        link: function ($scope) {
           
            //當勾選/取消 永久有效 (Checkbox)
            $scope.$watch("Permanent", function (newValue, oldValue) {

                if (newValue == true) {
                    $scope.DisabledOn = '9999/12/31'
                    $scope.disabledDisabledOn = true;
                }
                else {
                    var date = new Date();
                    $scope.DisabledOn = moment(date).format('YYYY/MM/DD');
                    $scope.disabledDisabledOn = false;
                }
            });
        },
        controller: function($scope, $element) {
            //The watch can put here, it will execute before link function.

            $scope.checkedPermanent = false; //ng-check for Checkbox : 永久有效
            $scope.disabledDisabledOn = false; //ng-disable for Textbox : 停用日期

            //Initialize
            var date = new Date();
            $scope.EnabledOn = moment(date).format('YYYY/MM/DD');
            $scope.DisabledOn = moment(date).format('YYYY/MM/DD');
        }
    }
});

Here we use templateUrl to include the reusable HTML in the directive.

The following picture shows the result.





▌Reference


Wednesday, April 20, 2016

[AngularJS] $interval

 AngularJS   $interval  


▌Background


I was implementing a function like the pop-up message on Booking.com
Referring to the following picture, we can see the pushed message from server-side and the message disappear within 3 – 5 seconds.


In .NET application, we can use SignalR to make a good practice of server-side pushing messages.

For the pop-up messages on front-end, we can use AngularJS service : $interval to set an interval of the messages.






▌Environment

l   AngularJS 1.4.7



▌Implement



▋HTML

<div ng-app="app" ng-controller="HomeCtrl">
    <div class="div-left-bottom" ng-hide="IsHideDivBroadcast">
        <ul ng-repeat="item in BroadcastMsgs">
            <li>{{item.User}}&nbsp;{{item.Message}}</li>
        </ul>
    </div>
</div>


▋CSS

.div-left-bottom {
width:150px;
float:left;
position:fixed;
left:0px;
bottom:0px;
z-index:20;
background-color:lightgrey;
border:2px solid #69c;
_position:absolute; /* position fixed for IE6 */
}

.div-left-bottom li {
font-size:10px;
font-family:"Consolas";
}



▋Javascript

var app =
  angular.module('app', [])
  .controller('HomeCtrl', function ($scope, $interval) {

      //Variables
      $scope.BroadcastMsgs = []; //Broadcast messages
      var broadcastIntervalPromise; //The promise for broadcasting messages

      /* Starts the interval */
      $scope.startBroadcastInterval = function () {
          $scope.IsHideDivBroadcast = false;

          // stops any running interval to avoid two intervals running at the same time
          $scope.stopBroadcastInterval();
          // store the interval promise
          broadcastIntervalPromise = $interval(resetBroadcastMsgs, 1000 * 6);
      };
      /* Stops the interval */
      $scope.stopBroadcastInterval = function () {
          $interval.cancel(broadcastIntervalPromise);
      };

      /* Stop the interval when the scope is destroyed  */
      $scope.$on('$destroy', function () {
          $scope.stopBroadcastInterval();
      });

      /* Clear the braodcast messages*/
      function resetBroadcastMsgs() {
          $scope.BroadcastMsgs = [];
      }


      //Testing from here --------------------------------------
      //Start the interval
      $scope.startBroadcastInterval();


      $scope.BroadcastMsgs.push({
          "User": "JB",
          "Message": "reserved a room!"
      })
      $scope.BroadcastMsgs.push({
          "User": "Lily",
          "Message": "reserved 2 rooms!"
      })
      //Testing ends here --------------------------------------

  });

broadcastIntervalPromise = $interval(resetBroadcastMsgs, 1000 * 6)  :
===>  Set an 6 secs interval for triggering the function : resetBroadcastMsgs







▌Reference