Tuesday, 10 March 2015

AngularJs Scopes & UI Router & Share data between controllers

There are four types of scopes:
1.  normal prototypal scope inheritance -- ng-include, ng-switch, ng-controller, directive with scope: true
2.  normal prototypal scope inheritance with a copy/assignment -- ng-repeat. Each iteration of ng-repeat creates a new child scope, and that new child scope always gets a new property.
3.  isolate scope -- directive with scope: {...}. This one is not prototypal, but '=', '@', and '&' provide a mechanism to access parent scope properties, via attributes.
4.  transcluded scope -- directive with transclude: true. This one is also normal prototypal scope inheritance, but it is also a sibling of any isolate scope.
For all scopes (prototypal or not), Angular always tracks a parent-child relationship (i.e., a hierarchy), via properties $parent and $$childHead and $$childTail.

---------------------------------------------------------------------------------------------------------

   There are two ways to  share data between controllers.
1) Using a service or exploiting depending parent/child relation between controller scopes.
In this post, we’ll analyze the last one method.

   It is possible to send data from parent controller to a child controller and viceversa.
To transmit data from the FirstController to SecondController, which the scope of the first one is parent to the scope of the second one, it should use $broadcast method in the FirstController.

angular.module('myApp', [])
.controller('ParentCtrl', ['$scope', function($scope) {
$scope.message = "Child updated from parent controller";

$scope.clickFunction = function() {
$scope.$broadcast('update_parent_controller', $scope.message);
};
}
])
.controller('ChildCtrl', ['$scope', function($scope) {
$scope.message = "Some text in child controller";

$scope.$on("update_parent_controller", function(event, message) {
$scope.message = message;
});
}
]);

Here a plunker for a live demo.

2)  Instead, if it need to send data from the SecondController (child) to the FirstController (parent), it should use the $emit method.

angular.module('myApp', [])
.controller('ParentCtrl', ['$scope', function ($scope) {
$scope.message = "Some text in parent";
$scope.$on("update_parent_controller", function(event, message){
$scope.message = message;
});

}])
.controller('ChildCtrl', ['$scope', function ($scope) {
$scope.clickFunction = function() {
$scope.message = "Parent updated";

$scope.$emit('update_parent_controller', $scope.message);
}

}]);

Here a plunker for a live demo.

   Finally, here a little trick where two controller have no parent/child relationship.
It should pass data from one controller through $rootScope and the $broadcast method.

angular.module('myApp', [])
.controller('FirstCtrl', ['$scope', '$rootScope', function($scope, $rootScope) {
$scope.message = "Clicked!";
$rootScope.clickFunction = function() {
$rootScope.$broadcast("Update", $scope.message);
};
}])
.controller('SecondCtrl', ['$scope','$rootScope', function($scope,$rootScope) {
$scope.message = "Waiting for a click...";

$rootScope.$on("Update", function(event, message) {
$scope.message = message;
});
}]);

Here a plunker for a live demo.

   In many scenarios, the data needs to be shared between different scopes. There are different ways to share the data among the scopes. The proper one can be chosen based on the need.

1. Sharing data between Parent scope and Child scope

If Parent-Child relationship can be established, then the data sharing can be implemented as follows.

In AngularJS, Scopes inherits prototypically. ie. In simple terms, if the property is not defined in the child scope, it will be accessed from the parent’s scope. The parent scope’s member variable can be accessed by “$scope.$parent” property from child’s scope.

