Explore angularjs + requirejs to fully implement on-demand loading routines, angularjsrequirejs

Source: Internet
Author: User

Explore angularjs + requirejs to fully implement on-demand loading routines, angularjsrequirejs

When performing a project of a certain scale, you usually want to achieve the following goals: 1. Support Complex page logic (dynamically display content according to business rules, such as permissions and data status ); 2. adhere to the basic principles of frontend and backend separation (when there is no separation, pages can be directly generated using the template engine on the backend ); 3. The page loading time is short (the third-party library must be referenced when the business logic is complex, but it is likely that the loaded library does not matter to the user's operation); 4, code maintenance is also required (when new logic is added, as few files are affected as possible ).

To achieve these goals at the same time, you must have a set of On-Demand Loading mechanisms. The content displayed on the page and all files to be dependent on can be loaded as needed according to the business logic. Recently, it has been developed based on angularjs. Therefore, this article mainly focuses on the various mechanisms provided by angularjs and explores the full implementation of on-demand loading routines.

1. Step by step
Basic Ideas: 1. first develop a framework page, which can complete some basic business logic and support the Extended Mechanism; 2. the business logic becomes complex, part of the logic needs to be split into sub-pages, and the sub-pages are loaded as needed; 3. The presentation content in the sub-pages has also changed to complicated, and needs to be split and loaded as needed; 4. The content of the subpage is complex to the external module, and the angular module needs to be loaded as needed.

1. Framework page
When it comes to front-end loading on demand, we will think of AMD (Asynchronous Module Definition). Now there are a lot of requirejs applications, so we should first consider introducing requires.

Index.html

<script src="static/js/require.js" defer async data-main="/test/lazyspa/spa-loader.js"></script>

Note: The method of manually starting angular does not exist in html.

Spa-loader.js

require.config({  paths: {    "domReady": '/static/js/domReady',    "angular": "//cdn.bootcss.com/angular.js/1.4.8/angular.min",    "angular-route": "//cdn.bootcss.com/angular.js/1.4.8/angular-route.min",  },  shim: {    "angular": {      exports: "angular"    },    "angular-route": {      deps: ["angular"]    },  },  deps: ['/test/lazyspa/spa.js'],  urlArgs: "bust=" + (new Date()).getTime()});

Spa. js

Define (["require", "angular", "angular-route"], function (require, angular) {var app = angular. module ('app', ['ngroup']); require (['domainready! '], Function (document) {angular. bootstrap (document, ["app"]);/* manually start angular */window. loading. finish ();});});

2. Load child pages as needed
Angular routeProvider + ng-view provides a complete method for loading sub-pages.
Note that you must set html5Mode. Otherwise, routeProvider will not intercept the url after it changes.

Index.html

<div>  <a href="/test/lazyspa/page1">page1</a>  <a href="/test/lazyspa/page2">page2</a>  <a href="/test/lazyspa/">main</a></div><div ng-view></div>

Spa. js

App. config (['$ locationProvider', '$ routeProvider', function ($ locationProvider, $ routeProvider) {/* must be set to take effect, otherwise, the following settings do not take effect */Listen 5 Mode (true);/* load content based on url changes */$ routeProvider. when ('/test/lazyspa/page1', {template: '<div> page1 </div> ',}). when ('/test/lazyspa/page2', {template:' <div> page2 </div> ',}). otherwise ({template: '<div> main </div>',}) ;}]);

3. Load content in the subpage as needed
The premise of using routeProvider is that the url needs to change, but sometimes only the part of the child page needs to change. If these changes are mainly related to the bound data and do not affect the page layout, or the impact is very small, the use of ng-if tags is basically solved. However, sometimes partial content must be completely changed based on the page status, such as the changes that may occur before and after logon. This means that the local layout may be quite complex, it must be treated as an independent unit.

Using ng-include can solve the problem of loading partial page content. However, we can consider more complex situations. The code corresponding to this page fragment is dynamically generated at the backend, not only html but also js. js defines the controller corresponding to the code fragment. In this case, we should not only consider the dynamic loading of html, but also the dynamic definition of controller. Controller is registered through the register Method of angular controllerProvider. Therefore, you need to obtain the instance of controllerProvider.

Spa. js

App. config (['$ locationProvider', '$ routeProvider', '$ controllerProvider', function ($ locationProvider, $ routeProvider, $ controllerProvider) {app. providers = {$ controllerProvider: $ controllerProvider // pay attention to this !!! };/* Must be set to take effect, otherwise the following settings will not take effect */Listen 5 Mode (true);/* load content according to url changes */$ routeProvider. when ('/test/lazyspa/page1 ',{/*!!! Introduce dynamic content to the page !!! */Template: '<div> page1 </div> <div ng-include = "\ 'page1.html \'"> </div> ', controller: 'ctrlpage1 '}). when ('/test/lazyspa/page2', {template:' <div> page2 </div> ',}). otherwise ({template: '<div> main </div>',}); app. controller ('ctrlpage1 ', [' $ scope ',' $ templateCache ', function ($ scope, $ templateCache) {/* with ng-include, dynamically Retrieve page content based on business logic *//*!!! Dynamic controller definition !!! */App. providers. $ controllerProvider. register ('ctrlpage1ls-dyna ', [' $ scope ', function ($ scope) {$ scope. openAlert = function () {alert ('page1 alert ') ;};}]);/*! Dynamically define page content !!! */Export templatecache.put('page1.html ',' <div ng-controller = "ctrlpage1ls-dyna"> <button ng-click = "openAlert () "> alert </button> </div> ') ;}]);

4. Dynamic Loading Module
There is a limitation in using the Loading Method of face-on-face page fragments, that is, various logics (js) need to be added to the startup module, which still limits the independent encapsulation of child page fragments. In particular, if the child page fragment needs to use a third-party module, and this module is not loaded in advance in the startup module, there is no way. Therefore, you must be able to dynamically load modules. Dynamic Loading of implementation modules is to extract the Loading modules during angular startup, and then process some special situations.

However, the Code in the article is actually problematic, that is, what is "$ injector? After studying angular source code injector. js, we can understand what is going on.

An application has two $ injector, providerInjector and instanceInjector. InvokeQueue and providerInjector, and instanceProvider for runBlocks. If $ injector is used incorrectly, the required service is found.

Dynamic Loading of module files in routeProvider.

Template: '<div ng-controller = "ctrlModule1"> <div> page2 </div> <button ng-click = "openDialog () "> open dialog </button> </div> ', resolve: {load: [' $ Q', function ($ q) {var defer = $ q. defer ();/* dynamically load angular module */require (['/test/lazyspa/module1.js'], function (loader) {loader. onload & loader. onload (function () {defer. resolve () ;}); return defer. promise;}]}

Dynamic Loading of angular Module

Angular. _ lazyLoadModule = function (moduleName) {var m = angular. module (moduleName); console. log ('register module: '+ moduleName);/* the injector of the application, which is not the same as the injector in config. It is instanceInject, and the returned result is through provider. $ get created instance */var $ injector = angular. element (document ). injector ();/* recursively load the dependent module */angular. forEach (m. requires, function (r) {angular. _ lazyLoadModule (r) ;});/* use the injector of the provider to run the controller and directive of the module. */angular. forEach (m. _ invokeQueue, function (invokeArgs) {try {var provider = providers. $ injector. get (invokeArgs [0]); provider [invokeArgs [1]. apply (provider, invokeArgs [2]);} catch (e) {console. error ('Load module invokeQueue failed: '+ e. message, invokeArgs) ;}});/* use the injector of the provider to run the config */angular of the module. forEach (m. _ configBlocks, function (invokeArgs) {try {providers. $ injector. invoke. apply (providers. $ injector, invokeArgs [2]);} catch (e) {console. error ('Load module configBlocks failed: '+ e. message, invokeArgs) ;}});/* use the injector of the application to run the run */angular module. forEach (m. _ runBlocks, function (fn) {$ injector. invoke (fn );});};

Definition Module
Module1.js

Define (["angular"], function (angular) {var onloads = []; var loadCss = function (url) {var link, head; link = document. createElement ('link'); link. href = url; link. rel = 'stylesheet '; head = document. querySelector ('head'); head. appendChild (link) ;}; loadCss ('// cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css ');/*!!! Dynamically define requirejs !!! */Require. config ({paths: {'ui-bootstrap-tpls ':' // cdn.bootcss.com/angular-ui-bootstrap/1.1.2/ui-bootstrap-tpls.min'}, shim: {"ui-bootstrap-tpls": {deps: ['angular '] }});/*! The third-party library must be referenced in the module to load the module that the module depends on !!! */Require (['ui-bootstrap-tpls '], function () {var m1 = angular. module ('lele1', ['ui. bootstrap ']); m1.config ([' $ controllerProvider ', function ($ controllerProvider) {console. log ('lele1-config in in');}]); m1.controller ('ctrlmodule1', ['$ scope', '$ uibmodal', function ($ scope, $ uibModal) {console. log ('lele1-ctrl in in ');/*!!! Open the angular ui dialog box !!! */Var dlg = '<div class = "modal-header">'; dlg + = '

Ii. complete code
Index.html

<!DOCTYPE html>

Spa-loader.js

Window. loading = {finish: function () {/* retain a method for processing after loading is completed. In my actual project, the animation loading ends here */}, load: function () {require. config ({paths: {"domReady": '/static/js/domReady', "angular": "// cdn.bootcss.com/angular.js/1.4.8/angular.min", "angular-route ": "// cdn.bootcss.com/angular.js/1.4.8/angular-route.min",}, shim: {"angular": {exports: "angular"}, "angular-route": {deps: ["angular"] },}, deps: ['/test/lazyspa/spa. js'], urlArgs: "bust =" + (new Date ()). getTime ()}) ;}}; window. loading. load ();

Spa. js

'Use strict '; define (["require", "angular", "angular-route"], function (require, angular) {var app = angular. module ('app', ['ngroup']);/* Delayed loading module */angular. _ lazyLoadModule = function (moduleName) {var m = angular. module (moduleName); console. log ('register module: '+ moduleName);/* the injector of the application, which is not the same as the injector in config. It is instanceInject, and the returned result is through provider. $ get created instance */var $ injector = angular. element (documen T ). injector ();/* recursively load the dependent module */angular. forEach (m. requires, function (r) {angular. _ lazyLoadModule (r) ;});/* use the injector of the provider to run the controller and directive of the module. */angular. forEach (m. _ invokeQueue, function (invokeArgs) {try {var provider = providers. $ injector. get (invokeArgs [0]); provider [invokeArgs [1]. apply (provider, invokeArgs [2]);} catch (e) {console. error ('Load module invokeQueue failed: '+ e. messa Ge, invokeArgs) ;}});/* use the injector of the provider to run the config */angular of the module. forEach (m. _ configBlocks, function (invokeArgs) {try {providers. $ injector. invoke. apply (providers. $ injector, invokeArgs [2]);} catch (e) {console. error ('Load module configBlocks failed: '+ e. message, invokeArgs) ;}});/* use the injector of the application to run the run */angular module. forEach (m. _ runBlocks, function (fn) {$ injector. invoke (fn) ;}) ;}; app. config ([' $ Injector ',' $ locationProvider ',' $ routeProvider ',' $ controllerProvider ', function ($ injector, $ locationProvider, $ routeProvider, $ controllerProvider) {/*** injector in config is not the same as the injector of the application. It is providerInjector, and the provider is obtained instead of the Instance created by the provider. * This injector cannot be obtained through angular, therefore, save the config file */app. providers = {$ injector: $ injector, $ controllerProvider: $ controllerProvider};/* must be set to take effect, otherwise the following settings do not take effect */ Required 5 Mode (true);/* load content based on url changes */$ routeProvider. when ('/test/lazyspa/page1', {template: '<div> page1 </div> <div ng-include = "\ 'page1.html \'"> </div> ', controller: 'ctrlpage1 '}). when ('/test/lazyspa/page2', {template: '<div ng-controller = "ctrlModule1"> <div> page2 </div> <button ng-click = "openDialog () "> open dialog </button> </div> ', resolve: {load: [' $ Q', function ($ q) {var Defer = $ q. defer ();/* dynamically load angular module */require (['/test/lazyspa/module1.js'], function (loader) {loader. onload & loader. onload (function () {defer. resolve () ;}); return defer. promise;}]}). otherwise ({template: '<div> main </div>',}) ;}]); app. controller ('ctrlmain', ['$ scope', '$ location', function ($ scope, $ location) {console. log ('main controller');/* automatically goes to the default view based on business logic */$ location. url ('/test /Lazyspa/page1 ');}]); app. controller ('ctrlpage1 ', [' $ scope ',' $ templateCache ', function ($ scope, $ templateCache) {/* with ng-include, dynamically Retrieve page content * // * dynamically define controller */app according to business logic. providers. $ controllerProvider. register ('ctrlpage1ls-dyna ', [' $ scope ', function ($ scope) {$ scope. openAlert = function () {alert ('page1 alert ') ;}}]);/* dynamically define page content */define templatecache.put('page1.html', '<div ng-controller = "ct Rlpage1ls-dyna "> <button ng-click =" openAlert () "> alert </button> </div> ') ;}]); require (['domready! '], Function (document) {angular. bootstrap (document, ["app"]) ;});

Module1.js

'use strict';define(["angular"], function(angular) {  var onloads = [];  var loadCss = function(url) {    var link, head;    link = document.createElement('link');    link.href = url;    link.rel = 'stylesheet';    head = document.querySelector('head');    head.appendChild(link);  };  loadCss('//cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css');  require.config({    paths: {      'ui-bootstrap-tpls': '//cdn.bootcss.com/angular-ui-bootstrap/1.1.2/ui-bootstrap-tpls.min'    },    shim: {      "ui-bootstrap-tpls": {        deps: ['angular']      }    }  });  require(['ui-bootstrap-tpls'], function() {    var m1 = angular.module('module1', ['ui.bootstrap']);    m1.config(['$controllerProvider', function($controllerProvider) {      console.log('module1 - config begin');    }]);    m1.controller('ctrlModule1', ['$scope', '$uibModal', function($scope, $uibModal) {      console.log('module1 - ctrl begin');      var dlg = '<div class="modal-header">';      dlg += '

The above is all the content of this article, hoping to help you learn.

Articles you may be interested in:
  • LazyLoad delayed loading (On-Demand Loading)
  • The Extjs4.1.x framework is built using the dynamic and On-Demand Loading of Application MVC modules.
  • Simulate the ready method in jQuery and implement loading css and js instance code as needed
  • On-Demand Loading of Treeview in Winform

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.