Skip to content
This repository has been archived by the owner on Aug 6, 2024. It is now read-only.

Tips and Tricks which helped me during my RIA development with AngularJS. [GitLab Mirror]

License

Notifications You must be signed in to change notification settings

Ulthes/angular-tips-und-tricks

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

35 Commits
 
 
 
 
 
 

Repository files navigation

angular-tips-und-tricks

Tips and Tricks which helped me during my RIA development with AngularJS..

Credits where credits due

This documentation would not exist if not the great AngularJS Design by John Papa. So, read his Style Guide before going through here.

Table of Contents

  1. angular.isDefined vs JavaScript's '!!'
  2. Distributing reference among directives via service
  3. Disposing watchers
  4. Calling parent directive's function from child directive
  5. Events broadcasts
  6. Clean up array without losing reference

Angular.isDefined vs JavaScript's '!!'

Let's look at scenario where you have and object with properties and you need some property from that object, but you don't know whether this object (and property!) is null/undefined or not. If you use angular.isDefined you will only check if variable is different from undefined ONLY, whereas !! notation will check if variable is different from null or undefined.

If you want to check if object is undefined/null just use ! (like !someVariable)

Distributing reference among directives via service

Normally, when we pass reference between directives we'd normally declare a variable inside controller, and then pass it through view by ng-model. However, this creates few problems:

  • We're polluting controller, which only creates an object and (mostly) nothing else.
  • We're polluting the view.
  • Finally, we're creating two watchers (ng-model are watchers) for sending a single reference.

Instead we could have done something like this:

Create a service which exposes just one field:

    (function(){
        angular
            .module("app")
            .service("someService", someService);

        someService.$inject = [];


        function someService(){
            var service = {
                reference : null
            }

            return service;
        }
    })();

This will be service which only exposes a reference to whatever entity will inject it. Now, let's make a directive which injects this service and assigns some function to reference:

    (function(){
        angular
            .module("app")
            .directive("firstDirective", firstDirective);

        firstDirective.$inject = ["someService"];

        function firstDirective(someService){
            var directive = {
                controller: MainController,
                scope: {},
                restrict: 'EA',
                template: '',
                controllerAs: 'vm',
                bindToController: true
            }

            return directive;

            function MainController(){
                var vm = this;

                function showAlertBox(){
                    alert("I'm from first directive!");
                }

                someService.reference = showAlertBox;
            }
        }
    })();

And now we're going to create a second directive which will activate function from first directive on button click:

    (function(){
        angular
            .module("app")
            .directive("secondDirective", secondDirective);

        secondDirective.$inject = ["someService"];

        function secondDirective(someService){
            var directive = {
                controller: MainController,
                scope: {},
                restrict: 'EA',
                template: '<div><button type="button" ng-click="vm.OnButtonClick()">Click Me!</button></div>',
                controllerAs: 'vm',
                bindToController: true
            }

            return directive;

            function MainController(){
                var vm = this;
                vm.OnButtonClick = OnButtonClick;

                function OnButtonClick(){
                    someService.reference();
                }
            }
        }
    })();

This way, when you click a button, function from firstDirective will be called. Of course, it's always better to use functions like that instead of watchers. Sure, you can use a watcher on a referenced object, you still will avoid polluting the view, so this way, you are omitting the Angular's $scope and the $digest cycle.

Disposing watchers

Sometimes, when you really have to use watchers or bindings in directive or controller, it's good to dispose them once $destroy event shots. But then again, when there is a lot of code you might forget about them. It's good place to put them in object so afterwards you can clean it up just by using simple loop on it's properties.

For instance, let's say we have 4 different watchers:

    $scope.$on("rootScopeEventOne", doSomethingOne);
    $scope.$on("rootScopeEventTwo", doSomethingTwo);
    $scope.$on("rootScopeEventThree", doSomethingThree);
    $scope.$on("rootScopeEventFour", doSomethingFour);

