Common 10 mistakes made by Angular Devs (by Pankaj Parkar)

Who am I?

  • Software Engineer, Loves to code in .Net + Angular
  • Active Angular community contributor
  • Microsoft MVP
  • Stackoverflow Topuser from India
  • Angular 1 & Angular 2 topuser on Stackoverflow
  • etc...

Pankaj Parkar

(TSS Consultancy PVT LTD.)

  1. Not have dot in models.
  2. Lack of Modularity
  3. polluting $rootScope, use factory/service to share data amongst app.
  4. Clean up async events.
  5. Inserting DOM in current page

 6. ng-if vs ng-show.

 7. Folder structure. 

 8. Follow Dependency Injection pattern.

 9. Utilization of JQuery. 

 10. Use ui-router named view feature.

10 Common Mistakes

1. Not have dot in model

  • Having scope variable without using Dot Rule can break scope bindings
  • You must be wondering, What is Dot Rule?

       When new prototypically inherited scope gets created inside child scope,

          that time primitive type variable copy gets passed down to the child                     scope & object value reference gets passed. If inner scope changes                     primitive value it doesn't update the parent value copy. Where reference           data value do it correctly.

  <div ng-app="app" ng-controller="outerCtrl" class="border">
    <p>Outer Controller Scope:</p>
    <p>{{ foo }}</p>
    <div ng-if="showInnerContent" class="border">
      <p>Inner Scope</p>
      <p>{{ foo }}</p>
      <button ng-click="foo='Something Else'">
        Change Foo
      </button>
    </div>
  </div>

1. a) Not have dot in model ctd..

Lets define object inside a controller like "$scope.model = {};" and place all property in it, here we're going to put `foo` in that.
<div ng-app="app" ng-controller="outerCtrl" class="border">
  <input ng-model="model.foo"/>
  <p>Outer Controller Scope:</p>
  <p>{{ model.foo }}</p>
  <div ng-if="showInnerContent" class="border">
    <p>Inner Scope</p>
    <p>{{ model.foo }}</p>
    <button ng-click="foo='Something Else'">
      Change Foo from inner scope
    </button>
  </div>
</div>

1. b) Not have dot in model ctd..

  • Another correct way to resolve binding issue is use `controllerAs` pattern where scope binding issue solved.
  • controllerAs syntax does remove scope dependency from that controller. 
<div ng-app="app" ng-controller="outerCtrl as vm" class="border">
  <input ng-model="vm.foo"/>
  <p>Outer Controller Scope:</p>
  <p>{{ vm.foo }}</p>
  <div ng-if="vm.showInnerContent" class="border">
    <p>Inner Scope</p>
    <p>{{ vm.foo }}</p>
    <button ng-click="vm.foo='Something Else'">
      Change Foo from inner scope
    </button>
  </div>
</div>

2. Lack of modularity

  • Generally what people do is, they create a application with single main module & keep on putting other stuff in the same module.
  • After certain time for large enterprise application, you will come a mess.
var app = angular.module('app', []);
app.controller('myCtrl', function($scope) {
    //controller data
});
app.service('sharedService', function() {
    //service code only.
});
//so other angular comonents like factory, filter, etc.
//would also gets added inside "app" module.
//In large scale application it become difficult to see which code appears to be where

2. Lack of modularity ctd.

  • For handling such case you could create a different different module inside your application. Consider module as namespace of application. By looking at module you could define the nature of things lies inside it.
  • Let's try to make thing modular by creating module & putting up there respective components in it.
//module for services
angular.module('myApp.services', []);
//module for services
angular.module('myApp.controllers', ['myApp.services']); 
//module for services
angular.module('myApp.filters', []);
//module for services 
angular.module('myApp.pipes', []); 
//module for services
angular.module('myApp.shareService', []); 
//can be combine to one our main application
angular.module('app', [
  'myApp.services', 
  'myApp.controllers', 
  'myApp.pipes', 
  'myApp.services'
]);

3. Polluting $rootScope.

  • Generally developer tends to prefer shortest way with what they do. Most of the time they use $rootScope for sharing data amongst various application component.
  • Polluting $rootScope is consider as bad practice.
  • Why its bad practice?
  • Once data pushed inside $rootScope, its applicable inside each application component.
  • Any changes in the $rootScope would run a digest cycle and unnecessarily one digest cycle will that will refresh all the bindings.
  • Using $rootScope you can't maintain that abstraction layer. 

4. Cleaning up async events

  • When you have async events (like $timeout, $interval, etc.) inside controller and after route navigation occurs you moved on different page with its own template & controller those events doesn't get removed. Generally developer missed this thing while building application.
  • Because behind the scene these events have been added inside Event Loop to process them asynchronously. 
  • For removing them you've to call `cancelInterval` /`cancelTimeout`/`deregisterEvent`

4. Cleaning up async events cntd.

  • To remove them manually you can need to handle such situation on controller destruction
  • While navigating from one page another page, based on router definition it loads new template & associate controller with it. Exactly before removing controller it it emits `$destroy` event over scope.

 

app.controller('myCtrl', function($scope, $element) {
  var count = 0;
  var interval = $interval(function() {
    console.log(++count)
  }, 5000)
  $scope.$on('$destroy', function(event, data) {
    $interval.cance(interval);
  });
  //since $scope would get remove once you use controllerAs
  //when you are using controllerAs pattern, use below thing
  $element.on('$destroy', function(event, data) {
    $interval.cance(interval);
  });
});

5. Inserting new DOM.

  • Whenever user wants to add new DOM inside a DOM tree they do go for plain jQuery.
$scope.output = 'Result';
var template = '<div>{{output}}</div>';
angular.element(document.getElementById('testDiv')).append(template);
  • By doing above what developer do expect is div to show "Result" value on HTML. But this doesn't happen in angular.
  • You need to compile a DOM with $compile service before injecting a DOM on page.
$scope.output = 'Result';
var template = '<div>{{output}}</div>';
var compiledTemplate = $compile(template)($scope);
angular.element(document.getElementById('testDiv')).append(compiledTemplate);

 6. ng-if vs ng-show 

  • Most of the developer uses ng-show/ng-hide every place where they want to show/hide DOM based on expression.
  • How ng-show and ng-if works?
  • That's what the problem is with ng-show, because when it hides DOM it present their in DOM tree which is bad.
  • But don't blindly replace ng-show with ng-if, after changing it to ng-if make sure 1st point is implemented.
<div ng-show="showGrids">

    <pm-kendo-grid></pm-kendo-grid>
    
    <pm-dev-express-grid></pm-dev-express-grid>
    
    <pm-ig-grid></pm-ig-grid>

<div>

 7. Folder structure

  • Generally most of the people follow MVC folder structure while organising angular code.

 7. Folder structure ctd.

  • Per convention do follow feature wise folder structure
  • It would make you one step ahead for angular 2 migration.

First lets see what is dependency injection

 

There are 3 types of DI Annotation

  • Inline Array Annotation
  • $inject property Annotation
  • Implicit Annotation

8. Follow Dependency Injection

8.1 Follow DI ctd.

Inline array anotation

Here we create an array inside which 1st we inject angular dependency in string & then use the same in function

someModule.controller('MyController', ['$scope', 'myService', 
   function($scope, myService) {
     
     //Awesome controller code here
   
   }
]);

someModule.service('myService', ['$log', 
   function($log) {
     
     //Awesome service code here
   
   }
]);

8.1 Follow DI ctd.

Inline array anotation

Here we create an array inside which 1st we inject angular dependency in string & then use the same in function

    function myController($scope, myService) {
    
        //Awesome controller code here
    
    }
    
    someModule.controller('myController', ['$scope', 'myService', MyController]);
    
    function myService($log) {
    
        //Awesome service code here
    
    }
    
    someModule.service('myService', ['$log', myService]);

8.2 Follow DI ctd.

$inject Property anotation

Here we create an array inside with dependency & add that on function $inject property.

    function myController($scope, myService) {
    
      //Awesome controller code here
    
    }
    
    myController.$inject = ['$scope', 'myService', MyController];
    
    someModule.controller('myController', myController);
    
    function myService($log) {
    
      //Awesome service code here
    
    }
    
    myService.$inject = ['$log'];
    
    someModule.service('myService', myService);

8.3 Follow DI ctd.

Implicit anotation

This one of the simplest DI technique to inject dependencies

    function myController($scope, myService) {
    
      //Awesome controller code here
    
    }
    
    someModule.controller('myController', myController);
    
    function myService($log) {
    
      //Awesome service code here
    
    }
    
    someModule.service('myService', myService);

8. Follow DI ctd.

Developer do use more simplest thing 

    function myController($scope, myService) {
    
      //Awesome controller code here
    
    }
    
    someModule.controller('myController', myController);

But what happens is when you minify such files to reduce number of roundtrip to server, this DI technique messed up minification 


 function myController(a,b){};someModule.controller('myController', myController);
  • You can see that minified file has changed the dependency name in function. from `$scope, myService` to `a,b`. This where error will appear on the page `aProvider is missing`.
  • Thus you should use Array inline annotation when injecting dependency.

9. Utilization of jQuery

  • Load jQuery before angular to get all the method available over DOM

10. Use ui-router named view feature

  • Use ui-router named view's, that helps to divide application in small component of an application

Common mistakes made by Angular Devs

By Pankaj Parkar

Common mistakes made by Angular Devs

  • 1,025