AngularJS Best Practices

This resource contains a collection of AngularJS best practices and AngularJS tips provided by our Toptal network members. As such, this page will be updated on a regular basis to include additional information and cover emerging AngularJS techniques. This is a community driven project, so you are encouraged to contribute as well, and we are counting on your feedback.

Unlike some other JavaScript frameworks, AngularJS requires the developer to do things the “Angular” way, which is a set of rules and best practices that are critical to building AngularJS web applications properly. Here we will embrace those best practices that will make you a better AngularJS developer.

Check out the Toptal resource pages for additional information on AngularJS common mistakes, AngularJS job description and AngularJS interview questions.

How to Use `controllerAs` Syntax properly?

Angular is a very powerful framework, sometimes too powerful causing some developers to make some architecture mistakes. The two-way data binding and the power of directives are awesome, but you need to think about what are you doing and try to use some best practices to avoid common pitfalls during the development process.

Controllers are class-like objects to “control” the model and update the view, and as you know everything is based around the magic and mystic $scope property.

A good practice is to avoid binding everything to $scope, because too many bindings crowd the watch list of the $digest loop. To avoid that, Angular give us the controllerAs property.

Writing controllers as classes

A class in Javascript (at least in ES5 for now) is something like this:

var aClass = function () {
  this.name = 'Class name';
};
var instance = new aClass();

With this you can use the instance variable to access methods and properties.

Using the controllerAs property we write our Controllers in the same way, using this instead of $scope

angular.module('myApp')
.controller('MyCtrl', function () {
    this.name = 'Controller Name';
});

Now this can be instantiated in the template with something like the following:


To access the properties and methods of the controller you use the vm instance.

With this you are namespacing the scopes, making the code cleaner and readable. Think about nested scopes.

{{ name }}
{{ name }}
{{ name }}

Here you can see that each controller is accessing the name property, but the question is: which one? That code looks very confusing and is probably that one controller takes precedence over another, but you don’t know which one.

Using the controllerAs syntax this will be much cleaner:

Base scope: {{ base.name }}
Section scope: {{ section.name }} Base scope: {{base.name}}
{{ final.name }}

As we can see in the above code, using controllerAs syntax allows us access to parent scopes without the hassle of scope collision and without using $parent to access it.

How to set watchers

One question that comes to mind when you use this kind of syntax is how to use a $watch call because you need to inject $scope. We fight to remove the use of $scope, and now we need to inject it anyway.

Well, we can keep using controllerAs and keep binding methods and properties to the this object that is binded to the current $scope. At the same time, we can keep the separation of concerns using $scope only for special cases, like $watch, $on, or $broadcast.

Keep in mind that using controllerAs the syntax for $watch method changes a little bit. Normally you would do something like the following:

app.controller('Ctrl', function ($scope) {
    $scope.name = 'name';

    $scope.$watch('name', function (newVal, oldVal) {

    });
});

But that doesn’t work now, because $watch is looking for the watched property inside the $scope, and you don’t directly bind that property to $scope. Instead watched property is binded to this. The correct way to do it now is as shown in the following example:

app.controller('Ctrl', function ($scope) {
    this.name = 'name';

    $scope.$watch(function () {
      return this.title
    }.bind(this), function (newVal, oldVal) {

    });
});

Alternative is using angular.bind:

app.controller('Ctrl', function ($scope) {
    this.name = 'name';

    $scope.$watch(angular.bind(function () {
      return this.title
    }), function (newVal, oldVal) {

    });
});

How can I declare controllerAs without using the DOM attributes?

In the case of directives, you have the controllerAs property inside the directive signature:

app.directive('Directive', function () {
    return {
      restrict: 'EA',
      templateUrl: 'template.html',
      scope: true,
      controller: function () {},
      controllerAs: 'vm'
    }
});

Or for controllers in the $routeProvider:

app.config(function ($routeProvider) {
  $routeProvider
  .when('/', {
    templateUrl: 'main.html',
    controllerAs: 'main',
    controller: 'MainCtrl'
  })
});

Contributors

jQuery counter to count up to a target number

<span class=”timer”></span>
<hr/>
<span id=”help”>From: 50 – To: 2500 / Over 5000 Milli-Seconds</span>

(function($) {
$.fn.countTo = function(options) {
// merge the default plugin settings with the custom options
options = $.extend({}, $.fn.countTo.defaults, options || {});

// how many times to update the value, and how much to increment the value on each update
var loops = Math.ceil(options.speed / options.refreshInterval),
increment = (options.to – options.from) / loops;

return $(this).each(function() {
var _this = this,
loopCount = 0,
value = options.from,
interval = setInterval(updateTimer, options.refreshInterval);

function updateTimer() {
value += increment;
loopCount++;
$(_this).html(value.toFixed(options.decimals));

if (typeof(options.onUpdate) == ‘function’) {
options.onUpdate.call(_this, value);
}

if (loopCount >= loops) {
clearInterval(interval);
value = options.to;

if (typeof(options.onComplete) == ‘function’) {
options.onComplete.call(_this, value);
}
}
}
});
};

$.fn.countTo.defaults = {
from: 0,  // the number the element should start at
to: 100,  // the number the element should end at
speed: 1000,  // how long it should take to count between the target numbers
refreshInterval: 100,  // how often the element should be updated
decimals: 0,  // the number of decimal places to show
onUpdate: null,  // callback method for every time the element is updated,
onComplete: null,  // callback method for when the element finishes updating
};
})(jQuery);

jQuery(function($) {
$(‘.timer’).countTo({
from: 50,
to: 2500,
speed: 5000,
refreshInterval: 50,
onComplete: function(value) {
console.debug(this);
}
});
});

jQuery – Click event doesn’t work on dynamically generated elements

The click() binding you’re using is called a “direct” binding which will only attach the handler to elements that already exist. It won’t get bound to elements created in the future. To do that, you’ll have create a “delegated” binding by using on():

Delegated events have the advantage that they can process events from descendant elements that are added to the document at a later time.

Here’s what you’re looking for:

$("h2").on('click', 'p.test', function() {
    alert('you clicked a p.test element');
});

The above works for those using jQuery version 1.7+. If you’re using an older version, refer to the previous answer below
Try using live()

$("button").click(function(){
    $("h2").html("<p class='test'>click me</p>")
});   


$(".test").live('click', function(){
    alert('you clicked me!');
});

Or there’s a new-fangled way of doing it with delegate()

$("h2").delegate("p", "click", function(){
    alert('you clicked me again!');
});

jquery attr(‘checked’,’checked’) works only once

If you face this kinda issue in jquery like if you checked multiple boxes by jquery and after that remove the checked property and again click to checked multiple checkboxes . Some time it never works .

Use prop(‘checked’, true / false) instead of removeAttr

$('input[name=foo]').prop('checked',true);
$('input[name=foo]').prop('checked',false);

JS jQuery – check if value is in array

You are comparing a jQuery object (jQuery('input:first')) to strings (the elements of the array).Change the code in order to compare the input’s value (wich is a string) to the array elements:

if(jQuery.inArray(jQuery("input:first").val(), ar)!=-1)

The inArray method returns -1 if the element wasn’t found in the array, so as your bonus answer to how to determine if an element is not in an array, use this : 

if(jQuery.inArray(el,arr)==-1){// the element is not in the array};