Friday, 19 October 2018

AngularJS Best Practices and Tips by Toptal Developers

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:
<div ng-controller="MyCtrl as vm">
  <h1 ng-bind="vm.name"></h1>
</div>
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.
<div ng-controller="BaseCtrl">
  {{ name }}
  <div ng-controller="SectionCtrl">
    {{ name }}
    <div ng-controller="FinalCtrl">
      {{ name }}
    </div>
  </div>
</div>
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:
<div ng-controller="BaseCtrl as base">
  Base scope: {{ base.name }}
  <div ng-controller="SectionCtrl as section">
    Section scope: {{ section.name }}
    Base scope: {{base.name}}
    <div ng-controller="FinalCtrl as final">
      {{ final.name }}
    </div>
  </div>
</div>
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

Matías is an experienced software engineer with more than seven years of work as a freelancer, giving him lots of experience with a number of companies all around the globe and a variety of challenging projects. He has proven experience helping companies to develop new features, improve performance, or fix their apps. As a consultant, he can help companies to grow and scale their apps for future features and changes.

Like what you're reading?Get the latest updates first.

Use bindToController in Your Directives.

One of the Angular best practices when writing directives is to use the controllerAs syntax. This syntax provides us a native namespacing in the controller.
Just to remember, this change was introduced in Angular version 1.2. Before, everyone was writing controllers using the mystical $scope object, which quickly resulted with $parent problems and the properties flying around the DOM.
The controllerAs syntax enables us to instantiate our controllers as class-like objects. In vanilla JavaScript, this would look like this:
var aClass = function () {
  this.name = "My class";
};
var instance = new aClass();
Here, we can use the instance variable to access the methods and properties from the aClass. In Angular, with controllerAs we get similar syntax for our controllers.
By covering the basics, we can now return to our main topic. When writing controllers using the controllerAssyntax we can face some issues. We are writing the components as class-like objects, but we keep injecting $scope only to get access to the isolated scope properties:
function controller ($scope, getNameFromSomeService) {
  this.foo = {};
  this.do = function () {
    getNameFromSomeService.get().then(function (response) {
      this.foo.bar = 'something';
      $scope.name = response; //reference to the isolated property
    }.bind(This));
  }
}

function directive () {
  return {
    restrict: 'E',
    scope: {
      name: '='
    },
    controller: 'controller',
    controllerAs: 'vm',
    templateUrl: 'some/template/file.html'
  }
}

angular.module('app')
.controller('controller', controller)
.directive('myDirective', directive);
As you can notice, our controller was in need to inject the $scope only to get access to name property. We want to get rid of this object and adopt Angular best practices.

bindToController to Rescue

The bindToController property allows us to bind isolated properties to the controller object instead of the $scope. Quote from the documentation:
When an isolate scope is used for a component (see above), and controllerAs is used, bindToController: true will allow a component to have its properties bound to the controller, rather than to scope. When the controller is instantiated, the initial values of the isolate scope bindings are already available.
Having this in mind, we can refactor our controller like this:
function controller (getNameFromSomeService) {
  this.foo = {};
  this.do = function () {
    getNameFromSomeService.get().then(function (response) {
      this.foo.bar = 'something';
      this.name = response; //reference to the isolated property but using `this`
    }.bind(This));
  }
}

function directive () {
  return {
    restrict: 'E',
    scope: {
      name: '='
    },
    bindToController: true
    controller: 'controller',
    controllerAs: 'vm',
    templateUrl: 'some/template/file.html'
  }
}
It is worth noting this new property has more power than described in the documentation. You can use object to define the property which will allow you to move the property from the scope definition to bindToController
{% highlight javascript %}
function directive () {
  return {
    restrict: 'E',
    scope: {},
    bindToController: {
      name: '='
    },
    controller: 'controller',
    controllerAs: 'vm',
    templateUrl: 'some/template/file.html'
  }
}
Now our controllers don’t need the injection of $scope (we will need that only for events or watchers).
After this refactor we need to change our template too. The original template probably looks something like this:
<div>
  <label ng-bind="name"></label>
  <label ng-bind="vm.foo.bar"></label>
</div>
We need to change this template to the following one:
<div>
  <label ng-bind="vm.name"></label>
  <label ng-bind="vm.foo.bar"></label>
</div>
Now the name property is accessed from the vm namespacing.

Contributors

Matías is an experienced software engineer with more than seven years of work as a freelancer, giving him lots of experience with a number of companies all around the globe and a variety of challenging projects. He has proven experience helping companies to develop new features, improve performance, or fix their apps. As a consultant, he can help companies to grow and scale their apps for future features and changes.

Like what you're reading?Get the latest updates first.

How To Make ‘ng-click’ Conditional?

Say you have an element that must always be visible, no matter if the user is logged in or not. If the user is logged in, the element needs to be clickable. Example pseudo code would look like this:
<div ng-click=”somefunction() if currentUser.signedIn()”>Click me</div>
Unfortunately, ng-if is not an option. If ng-if is set to false, the element will not be seen at all. You could let it always be clickable, and then check if the user is logged in when you enter ng-click function, but this is not the best solution.
Toptal engineers have suggested two solutions to this problem.
One solution is to precede the function call with a boolean expression, as in this example:
<div ng-click=“currentUser.signedIn() && somefunction()”>Click me</div>
The solution works, but it is not easily readable and therefore not recommended. Using non standard techniques and applying tricks to the code can become a big code maintenance problem. Even the developer who wrote the code could forget what problem he was solving, and why it was written this way. And what if a new developer takes over?
Another solution involves writing more HTML code and adding two identical elements that have different behaviors.
Example pseudo code in that case would look like this:
<div ng-if="currentUser.signedIn()" ng-click="somefunction()">Click me</div>
<div ng-if="!currentUser.signedIn()">You can’t click me</div>
This should work well, especially when the duplicated elements are small, but it is not recommended when working with larger amounts of DOM elements. One way to eliminate this drawback is to put the elements into separate templates, so, even if the code is duplicated, it becomes more readable.

