AngularJS Tips and Tricks for Beginners

AngularJS Tips and Tricks for Beginners

Unlike some other JavaScript frameworks, AngularJS requires the developer to do things the “Precise” way, which is a set of rules and best practices that are critical to building AngularJS web applications properly. This article contains a collection of AngularJS Tips provided by Fixtheerror.com network.

How To Make ‘ng-click’ Conditional ?

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.

Here is the solution

<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, particularly when the duplicated elements are small, but it is not recommended when working with larger amounts of DOM elements. One approach to eliminate this drawback is to put the elements into separate templates, so even if the code is duplicated, it becomes more readable.

How to Use ‘controllerAs’ Syntax properly ?

Writing controllers as classes :

A class in Javascript 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 similarly, using this rather than $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 can use the vm instance.

Condider 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 most likely 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 permits us access to parent scopes without the bother of scope collision and without using $parent to access it.

How to Pass Callback Function to the Directive ?

You have some kind of function that the directive needs to call from outside to get back some data. There is a kind of data binding to use. However, it’s a bit difficult to work with and it is called expression binding.

<!DOCTYPE html>
<html>
<body ng-app="myApp" ng-controller="MyController as myCtrl">
            <expandable-section  section-title="myCtrl.myTitle"
                                 section-body="myCtrl.myBody"
                                 some-expression="myCtrl.someFunction()">
            </expandable-section>
            <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
            <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.19/angular.min.js"></script>
            <script src="app.js"></script>
</body>
</html>

With normal binding, you simply pass in the function, however you lose the context (in this case, myCtrl). To retain that context, you’d need to do some special stuff to make it work.

var app = angular.module( 'myApp', [ ] );
app.controller('MyController', function() {
            this.myTitle = "Some title" ;
            this.myBody = "Some body" ;
               console.log('Hello', this)
});

app.directive('expandableSection', function() {
            return {
            restrict: 'E',
            template: '<section><h2>{{title}}</h2><article>{{body}}</article>'
            replace: true,
            scope: {
            title: '=sectionTitle',
            body: '=sectionBody'
            expr: '&someExpression' //here you can bind the someExpression with the & sign
            },
            link: function($scope, $element, attrs) {
                        $scope.expr();
                        $element.find('article').hide();
                        $element.find('h2', click(function() {
                                    $element.find('article').toggle();
                        });
              }
            };
});

After you do this, the $scope.expr() becomes a function that evaluates the expression myCtrl.someFunction.

However, this is only nice when you don’t have any arguments to pass back. When you have arguments in myCtrl.someFunction(), you’ll notice your expression binding does not quite work anymore.

You can fix this by changing expr: ‘&someExpression’ to expr: ‘=someExpression’ and

some-expression = “myCtrl.someFunction(magicArgument)” to some-expression = “myCtrl.someFunction”. However,  you’d lose the context of myCtrl this way.

A way several internals of the Angular directive chooses to it is to keep the Angular Argument but instead of binding the expression to the scope, it is parsed.

attrs.someExpression
$scope.expr(42);

var app = angular.module('myApp', []);
app.controller('MyController', function() {

            this.myTitle = "Some title";
            this.myBody = 'Some body';
              console.log('Hello', this)
            this.someFunction = function(magicArg) {
              console.log('hello', magicArg, this);
}
});

app.directive('expandableSection', function($parse) { //this injects Angular's $parse services into the directive
            return {
            restrict: 'E',
            template: '<section><h2>{{title}}</h2><article>{{body}}</article>'
            replace: true,
            scope: {
                      title: '=sectionTitle',
                      body: '=sectionBody' //instead of binding someExpression to the scope, we parse it instead
            },

            link: function($scope, $element, attrs) {
                        var expr = $parse(attrs.someExpression)
                        expr($scope.$parent, {magicArgument: 42}); //we'd have to parse the expression to the parent scope and pass in the named locals for it, which is {magicArgument: 42}
                        $element.find('article').hide();
                        $element.find('h2', click(function() {
                                    $element.find('article').toggle();
                        });
              }
            };
});

I’ve seen this done in the Angular source code, but I don’t especially think it’s a good idea to do a lot of this in the application code, since it looks like internal stuff that maybe you shouldn’t be doing. However, from the user’s point of view, this is probably the cleanest and most pleasant way to do things, as it hides the complex stuff inside the directive.

How to define functions in the $rootScope ?

The $rootScope is a global, which means that anything you add here, automatically becomes available in $scope in all controller. To set it up, you need to do something like this.

app.run(function($rootScope) {
  $rootScope.hello = function() {
    console.log('hello');
  }
});

This should now be available in controllers

function MainCtrl = function($scope) {
  $scope.save = function() {
  $scope.hello();
  }
};

How to set watchers ?

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 searching 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 right approach 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) {
    });
});

Service vs Factory in AngularJS

These names cause confusion for almost every AngularJS developer when beginning. Here are their definitions.

function factory(name, factoryFn) {
    return provider(name, { $get: factoryFn });
}

function service(name, constructor) {
    return factory(name, ['$injector', function($injector) {
      return $injector.instantiate(constructor);
    }]);
}

From the source you can see that service just calls the factory function which in turn calls the provider function. In fact, AngularJS also offers a few additional provider wrappers with value, constant, and decorator. These other objects don’t cause nearly the same level of confusion and the docs are pretty clear on their use cases.

Since service just calls the factory function, what makes it different? The clue is in $injector.instantiate; within this function the $injector creates a new instance of the service’s constructor function.

Here’s an example of a service and a factory that accomplish the same thing.

var app = angular.module('app',[]);
app.service('helloWorldService', function(){
    this.hello = function() {
        return "Hello World";
    };
});

app.factory('helloWorldFactory', function(){
    return {
        hello: function() {
            return "Hello World";
        }
    }
});

When either helloWorldService or helloWorldFactory are injected into a controller, they will both have a hello method that returns “Hello World”. The service constructor function is instantiated once at declaration and the factory object is passed around every time it is injected, but there is still just one instance of the factory. All providers are singletons.

Here’s an example controller using the service and the two factories. With the helloFactory returning a function, the name value is set when the object is new.

app.controller('helloCtrl', function($scope, helloWorldService, helloWorldFactory, helloFactory) {
    init = function() {
      helloWorldService.hello();    //'Hello World'
      helloWorldFactory.hello();   //'Hello World'
      new helloFactory('Readers').hello()   //'Hello Readers'
    }
    init();
});

Submit your AngularJS Errors, issues or problems and get it fixed by an Expert Programmer