Angularjs custom Plug-ins to implement a site user Boot function Example _angularjs

Source: Internet
Author: User
Tags bind button type constant extend


The example of this article describes the Angularjs custom plug-in to implement the website user boot function. Share to everyone for your reference, specific as follows:



Recently because the project has a larger revision, in order to allow users to adapt to the new revision, so the introduction of the system "user-led" function for the first time to enter the system users some simple use of training training. This is a very common feature for most Web sites. So before developing this task, bloggers tried to abstract it, separate it from the business logic of the existing system and encapsulate it as a general-purpose plug-in, making it easier to extend and maintain the code.



No picture, no truth, first figure:






The use of this trainning plug-in is simple, it takes the same configuration as angular routing, simply configure each step training information.


title: The heading information of step;
Template/templateurl:The content template information for step. This class can configure HTML elements, or the URL address of the template, while Templateurl also supports the same function syntax as angular route;
Controller:The controller configuration of step, the following parameters can be injected into the controller: the current step–currentstep, the configuration of all the steps –trainnings, the configuration of the current step –currenttrainning, and the next action callback- Trainninginstance (where NeXTSTEP: For the next callback, Cancel to cancel the user boot callback);
Controlleras:The alias of the Controller;
Resolve:The data configuration before controller initialization, and the resolve in the same angular routing;
Locals:Local variables, similar to resolve, can be passed into the controller. The difference is that it does not support function calls and is more convenient for constant writing than for resolve;
Placement:The display orientation of the triangular arrows on the step container,
Position:The concrete display position of the step container, which is an absolute coordinate, the absolute coordinates of {left:100, top:100} can be passed, or the placement position of the #steppanelhost configuration relative to this element. It also supports custom function and other component syntax for injecting angular. And the default can be injected: All step configuration –trainnings, the current steps –step, the configuration of the current –currenttrainning, and the stage container node –steppanel;
backdrop:Whether the mask layer needs to be displayed and, by default, the matte layer is not displayed unless the display is declared as false;
Stepclass:The style information of each step container;
Backdropclass:The style information for each mask layer.


After understanding these configurations and customizing the entire user-booted configuration information based on specific requirements, we can use the Trainningservice trainning method to launch the user boot in a specific practical way, passing in the parameters for each step of the configuration information. and may register its done or cancel event:


Trainningservice.trainning (trainningcourses.courses)
. Done (function () {
 Vm.isdone = true;
});


Here is a demo of the configuration information:


. Constant (' trainningcourses ', {
  courses: [{
   title: ' Step 1: ',
   templateurl: ' trainning-content.html ',
   controller: ' Steppanelcontroller ',
   controlleras: ' Steppanel ',
   placement: ' Left ',
   position: ' # BlogControl '
  },{
   title: ' Step 3: ',
   templateurl: ' trainning-content.html ',
   controller: ' Steppanelcontroller ',
   controlleras: ' Steppanel ',
   placement: ' Top ',
   position: {
    top:200,
    left:100
   }
  },
   ...
  {
   stepclass: ' Last-step ',
   backdropclass: ' Last-backdrop ',
   templateurl: ' Trainning-content-done.html ',
   controller: ' Steppanelcontroller ',
   controlleras: ' Steppanel ',
   Position: [' $window ', ' Steppanel ', function ($window, steppanel) {
    //custom functions to center its screen
    var win = angular.element ($ window);
    return {top
     : (Win.height ()-steppanel.height ())/2, left
     : (Win.width ()-steppanel.width ())/2
    }
   }]
  }]
})


This article plug-in source code and demo effect only Codepen, the effect chart is as follows:






In the Trainning plug-in source design, contains the following points:



Provides service APIs. Because about trainning this plug-in, it is a global plug-in, just in angular all the service is also a single example, so the user guidance logic encapsulated into the angular service is a good design. However, for each step of trainning, the content information is DOM operation, in angular processing it should not exist in the service, the best way is to encapsulate him in the directive. So here the definition of directive, and in the service compile, and then append into the body.



Each such standalone plug-in should encapsulate a separate scope that facilitates subsequent destruction and does not conflict with existing scope variables.



$q Wrap the result of the delay trigger. It is a good choice to use promise encapsulation for such operations as the Trainning plug-in or modal. It replaces the complexity of callback parameters and is presented in a fluent API, which is more readable to the code. It also can be integrated with other angular service to return the API.



For controller, Controlleras, resolve, template, Templateurl, such as routing of the processing code, can be moved to your similar plug-ins. They can add more customization extensions to plug-ins. On this part of the code explanation, the blogger will be in the follow-up article for everyone to push.