Contributors

Rodrigo is an experienced and results-driven full-stack software developer with great problem-solving and communication skills. He strives to deliver efficient and reliable products that delight both clients and users.
Kamil is an experienced full-stack web developer, fully capable of carrying out complex projects from the concept phase through architecture design, implementation, and DevOps to maintenance. He takes care of all technical details along the way so his clients don't have to!
Richard is a friendly, well-spoken, outgoing, intelligent individual who enjoys problem-solving. He carefully assesses the requirements of a task, taking the opportunity to think it through and working efficiently to produce easily maintainable and complete products.

Like what you're reading?Get the latest updates first.

What Is One-Time Binding?

Angular 1.3 introduced an awesome new performance boost feature - one-time binding. Official Angular documentation describes it as:
One-time expressions will stop recalculating once they are stable, which happens after the first digest if the expression result is a non-undefined value.

So, how does it work?

Let’s remember the $digest cycle is basically a loop that runs under the hood of Angular where every binded data is added to a watcher list. The loop checks list for changes, it ends when no further change is found, and then triggers a DOM re-rendering.
When regular binding is used, that property is binded to the watcher list and it’s never removed from the list. This makes the $digest loop run through all the binded data. The bigger the application, the bigger list of bindings and watchers is, and finally the bigger the size of our $digest is. With bigger loops, performances decrease, and in the case of a really big code base, the application could become even unresponsive.
As mentioned, starting from Angular 1.3 we can declare a value with the new one-time binding syntax. Angular will render this new value when it becomes defined and immediately unbind it from the watchers. This way, the number of bindings inside the $digest loop will be reduced.

So, how can I use it?

The syntax for this is very simple: just add :: in front of any value and it will be considered as an one-time binding.
Usually you would do something like this:
<label>{{'BASE_STRING' | translate}}</label>
New one-time binding versions look like this:
<label>{{::'BASE_STRING' | translate}}</label>
The translation value will not change, so it is not necessary to have it inside the $digest loop forever. By unbinding translation value, we increase application performance.
Another use case is with ng-repeat, like in the following example:
<ul>
  <li ng-repeat="item in ::vm.list"></li>
</ul>
This will unbind items from the list when the ng-repeat finish the population process, removing a lot of watchers from our $digest loop.
Go ahead, upgrade to Angular 1.3 and try this to improve your application.

Contributors

Matías is an experienced software engineer with more than seven years of work as a freelancer, giving him lots of experience with a number of companies all around the globe and a variety of challenging projects. He has proven experience helping companies to develop new features, improve performance, or fix their apps. As a consultant, he can help companies to grow and scale their apps for future features and changes.

Like what you're reading?Get the latest updates first.

How To Deploy An AngularJS Apps?

There’s more than one way to skin a system architecture. How do the AngularJS professional engineers at Toptal do it?
One Toptal developer recently described a system architecture he had successfully implemented. Each service required by the application, be it a load balancer, a database, or the app server, is run in its own Docker image as a sort of miniaturized Service-Oriented Architecture. This way, only a few of the images are redeployed for each production push, leaving the rest up and running without interruption.
The advantage of this approach is that you don’t need to worry about server configuration. With Docker, you can easily create the production environment on your laptop, test it, play with it, and then launch the same virtual machine-images on the production server and still be 100 percent sure that they will behave the same way. You can also easily scrap the whole server at any time and re-build it from a script. In this scenario, the software will always run in a pristine environment. For example, sometimes you configure a server, edit a system configuration file and then forget about it and your software works fine. But when you try to run it on another server (which doesn’t have the configuration change you’ve forgotten about), your software will not run and you will spend a lot of time chasing this ”weird” problem. With Docker, such things do not exist; servers are re-built with 100 percent repeatability.
Another advantage working with Docker is that it easily allows you to separate dependencies in various parts of your architecture. For example, if your web server requires Ruby 2.x and some other part of the app (like some background worker) requires Ruby 1.x, typically, it is difficult to have that working on a single server. With Docker, you bundle Ruby 2.x with the app server, and Ruby 1.x with the worker on another image, and they happily live with each other, not interfering at all.
How do you manage so many images? Deployment configuration tools such as Ansible do the trick nicely. By giving you easy and comprehensive control over system configurations, version tracking, and deployments, managing the entire build is a breeze. You can write Ansible snippets for setting up Docker remotely, transferring machine-images to the server, and then starting containers remotely, along with some minimal supervisory configuration to keep them alive in case of problems. So basically, you end up deploying whole app simultaneously onto several servers in parallel, without doing single SSH manually to any of those servers. By making sure you execute the Ansible scripts carefully, and in the right order, it is relatively easy to achieve zero-downtime by performing rolling upgrades ith a microservice architecture, all parts are duplicated and behind the load balancer.
The only disadvantage is the complexity of this setup, so it is not for novice developers.

No comments:

Post a Comment

Angular Tutorial (Update to Angular 7)

As Angular 7 has just been released a few days ago. This tutorial is updated to show you how to create an Angular 7 project and the new fe...