How to update a view from another controller in angular.js without using a directive?

I have an angular app that has 2 sections on a page.

Section

1 is a sidebar that gives an overview. Say he says:

Players 5 // {{ numOfPlayers }} 

Code is an ajax call. I do not want to increase, since this number can be increased by another call. I need to make an ajax call after getting the length of the array.

 angular.module('app').controller('nav', function($scope,$http) { $http.get('/players').then(function(data) { $scope.numOfPlayers = data.players.length; }); }); 

Now in a completely separate controller, which is located on the main page. User can add player. How to do this so that I can update the nav controller?

 angular.module('app').controller('mainController', function($scope,$http) { $http.post(.....).then(function(data) { //update the numOfPlayers so the nav is updated. }); }); 
+6
source share
8 answers

Use service

You can use the service to store general data and changes to $watch :

 var app = angular.module('TestApp', []); app.service("playersService", function () { this.numOfPlayers = 0; }); app.controller("navController", function ($scope, $http, playersService) { // Update the shared resource initial value with GET result // $http.get('/players').then(function(data) { // playersService.numOfPlayers = response.data.length; // }); playersService.numOfPlayers = 0; $scope.$watch(function () { return playersService.numOfPlayers; }, function (value) { $scope.numOfPlayers = value; }); }); app.controller("mainController", function ($scope, playersService) { $scope.addPlayer = function () { // POST and update the shared resource with result // $http.post(.....).then(function(data) { playersService.numOfPlayers++; } }); 
 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script> <div ng-app="TestApp"> <section ng-controller="navController"> <h1>Nav</h1> Players {{ numOfPlayers }} </section> <section ng-controller="mainController"> <h1>Main</h1> <button ng-click="addPlayer()"> Add player </button> </section> </div> 

Use parent controller

You can use a parent controller (e.g. pageController) to store shared data:

 var app = angular.module('TestApp', []); app.controller("pageController", function ($scope) { $scope.numOfPlayers = null; }); app.controller("mainController", function ($scope, $http) { $scope.addPlayer = function () { // POST and update the shared resource with result // $http.post(.....).then(function(data) { $scope.$parent.numOfPlayers++; }; }); app.controller("navController", function ($scope, $http) { // Update the shared resource initial value with GET result // $http.get('/players').then(function(data) { // $scope.$parent.numOfPlayers = response.data.length; // }); $scope.$parent.numOfPlayers = 0; }); 
 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script> <div ng-app="TestApp" ng-controller="pageController"> <section ng-controller="navController"> <h1>Nav</h1> Players {{ numOfPlayers }} </section> <section ng-controller="mainController"> <h1>Main</h1> <button ng-click="addPlayer()"> Add player </button> </section> </div> 

Side notes:

In both approaches:

  • It might be better to use an array of players as a shared resource. In this example, I tried to keep it simple.

  • It might be better to update the initial resource value from mainController rather than navController . In this example, I tried to match your code.

+4
source

First of all, I would suggest using best practices and using a component instead of an ng controller.

So you have 2 components:

 angular.module('app').component('nav', {}); 

and

 angular.module('app').component('main', {}); 

Now you can share state data between them through services :

 angular.module('app').service('PlayersService', function(){ this.players = []; this.getAll() = () => {}; this.add(player) = () => {}; }); 

Only one tricky part is what you need to look at in all of your components for players :

 angular.module('app').component('nav', { controller: function($scope, PlayersService){ PlayersService.getAll(); $scope.$watch(() => PlayersService.players.length, (playersLength) => this.numOfPlayers = playersLength) } }); angular.module('app').component('main', { controller: function($scope, PlayersService){ //PlayersService.add(player); $scope.$watch(() => PlayersService.players.length, (playersLength) => this.numOfPlayers = playersLength) } }); 

So, in both cases, the scope numOfPlayers property expands .

+4
source

Since you have two sections on the same page, my suggestion is to use a component or components instead of separate controllers. Example below:

 angular .module('exampleApp', []) .controller('ExampleController', ExampleController); function ExampleController() { var vm = this; vm.numPlayers = 0; } angular .module('exampleApp') .component('playerSummary', { bindings: { numPlayers: '<' }, template: `<p>{{ $ctrl.numPlayers }}</p>` }); angular .module('exampleApp') .component('playerAddition', { bindings: { numPlayers: '=' }, controller: function() { function addPlayer() { this.numPlayers++; } this.addPlayer = addPlayer; }, template: `<button type="button" ng-click="$ctrl.addPlayer()">+</button>` }); 
 <!DOCTYPE html> <html ng-app='exampleApp'> <head> <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular.min.js"></script> </head> <body ng-controller="ExampleController as vm"> <player-summary num-players="vm.numPlayers"></player-summary> <player-addition num-players="vm.numPlayers"></player-addition> </body> </html> 

Not recommended, but you can also use the $ rootScope or catch and emit events.

You can also bind to a property of the service, the example below or directly to the service.

 angular .module('exampleApp', []); angular .module('exampleApp') .controller('FirstController', FirstController); function FirstController(PlayerService) { var vm = this; vm.players = PlayerService.players; } FirstController.$inject = ['PlayerService']; angular .module('exampleApp') .controller('SecondController', SecondController); function SecondController(PlayerService) { var vm = this; vm.addPlayer = function() { PlayerService.addPlayer(); } } SecondController.$inject = ['PlayerService']; angular .module('exampleApp') .service('PlayerService', PlayerService); function PlayerService() { var PlayerService = this; PlayerService.players = []; PlayerService.addPlayer = function() { PlayerService.players.push({}); } } 
 <!DOCTYPE html> <html ng-app='exampleApp'> <head> <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular.min.js"></script> </head> <body> <div ng-controller="FirstController as vm"> <p>{{vm.players.length}}</p> </div> <div ng-controller="SecondController as vm"> <button ng-click="vm.addPlayer()">+</button> </div> </body> </html> 

As Halil Malki mentioned, you can see the cost of the service

As Jaken H'gar mentioned, you can use the parent controller.

+2
source

You can do this using angular.factory()

or angular.service(); or $localStorage and or $sessionStorage angular.service(); or $localStorage and or $sessionStorage .

First, to call a global call instead of a call inside the controller:

Example:

 angular.factory('updateVal', function(){ var data; return { getPlayers: function(){ return $http.get('/players').success(function(results){ data = results; return data; }); }, setPlayers: function(val){ if(val){ data = val; return data; } else { return data; } } } }); 

In your controller: First controller

 angular.module('app').controller('nav', function($scope,updateVal){ $scope.numOfPlayers = updateVal.getPlayers(); $scope.$watch(function(){ return updateVal.setPlayers().length > 0; }, function(){ $scope.numOfPlayers = updateVal.setPlayers(); }) }) 

Second controller:

 angular.module('app').controller('mainController', function($scope,$http,updateVal) { // this function update the players updateVal.getPlayers(); $http.post(.....).then(function(data) { //update the numOfPlayers so the nav is updated. // after posting call this function: updateVal.setPlayers(data); }); }); 

Using angular.service:

It can be done:

 angular.service('updatePlayers', function(){ var updatedPlayers; this.setPlayers = function(args){ updatedPlayers = args; } this.getPlayers = function(){ return updatedPlayers; } }) 

In your first controller:

 angular.module('app').controller('nav', function($scope,$http,updatePlayers) { $http.get('/players').then(function(data) { updatePlayers.setPlayers(data.players.length); $scope.numOfPlayers = data.players.length; }); }); 

In the second controller:

 angular.module('app').controller('mainController', function($scope,$http,updatePlayers) { $http.post(.....).then(function(data) { //update the numOfPlayers so the nav is updated. updatePlayers.getPlayers(); }); }); 

EDITED for typo correction

+1
source

The best way to do this - two have two directives:

  • a page directive
  • a nav directive

The nav directive will take some data as its input. The number of players in this case can be one of the inputs. Then, when you insert the nav directive inside the page directive, you can transfer your data from page to nav , and nav will automatically update when the values ​​change:

HTML

 <page></page> 

Javascript

 app.directive('page', function() { return { restrict: 'E', controller: function($scope, playerSvc) { playerSvc.getPlayers.then(function(resp) { $scope.players = resp.data; }); }, template: '<header> blah</header> <nav player-count="players.length"></nav> <footer></footer>' }; }); app.directive('nav', function() { return { restrict: 'E', scope: { playerCount: '=' }, template: '<div> player count: {{playerCount}} </div>' }; }); 

In doing so, you can isolate your directives (components) and create clear boundaries. Each component or directive will have a small responsibility. In this case, nav receives some data and displays it, and is also responsible for navigation. The page directive provides global data for various components or directives on a page. You can use the same idea and decide who will add the player to the list of players. And since the nav directive is connected to players.length , it will be updated automatically after updating players .

+1
source

maybe there is a method call in the nav controller to update the region variable, ultimately updating your view with two way binding

 angular.module('app').controller('nav', function($scope,$http) { $rootScope.$on("CallMethodNavController", function(){ $scope.navMethod(); }); $scope.CallMethodNavController=function(){ $http.get('/players').then(function(data) { $scope.numOfPlayers = data.players.length; } }); }); 

then in the second controller you call this method as soon as the player is added like this:

  $rootScope.$emit("CallMethodNavController", {}); 
+1
source

There is no need to create a watch or event for this.

Use the service to receive, store, update and share an array of players in the application.

Instead of creating a primitive variable that is a length ... save the array reference in the controller and in the view do {{players.length}} , and let the angular observer look after the updates.

 angular.module('app').factory('playersService', function($http){ // now have an array to share across app // and reference to addPlayer function var factory ={players:[], addPlayer: addPlayer }; // load the players and add them to array $http.get('/players').then(function(response) { Array.prototype.push.apply(factory.players, response.data); }); function addPlayer(player){ return $http.post(url, player).then(function(resp){ // add new player to shared array factor.push(resp.data); } } return factory; }); 

Nav controller

 angular.module('app').controller('nav', function($scope,playersService) { $scope.players = playersService.players; // store full array reference }); 

Nav view

  Number Players: {{players.length}} <!-- angular will automatically watch and update --> 

Another controller

 angular.module('app').controller('mainController', function($scope,playersService) { $scope.newPlayer={};// bind to ng-model in form $scope.saveNewPlayer = function(){ playersService.addPlayer($scope.newPlayer).then(function(){ alert('Number of players in nav will already be updted'); // clear form $scope.newPlayer={}; }) }); }); 
+1
source

use $ emit and $ on From the main $ rootScope Zintroller. $ broadcast ('update', 'status') {$ http.post (.....). then (function (data) {// update numOfPlayers to update nav.});}

 and in your nav controller use $scope.$on('eventName', function (event, args) { $http.get('/players').then(function(data) { $scope.numOfPlayers = data.players.length;});}); 
Broadcast

$ is passed from parent to child, so any change to the parent controller will be displayed in the child controller

+1
source

All Articles