Dynamic injection and invocation of angular service with $injector.invoke can not only obtain the extensibility of angular other service injection, but also obtain the dynamic of function. As shown in the previous example, the custom extension method is centered on the screen.



Such design essentials can also be applied to global plug-ins such as modal, alert, and overload. Interested readers, you can read this code Http://codepen.io/greengerong/pen/pjwXQW in the blogger's Codepen notes.



The above code extracts are as follows:



Html:


<div ng-app="com.github.greengerong" ng-controller="DemoController as demo">
 <div class="alert alert-success fade in" ng-if='demo.isDone'>
  <strong>All trainning setps done!</strong>
 </div>
 <button id="startAgain" class="btn btn-primary start-again" ng-click="demo.trainning()">You can start trainning again</button>
 <div class="blog">
  <form class="form-inline">
   <div class="form-group">
    <label class="sr-only" for="exampleInputAmount">Blog :</label>
    <div class="input-group">
     <input id="blogControl" type="text" class="form-control" />
    </div>
   </div>
   <button id="submitBlog" class="btn btn-primary" ng-click="demo.backdrop()">Public blog</button>
  </form>
 </div>
 <script type="text/ng-template" id="modal-backdrop.html">
  <div class="modal-backdrop fade in {{backdropClass}}" ng-style="{'z-index': zIndex || 1040}"></div>
 </script>
 <script type="text/ng-template" id="trainning-step.html">
  <div class="trainning-step">
   <div style="display:block; z-index:1080;left:-1000px;top:-1000px;" ng-style="positionStyle" class="step-panel {{currentTrainning.placement}} fade popover in {{currentTrainning.stepClass}}" ng-show="!isProgressing">
    <div class="arrow"></div>
    <div class="popover-inner">
     <h3 class="popover-title" ng-if='currentTrainning.title'>{{currentTrainning.title}}</h3>
     <div class="popover-content">
     </div>
    </div>
   </div>
   <ui-backdrop backdrop-class="currentTrainning.backdropClass" ng-if="currentTrainning.backdrop !== false"></ui-backdrop>
  </div>
 </script>
 <script type="text/ng-template" id="trainning-content.html">
  <div class="step-content">
   <div>{{ stepPanel.texts[stepPanel.currentStep - 1]}}</div>
   <div class="next-step">
    <ul class="step-progressing">
    <li data-ng-repeat="item in stepPanel.trainnings.length | range"
     data-ng-class="{active: stepPanel.currentStep == item}">
    </li>
   </ul>
    <button type="button" class="btn btn-link btn-next pull-right" ng-click="stepPanel.trainningInstance.nextStep({$event:$event, step:step});">Next</button>
   </div>
  </div>
 </script>
 <script type="text/ng-template" id="trainning-content-done.html">
  <div class="step-content">
    <div>
 {{ stepPanel.texts[stepPanel.currentStep - 1]}}
   </div>
   <div class="next-step">
    <ul class="step-progressing">
    <li data-ng-repeat="item in stepPanel.trainnings.length | range"
     data-ng-class="{active: stepPanel.currentStep == item}">
    </li>
   </ul>
    <button type="button" class="btn btn-link pull-right" ng-click="nextStep({$event:$event, step:step});">Got it</button>
   </div>
  </div>
 </script>
</div>


Css:


. last-step{/
 * background-color:blue;*/
}
. last-backdrop{
 background-color: #FFFFFF
;
blog{
 Position:absolute;
 left:300px;
 top:100px
}
. start-again{
 Position:absolute;
 left:400px;
 top:250px
}
. Next-step {
 . step-progressing {
  margin:10px 0px;
  Display:inline-block;
  Li {
  margin-right:5px;
  border:1px solid #fff;
  Background-color: #6E6E6E;
  width:12px;
  height:12px;
  border-radius:50%;
  Display:inline-block;
  &.active {
   background-color: #0000FF;
  }
  }
 }



Js:


//Please set step content to fixed width when complex content or dynamic loading.
angular.module('com.github.greengerong.backdrop', [])
 .directive('uiBackdrop', ['$document', function($document) {
  return {
   restrict: 'EA',
   replace: true,
   templateUrl: 'modal-backdrop.html',
   scope: {
    backdropClass: '=',
    zIndex: '='
   }
   /* ,link: function(){
    $document.bind('keydown', function(evt){
     evt.preventDefault();
     evt.stopPropagation();
    });
    scope.$on('$destroy', function(){
     $document.unbind('keydown');
    });
    }*/
  };
 }])
 .service('modalBackdropService', ['$rootScope', '$compile', '$document', function($rootScope, $compile, $document) {
  var self = this;
  self.backdrop = function(backdropClass, zIndex) {
   var $backdrop = angular.element('<ui-backdrop></ui-backdrop>')
    .attr({
     'backdrop-class': 'backdropClass',
     'z-index': 'zIndex'
    });
   var backdropScope = $rootScope.$new(true);
   backdropScope.backdropClass = backdropClass;
   backdropScope.zIndex = zIndex;
   $document.find('body').append($compile($backdrop)(backdropScope));
   return function() {
    $backdrop.remove();
    backdropScope.$destroy();
   };
  };
 }]);
angular.module('com.github.greengerong.trainning', ['com.github.greengerong.backdrop', 'ui.bootstrap'])
 .directive('trainningStep', ['$timeout', '$http', '$templateCache', '$compile', '$position', '$injector', '$window', '$q', '$controller', function($timeout, $http, $templateCache, $compile, $position, $injector, $window, $q, $controller) {
  return {
   restrict: 'EA',
   replace: true,
   templateUrl: 'trainning-step.html',
   scope: {
    step: '=',
    trainnings: '=',
    nextStep: '&',
    cancel: '&'
   },
   link: function(stepPanelScope, elm) {
    var stepPanel = elm.find('.step-panel');
    stepPanelScope.$watch('step', function(step) {
     if (!step) {
      return;
     }
     stepPanelScope.currentTrainning = stepPanelScope.trainnings[stepPanelScope.step - 1];
     var contentScope = stepPanelScope.$new(false);
     loadStepContent(contentScope, {
      'currentStep': stepPanelScope.step,
      'trainnings': stepPanelScope.trainnings,
      'currentTrainning': stepPanelScope.currentTrainning,
      'trainningInstance': {
       'nextStep': stepPanelScope.nextStep,
       'cancel': stepPanelScope.cancel
      }
     }).then(function(tplAndVars) {
      elm.find('.popover-content').html($compile(tplAndVars[0])(contentScope));
     }).then(function() {
      var pos = stepPanelScope.currentTrainning.position;
      adjustPosition(stepPanelScope, stepPanel, pos);
     });
    });
    angular.element($window).bind('resize', function() {
     adjustPosition(stepPanelScope, stepPanel, stepPanelScope.currentTrainning.position);
    });
    stepPanelScope.$on('$destroy', function() {
     angular.element($window).unbind('resize');
    });
    function getPositionOnElement(stepScope, setpPos) {
     return $position.positionElements(angular.element(setpPos), stepPanel, stepScope.currentTrainning.placement, true);
    }
    function positionOnElement(stepScope, setpPos) {
     var targetPos = angular.isString(setpPos) ? getPositionOnElement(stepScope, setpPos) : setpPos;
     var positionStyle = stepScope.currentTrainning || {};
     positionStyle.top = targetPos.top + 'px';
     positionStyle.left = targetPos.left + 'px';
     stepScope.positionStyle = positionStyle;
    }
    function adjustPosition(stepScope, stepPanel, pos) {
     if (!pos) {
      return;
     }
     var setpPos = angular.isFunction(pos) || angular.isArray(pos) ? $injector.invoke(pos, null, {
      trainnings: stepScope.trainnings,
      step: stepScope.setp,
      currentTrainning: stepScope.currentTrainning,
      stepPanel: stepPanel
     }) : pos;
     //get postion should wait for content setup
     $timeout(function() {
      positionOnElement(stepScope, setpPos);
     });
    }
    function loadStepContent(contentScope, ctrlLocals) {
     var trainningOptions = contentScope.currentTrainning,
      getTemplatePromise = function(options) {
       return options.template ? $q.when(options.template) :
        $http.get(angular.isFunction(options.templateUrl) ? (options.templateUrl)() : options.templateUrl, {
         cache: $templateCache
        }).then(function(result) {
         return result.data;
        });
      },
      getResolvePromises = function(resolves) {
       var promisesArr = [];
       angular.forEach(resolves, function(value) {
        if (angular.isFunction(value) || angular.isArray(value)) {
         promisesArr.push($q.when($injector.invoke(value)));
        }
       });
       return promisesArr;
      },
      controllerLoader = function(trainningOptions, trainningScope, ctrlLocals, tplAndVars) {
       var ctrlInstance;
       ctrlLocals = angular.extend({}, ctrlLocals || {}, trainningOptions.locals || {});
       var resolveIter = 1;
       if (trainningOptions.controller) {
        ctrlLocals.$scope = trainningScope;
        angular.forEach(trainningOptions.resolve, function(value, key) {
         ctrlLocals[key] = tplAndVars[resolveIter++];
        });
        ctrlInstance = $controller(trainningOptions.controller, ctrlLocals);
        if (trainningOptions.controllerAs) {
         trainningScope[trainningOptions.controllerAs] = ctrlInstance;
        }
       }
       return trainningScope;
      };
     var templateAndResolvePromise = $q.all([getTemplatePromise(trainningOptions)].concat(getResolvePromises(trainningOptions.resolve || {})));
     return templateAndResolvePromise.then(function resolveSuccess(tplAndVars) {
      controllerLoader(trainningOptions, contentScope, ctrlLocals, tplAndVars);
      return tplAndVars;
     });
    }
   }
  };
 }])
 .service('trainningService', ['$compile', '$rootScope', '$document', '$q', function($compile, $rootScope, $document, $q) {
  var self = this;
  self.trainning = function(trainnings) {
   var trainningScope = $rootScope.$new(true),
    defer = $q.defer(),
    $stepElm = angular.element('<trainning-step></trainning-step>')
    .attr({
     'step': 'step',
     'trainnings': 'trainnings',
     'next-step': 'nextStep($event, step);',
     'cancel': 'cancel($event, step)'
    }),
    destroyTrainningPanel = function(){
     if (trainningScope) {
      $stepElm.remove();
      trainningScope.$destroy();
     }
    };
   trainningScope.cancel = function($event, step){
    defer.reject('cancel');
   };
   trainningScope.nextStep = function($event, step) {
    if (trainningScope.step === trainnings.length) {
     destroyTrainningPanel();
     return defer.resolve('done');
    }
    trainningScope.step++;
   };
   trainningScope.trainnings = trainnings;
   trainningScope.step = 1;
   $document.find('body').append($compile($stepElm)(trainningScope));
   trainningScope.$on('$locationChangeStart', destroyTrainningPanel);
   return {
    done: function(func) {
     defer.promise.then(func);
     return this;
    },
    cancel: function(func) {
     defer.promise.then(null, func);
     return this;
    }
   };
  };
 }]);
angular.module('com.github.greengerong', ['com.github.greengerong.trainning'])
.filter('range', [function () {
   return function (len) {
    return _.range(1, len + 1);
 };
 }])
 .controller('StepPanelController', ['currentStep', 'trainnings', 'trainningInstance', 'trainnings', function(currentStep, trainnings, trainningInstance, trainnings) {
  var vm = this;
  vm.currentStep = currentStep;
  vm.trainningInstance = trainningInstance;
  vm.trainnings = trainnings;
  vm.texts = ['Write your own sort blog.', 'Click button to public your blog.', 'View your blog info on there.', 'Click this button, you can restart this trainning when .', 'All trainnings done!'];
  return vm;
 }])
 .constant('trainningCourses', {
  courses: [{
   title: 'Step 1:',
   templateUrl: 'trainning-content.html',
   controller: 'StepPanelController',
   controllerAs: 'stepPanel',
   placement: 'left',
   position: '#blogControl'
  }, {
   title: 'Step 2:',
   templateUrl: 'trainning-content.html',
   controller: 'StepPanelController',
   controllerAs: 'stepPanel',
   placement: 'right',
   backdrop: false,
   position: '#submitBlog'
  }, {
   title: 'Step 3:',
   templateUrl: 'trainning-content.html',
   controller: 'StepPanelController',
   controllerAs: 'stepPanel',
   placement: 'top',
   position: {
    top: 200,
    left: 100
   }
  }, {
   title: 'Step 4:',
   templateUrl: 'trainning-content.html',
   controller: 'StepPanelController',
   controllerAs: 'stepPanel',
   placement: 'bottom',
   position: '#startAgain'
  }, {
   stepClass: 'last-step',
   backdropClass: 'last-backdrop',
   templateUrl: 'trainning-content-done.html',
   controller: 'StepPanelController',
   controllerAs: 'stepPanel',
   position: ['$window', 'stepPanel', function($window, stepPanel) {
    var win = angular.element($window);
    return {
     top: (win.height() - stepPanel.height()) / 2,
     left: (win.width() - stepPanel.width()) / 2
    }
   }]
  }]
 })
 .controller('DemoController', ['trainningService', 'trainningCourses', 'modalBackdropService', function(trainningService, trainningCourses, modalBackdropService) {
  var vm = this;
  vm.trainning = function() {
   //call this service should wait your really document ready event.
   trainningService.trainning(trainningCourses.courses)
    .done(function() {
     vm.isDone = true;
    });
  };
  var backdropInstance = angular.noop;
  vm.backdrop = function() {
   modalBackdropService.backdrop();
  };
  vm.trainning();
  return vm;
 }]);


I hope this article will help you with ANGULARJS programming.


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.