Javascriptfunction Parent($scope) { $scope.x= 5; $scope.y= 5; } function Child($scope) { $scope.modifyBothScopes= function() { $scope.$parent.x++; }; $scope.modifyonlyChildscope= function() { // member "y" will be created in the child scope // So, after the following statment, $scope.$parent.y++ will only increment the parent scope. // It would not affect the child scope. $scope.y++; }; }

Html<div ng-controller="Parent"> parentX = {{x}} <br/> parentY = {{y}}<br/> <div ng-controller="Child"> childX = {{x}}<br/> childY = {{y}}<br/> <a ng-click="modifyBothScopes()">modifyBothScopes</a><br> <a ng-click="modifyonlyChildscope()">modifyonlyChildscope</a> </div> </div>​

JS Fiddle

http://jsfiddle.net/ramandv/JHwxP/

2. Sharing data using Service [like a global variable]

To share the data among independent controllers, Services can be used. Create a service with the data model that needs to be shared. Inject the service in the respective controllers.

In the following example, Service is used to store the variable x. The independent controllers will check the value of X whenever needed.

Javascriptangular.module('myApp', []) .service('myService', function () { var x=5 ; return { increase : function() { x++; }, getX : function() { return x; } }; }) function ControllerA($scope, myService) { $scope.x = 1; $scope.incrementDataInService= function() { myService.increase(); } $scope.syncDataWithService= function() { $scope.x = myService.getX(); } } function ControllerB($scope, myService) { $scope.x = 1; $scope.incrementDataInService= function() { myService.increase(); } $scope.syncDataWithService= function() { $scope.x = myService.getX(); } }​​
HTML<div ng-app="myApp"> <div ng-controller="ControllerA"> ControllerA.X = {{x}}<br/> <a ng-click="incrementDataInService()">incrementDataInService</a><br> <a ng-click="syncDataWithService()">syncDataWithService</a><br> </div> <hr/> <div ng-controller="ControllerB"> ControllerB.X = {{x}}<br/> <a ng-click="incrementDataInService()">incrementDataInService</a><br> <a ng-click="syncDataWithService()">syncDataWithService</a><br> </div> </div>​​

JS Fiddle

http://jsfiddle.net/ramandv/kR859/

3. Sharing data using Service – monitor the changes

In some times, it is important to monitor the changes in the service. AngularJS provides “$broadcast” to broadcast the data to the listening subscribers.

The following contrived example shows the usage of $broadcast. The ControllerA and ControllerB subscribed to the event “XChanged”. The service “myService” will broadcast the event “XChanged” when the value of “x” changes.

Javascriptangular.module('myApp', []) .service('myService', function ($rootScope) { var x=5 ; return { increase : function() { x++; $rootScope.$broadcast('XChanged', x); } }; }) function ControllerA($scope, myService) { $scope.x = 1; $scope.incrementDataInService= function() { myService.increase(); } $scope.$on('XChanged', function(event, x) { $scope.x = x; }); } function ControllerB($scope, myService) { $scope.x = 1; $scope.incrementDataInService= function() { myService.increase(); } $scope.$on('XChanged', function(event, x) { $scope.x = x; }); }​​

HTML<div ng-app="myApp"> <div ng-controller="ControllerA"> ControllerA.X = {{x}}<br/> <a ng-click="incrementDataInService()">incrementDataInService</a><br> </div> <hr/> <div ng-controller="ControllerB"> ControllerB.X = {{x}}<br/> <a ng-click="incrementDataInService()">incrementDataInService</a><br> </div> </div>

JS Fiddle

http://jsfiddle.net/ramandv/25CVc/
---------------------------------------------------------------------------------------
AngularUI Router
   AngularUI Router is a routing framework for AngularJS, which allows you to organize the parts of your interface into a state machine. Unlike the $route service in the Angular ngRoute module, which is organized around URL routes, UI-Router is organized around states, which may optionally have routes, as well as other behavior, attached.
States are bound to named, nested and parallel views, allowing you to powerfully manage your application's interface.
   Check out the sample app: http://angular-ui.github.io/ui-router/sample/
Note: UI-Router is under active development. As such, while this library is well-tested, the API may change. Consider using it in production applications only if you're comfortable following a changelog and updating your usage accordingly.
Get Started
(1) Get UI-Router in one of the following ways:
•   clone & build this repository
•   download the release (or minified)
•   via Bower: by running $ bower install angular-ui-router from your console
•   or via npm: by running $ npm install angular-ui-router from your console
•   or via Component: by running $ component install angular-ui/ui-router from your console
(2) Include angular-ui-router.js (or angular-ui-router.min.js) in your index.html, after including Angular itself (For Component users: ignore this step)
(3) Add 'ui.router' to your main module's list of dependencies (For Component users: replace'ui.router' with require('angular-ui-router'))
When you're done, your setup should look similar to the following:
<!doctype html>
<html ng-app="myApp">
<head>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js"></script>
    <script src="js/angular-ui-router.min.js"></script>
    <script>
        var myApp = angular.module('myApp', ['ui.router']);
        // For Component users, it should look like this:
        // var myApp = angular.module('myApp', [require('angular-ui-router')]);
    </script>
    ...
</head>
<body>
    ...
</body>
</html>
Nested States & Views
The majority of UI-Router's power is in its ability to nest states & views.
(1) First, follow the setup instructions detailed above.
(2) Then, add a ui-view directive to the <body /> of your app.
<!-- index.html -->
<body>
    <div ui-view></div>
    <!-- We'll also add some navigation: -->
    <a ui-sref="state1">State 1</a>
    <a ui-sref="state2">State 2</a>
</body>
(3) You'll notice we also added some links with ui-sref directives. In addition to managing state transitions, this directive auto-generates the href attribute of the <a /> element it's attached to, if the corresponding state has a URL. Next we'll add some templates. These will plug into the ui-viewwithin index.html. Notice that they have their own ui-view as well! That is the key to nesting states and views.
<!-- partials/state1.html -->
<h1>State 1</h1>
<hr/>
<a ui-sref="state1.list">Show List</a>
<div ui-view></div>
<!-- partials/state2.html -->
<h1>State 2</h1>
<hr/>
<a ui-sref="state2.list">Show List</a>
<div ui-view></div>
(4) Next, we'll add some child templates. These will get plugged into the ui-view of their parent state templates.
<!-- partials/state1.list.html -->
<h3>List of State 1 Items</h3>
<ul>
  <li ng-repeat="item in items">{{ item }}</li>
</ul>
<!-- partials/state2.list.html -->
<h3>List of State 2 Things</h3>
<ul>
  <li ng-repeat="thing in things">{{ thing }}</li>
</ul>
(5) Finally, we'll wire it all up with $stateProvider. Set up your states in the module config, as in the following:
myApp.config(function($stateProvider, $urlRouterProvider) {
  //
  // For any unmatched url, redirect to /state1
  $urlRouterProvider.otherwise("/state1");
  //
  // Now set up the states
  $stateProvider
    .state('state1', {
      url: "/state1",
      templateUrl: "partials/state1.html"
    })
    .state('state1.list', {
      url: "/list",
      templateUrl: "partials/state1.list.html",
      controller: function($scope) {
        $scope.items = ["A", "List", "Of", "Items"];
      }
    })
    .state('state2', {
      url: "/state2",
      templateUrl: "partials/state2.html"
    })
    .state('state2.list', {
      url: "/list",
      templateUrl: "partials/state2.list.html",
      controller: function($scope) {
        $scope.things = ["A", "Set", "Of", "Things"];
      }
    });
});
(6) See this quick start example in action.
Go to Quick Start Plunker for Nested States & Views
(7) This only scratches the surface
Dive Deeper!
Multiple & Named Views
Another great feature is the ability to have multiple ui-views view per template.
Pro Tip: While multiple parallel views are a powerful feature, you'll often be able to manage your interfaces more effectively by nesting your views, and pairing those views with nested states.
(1) Follow the setup instructions detailed above.
(2) Add one or more ui-view to your app, give them names.
<!-- index.html -->
<body>
    <div ui-view="viewA"></div>
    <div ui-view="viewB"></div>
    <!-- Also a way to navigate -->
    <a ui-sref="route1">Route 1</a>
    <a ui-sref="route2">Route 2</a>
</body>
(3) Set up your states in the module config:
myApp.config(function($stateProvider) {
  $stateProvider
    .state('index', {
      url: "",
      views: {
        "viewA": { template: "index.viewA" },
        "viewB": { template: "index.viewB" }
      }
    })
    .state('route1', {
      url: "/route1",
      views: {
        "viewA": { template: "route1.viewA" },
        "viewB": { template: "route1.viewB" }
      }
    })
    .state('route2', {
      url: "/route2",
      views: {
        "viewA": { template: "route2.viewA" },
        "viewB": { template: "route2.viewB" }
      }
    })
});




No comments:

Post a Comment