Angular 2 Async Form Store on Device to Upload Later

Unmarried page apps demand the forepart-end developers to get better software engineers. CSS and HTML are not the biggest concern anymore, in fact, at that place is no longer just a single concern. The forepart-stop programmer needs to handle XHRs, application logic (models, views, controllers), performance, animations, styles, structure, SEO, and integration with external services. The result which emerges from all those combined is the User Experience (UX) which should always be prioritized.

AngularJS is a very powerful framework. Information technology is the third most starred repository on GitHub. Information technology is not difficult to start using, but the goals that information technology is intended to achieve demand comprehension. No longer tin AngularJS developers ignore memory consumption, because information technology volition not reset on navigation anymore. This is the vanguard of web development. Let's comprehend it!

Common AngularJS mistakes

Mutual Mistake #1: Accessing The Scope Through The DOM

At that place are a few optimization tweaks recommended for production. One of them is disabling debug info.

DebugInfoEnabled is a setting which defaults to true, and allows for telescopic admission through DOM nodes. If you want to try that through the JavaScript console, select a DOM element and admission its scope with:

          angular.element(document.body).scope()                  

Information technology can be useful even when not using jQuery with its CSS, merely should not be used outside of the console. The reason being that when $compileProvider.debugInfoEnabled is ready to imitation, calling .scope() on a DOM node will render undefined.

That is one of the few recommended options for production.

Please annotation that you can yet access the telescopic through the console, fifty-fifty when on product. Call angular.reloadWithDebugInfo() from the panel and the app will practise but that.

Common Fault #2: Not Having a Dot In In that location

You probably have read that if you were not having a dot in your ng-model, you were doing it incorrect. When information technology regards inheritance, that argument is oftentimes true. Scopes have a prototypal model of inheritance, typical to JavaScript, and nested scopes are mutual to AngularJS. Many directives create child scopes such as ngRepeat, ngIf, and ngController. When resolving a model, the lookup starts on the current telescopic and goes through every parent telescopic, all the way to $rootScope.

But, when setting a new value, what happens depends on what kind of model (variable) nosotros desire to change. If the model is a primitive, the child scope volition just create a new model. But if the alter is to a belongings of a model object, the lookup on parent scopes will find the referenced object and change its actual property. A new model would not be set on the current scope, so no masking would occur:

          function MainController($scope) {   $scope.foo = 1;   $scope.bar = {innerProperty: two}; }  angular.module('myApp', []) .controller('MainController', MainController);                  
          <div ng-controller="MainController">   <p>OUTER SCOPE:</p>   <p>{{ foo }}</p>   <p>{{ bar.innerProperty }}</p>   <div ng-if="foo"> <!— ng-if creates a new scope —>     <p>INNER SCOPE</p>     <p>{{ foo }}</p>     <p>{{ bar.innerProperty }}</p>     <button ng-click="foo = 2">Ready primitive</button>     <button ng-click="bar.innerProperty = 3">Mutate object</button>   </div> </div>                  

Clicking the button labelled "Prepare primitive" volition fix foo in the inner scope to 2, but volition non modify foo in the outer scope.

Clicking the push button labelled "Alter object" will modify the bar property from the parent scope. Since there is no variable on the inner scope, no shadowing will happen, and the visible value for bar volition be 3 in both scopes.

Another way to do this is to leverage the fact that the parent scopes and the root Scope are referenced from every scope. The $parent and $root objects can be used to access the parent telescopic and $rootScope, respectively, directly from the view. It may be a powerful mode, but I am not a fan of it due to the problem with targeting a item scope up the stream. At that place is some other way to set and access properties specific to a telescopic - using the controllerAs syntax.

Common Mistake #iii: Not Using controllerAs Syntax

