Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Angular Mini-Challenges

851 views

Published on

AngularJs Mini-Challenges is a growing collection of “Challenges” about the most quirky parts of the AngularJs framework. It encourage you to find and fix common mistakes and subtle bugs, as well as performance issues and bad practices, that non-expert AngularJs programmers may encounter on their endeavours into the depths of the framework.
AngularJs Mini-Challenges does not aim to teach you AngularJs. Former knowledge of the Framework is strongly recommended in order to understand the topics covered in this Challenges.

Published in: Software

Angular Mini-Challenges

  1. 1. AngularJs Mini Challenge Jose Mendez http://jomendez.com @joMendezDev
  2. 2. AngularJs Mini-Challenges is a growing collection of “Challenges” about the most quirky parts of the AngularJs framework. It encourage you to find and fix common mistakes and subtle bugs, as well as performance issues and bad practices, that non-expert AngularJs programmers may encounter on their endeavours into the depths of the framework. AngularJs Mini-Challenges does not aim to teach you AngularJs. Former knowledge of the Framework is strongly recommended in order to understand the topics covered in this Challenges. In order to learn the basics of the Framework, please head over to this excellent guide https://www.ng-book.com/
  3. 3. Index: 1. Problem when using JS events in AngularJs. 2. Scoping scope. 3. How to reset scope data to initials value. 4. Dependency injection issue when minify the js. 5. Debugging Two ways, to see the content of an scope var. 6. How to insert html into a view. 7. Insert conditional css class in AngularJs . 8. Focus on input field in AngularJS. 9. How two controllers can communicate between each other . 10. Prevent flicker when Angular route change until model gets loaded. First Part
  4. 4. The Challenge: Let's suppose we have an angular app with a button, when you click that button, a text should appear in the view. As requirement we have to handle the onclick event of the button with vanillaJs. E.g. document.getElementById(‘btn’).onclick = function (e) { … }); Problem when using JS events in AngularJs.
  5. 5. Problem when using JS events in AngularJs. This is how the view looks like: <div><button id="btn" type="button">Click me</button> <div>{{name}}</div> </div> This is how the controller looks like: var myApp = angular.module('myApp', []); myApp.controller('MyCtrl', function ($scope) { document.getElementById('btn').onclick = function (e) { $scope.name = "Button clicked"; } });
  6. 6. Problem when using JS events in AngularJs. Notes: Even when for this challenge you can’t use directives (see the constraint above), I strongly recommend to use directives to handling events all the time, when working with angularJs, try to avoid to use JavaScript Event handling in real life projects. For the sake of this challenge as a constraints requirements, we can’t use the ng-click directive. As a result, the code doesn’t work, so, the challenge is to solve it without use ng-* angularJs directive. For more info you can go to: http://www.jomendez.com/2015/05/05/mini-challenge-1-problem-js-events-angularjs/
  7. 7. Solution: The fastest and more effective answer is to wrap up the code you want to defer (event or AJAX call) within an AngularJs $timeout() function, see the code below: myApp.controller('MyCtrl', function ($scope, $timeout) { document.getElementById('btn').onclick = function (e) { $timeout(function () { $scope.name = "Button clicked"; }); } }); http://jsfiddle.net/d0y2z276/8/light/ Problem when using JS events in AngularJs.
  8. 8. Problem when using JS events in AngularJs. For more info you can go to: http://www.jomendez.com/2015/05/05/angularjs-mini-challenge-1-event-answer/
  9. 9. The Challenge: We have an Angular application with two nested controllers, the requirement is that when the user input a text into the TextBox the “user” variable get updated in both places where it is bound in the view, but when we entering text into the input text, only one “user” variable gets updated. You can see the JsFiddle below: This is the View: <div ng-controller="navCtrl"> <div>navCtrl text: {{user}}</div> <div ng-controller="loginCtrl"> <div> loginCtrl text: {{user}}</div> <input ng-model="user"></input> </div> </div> Scoping Scope
  10. 10. Scoping Scope In the controller we use the scope to bind the “user” variable This is the controllers: var myApp = angular.module('myApp', []); myApp.controller('navCtrl', function ($scope) { $scope.user = ""; }); myApp.controller('loginCtrl', function ($scope) { }); The problem is that only loginCtrl’s “user” variable is getting updated. The challenge consist in find a solution to this problem.
  11. 11. The Solution To scoping the scope and avoid this behavior, one solution is to create an object inside the scope instead of call the property scoping scope directly from the scope, like this: $scope.obj.myProp (see code below). myApp.controller('navCtrl', function ($scope) { $scope.obj = {}; }); In the view: <div ng-controller="navCtrl"> <div>navCtrl text: {{obj.name}}</div> <div ng-controller="loginCtrl"> <div> loginCtrl text: {{obj.name}}</div> <br/> <input ng-model="obj.name"></input> </div> </div> Scoping Scope
  12. 12. Scoping Scope For more info you can go to: http://www.jomendez.com/2015/05/08/scoping-scope-mini-challenge-2-answer/
  13. 13. How to reset scope data to initials value The Challenge: We have an Angular application where we want to create a starting point with the “default” data in the Angular scope, so we can create a reset button and restore the scope to the initial values. var myApp = angular.module('myApp', []); myApp.controller('myCtrl',myCtrl); function myCtrl($scope) { $scope.data = [{ text: 'Lorem ipsum dolor sit amet', text2: 'consectetur adipiscing elit' }, { text: ' Nullam eu molestie mi', text2: 'Proin eu congue nisi.' }]; $scope.origData = $scope.data; $scope.reset = function () { $scope.data = $scope.origData; }; }
  14. 14. How to reset scope data to initials value This is the view: <div ng-controller="myCtrl"> Modify the text in the inputs: <div ng-repeat="item in data"> <input type="text" ng-model="item.text"/> <input type="text" ng-model="item.text2"/> </div> <button type="button" ng-click="reset()">Reset</button> </div> The code example above is not working because when a JavaScript object (such as $scope.origData) is assigned to a variable, it is assigned by reference, not by value, the point of this challenge is to find a solution to make the reset button works. http://www.jomendez.com/2015/05/08/reset-scope-data-initials-value-challenge-3/
  15. 15. How to reset scope data to initials value Solution: In JavaScript when you copy a variable to another variable, like in the code below, the contain of the variable is get passed by reference and not by value, so when you update the origin variable, the destination variable gets updated too and vice versa: var var1 = ['value1','value2','value3']; var var2 = var1; var2[1] = 'different value'; console.log(var1); console.log(var2); /*//result var1: ["value1", "different value", "value3"] var2: ["value1", "different value", "value3"] */
  16. 16. How to reset scope data to initials value If you run the code above you will notice the value gets updated in both variables, this happened because JavaScript copy the data by reference not by value. The “Angular way” to solve this problem is with the copy method of the angular object: angula.copy(). var var1 = ['value1','value2','value3']; var var2 = var1; var var2 = angular.copy(var2); var2[1] = 'different value'; console.log(var1); console.log(var2); /*//result var1: ["value1", "value2", "value3"] var2: ["value1", "different value", "value3"] */ http://www.jomendez.com/2015/05/09/reset-scope-data-to-initials-value-angular-copy-mini-challenge-3- answer/
  17. 17. Dependency injection issue when minify the js The Challenge We have the following Angular app and we need to minify the code, in the controller of the application we are injecting two services $scope and $timeout (see controller below), we are having errors with the injection when we minify the code: The view: <div><button id="btn" type="button">Click me</button> <div>{{name}}</div> </div> The controller: var myApp = angular.module('myApp', []); myApp.controller('MyCtrl', function ($scope, $timeout) { document.getElementById('btn').onclick = function (e) { $timeout(function () { $scope.name = "Button clicked"; }); } });
  18. 18. Dependency injection issue when minify the js What you should change in the js code in order to avoid errors when minifying Angular code Notes: You can use this online tool to minify js code: http://jscompress.com/ http://www.jomendez.com/2015/05/11/dependency-injection-issue-minify-js-challenge-4/
  19. 19. Solution The parameters to the component (e.g. controller, factory, etc) for dependency injection, will be converted to mangled variables. For example, $scope and $timeout may become a or b and not be found by Angular in the code inside the controller, factory, etc. (...) myApp.controller('MyCtrl', function ($scope, $timeout) { (...) } }); Could be converted in something like this. (...) myApp.controller('MyCtrl', function (a, b) { (...) } }); If you don’t use the $inject service and pass the array of dependencies, you could have errors in your application http://www.jomendez.com/2015/05/12/dependency-injection-issue-when-minify-the-js-mini-challenge-4-answer/ Dependency injection issue when minify the js
  20. 20. Debugging, see the content of an scope var. The Challenge: Find two (or more) ways to see the content of the controller’s scope variable, without adding breakpoints in the browser’s developer console. Below we proposed a solution. That doesn’t mean those are the only solutions. Please feel free to leave a comment with your ingenious solution to see the scope variable content. Note: A tip for developing with angularJs use – Batarang chrome extension: chrome.google.com/webstore/detail/angularjs-batarang-stable/ This extension will allow you to see the scope variables among other things. http://www.jomendez.com/2015/06/02/content-scope-challenge-5/
  21. 21. Debugging, see the content of an scope var. Hint: This is how it should looks like.
  22. 22. Debugging, see the content of an scope var. Solution: Debugging Two ways, to see the content of an scope var (Mini-challenge 5) Answer To answer this challenge we going to show to ways to see the content of a variable in the scope when debugging an AngularJs application. One way to do it is using the Angular JSON filter in the view, this will print the content of the variable: <div>{{vm | json}}</div> The other way to do it, is writing the following code in the browser developer console: angular.element($('#main')).scope() where $(‘#main’) is an element inside of the controller that you want to see the variable’s content. http://www.jomendez.com/2015/06/02/debugging-two-ways-to-see-the-content-scope-mini-challenge-5-answer/
  23. 23. How to insert html into a view. The Challenge: The requirement is that part of the HTML that needs to be rendered in the view, should be processed/created in the controller, and then rendered in the html view. This challenge is about to insert html into a variable in the controller, and then print the html in the view, the method described below is not working, the challenge is for you to find a solution. See code example below: View: <div>{{html}}</div> Controller: function myController($scope) { $scope.html = "<b>HTML inseted</b>"; }; http://www.jomendez.com/2015/06/09/how-insert-html-into-view-mini-challenge-6/
  24. 24. Solution: It is not trivial to print an html in the angularjs view where the content is stored in a variable in the scope. For Angular 1.3 you can use ng-bind-html in the HTML view: <div ng-bind-html="html"></div> and you can use $sce service in the controller to convert the html string. $scope.html = $sce.trustAsHtml("<div>Hello World</div>"); Notes for Angular 1.2, use: <div ng-bind-html="html"></div> The “OLD WAY” to do this is: <div ng-bind-html="expression"></div> //instead of <div>{{expression}}</div> http://www.jomendez.com/2015/06/09/how-to-insert-html-into-a-view-mini-challenge-6-answer/ How to insert html into a view.
  25. 25. Insert conditional css class in AngularJs The Challenge: For this challenge we have an object array that is rendered in a ul with an li for each element, also we have a property on the controller called selectedIndex. What would be the best way to add a css class to the li if the index of the array is equal to selectedIndex in AngularJS? View: This is how the view code looks like, the challenge for you is to find a solution <div ng-controller="Ctrl"> <ul> <li class="green" ng-repeat="item in list"> <!--apply "green" class to the 3er element --> {{item.name}} </li> </ul> </div>
  26. 26. Insert conditional css class in AngularJs Controller: function Ctrl($scope) { $scope.list = [ {"name": "Item 1", "disposition": "true"}, {"name": "Item 2", "disposition": "false"}, {"name": "Item 3", "disposition": "true"}, {"name": "Item 4", "disposition": "false"}, {"name": "Item 5", "disposition": "true"} ]; $scope.selectedItem = 3; } http://www.jomendez.com/2015/07/28/insert-conditional-css-class-in-angularjs-mini-challenge-7/
  27. 27. Insert conditional css class in AngularJs Solution: In this case you have an array of object that is rendered in a ul with an li for each element and a property on the controller called selectedIndex. In this scenario we have to provide a conditional css class in the htmls view. Using the ng-class directive should be the answer: <li ng-class="{green: $index==selectedIndex}"> With this object notation ({green: $index==selectedIndex}), we can put a condition on where we want to apply the css class. Here is the ng-class official documentation: https://docs.angularjs.org/api/ng/directive/ngClass
  28. 28. Insert conditional css class in AngularJs The Controller: var myApp = angular.module('myApp', []); myApp.controller('Ctrl', myController); myController.$inject = ['$scope'] function myController($scope) { $scope.list = [ {"name": "Item 1", "disposition": "true"}, {"name": "Item 2", "disposition": "false"}, {"name": "Item 3", "disposition": "true"}, {"name": "Item 4", "disposition": "false"}, {"name": "Item 5", "disposition": "true"} ]; $scope.selectedIndex = 5; };
  29. 29. The view: <div ng-controller="Ctrl"> <ul> <li ng-class="{green: $index==selectedIndex}" ng-repeat="item in list"> <!--apply "green" class to the 3er element --> {{item.name}} </li> </ul> </div> http://www.jomendez.com/2015/07/29/insert-conditional-css-class-in-angularjs-mini-challenge-7-answer/ Insert conditional css class in AngularJs
  30. 30. Focus on input field in AngularJs The Challenge: What is the ‘Angular way’ to set focus on input field in AngularJS? More specific requirements: When a Modal pop-up is opened, set focus on a predefined <input> inside this Modal. I tried to achieve the first requirement with autofocus, but this works only when the Modal is opened for the first time, and only in certain browsers (e.g. in Firefox it doesn’t work). What would be and elegant way to solve this problem? http://www.jomendez.com/2015/10/05/focus-on-input-field-in-angularjs-mini-challenge-8/
  31. 31. Focus on input field in AngularJs HTML Code <div ng-controller="ModalController"> <button class="btn" ng-click="open()">Click to Open Modal </button> <div modal="isOpen" close="close()"> <div class="modal-header"> <h4>Modal</h4> </div> <div class="modal-body"> Name: <input type="text" autofocus> </div> <div class="modal-footer"> <button class="btn btn-warning cancel" ng-click="close()">Cancel</button> </div> </div> </div>
  32. 32. Focus on input field in AngularJs The controller: angular.module('MyApp', ['ui.bootstrap']); var ModalController = function ($scope) { $scope.open = function () { $scope.isOpen = true; }; $scope.close = function () { $scope.isOpen = false; }; };
  33. 33. Focus on input field in AngularJs Answer: The Angular way to set the focus to an element inside a modal, is with a directive (since this is related to the DOM and Events). When a Modal is opened, set focus on a predefined <input> inside this Modal.Focus on input field in AngularJS (Mini- Challenge 8) Answer Define a directive and have it $watch a property/trigger so it knows when to focus the element: Name: <input type="text" focus-me="isOpen">
  34. 34. The Directive: app.directive('focusMe', function($timeout, $parse) { return { //scope: true, // optionally create a child scope link: function(scope, element, attrs) { var model = $parse(attrs.focusMe); scope.$watch(model, function(value) { if(value === true) { $timeout(function() { element[0].focus(); }); } }); // on blur event: element.bind('blur', function() { scope.$apply(model.assign(scope, false)); }); } }; }); http://www.jomendez.com/2015/10/05/focus-on-input-field-in-angularjs-mini-challenge-8-answer/ Focus on input field in AngularJs
  35. 35. How 2 controllers can communicate between each other The Challenge: I have two controllers in an AngularJs application, I need to make a change to a variable in one controller and click a button that will going to talk to the second controller, lets suppose we have this in the View: <div> <div ng-controller="Ctrl1"> <input type="text" ng-model="foo" /> <input type="button" value="Talk to Ctrl2" ng-click="talkToCtrl2()" /> </div> <div ng-controller="Ctrl2">{{foo}}</div> </div>
  36. 36. How 2 controllers can communicate between each other And then we have this in the controllers: var myApp = angular.module('myApp', []); myApp.controller('Ctrl1', myController); myController.$inject = ['$scope']; function myController($scope) { $scope.foo = "setted in ctrl1"; $scope.talkToCtrl2 = function () { //Implement this function } }; myApp.controller('Ctrl2', myController2); myController2.$inject = ['$scope'] function myController2($scope) { $scope.foo = "setted in ctrl2"; }; The challenge for you is to implement the talkToCtrl2() function, so when you click the button the data in the input text get passed to Ctrl2 and gets printed in the view. http://www.jomendez.com/2015/10/19/how-two-controllers-can-communicate-between-each-other-mini-challenge-9/
  37. 37. How 2 controllers can communicate between each other Solution: There are several ways that one controllers can talk to another, This google group thread, http://groups.google.com/d/topic/angular/m_mn-8gnNt4/discussion, discusses 5 ways controllers can talk to each other.Controllers Mini-Challenge 9 Answer 1. Shared service. 2. Events. 3. Prudent pragmatic use of $root scope. 4. Watchers. And a 5th – scope prototypical inheritance? http://docs.angularjs.org/guide/scope For a sake of simplicity we are going to show a possible answer to this challenge with Events, using $broadcasting and $on
  38. 38. How 2 controllers can communicate between each other This is the view: <div> <div ng-controller="Ctrl1"> <input type="text" ng-model="foo" /> <input type="button" value="Talk to Ctrl2" ng-click="talkToCtrl2()" /> </div> <div ng-controller="Ctrl2">{{foo}}</div> </div>
  39. 39. How 2 controllers can communicate between each other And this is the javascript: var myApp = angular.module('myApp', []); myApp.controller('Ctrl1', myController); myController.$inject = ['$scope', '$rootScope'] function myController($scope, $rootScope) { $scope.foo = "setted in ctrl1"; $scope.talkToCtrl2 = function () { $rootScope.$broadcast('emitedFromController1', $scope.foo); } }; myApp.controller('Ctrl2', myController2); myController2.$inject = ['$scope'] function myController2($scope) { $scope.foo = "setted in ctrl2"; $scope.$on('emitedFromController1', function(event, args) { $scope.foo = args; }); };
  40. 40. How 2 controllers can communicate between each other In the talkToCtrl2 function we can implement $rootScope.$broadcast(’emitedFromController1′, $scope.foo); and then in the second controller we can implement: $scope.$on(’emitedFromController1′, function(event, args) { $scope.foo = args; }); http://www.jomendez.com/2015/10/19/how-two-controllers-can-communicate-between-each-other-mini-challenge-9- answer/
  41. 41. Prevent flicker when Angular route change until model gets loaded The Challenge: Sometimes it happen that when you navigate from one route to another, and you are using services to call apis and fetch the model, the html view gets loaded first provoking a flick. This is an example of what we want to achieve with this challenge, if there were a PeopleController that listed all Projects and people.html which is the html template that shows the list of peoples, MyService.PeopleQuery() would be fetched completely before showing the new html page. Until then, the current page would still continue to show. With the solution that you going to provide for this challenge we also going to prevent that the view show the double curly braces in its raw (uncompiled) form: {{ myVariable }}. Note: Notice that using ng-cloak is not the solution, with this directive the HTML is shown, this directive only prevent the view to show the uncompiled angular. ngCloak Documentation. http://www.jomendez.com/2015/10/21/prevent-flicker-when-angular-route-change-until-model-gets-loaded-mini- challenge-10/
  42. 42. Prevent flicker when Angular route change until model gets loaded Solution: The $routeProvider service’s resolve property allows delaying of route change until data is loaded. First define a route with resolve attribute like this. angular.module('myApp', ['myAppFilters', 'myAppServices', 'myAppDirectives']). config(['$routeProvider', function($routeProvider) { $routeProvider. when('/users', { templateUrl: 'partials/user-list.html', controller: userListCtrl, resolve: userListCtrl.resolve}). when('/users/:userId', { templateUrl: 'partials/user-detail.html', controller: userDetailCtrl, resolve: userDetailCtrl.resolve}). otherwise({redirectTo: '/users'}); }]);
  43. 43. Prevent flicker when Angular route change until model gets loaded Notice that the resolve property is defined on the route. function userListCtrl($scope, users) { $scope.users = users; } userListCtrl.resolve = { users: function(user, $q) { var deferred = $q.defer(); user.query(function(successData) { deferred.resolve(successData); }, function(errorData) { deferred.reject(); }); return deferred.promise; }, delay: function($q, $defer) { var delay = $q.defer(); $defer(delay.resolve, 1000); return delay.promise; } }
  44. 44. Prevent flicker when Angular route change until model gets loaded Notice that the controller definition contains a resolve object which declares users prop which should be available to the controller constructor and gets injected as dependency and it is defined in the resolve property. The resolve.users function is responsible for returning a promise. All of the promises are collected and the route change is delayed until after all of the promises are resolved, in this example we are delaying the promise to better illustrate the example. http://www.jomendez.com/2015/10/21/prevent-flicker-when-angular-route-change-until-model-gets-loaded-mini- challenge-10-answer/
  45. 45. To extend more on this topic and get working code an extra examples you can go to: http://www.jomendez.com/angularjs-mini-challenges/ If you found these topics interesting, I will be publishing more, I’ll share on twitter, you can follow me: @joMendezDev Download this Presentation: End of the first part

×