Month: October 2015

Prevent flicker when Angular route change until model gets loaded (Mini-Challenge 10) Answer

Check out Mini-challenge 10 Instructions

Answer:route

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'});
}]);

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;
  }
}

 

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.

 

References:

https://docs.angularjs.org/api/ngRoute/provider/$routeProvider

If you have a solution for this challenge, different from the proposal solution on this post, don’t be shy and share it with us 🙂

Prevent flicker when Angular route change until model gets loaded (Mini-Challenge 10)

Mini-Challenge 10

ng-challenge modelAngularJs 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 book:

 

book

Book Tip

ng-book - The Complete Book on AngularJS
Ready to master AngularJS? What if you could master the entire framework – with solid foundations – in less time without beating your head against a wall?


Amazon

 

 

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

 

Here you can find a possible solution

If you feel that the solution is poor or incomplete or there is room for improvement, please share with every one, you can leave a comment.

How two controllers can communicate between each other (Mini-Challenge 9) Answer

Check out Mini-challenge 9 Instructions

Answer:

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:
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>

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;
     });

};

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;
});

You can see a working example in this jsFiddle:

If you have a solution for this challenge, different from the proposed solution on this post, don't be shy and share it with us 🙂

How two controllers can communicate between each other (Mini-Challenge 9)

Mini-Challenge 9

ng-challenge controllersAngularJs 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 book:

 

book

Book Tip

ng-book - The Complete Book on AngularJS
Ready to master AngularJS? What if you could master the entire framework – with solid foundations – in less time without beating your head against a wall?


Amazon

 

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>

 

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.
Here is a working example in jsFiddle:

Here you can find a possible solution

If you feel that the solution is poor or incomplete or there is room for improvement, please share with every one, you can leave a comment.

angular set focus on input field (Mini-Challenge 8) Answer

Check out Mini-challenge 8 Instructions (angular set focus)

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.angular set focus
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">

 

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));
      });
    }
  };
});

Notice that we also added a code for when the element loose the focus (blur) it sets the isOpen property back to false.

Here is a working example:
http://jsfiddle.net/7L8vqf8u/3/

Recommendations:

It is highly recommended try to avoid as much as possible the use of the $watch service. This is another possible solution using angular events:

app.directive('focusOn', function($timeout) {
   return function(scope, element, attrs) {
      scope.$on(attr.focusOn, function(e) {
          $timeout(function() {
            element[0].focus(); 
          });
      });
   };
});

 

Now you can put your directive in the view:

<input type="text" focus-on="isOpen" />

 

and then, in your controller:

$scope.open = function () {
    $scope.$broadcast('isOpen');
};

 

book

Book Tip

ng-book - The Complete Book on AngularJS
Ready to master AngularJS? What if you could master the entire framework – with solid foundations – in less time without beating your head against a wall?


Amazon

 

If you have a better solution on how angular set focus, for this challenge, different from the proposed solution on this post, don't be shy and share it with us 🙂 you can leave a message on this blog or you can twit me @JoMendezdev.

Focus on input field in AngularJS (Mini-Challenge 8)

Mini-Challenge 8

ng-challengeAngularJs 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 book:

 

book

Book Tip

ng-book - The Complete Book on AngularJS
Ready to master AngularJS? What if you could master the entire framework – with solid foundations – in less time without beating your head against a wall?


Amazon

 

The Challenge:

What is the 'Angular way' to set focus on input field in AngularJS?

More specific requirements:

  1. 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?

 

HTM 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>

 

Here you can find a working example:

Here you can find a possible solution

If you feel that the solution is poor or incomplete or there is room for improvement, please share with every one, you can leave a comment.