If we put them all in object, like this:

    var Listeners = {};
    Listeners.firstListener = $scope.$on("rootScopeEventOne", doSomethingOne);
    Listeners.secondListener = $scope.$on("rootScopeEventTwo", doSomethingTwo);
    Listeners.thirdListener = $scope.$on("rootScopeEventThree", doSomethingThree);
    Listeners.fourthListener = $scope.$on("rootScopeEventFour", doSomethingFour);

We can clear all watchers by doing this, once $destroy event fires up:

    function onDestroy(){
        for (var key in Listeners){
            Listeners[key]();
        }
    }

    $scope.$on("$destroy", onDestroy);

Same goes for $watch, like this:

    var Listeners = {};

    Listeners.someWatcher = $watch("mouseclick", functionToEvaluateForClickEvent);

    function onDestroy(){
        for (var key in Listeners){
            Listeners[key]();
        }

    }

Calling parent directive's function from child directive

You can call parent directive's function from it's child, if you assign it to the this. Let's assume we're going with John Papa's styleguide, so we would do this for every directive:

var vm = this;

We're assigning the context's instance to variable vm and we're not polluting the $scope, but we can still use expose any variables and functions, which are assigned to the vm. Now comes the funny part. Once you expose the function in first directive, you can inform the second directive that it requires the first directive. That way, you can use anything you have exposed in first directive to vm.

(function(){
    angular
            .module("app")
            .directive("firstDirective", firstDirective);

        firstDirective.$inject = [];

        function firstDirective(){
            var directive = {
                controller: MainController,
                scope: {},
                restrict: 'EA',
                template: '',
                controllerAs: 'vm',
                bindToController: true
            }

            return directive;

            function MainController(){
                var vm = this;
                vm.someFunctionToBeExposed = someFunctionToBeExposed;

                function someFunctionToBeExposed(args){
                    // do something here
                }
            }
        }

        angular
            .module("app")
            .directive("secondDirective", secondDirective);

        secondDirective.$inject = [];

        function secondDirective(){
            var directive = {
                link: MainLink,
                controller: MainController,
                scope: {},
                require: ['^firstDirective'], //important to work! The '^' sign informs the secondDirective to look for the firstDirective up the DOM tree.
                restrict: 'EA',
                template: '',
                controllerAs: 'vm',
                bindToController: true
            }

            return directive;

            function MainController() {
                var vm = this;
            }

            function MainLink(scope, element, attrs, firstDirectiveCtrl){ //inject the elements from 'require' array
                //Now, in order to invoke the function you use it like this:
                firstDirectiveCtrl[0].someFunctionToBeExposed(someArguments);
            }
        }
})();

For some reason I couldn't re-create communication with directives on the same DOM level. I'm not sure why, so be mindful that it might not work. More info about require in AngularJS docs:

Events broadcasts


Remember:

    $rootScope.$broadcast(..);

is a no-go!


When you use it, you send that event EVERYWHERE (and you probably just wanted to send event from/to controller/directive to/from directive). When you use that kind of broadcast you can slow down your application pretty much and cause lots of problems with synchonization between components.

If you REALLY need to send an event, better use:

    $scope.$broadcast(..);

to send event from parent to children OR:

    $scope.$emit(..);

to send event from the child to parent. Either way, you keep it in the gean... geanola...you keep it in the family tree of the view.

Live Example Coming Soon

Clean up array without losing reference

Let's say, you have an array, which is bound to the view (or it is referenced to some service endpoint) and you need to clean it up completely. You probably would use this:

    someArray = [];

What does that do? It cleans up the array, sure, but at the same time you set a new instance of that array and completely loose any references of that array in other places.

But, if you use it like this:

    someArray.length = 0;

You clean up array and you keep reference.

Live Example Coming Soon.

About

Tips and Tricks which helped me during my RIA development with AngularJS. [GitLab Mirror]

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published