The alternative and most efficient way to assign models to utilize a controller object instead of the injected $scope. Instead of injecting telescopic, we can define models similar this:

          function MainController($scope) {   this.foo = ane;   var that = this;   var setBar = part () {     // that.bar = {someProperty: two};     this.bar = {someProperty: 2};   };   setBar.telephone call(this);   // there are other conventions:    // var MC = this;   // setBar.call(this); when using 'this' inside setBar() }                  
          <div>   <p>OUTER Telescopic:</p>   <p>{{ MC.foo }}</p>   <p>{{ MC.bar.someProperty }}</p>   <div ng-if="test1">     <p>INNER Scope</p>     <p>{{ MC.foo }}</p>     <p>{{ MC.bar.someProperty }}</p>     <push ng-click="MC.foo = three">Change MC.foo</push>     <button ng-click="MC.bar.someProperty = v">Change MC.bar.someProperty</push button>   </div> </div>                  

This is much less confusing. Especially when there are many nested scopes, as can be the case with nested states.

There is more to the controllerAs syntax.

Common Mistake #4: Not Fully Utilising The controllerAs Syntax

At that place are a few caveats with how the controller object is exposed. It is basically an object set on the controller's scope, just like a normal model.

If y'all need to watch a belongings of the controller object, you can watch a function only you are not required to. Here is an instance:

          function MainController($scope) {   this.championship = 'Some championship';   $telescopic.$watch(athwart.bind(this, office () {     return this.championship;   }), function (newVal, oldVal) {     // handle changes   }); }                  

It is easier to simply do:

          function MainController($scope) {   this.title = 'Some title';   $telescopic.$watch('MC.championship', function (newVal, oldVal) {     // handle changes   }); }                  

Meaning that also downwardly the scope chain, you could access MC from a kid controller:

          part NestedController($scope) {   if ($scope.MC && $scope.MC.championship === 'Some title') {     $telescopic.MC.title = 'New title';   } }                  

However, to exist able to do that you need to be consistent with the acronym y'all use for controllerAs. In that location are at least three means to ready it. Y'all already saw the first one:

          <div ng-controller="MainController as MC">    … </div>                  

Even so, if yous use ui-router, specifying a controller that way is prone to error. For states, controllers should be specified in the land configuration:

          athwart.module('myApp', []) .config(function ($stateProvider) {   $stateProvider   .state('main', {     url: '/',     controller: 'MainController as MC',     templateUrl: '/path/to/template.html'   }) }). controller('MainController', office () { … });                  

There is another style to annotate:

          (…) .state('chief', {   url: '/',   controller: 'MainController',   controllerAs: 'MC',   templateUrl: '/path/to/template.html' })                  

Yous tin do the same in directives:

          office AnotherController() {   this.text = 'abc'; }  function testForToptal() {   return {     controller: 'AnotherController equally Ac',     template: '<p>{{ Air-conditioning.text }}</p>'   }; }  angular.module('myApp', []) .controller('AnotherController', AnotherController) .directive('testForToptal', testForToptal);                  

The other manner to annotate is valid too, although less concise:

          function testForToptal() {   return {     controller: 'AnotherController',     controllerAs: 'AC',     template: '<p>{{ Air conditioning.text }}</p>'   }; }                  

Common Mistake #5: Not Using Named Views With UI-ROUTER For Power"

The de facto routing solution for AngularJS has been, until now, the ui-router. Removed from core sometime ago, ngRoute module, was too basic for more sophisticated routing.

There is a new NgRouter on information technology'due south way, just the authors withal consider it too early for production. When I am writing this, the stable Athwart is 1.3.xv, and ui-router rocks.

The main reasons:

  • awesome state nesting
  • road abstraction
  • optional and required parameters

Hither I will embrace state nesting to avert AngularJS errors.

Think of this equally a circuitous yet standard use case. There is an app, which has a homepage view and a product view. The production view has iii separate sections: the intro, the widget, and the content. We want the widget to persist and not reload when switching betwixt state. But the content should reload.

Consider the following HTML production alphabetize page construction:

          <body>   <header>     <!-- SOME STATIC HEADER CONTENT -->   </header>    <department class="master">     <div course="page-content">      <div form="row">       <div course="col-xs-12">         <section class="intro">           <h2>SOME Product SPECIFIC INTRO</h2>         </section>       </div>     </div>      <div grade="row">       <div class="col-xs-3">         <section class="widget">           <!-- some widget, which should never reload -->         </section>       </div>       <div class="col-xs-9">         <section grade="content">           <div class="product-content">           <h2>Product title</h2>           <span>Context-specific content</span>         </div>         </section>       </div>     </div>    </div>    </department>    <footer>     <!-- SOME STATIC HEADER CONTENT -->   </footer>  </trunk>                  

This is something we could get from the HTML coder, and now need to separate it into files and states. I generally go with the convention that there is an abstract Main state, which keeps the global data if needed. Use that instead of $rootScope. The Main state will also continue static HTML that is required on every page. I keep index.html make clean.

          <!— alphabetize.html —> <torso>   <div ui-view></div> </body>                  
          <!— main.html —> <header>   <!-- SOME STATIC HEADER CONTENT --> </header>  <department class="main">   <div ui-view></div> </department>  <footer>   <!-- SOME STATIC HEADER CONTENT --> </footer>                  

Then permit's see the product index page:

          <div class="folio-content">    <div class="row">     <div course="col-xs-12">       <department course="intro">         <div ui-view="intro"></div>       </department>     </div>   </div>    <div class="row">     <div class="col-xs-3">       <section class="widget">         <div ui-view="widget"></div>       </section>     </div>     <div class="col-xs-9">       <section class="content">         <div ui-view="content"></div>       </section>     </div>   </div>  </div>                  

Every bit you can see, the production index page has three named views. One for the intro, ane for the widget, and i for the production. Nosotros encounter the specs! So now allow's gear up routing:

          part config($stateProvider) {   $stateProvider     // Main Abstract Land, ALWAYS ON     .state('chief', {       abstract: true,       url: '/',       controller: 'MainController every bit MC',       templateUrl: '/routing-demo/master.html'     })     // A SIMPLE HOMEPAGE     .state('master.homepage', {       url: '',       controller: 'HomepageController as HC',       templateUrl: '/routing-demo/homepage.html'     })     // THE ABOVE IS ALL GOOD, HERE IS TROUBLE     // A COMPLEX Production PAGE     .state('main.production', {       abstract: true,       url: ':id',       controller: 'ProductController as PC',       templateUrl: '/routing-demo/product.html',     })     // Production DEFAULT SUBSTATE     .state('main.product.index', {       url: '',       views: {         'widget': {           controller: 'WidgetController as PWC',           templateUrl: '/routing-demo/widget.html'          },         'intro': {           controller: 'IntroController as PIC',           templateUrl: '/routing-demo/intro.html'          },         'content': {           controller: 'ContentController as PCC',           templateUrl: '/routing-demo/content.html'         }       }     })     // PRODUCT DETAILS SUBSTATE     .country('main.product.details', {       url: '/details',       views: {         'widget': {           controller: 'WidgetController equally PWC',           templateUrl: '/routing-demo/widget.html'          },         'content': {           controller: 'ContentController every bit PCC',           templateUrl: '/routing-demo/content.html'         }       }     }); }  angular.module('articleApp', [   'ui.router' ]) .config(config);                  

That would be the commencement arroyo. Now, what happens when switching between main.product.alphabetize and main.product.details? The content and widget get reloaded, but we only want to reload the content. This was problematic, and developers actually created routers that would support just that functionality. I of the names for this was sticky views. Fortunately, ui-router supports that out of the box with absolute named view targeting.

          // A Circuitous Product Folio // WITH NO More than TROUBLE .state('main.product', {   abstract: truthful,   url: ':id',   views: {     // TARGETING THE UNNAMED VIEW IN Main.HTML     '@main': {       controller: 'ProductController as PC',       templateUrl: '/routing-demo/production.html'      },     // TARGETING THE WIDGET VIEW IN PRODUCT.HTML     // BY DEFINING A Child VIEW ALREADY Here, WE ENSURE Information technology DOES NOT RELOAD ON Child Land Modify     'widget@main.product': {       controller: 'WidgetController as PWC',       templateUrl: '/routing-demo/widget.html'      }   } }) // PRODUCT DEFAULT SUBSTATE .state('main.production.index', {   url: '',   views: {     'intro': {       controller: 'IntroController as PIC',       templateUrl: '/routing-demo/intro.html'      },     'content': {       controller: 'ContentController every bit PCC',       templateUrl: '/routing-demo/content.html'     }   } }) // PRODUCT DETAILS SUBSTATE .land('primary.product.details', {   url: '/details',   views: {     'content': {       controller: 'ContentController as PCC',       templateUrl: '/routing-demo/content.html'     }   } });                  

By moving the state definition to the parent view, which also is abstract, nosotros tin preserve the child view from reloading when switching urls which ordinarily affects that kid's siblings. Of grade, the widget could be a simple directive. Simply the point is, it could also be some other complex nested state.

There is another way to do this through the use of $urlRouterProvider.deferIntercept(), but I think that using land configuration is really improve. If you lot are interested in intercepting routes, I wrote a small tutorial on StackOverflow.

Mutual Mistake #6: Declaring Everything In The Athwart World Using Anonymous Functions

This fault is of a lighter calibre, and is more than a question of mode than avoiding AngularJS error messages. You may have previously noticed that I seldom laissez passer anonymous functions to angular internal'south declarations. I normally just ascertain a function commencement then pass it in.

This regards more than than simply functions. I got this approach from reading style guides, peculiarly Airbnb's and Todd Motto's. I believe there are several advantages and almost no drawbacks to it.

Starting time of all, y'all can manipulate and mutate your functions and objects much easier if they are assigned to a variable. Secondly, the lawmaking is cleaner and tin can exist easily carve up into files. That means maintainability. If you don't want to pollute the global namespace, wrap every file in IIFEs. The tertiary reason is testability. Consider this case:

          'utilize strict';  function yoda() {    var privateMethod = part () {     // this function is not exposed   };    var publicMethod1 = function () {     // this function is exposed, just information technology'southward internals are not exposed     // some logic...   };    var publicMethod2 = office (arg) {     // THE Beneath Phone call CANNOT BE SPIED ON WITH JASMINE     publicMethod1('someArgument');   };    // IF THE LITERAL IS RETURNED THIS Fashion, Information technology CAN'T BE REFERRED TO FROM INSIDE   render {     publicMethod1: function () {       return publicMethod1();     },     publicMethod2: function (arg) {       return publicMethod2(arg);     }   }; }  angular.module('app', []) .manufacturing plant('yoda', yoda);                  

And then now we could mock the publicMethod1, simply why should nosotros do that since it is exposed? Wouldn't it be easier simply to spy on the existing method? However, the method is actually another function - a thin wrapper. Have a look at this approach:

          role yoda() {    var privateMethod = function () {     // this function is non exposed   };    var publicMethod1 = role () {     // this role is exposed, but information technology's internals are not exposed     // some logic...   };    var publicMethod2 = function (arg) {     // the below phone call cannot be spied on     publicMethod1('someArgument');      // BUT THIS ONE CAN!     hostObject.publicMethod1('aBetterArgument');   };    var hostObject = {     publicMethod1: function () {       return publicMethod1();     },     publicMethod2: part (arg) {       return publicMethod2(arg);     }   };    return hostObject; }                  

This is not only about style, since in effect the code is more reusable and idiomatic. The developer gets more expressive power. Splitting all code into self-contained blocks just makes it easier.

Common Fault #7: Doing Heavy Processing In Angular AKA Using Workers

In some scenarios, it may be required to process a large array of complex objects by passing them through a prepare of filters, decorators, and finally a sorting algorithm. One use case is when the app should piece of work offline or where the performance of displaying information is key. And since JavaScript is single-threaded, it is relatively piece of cake to freeze the browser.

It is also piece of cake to avert information technology with web workers. There don't seem to be whatsoever popular libraries that handle that specifically for AngularJS. It might be for the best though, since the implementation is easy.

Start, let's setup the service:

          function scoringService($q) {      var scoreItems = function (items, weights) {     var deferred = $q.defer();     var worker = new Worker('/worker-demo/scoring.worker.js');     var orders = {       items: items,       weights: weights     };     worker.postMessage(orders);     worker.onmessage = function (due east) {       if (e.data && e.information.prepare) {         deferred.resolve(eastward.data.items);       }     };      render deferred.promise;   };   var hostObject = {     scoreItems: role (items, weights) {       return scoreItems(items, weights);     }   };    return hostObject;  }  angular.module('app.worker') .factory('scoringService', scoringService);                  

Now, the worker:

          'use strict';  function scoringFunction(items, weights) {   var itemsArray = [];   for (var i = 0; i < items.length; i++) {     // some heavy processing     // itemsArray is populated, etc.   }    itemsArray.sort(part (a, b) {     if (a.sum > b.sum) {       return -i;     } else if (a.sum < b.sum) {       return 1;     } else {       return 0;     }   });    render itemsArray; }  self.addEventListener('message', office (e) {   var respond = {     prepare: true   };   if (e.data && e.information.items && e.data.items.length) {     reply.items = scoringFunction(e.data.items, eastward.information.weights);   }   self.postMessage(answer); }, faux);                  

Now, inject the service equally usual and treat scoringService.scoreItems() as you would any service method that returns a promise. The heavy processing volition be carried out on a separate thread, and no harm will be done to the UX.

What to expect out for:

  • at that place does not seem to be a full general dominion for how many workers to spawn. Some developers merits that 8 is a good number, but apply an online estimator and suit yourself
  • check compatibility with older browsers
  • I run into an issue when passing the number 0 from the service to the worker. I applied .toString() on the passed holding, and information technology worked correctly.

Common Mistake #8: Overusing And Misunderstanding Resolves

Resolves add extra time to the loading of the view. I believe that high performance of the front end-end app is our primary goal. It should non be a problem to render some parts of the view while the app waits for the data from the API.

Consider this setup:

          part resolve(index, timeout) {   return {     data: office($q, $timeout) {       var deferred = $q.defer();       $timeout(office () {         deferred.resolve(console.log('Data resolve called ' + index));       }, timeout);       return deferred.promise;     }   }; }  function configResolves($stateProvide) {   $stateProvider     // MAIN Abstract Country, Ever ON     .country('main', {       url: '/',       controller: 'MainController equally MC',       templateUrl: '/routing-demo/chief.html',       resolve: resolve(i, 1597)     })     // A COMPLEX Product Page     .state('main.product', {       url: ':id',         controller: 'ProductController as PC',       templateUrl: '/routing-demo/production.html',       resolve: resolve(2, 2584)     })     // PRODUCT DEFAULT SUBSTATE     .land('primary.production.index', {       url: '',       views: {         'intro': {           controller: 'IntroController as Motion picture',           templateUrl: '/routing-demo/intro.html'         },         'content': {           controller: 'ContentController as PCC',           templateUrl: '/routing-demo/content.html'         }       },       resolve: resolve(three, 987)     }); }                  

The console output will be:

          Data resolve called 3 Information resolve called 1 Data resolve called two Principal Controller executed Product Controller executed Intro Controller executed                  

Which basically ways that:

  • The resolves are executed asynchronously
  • We tin can't rely on an order of execution (or at least demand to flex quite a bit)
  • All usa are blocked until all resolves do their affair, even if they are non abstract.

This means that before the user sees any output, he/she must wait for all the dependencies. Nosotros demand to have that data, certain, okay. If it absolutely necessary to have it earlier the view, put it in a .run() cake. Otherwise, just make the call to the service from the controller and handle the half-loaded state gracefully. Seeing piece of work in progress - and the controller is already executed, and then information technology actually is progress - is better than having the app stall.

Common Mistake #9: Not Optimizing The App - 3 Examples

a) Causing too many digest loops, such equally attaching sliders to models

This is a general problem that tin result in AngularJS errors, but I will discuss it at the example of sliders. I was using this slider library, angular range slider, because I needed the extended functionality. That directive has this syntax in the minimal version:

          <body ng-controller="MainController equally MC">   <div range-slider      min="0"      max="MC.maxPrice"      pin-handle="min"      model-max="MC.price"   >   </div> </body>                  

Consider the following code in the controller:

          this.maxPrice = '100'; this.cost = '55';  $scope.$watch('MC.price', function (newVal) {   if (newVal || newVal === 0) {     for (var i = 0; i < 987; i++) {       console.log('ALL YOUR BASE ARE Belong TO U.s.a.');     }   } });                  

So that works ho-hum. The casual solution would be to set a timeout on the input. Merely that is not always handy, and sometimes nosotros don't really want to filibuster the bodily model change in all cases.

So we volition add together a temporary model bound to change the working model on timeout:

          <torso ng-controller="MainController as MC">   <div range-slider      min="0"      max="MC.maxPrice"      pin-handle="min"      model-max="MC.priceTemporary"   >   </div> </body>                  

and in the controller:

          this.maxPrice = '100'; this.toll = '55'; this.priceTemporary = '55';  $scope.$watch('MC.price', part (newVal) {   if (!isNaN(newVal)) {     for (var i = 0; i < 987; i++) {       console.log('ALL YOUR Base of operations ARE BELONG TO US');     }   } });  var timeoutInstance; $scope.$lookout man('MC.priceTemporary', function (newVal) {   if (!isNaN(newVal)) {     if (timeoutInstance) {       $timeout.cancel(timeoutInstance);     }      timeoutInstance = $timeout(function () {       $scope.MC.price = newVal;     }, 144);        } });                  

b) Non using $applyAsync

AngularJS does non have a polling mechanism to call $digest(). Information technology is simply executed considering we use the directives (e.g. ng-click, input), services ($timeout, $http), and methods ($watch) which evaluate our code and phone call a assimilate after.

What .$applyAsync() does is it delays the resolution of expressions until the adjacent $digest() cycle, which is triggered after a 0 timeout, which actually is ~10ms.

There are two ways to use applyAsync now. An automated manner for $http requests, and a manual mode for the rest.

To make all http requests that return in around the same time resolve in one digest, do:

          mymodule.config(function ($httpProvider) {   $httpProvider.useApplyAsync(true); });                  

The manual way shows how it actually works. Consider some role that runs on the callback to a vanilla JS event listener or a jQuery .click(), or another external library. After it executes and changes models, if you didn't already wrap information technology in an $apply() you demand to call $scope.$root.$digest() ($rootScope.$digest()), or at least $telescopic.$digest(). Otherwise, you volition see no change.

If you do that multiple times in one flow, it might start running wearisome. Consider calling $telescopic.$applyAsync() on the expressions instead. Information technology will gear up simply phone call i digest wheel for all of them.

c) Doing heavy processing of images

If you experience bad performance, you can investigate the reason by using the Timeline from Chrome Developer Tools. I will write more about this tool in mistake #17. If your timeline graph is dominated with the color green later on recording, your performance issues may be related to processing of images. This is not strictly related to AngularJS, just may happen on top of AngularJS performance issues (which would be by and large yellowish on the graph). As front-end engineers, nosotros need to recall about the complete end project.

Accept a moment to assess:

  • Exercise you lot utilise parallax?
  • Practise you have several layers of content overlapping one another?
  • Do you move your images effectually?
  • Exercise you lot scale images (east.m. with background-size)?
  • Do you resize images in loops, and possibly cause digest loops on resize?

If you answered "yes" to at to the lowest degree three of the above, consider easing it. Peradventure y'all tin can serve various image sizes and not resize at all. Maybe you could add the "transform: translateZ(0)" forcefulness GPU processing hack. Or use requestAnimationFrame for handlers.

Common Fault #10: jQuerying Information technology - Detached DOM Tree

Many times yous probably hear that information technology's not recommended to use jQuery with AngularJS, and that it should exist avoided. It is imperative to sympathize the reason behind these statements. There are at least three reasons, equally far as I can run into, but none of them are actual blockers.

Reason one: When yous execute jQuery code, you need to phone call $digest() yourself. For many cases, in that location is an AngularJS solution which is tailored for AngularJS and tin exist of improve utilise inside Angular than jQuery (eastward.g. ng-click or the event system).

Reason 2: The method of idea nearly building the app. If yous accept been adding JavaScript to websites, which reload when navigating, you did not accept to worry about memory consumption too much. With single-page apps, you do have to worry. If y'all don't clean up, users who spend more than than a few minutes on your app may experience growing operation issues.

Reason 3: Cleaning up is not actually the easiest affair to do and analyze. In that location is no fashion to telephone call a garbage collector from the script (in the browser). Yous may terminate upwardly with detached DOM trees. I created an example (jQuery is loaded in alphabetize.html):

          <section>   <test-for-toptal></test-for-toptal>   <button ng-click="MC.removeDirective()">remove directive</push button> </section>                  
          function MainController($rootScope, $scope) {   this.removeDirective = function () {     $rootScope.$emit('destroyDirective');   }; }  function testForToptal($rootScope, $timeout) {   render {     link: function (scope, element, attributes) {        var destroyListener = $rootScope.$on('destroyDirective', function () {         telescopic.$destroy();       });        // adding a timeout for the DOM to get set up       $timeout(function () {         scope.toBeDetached = chemical element.find('p');       });        scope.$on('$destroy', part () {         destroyListener();         element.remove();       });     },     template: '<div><p>I AM DIRECTIVE</p></div>'   }; }  angular.module('app', []) .controller('MainController', MainController) .directive('testForToptal', testForToptal);                  

This is a simple directive that outputs some text. In that location is a button below it, which will just destroy the directive manually.

So when the directive is removed, at that place remains a reference to the DOM tree in scope.toBeDetached. In chrome dev tools, if you access the tab "profiles" and so "have heap snapshot", you volition see in the output:

You can live with a few, simply information technology is bad if you accept a ton. Especially if for some reason, like in the instance, you shop information technology on the telescopic. The whole DOM volition be evaluated on every digest. The problematic discrete DOM tree is the one with 4 nodes. And then how tin can this be solved?

          scope.$on('$destroy', function () {    // setting this model to null   // will solve the problem.   scope.toBeDetached = null;    destroyListener();   element.remove(); });                  

The detached DOM tree with 4 entries is removed!

In this example, the directive uses the same scope, and stores the DOM element on the telescopic. Information technology was easier for me to demonstrate it that way. It does non always go that bad, as you could shop it in a variable. Nevertheless, it would notwithstanding take up memory if whatsoever closure that had referenced that variable or whatever other from the aforementioned office scope lived on.

Common Mistake #11: Overusing Isolated Scope

Whenever y'all need a directive that yous know will be used in a unmarried place, or which y'all don't expect to conflict with whatever surround information technology is used in, in that location is no need to employ isolated telescopic. Lately, there is a trend to create reusable components, but did yous know that core angular directives don't use isolated scope at all?

There are two chief reasons: you can't apply ii isolated scope directives to an element, and you may encounter bug with nesting / inheritance / consequence processing. Especially regarding transclusion - the effects may non exist what you expect.

So this would fail:

          <p isolated-scope-directive another-isolated-scope-directive ng-if="MC.quux" ng-echo="q in MC.quux"></p>                  

And even if yous use only one directive, you will observe that neither the isolated scope models nor events broadcasted in isolatedScopeDirective will not be available to AnotherController. That beingness sad, you can flex and use transclusion magic to get in piece of work - just for most utilise cases, there is no need to isolate.

          <p isolated-scope-directive ng-if="MC.quux" ng-repeat="q in MC.quux">   <div ng-controller="AnotherController">     … the isolated scope is not available hither, await: {{ isolatedModel }}   </div> </p>                  

So, two questions now:

  1. How can you process parent telescopic models in a same-scope directive?
  2. How can you instantiate new model values?

There are two ways, in both of them you laissez passer values to attributes. Consider this MainController:

          function MainController($interval) {   this.foo = {     bar: 1   };   this.baz = one;   var that = this;   $interval(part () {     that.foo.bar++;   }, 144);    $interval(function () {     that.baz++;   }, 144);    this.quux = [1,two,3]; }                  

That controls this view:

          <trunk ng-controller="MainController every bit MC">    <div class="cyan-surface">     <h1 fashion="font-size: 21px;">Attributes test</h1>     <test-directive watch-aspect="MC.foo" observe-attribute="current index: {{ MC.baz }}"></examination-directive>   </div>  </body>                  

Notice that "watch-attribute" is non interpolated. It all works, due to JS magic. Hither is the directive definition:

          function testDirective() {   var postLink = function (scope, element, attrs) {     scope.$sentinel(attrs.watchAttribute, part (newVal) {       if (newVal) {         // take a expect in the panel         // we can't use the attribute directly         console.log(attrs.watchAttribute);          // the newVal is evaluated, and it can be used         scope.modifiedFooBar = newVal.bar * 10;       }     }, true);      attrs.$detect('observeAttribute', function (newVal) {       scope.observed = newVal;     });   };    return {     link: postLink,     templateUrl: '/attributes-demo/test-directive.html'   }; }                  

Notice that attrs.watchAttribute is passed into scope.$lookout man() without the quotation marks! That means what was actually passed to $sentinel was the string MC.foo! It does work, yet, because any string passed into $watch() gets evaluated against the scope and MC.foo is available on the telescopic. That is too the most common style that attributes are watched in AngularJS core directives.

Run into the code on github for the template, and wait into $parse and $eval for fifty-fifty more awesomeness.

Common Error #12: Non Cleaning Up After Yourself - Watchers, Intervals, Timeouts And Variables

AngularJS does some work on your behalf, merely not all. The following need to be manually cleaned up:

  • Any watchers that are non bound to the electric current scope (e.g. bound to $rootScope)
  • Intervals
  • Timeouts
  • Variables referencing DOM in directives
  • Dodgy jQuery plugins, e.one thousand. those that don't have handlers reacting to the JavaScript $destroy result

If yous don't exercise that manually, you will encounter unexpected behaviour and memory leaks. Even worse - these will not be instantly visible, merely they volition creep up eventually. Murphy's constabulary.

Amazingly, AngularJS provides handy ways to deal with all of those:

          part cleanMeUp($interval, $rootScope, $timeout) {   var postLink = role (scope, element, attrs) {     var rootModelListener = $rootScope.$lookout('someModel', office () {       // do something     });      var myInterval = $interval(office () {       // do something in intervals     }, 2584);      var myTimeout = $timeout(function () {       // defer some action here     }, 1597);      telescopic.domElement = chemical element;      $timeout(role () {       // calling $destroy manually for testing purposes       scope.$destroy();     }, 987);      // here is where the cleanup happens     scope.$on('$destroy', function () {       // disable the listener       rootModelListener();        // abolish the interval and timeout       $interval.abolish(myInterval);       $timeout.cancel(myTimeout);        // nullify the DOM-bound model       scope.domElement = zero;     });      element.on('$destroy', office () {       // this is a jQuery event       // clean upward all vanilla JavaScript / jQuery artifacts here        // respectful jQuery plugins take $destroy handlers,       // that is the reason why this event is emitted...       // follow the standards.     });    };                  

Notice the jQuery $destroy event. It is called similar the AngularJS one, but it is handled separately. Telescopic $watchers will not react to the jQuery effect.

Mutual Error #13: Keeping Likewise Many Watchers

This should exist quite uncomplicated now. At that place is one affair to understand here: $digest(). For every binding {{ model }}, AngularJS creates a watcher. On every digest phase, each such binding is evaluated and compared against the previous value. That is called muddy-checking, and that'south what $digest does. If the value changed since the terminal check, the watcher callback is fired. If that watcher callback modifies a model ($scope variable), a new $digest bicycle is fired (upward to a maximum of 10) when an exception is thrown.

Browsers don't have problems even with thousands of bindings, unless the expressions are complex. The common answer for "how many watchers are ok to have" is 2000.

So, how can we limit the number of watchers? By not watching scope models when we don't wait them to change. It is fairly easy onwards from AngularJS 1.three, since 1-time bindings are in cadre now.

          <li ng-repeat="particular in ::vastArray">{{ ::item.velocity }}</li>                  

Later on vastArray and item.velocity are evaluated once, they will never modify over again. You lot tin still apply filters to the array, they volition work just fine. Information technology is just that the array itself will not be evaluated. In many cases, that is a win.

Common Mistake #14: Misunderstanding The Digest

This AngularJS mistake was already partly covered in mistakes 9.b and in 13. This is a more thorough caption. AngularJS updates DOM every bit a result of callback functions to watchers. Every binding, that is the directive {{ someModel }} sets up watchers, but watchers are too set for many other directives like ng-if and ng-repeat. Only accept a look at the source code, it is very readable. Watchers can besides be gear up manually, and you take probably done that at to the lowest degree a few times yourself.

$sentry()ers are bound to scopes. $Watchers can accept strings, which are evaluated against the scope that the $scout() was jump to. They can also evaluate functions. And they too take callbacks. So, when $rootScope.$digest() is called, all the registered models (that is $scope variables) are evaluated and compared against their previous values. If the values don't match, the callback to the $watch() is executed.

It is important to empathize that even though a model's value was changed, the callback does not fire until the adjacent digest phase. It is called a "phase" for a reason - it tin can consist of several digest cycles. If only a watcher changes a telescopic model, another digest cycle is executed.

Only $digest() is not polled for. It is called from core directives, services, methods, etc. If yous change a model from a custom role that does not call .$use, .$applyAsync, .$evalAsync, or anything else that eventually calls $digest(), the bindings volition non be updated.

By the way, the source lawmaking for $digest() is actually quite circuitous. It is all the same worth reading, as the hilarious warnings make upward for information technology.

Common Mistake #15: Non Relying On Automation, Or Relying On Information technology Likewise Much

If you follow the trends within forepart stop development and are a bit lazy - like me - then yous probably try to not do everything past paw. Keeping runway of all your dependencies, processing sets of files in different ways, reloading the browser afterwards every file salvage - there is a lot more than to developing than just coding.

Then you may be using bower, and possibly npm depending on how you lot serve your app. There is a chance that you may be using grunt, gulp, or brunch. Or fustigate, which also is cool. In fact, yous may take started your latest projection with some Yeoman generator!

This leads to the question: do you understand the whole process of what your infrastructure actually does? Do you need what you lot have, especially if you just spent hours trying to fix your connect webserver livereload functionality?

Take a 2d to assess what you need. All those tools are only hither to help you, there is no other reward for using them. The more experienced developers I talk to tend to simplify things.

Common Fault #16: Not Running The Unit Tests In TDD Way

Tests will not make your code complimentary of AngularJS error messages. What they will do is clinch that your team doesn't run into regression issues all the time.

I am writing specifically well-nigh unit tests here, non because I experience they are more important than e2e tests, just because they execute much faster. I must acknowledge that the process I am about to describe is a very pleasurable i.

Test Driven Evolution as an implementation for east.grand. gulp-karma runner, basically runs all your unit tests on every file save. My favorite mode to write tests is, I but write empty assurances starting time:

          depict('some module', function () {   it('should telephone call the name-it service…', function () {     // exit this empty for now   });   ... });                  

After that, I write or refactor the actual lawmaking, then I come up back to the tests and fill in the assurances with bodily exam code.

Having a TDD task running in a concluding speeds up the process past most 100%. Unit tests execute in a affair of a few seconds, even if yous have a lot of them. Only save the examination file and the runner will pick it up, evaluate your tests, and provide feedback instantly.

With e2e tests, the process is much slower. My advice - split e2e tests up into test suites and simply run 1 at a time. Protractor has support for them, and below is the lawmaking I use for my test tasks (I similar gulp).

          'apply strict';  var gulp = crave('gulp'); var args = crave('yargs').argv; var browserSync = require('browser-sync'); var karma = require('gulp-karma'); var protractor = require('gulp-protractor').protractor; var webdriverUpdate = require('gulp-protractor').webdriver_update;  role test() {   // Be sure to return the stream   // NOTE: Using the faux './foobar' so as to run the files   // listed in karma.conf.js INSTEAD of what was passed to   // gulp.src !   return gulp.src('./foobar')     .pipe(karma({       configFile: 'test/karma.conf.js',       activeness: 'run'     }))     .on('error', function(err) {       // Make sure failed tests cause gulp to exit non-nada       // console.log(err);       this.emit('finish'); //instead of erroring the stream, cease it     }); }  office tdd() {   return gulp.src('./foobar')     .piping(karma({       configFile: 'test/karma.conf.js',       action: 'start'     }))     .on('fault', function(err) {       // Make certain failed tests crusade gulp to exit not-zero       // console.log(err);       // this.emit('end'); // not catastrophe the stream here     }); }  part runProtractor () {    var statement = args.suite || 'all';      // NOTE: Using the false './foobar' so as to run the files   // listed in protractor.conf.js, instead of what was passed to   // gulp.src   return gulp.src('./foobar')     .pipe(protractor({       configFile: 'test/protractor.conf.js',       args: ['--suite', statement]     }))     .on('mistake', part (err) {       // Make sure failed tests crusade gulp to go out not-zero       throw err;     })     .on('end', office () {       // Close browser sync server       browserSync.exit();     }); }  gulp.chore('tdd', tdd); gulp.job('test', exam); gulp.task('test-e2e', ['webdriver-update'], runProtractor); gulp.task('webdriver-update', webdriverUpdate);                  

A - chrome breakpoints

Chrome dev tools let you to point at a specific place in any of the files loaded into the browser, pause code execution at that point, and let you interact with all the variables available from that point. That is a lot! That functionality does not require you to add whatsoever lawmaking at all, everything happens in the dev tools.

Not but yous become access to all the variables, you too see the phone call stack, print stack traces, and more than. You can even configure it to piece of work with minified files. Read about information technology here.

There are other ways you can get similar run-time admission, e.one thousand. by adding panel.log() calls. Just breakpoints are more sophisticated.

AngularJS also allows you to access scope through DOM elements (as long as debugInfo is enabled), and inject bachelor services through the console. Consider the following in the console:

          $(document.torso).scope().$root                  

or bespeak at an element in the inspector, and and so:

          $($0).telescopic()                  

Even if debugInfo is not enabled, y'all can do:

          angular.reloadWithDebugInfo()                  

And have it available after reload:

To inject and interact with a service from the console, endeavour:

          var injector = $(document.torso).injector(); var someService = injector.get('someService');                  

B - chrome timeline

Another great tool that comes with dev tools is the timeline. That will allow you to record and analyse your app's live functioning as y'all are using it. The output shows, among others, retention usage, frame charge per unit, and the dissection of the unlike processes that occupy the CPU: loading, scripting, rendering, and painting.

If you feel that your app's operation degrades, you will nigh probable be able to find the cause for that through the timeline tab. Just tape your actions which led to performance problems and see what happens. Too many watchers? You will see yellow bars taking a lot of space. Memory leaks? You tin can see how much memory was consumed over fourth dimension on a graph.

A detailed clarification: https://developer.chrome.com/devtools/docs/timeline

C - inspecting apps remotely on iOS and Android

If you are developing a hybrid app or a responsive web app, y'all tin can admission your device's console, DOM tree, and all other tools available either through Chrome or Safari dev tools. That includes the WebView and UIWebView.

First, start your web server on host 0.0.0.0 so that information technology is attainable from your local network. Enable spider web inspector in settings. And then connect your device to your desktop and access your local development page, using your auto'due south ip instead of the regular "localhost". That is all it takes, your device should now be bachelor to you from your desktop'southward browser.

Here are the detailed instructions for Android And for iOS, unofficial guides are to be found easily through google.

I recently had some cool experience with browserSync. Information technology works in a similar way to livereload, but it besides really syncs all browsers that are viewing the same page through browserSync. That includes user interaction such every bit scrolling, clicking on buttons, etc. I was looking at the iOS app'southward log output while controlling the folio on the iPad from my desktop. It worked nicely!

Common Mistake #18: Not Reading The Source Code On The NG-INIT Example

Ng-init, from the audio of it, should exist like to ng-if and ng-echo, right? Did y'all ever wonder why there is a comment in the docs that it should not be used? IMHO that was surprising! I would expect the directive to initialize a model. That's as well what it does, but… it is implemented in a different style, that is, it does not watch the attribute value. You don't need to browse through AngularJS source lawmaking - let me bring it to you:

          var ngInitDirective = ngDirective({   priority: 450,   compile: function() {     return {       pre: role(scope, element, attrs) {         scope.$eval(attrs.ngInit);       }     };   } });                  

Less than yous would expect? Quite readable, likewise the bad-mannered directive syntax, isn't it? The 6th line is what it is all about.

Compare it to ng-evidence:

          var ngShowDirective = ['$animate', function($breathing) {   return {     restrict: 'A',     multiElement: true,     link: function(telescopic, chemical element, attr) {       scope.$watch(attr.ngShow, role ngShowWatchAction(value) {         // we're calculation a temporary, blitheness-specific class for ng-hide since this style         // we can control when the chemical element is really displayed on screen without having         // to have a global/greedy CSS selector that breaks when other animations are run.         // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845         $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {           tempClasses: NG_HIDE_IN_PROGRESS_CLASS         });       });     }   }; }];                  

Again, the sixth line. There is a $watch there, that's what makes this directive dynamic. In the AngularJS source code, a big part of all the code are comments that depict code that was by and large readable from the beginning. I believe it is a great manner to larn about AngularJS.

Determination

This guide roofing most common AngularJS mistakes is nigh twice equally long as the other guides. Information technology turned out that style naturally. The demand for high quality JavaScript front end cease engineers is very high. AngularJS is and then hot right now, and it has been holding a stable position among the about popular evolution tools for a few years. With AngularJS 2.0 on the fashion, it volition probably boss for years to come.

What is great about front-end development is that it is very rewarding. Our work is visible instantly, and people interact directly with the products we deliver. The time spent learning JavaScript, and I believe we should focus on the JavaScript language, is a very good investment. Information technology is the language of the Internet. The competition is super stiff! At that place is one focus for u.s.a. - user feel. To be successful, we need to cover everything.

Source lawmaking used in these examples tin can exist downloaded from GitHub. Feel free to download it and make it your own.

I wanted to requite credits to four publishing developers that inspired me the most:

  • Ben Nadel
  • Todd Motto
  • Pascal Precht
  • Sandeep Panda

I besides wanted to thank all the cracking people on FreeNode #angularjs and #javascript channels for many first-class conversations, and continuous support.

And finally, always remember:

          // when in incertitude, comment information technology out! :)                  

steinbergabiall.blogspot.com

Source: https://www.toptal.com/angular-js/top-18-most-common-angularjs-developer-mistakes

0 Response to "Angular 2 Async Form Store on Device to Upload Later"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel