Tuesday, June 17, 2014

Angular Directives Part 7 - Angular directive shows $comple

Shows how to repeat elements

Very simple directive that will repeat a particular element a given number of times:

<div repeat-x="5">Hello</div>

// Directive to repeat an element x number of times
angular.module('app').directive('repeatX', [function () {
    return {
        link: function (scope, element, attrs, controller) { // The link function gets called for every instance of this directive.
            for (var i = 0; i < Number(attrs.repeatX) - 1; i  ) {
                // When we clone the element, we need to modify the attribute because it has repeat-x attribute.
                // We modify the attribute of the cloned element so that they repeat zero times.
                element.after(element.clone().attr('repeat-x', 0));
            }
        }
    };
}]);
But if you try to add binding, it's not going to work. The solution is to use the compile service:
    Text to repeat: <input type="text" ng-model="text" />
    <div repeat-x="5">{{text}}</div>

JavaScript

// Directive to repeat an element x number of times
angular.module('app').directive('repeatX', ['$compile', function ($compile) {
    return {
        link: function (scope, element, attrs, controller) { // The link function gets called for every instance of this directive.
            for (var i = 0; i < Number(attrs.repeatX) - 1; i  ) {
                // When we clone the element, we need to modify the attribute because it has repeat-x attribute.
                // We modify the attribute of the cloned element so that they repeat zero times.
                element.after($compile(element.clone().attr('repeat-x', 0))(scope));
            }
        }
    };
}]);
The $compile service is pretty expensive. The alternative is to use the compiler function:

JavaScript

// Directive to repeat an element x number of times
angular.module('app').directive('repeatX', [function () {
    return {
        // The compile function is used to manipulate the dom prior to the link function executing
        // Note that unlike the link function, there's no scope or controller being passed in.
        // compile function runs once and affects all instances of the directive the same way.
        // The link function runs individually for each instance / usage of the directive in the HTML.
        compile: function (element, attrs) {
            for (var i = 0; i < Number(attrs.repeatX) - 1; i  ) {
                // When we clone the element, we need to modify the attribute because it has repeat-x attribute.
                // We modify the attribute of the cloned element so that they repeat zero times.
                element.after(element.clone().attr('repeat-x', 0));
            }
        }
    };
}]);
The compile function can return a link function. Let's change the html and give a text attribute:
    Text to repeat: <input type="text" ng-model="text" />
    <div repeat-x="5" text="{{text}}">{{text}}</div>
Now observe the text attribute and when it changes to "Hello World", make it red.
// Directive to repeat an element x number of times
angular.module('app').directive('repeatX', [function () {
    return {
        // The compile function is used to manipulate the dom prior to the link function executing
        // Note that unlike the link function, there's no scope or controller being passed in.
        // compile function runs once and affects all instances of the directive the same way.
        // The link function runs individually for each instance / usage of the directive in the HTML.
        compile: function(element, attrs) {
            for (var i = 0; i < Number(attrs.repeatX) - 1; i++) {
                // When we clone the element, we need to modify the attribute because it has repeat-x attribute.
                // We modify the attribute of the cloned element so that they repeat zero times.
                element.after(element.clone().attr('repeat-x', 0));
            }

            // Optionally return a link function, which will be executed for each element.
            return function (scope, element, attrs, controller) {
                attrs.$observe('text', function(newValue) {
                    if (newValue === 'Hello World') {
                        element.css('color', 'red');
                    }
                });
            };
        }
    };
}]);

Monday, June 16, 2014

Angular Directives Part 6 - Angular directive with transclude



Convert HTML that looks like this:

    <collapsible title="Star Wars">
        <h6>Show times:</h6>
        <p>12 PM, 3 PM, 6:15 PM, 8:45 PM</p>
        <h6>Theaters:</h6>
        <p>Sherman Oaks Galleria, La Reina Theater</p>
    </collapsible>

to use a collapsable directive like this:

    <h4>Star Wars</h4>
    <h6>Show times:</h6>
    <p>12 PM, 3 PM, 6:15 PM, 8:45 PM</p>
    <h6>Theaters:</h6>
    <p>Sherman Oaks Galleria, La Reina Theater</p>

JavaScript:

// Use transclusion, without it, the replace=true will replace all child elements, too.
// by transclude=true will replace the element with the ng-transclude attribute with the child elements.
angular.module('app').directive('collapsible', [function () {
    return {
        restrict: 'E',
        replace: true,
        template: '

', controller: function($scope) { $scope.visible = true; $scope.toggleVisibility = function() { $scope.visible = !$scope.visible; } }, transclude: true, scope: { title: "@" } }; }]);

Sunday, June 15, 2014

Angular Directives Part 5 - Angular directive with shared controllers


HTML - Option 1:

<greeting hindi finnish />

HTML - Option 2:

<!--Can be nested--> <greeting> <div hindi finnish></div> </greeting>

JavaScript:


angular.module('app').directive('greeting', [function () {
    return {
        restrict: 'E',
        replace: true,
        transclude: true,
        template: "<div><button class='btn' ng-click='sayHello()'>Say Hello</button><div ng-transclude></div></div>",
        controller: function ($scope) {
            var greetings = ['hello'];
            $scope.sayHello = function () {
                alert(greetings.join());
            }

            // Let's add on this controller a way to add different greetings.
            this.addGreeting = function (greeting) {
                greetings.push(greeting);
            }
        }
    };
}]);


angular.module('app').directive('finnish', [function () {
    return {
        restrict: 'A',

        // The ^ tells Angular to support this direcitve being nested in the specified directive.
        require: '^greeting', // << require a name of a directive that has a controller in it.
                            // "In order to use this 'finnish' directive, i'm going require that there
                            // be another directive called 'greeting' and that 'greeting' controller needs
                            // to have a controller on it, which will become the shared controller for
                            // these directives.   Also, needs to be on the same element.
        // The link function gets called for every instance of this directive.
        link: function (scope, element, attrs, controller) { // The link function gets called for every instance of this directive.
            controller.addGreeting('hei');
        }
      };
}]);

angular.module('app').directive('hindi', [function () {
    return {
        restrict: 'A',
        // The ^ tells Angular to support this direcitve being nested in the specified directive.
        require: '^greeting', // << require a name of a directive that has a controller in it.
        link: function (scope, element, attrs, controller) { // The link function gets called for every instance of this directive.
            controller.addGreeting('%u0928%u092E%u0938%u094D%u0924%u0947');
        }
    };
}]);

Saturday, June 14, 2014

Angular Directives Part 4 - Angular directives with Controllers


HTML

    <div>
        <greeting1 />
    </div>
    <div>
        <greeting2 />
    </div>
    <div>
        <greeting3 greeting-controller="greetingController3" />
    </div>

JavaScript

// Controller's built-in to the directive.
angular.module('app').directive('greeting1', [function () {
    return {
        restrict: 'E',
        replace: true,
        template: "<button class='btn' ng-click='sayHello1()'>Say Hello</button>",
        controller: function($scope) {
            $scope.sayHello1 = function() {
                alert('Hello1');
            }
        }
    };
}]);

// Controller's specified externally.
angular.module('app').directive('greeting2', [function () {
    return {
        restrict: 'E',
        replace: true,
        template: "<button class='btn' ng-click='sayHello2()'>Say Hello2</button>",
        controller: 'greetingController2'
    };
}]);

angular.module('app').controller('greetingController2', ['$scope', function ($scope) {
    $scope.sayHello2 = function() {
        alert('Hello2');
    };
}]);

// Controller's specified explictly via attribute.
angular.module('app').directive('greeting3', [function () {
    return {
        restrict: 'E',
        replace: true,
        template: "<button class='btn' ng-click='sayHello3()'>Say Hello3</button>",
        controller: '@', // This means the controller is specified via HTML
                         // in the attribute whose name is indicated in the name property of this object
        name: 'greetingController' // The attribute name that contains the name of the controller.
    };
}]);

angular.module('app').controller('greetingController3', ['$scope', function ($scope) {
    $scope.sayHello3 = function () {
        alert('Hello3');
    };
}]);

Friday, June 13, 2014

Angular Directives Part 3 - Small app on a page using Angular


You see this code run here: http://kasajian.github.io/Ancus/ball.html
 <html ng-app="appz" ng-cloak>
<head>
    <title ng-bind="title"></title>

    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">


    <script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
    <script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.18/angular.js"></script>


    <script>
angular.module('appz', [])
angular.module('appz').directive('ball', ['googleFactory', function (googleFactory) {

    var setAttribute = function(attrs, newValue, element) {

        googleFactory.getSearchResults(" ball "   newValue, 'images').then(function (response) {
            var currentValue = element[0].attributes['type'].value;
            if (currentValue !== newValue) {
                setAttribute(attrs, currentValue, element);
            } else {
                var results = response.data.responseData.results;
                attrs.$set('src', results[0].unescapedUrl);
            }
        }).catch(function (error) {
            console.error(error);
        });
    };

    return {
        restrict: 'E',
        template: '<img height="100"/>',
        replace: true,
        link: function (scope, element, attrs) {
            attrs.$observe('type', function(newValue, oldValue) {
                if (newValue !== oldValue) {
                    setAttribute(attrs, newValue, element);
                }
            });
        },
    };
}]);


angular.module('appz').factory('googleFactory', ['$http', function ($http) {

    // what = 'web', 'images', 'local', 'video', blogs', 'news', 'books', 'patent', etc.
    var getSearchResults = function (searchTerm, what) {

        if (httpPromise) {
            console.log("Rejected: "   searchTerm);
            return httpPromise;
        }

        console.log("Searching: "   searchTerm);

        var host = 'https://ajax.googleapis.com/ajax/services/search/'   what;

        var args = {
            'version': '1.0',
            'searchTerm': searchTerm,
            'results': '1',
            'callback': 'JSON_CALLBACK'
        };
        var params = ('?v='   args.version   '&q='   args.searchTerm   '&rsz='  
            args.results   '&callback='   args.callback);

        httpPromise = $http.jsonp(host   params);

        return httpPromise.then(function (response) {
            return response;
        }).catch(function(error) {
            console.error(error);
        }).finally(function() {
            httpPromise = null;
        });
    };

    var httpPromise = null;

    return {
        getSearchResults: getSearchResults
    };
}]);
    </script>

</head>
<body>
    <p ng-bind="title"></p>
    <span my-sample></span>
    <p></p>
    <p>Enter ball type: <input type="text" ng-model="ballType" /></p>
    <ball ng-init="ballType='red'"type="{{ballType}}" />

</body>
</html>

Thursday, June 12, 2014

Angular Directives Part 2 - One HTML shows off basic AngularJS

No need for a folder tree full of files just to show off AngularJS. Just one HTML file is all you need.
<html ng-app="app" ng-cloak ng-controller="controller1">
<head>
    <title ng-bind="title"></title>

    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">

</head>
<body>
    <p ng-bind="title"></p>
    <span my-sample></span>

    <script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
    <script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js"></script>

    <script>
        angular.module('app', []);

        angular.module('app').run(['$q', '$rootScope', function ($q, $rootScope) {
            $rootScope.title = 'My application title';
        }]);

        angular.module('app').controller('controller1', ['$scope', 'factory1', function ($scope, factory1) {
            $scope.title = factory1.getTitle();
        }]);

        angular.module('app').factory('factory1', ['$rootScope', '$http', function ($rootScope, $http) {
            var getTitle = function() {
                return $rootScope.title;
            };

            return {
                getTitle: getTitle
            };
        }]);

        angular.module('app').directive('mySample', ['$compile', function ($compile) {
            return {
                restrict: 'A', // <-- default, doesn't need to be specified.
                link: function (scope, element, attrs, controller) {
                    var markup = "<input type='text' ng-model='sampleData' /> {{sampleData}}<br/>";
                    angular.element(element).html($compile(markup)(scope));
                }
            };
        }]);

    </script>
</body>
</html>

Wednesday, June 11, 2014

Angular Directives Part 1 - Very basic Angular Directives Raw

If you are learning Angular directives, these samples should help you out. They're small and simple and illustrate some of the basic concepts. Take a look at the comments where each directive is defined.

HTML:

    my-sample: <span my-sample></span>
    my-sample2: <my-sample2></my-sample2>
    my-sample3: <my-sample3></my-sample3>
    my-sample4: <span class="my-sample4"></span>

    my-sample5: <span my-sample5 samp5-attr="samp5ScopeVar1" samp5-func-attr="getDate()"
      samp5-attr-a="5 6" samp5-attr-b="{{5 6}}"></span>

    <p></p>
    <p>Enter date: <input type="text" date-keys /></p>

sample5.html:

<input type='text' ng-model='samp5ScopeVar' />
{{samp5ScopeVar}} @ {{now()}}.  {{samp5AttrA}}/{{samp5AttrB}}<br />

JavaScript:

// Input field and text to show it
// <span my-sample></span>
angular.module('app').directive('mySample', ['$compile', function ($compile) {
    return {
        restrict: 'A', // <-- default, doesn't need to be specified.
        link: function (scope, element, attrs, controller) {
            var markup = "<input type='text' ng-model='sampleData' /> {{sampleData}}<br/>";
            angular.element(element).html($compile(markup)(scope));
        }
    };
}]);

// Version that restricts it to be an element.
// <my-sample2 />
angular.module('app').directive('mySample2', ['$compile', function ($compile) {
    return {
        restrict: 'E', // <-- indicates it has to be an element
        link: function (scope, element, attrs, controller) {
            var markup = "<input type='text' ng-model='sampleData2' /> {{sampleData2}}<br/>";
            angular.element(element).html($compile(markup)(scope));
        }
    };
}]);

// Same as mySample2, but using template syntax.
// <my-sample3 />
angular.module('app').directive('mySample3', ['$compile', function ($compile) {
    return {
        restrict: 'E',
        template: "<input type='text' ng-model='sampleData3' /> {{sampleData3}}<br/>"
    };
}]);

// Same as mySample3, but using 'C' for class instead.
// <span class="my-sample4"></span>
angular.module('app').directive('mySample4', ['$compile', function ($compile) {
    return {
        restrict: 'C',  // <-- indicates it has to be an class
        template: "<input type='text' ng-model='sampleData4' /> {{sampleData4}}<br/>"
    };
}]);

// using templateUrl and isolate scope
// <span my-sample5 samp5-attr="samp5ScopeVar1"></span>
angular.module('app').directive('mySample5', ['$compile', function ($compile) {
    return {
        restrict: 'A',
        templateUrl: "sample5.html",
        scope: {
            samp5ScopeVar: "=samp5Attr", // if samp5ScopeVar matches samp5Attr, then you just need "="
            now: "&samp5FuncAttr", // If samp5FuncAttr was called now, we could just say "&"
            samp5AttrA: "@", // @ = string
            samp5AttrB: "@", // @ = string
        } // isolate scope, not inherited.
    };
}]);

// Shows how to reject certain keystrokes
// <p>Enter date: <input type="text" date-keys /></p>
angular.module('app').directive('dateKeys', ['$compile', function ($compile) {
    return {
        restrict: 'A', // <-- default, doesn't need to be specified.
        link: function (scope, element, attrs, controller) {
            element.on('keydown', function(event) {
                if ((event.keyCode >= 48 && event.keyCode <= 57) || // numbers
                    (event.keyCode === 191) || // forward slash
                    (event.keyCode === 8) // backslash
                    ) {
                    return true;
                }
                return false;
            });
        }
    };
}]);