AngularJS access parent scope - angularjs

I'm new to angularjs and I'm trying to do some things that I don't know if is a good practice.
Well, I have a main page where I want to show the messages of all other controller and views.
In this "main" view I have the following
<div class="row" data-ng-repeat="msg in messages">
<div class="col-lg-12">{{mgs.message}}
<a data-ng-click="close(msg)">×</a>
</div>
</div>
When I set a message in my MainController the message is shown, but when I navigate to another controller it isn't.
Google told me that it happens because I'm working with different scopes.
I would like to know:
How do I add messages into the "parent" scope? or
How do I call a method of MainController inside my Functionality2Controller or
What are the other better ways to show messages in the main page?

Different controller use different scopes and thats right and you should try to change that. First thing you could do is using the $rootScope, where all other scopes are derivated from. But that kind of a misuse, the imo better solution is creating a shared messageService:
.factory('messageService', [
function() {
var messages = [];
return {
getMessages: function() {
return messages;
},
addMessage: function(message) {
messages.push(message)
}
}
}
])
and inject it to your controller:
.controller('ctrl', ['$scope', 'messageService',
function($scope, messageService) {
$scope.messages = messageService.getMessages();
}
])
Plunker: http://plnkr.co/edit/6cnbx9okRA03tut7JzuF?p=preview

Related

Read Data From One Controller To Another

I've two controllers and two views in ASP.NET MVC project. My requirement is to pass data from one controller to another on ng-click that should reflect in another view (As well from another controller). Simple! I know, it could be done using service but I was preferring for testing purpose $broadcast and $on. So I tried the following:
app.controller('FirstController', function ($rootScope, $scope, productService) {
$scope.showData = function (m) { //This is the event on which I'll get data in another controller as well in another view
alert(m); //This works and gets a name from the first view
$rootScope.$broadcast('sample', $scope.m); //This is what I am using to deliver in another controller
}
});
app.controller('SecondController', function ($scope) {
$scope.$on('sample', function (events, d) {
alert(d);
})
In another view, I used something like this:
<div ng-app="app" ng-controller="SecondController">
<ul class="nav navbar-nav">
<li> Product {{ m }}</li>
</ul>
</div>
Actually I am doing this all for demo purpose. But unfortunately, the above doesn't work. Am I missing something?
Update 1 - See the updated code:
app.controller('FirstController', function ($rootScope, $scope, productService) {
$scope.showData = function (m) { //This is the event on which I'll get data in another controller as well in another view
alert(m); //This works and gets a name from the first view
$timeout(function () {
$scope.$broadcast('sample', m);
});
}
});
app.controller('SecondController', function ($scope) {
$scope.$on('sample', function (events, d) {
alert(d);
})
In your scenario it will not work in one case:
You call $rootScope.$broadcast('sample', $scope.m); before
$scope.$on() is registered to listen on 'sample event a.e. before SecondController is created.
If you know that SecondController is created , you can wrap $rootScope.$broadcast('sample', $scope.m); with $timeout. a.e.:
$timeout(function(){
$rootScope.$broadcast('sample', $scope.m);
});
In this case $broadcast execution will be moved to end of events queue a.e. before next digest cycle that will be guarantee that Second Controller has been created and $scope.$on() is registered.
It's not entirely clear how you are using the second view & controller. Is it somewhere within the template that FirstController is assigned to? Seeing the template assigned to FirstController would help clarify. In any case, I've attached a simple plunker which shows how you can broadcast an event from a button click to a second controller.
https://plnkr.co/edit/KzNftVAYwPuCvsnflIz

Angular - Template not refreshed with data bound from two different scopes to the same service

I bind data of two different scopes to the same common service data. When I update the data through controller 1, the data in controller 2 is not refreshed in template.
Example showing the issue :
<body ng-app="demoShareBindApp">
<div ng-controller="FirstCtrl as first">
Set share data to : <a href ng-click="setShareDataTo('Me')">"Me"</a>
- <a href ng-click="setShareDataTo('Myself')">"Myself"</a>
- <a href ng-click="setShareDataTo('I')">"and I"</a>
</div>
<div ng-controller="SecondCtrl as second">
Text entered : {{sShareData}}
<br>
<br><a href ng-click="revealShareData()">Reveal data</a>
</div>
<script src="bower_components/angular/angular.js"></script>
<script>
angular
.module('demoShareBindApp', [])
.service('myService', function () {
return {
shareData: null
}
})
.controller('FirstCtrl', ['$scope', 'myService', function ($scope, myService) {
$scope.setShareDataTo = function(content) {
myService.shareData = content;
};
}])
.controller('SecondCtrl', ['$scope', 'myService', function ($scope, myService) {
$scope.sShareData = myService.shareData;
$scope.revealShareData = function() {
console.log(myService.shareData);
}
}]);
</script>
</body>
The Text entered : {{sShareData}} is never updated whereas clicking "Reveal data" shows the right share data in console.
I can't find any clue in other SO post on this particular subject. I guess it could be a matter of "$watch/$digest" but I can't figure out what is really going on here.
Any detailed explanation welcome !
You are sharing data through service. When data in changed under first controller, you set this updated data to service but second controller does not know that shared data referenced by service has changed, so we need to notify the second controller that data has changed or we can shared data through events
I created a fiddle, check it
https://jsbin.com/midebu/edit?html,js,output
First approach. Using service
$scope.setShareDataTo = function(content) {
myService.shareData = content;
$rootScope.$broadcast('datacChanged');
};
In second cntroller
$rootScope.$on('dataChanged', function(){
// get updated data
$scope.sShareData = myService.shareData;
})
Other way is that, we do not need to use service , we can simply pass that shared data using events
$scope.setShareDataTo = function(content) {
$rootScope.$broadcast('datacChanged', content);
};
And in Second controller
$rootScope.$on('dataChanged', function(event, data){
// get updated data
$scope.shareData = data;
})

Change ng-show in another controller?

I want to change ng-show in another controller than ng-show is.
myApp.controller('popupCtrl', function() {});
myApp.controller('changePopup', function($rootScope){
// now i wanna show my Ppopup
$rootScope.popup = true;
});
<div ng-controller="popupCtrl">
<div ng-show="popup">
Popuptext
</div>
</div>
But this doesn't work... How can I fix it?
Thanks!
So first thing, you should never add to the $rootScope or change it in anyway. It has been optimised by the angular team.
Second thing, there is no need to involve the $rootScope.
Here is a demo showing how to communicate across two controllers.
The key is the event aggregator pattern:
Communicator.register(function (newValue) {
vm.value = Communicator.value;
});
I created a function in the Communicator to register a callback function. The aim is that when a value gets changed the callback function is fired off. I.e. an event is triggered (change event).
The second key part is fire that change event off:
Communicator.change(!Communicator.value);
Here we pass through to the change function a new value which will do two things:
Update the internal value so we can keep track of it
Loop through all the registered callbacks and execute them passing in the new value.
By implementing this pattern, we can minimise the extent to which we communicate around our application ($rootScope can have a tendency to traverse the scope heirarchy when you $broadcast).
Now we can follow more closely the principle of single responsibility. Our class is aptly named in its current scope, when we look at this factory we can tell it is supposed to "communicate".
Finally, with a global event aggregator pattern ($rootScope) it is far more difficult to keep track of where these events are being broadcast from, and where they'll end up. Here we don't have that issue
One way to solve this is to use $rootScope.$broadcast
Here is an example: http://plnkr.co/edit/EmJnZvXFRWv6vjKF7QCd
var myApp = angular.module('myApp', []);
myApp.controller('popupCtrl', ['$rootScope', '$scope', function($rootScope,$scope) {
$scope.popup = false;
$rootScope.$on('changePopup', function(event, data) {
$scope.popup = !$scope.popup;
});
}]);
myApp.controller('changePopup', ['$rootScope', '$scope', function($rootScope, $scope) {
$scope.changePopup = function() {
$rootScope.$broadcast('changePopup', 'data could be sent here');
}
}]);
View:
<div ng-controller="popupCtrl">
<div ng-show="popup">
Popuptext
</div>
<div ng-controller="changePopup">
<button ng-click="changePopup()">Change the popup</button>
</div>
Using a service/factory is a better solution for cross controller communication if you are working on a large application, but for a smaller app I would say using $broadcast, $emit and $on is sufficient.
Here is a working demo for you - sorry I changed the controller names, but I am sure you will be able to build on this. Good luck
angular.module('myApp', [])
.controller('c1', function($scope) {
// now i wanna show my Ppopup
$scope.popup = false;
$scope.$on('popup', function() {
$scope.popup = true;
});
})
.controller('changepopup', function($rootScope, $scope) {
// now i wanna show my Ppopup
$scope.clicker = function() {
$rootScope.$broadcast('popup')
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="c1">
<div ng-show="popup">
Popuptext
</div>
</div>
<button ng-controller="changepopup" ng-click="clicker()">Click me</button>
</div>

Angular.js "Controller as ..." + $scope.$on

If i'd like to use the "Controller as ..." syntax in Angular, how should I approach things like $scope.$on(...) that i need to put inside the controller?
I get an impression i could do it some other way than the one shown below.
Here, to get $scope.$on working i bind "this" to the callback function. I tried to invoke $on on "this" inside the controller but it didn't work.
Could you give me a hint here or if i'm completely messing up, could you point me to some right way to do it? Thanks.
main.js:
angular.module('ramaApp')
.controller('MainCtrl', ['$scope', '$location', function ($scope, $location) {
this.whereAmINow = 'INDEX';
$scope.$on('$locationChangeStart', function(event) {
this.whereAmINow = $location.path();
}.bind(this));
this.jumpTo = function(where) { $location.path(where); }
}]);
index.html:
<div ng-controller="MainCtrl as main">
<p>I am seeing the slide named: {{ main.whereAmINow }}</p>
<div ng-click="main.jumpTo('/slide1')">Slide 1</div>
<div ng-click="main.jumpTo('/slide2')">Slide 2</div>
<div ng-click="main.jumpTo('/slide3')">Slide 3</div>
</div>
As far as I know, you need to inject $scope if you want $scope watchers/methods. ControllerAs is just syntactic sugar to enable to see more clearly the structure of your nested controllers.
Three ideas though which may simplify your code.
Use var vm = this, in order to get rid of the bind(this).
var vm = this;
vm.whereAmINow = "/";
$scope.$on('$locationChangeStart', function(event) {
vm.whereAmINow = $location.path();
});
vm.jumpTo = function(where) {
$location.path(where);
}
The whole whereamINow variable makes sense putting it into the initialization of app aka .run() (before config) since I assume it's a global variable and you don't need to use a $scope watcher/method for it.
Another option is to use a factory to make the changes persist, so you simply create a location factory which holds the current active path.
Inject $scope and your controller is accessible by whatever you named it
EG:
$stateProvider
.state('my-state', {
...
controller: 'MyCtrl',
controllerAs: 'ctrl',
...
});
.controller('MyCtrl', function($scope) {
var $this = this;
$scope.$on('ctrl.data', function(new, old) {
// whatevs
});
$timeout(function() {
$this.data = 'changed';
}, 1000);
});
Ok, i think people just do the same, just as in this question:
Replace $scope with "'controller' as" syntax

Angular View not updating from a model change

I have 2 modals that use the same controller. One has:
<div id="addToPlaylist" ng-controller="PlaylistModalCtrl">
<select name="playlist" ng-model="playlist" ng-options="playlist.id as playlist.name|rmExt for playlist in playlists">
</select>
The other has:
<div id="newPlaylist" ng-controller="PlaylistModalCtrl">
<button ng-click="createPlaylist(playlistName);">Save</button>
In my controller, I have:
angular.module('MyApp')
.controller('PlaylistModalCtrl', function ($scope) {
$scope.playlists = [];
$scope.updatePlaylists = function() {
getPlaylist.then(function (response) {
$scope.playlists = response.data.data;
$scope.$$phase || $scope.$apply();
});
}
$scope.createPlaylist = function(playlist_name) {
addPlaylist(playlist_name).then(function(response) {
$("#newPlaylist").modal('hide');
});
}
$scope.updatePlaylists();
});
So one would expect that my first view would have an updated "playlists" in the dropdown, but this isn't the case. So how can I get that view to be updated?
You don't seem to understand how scoping works. Here is a plunkr illustrating that two different controllers have different scopes and stuff.
http://plnkr.co/edit/LqLuLvVkE9ltzcJH6XdN?p=preview
As you can clearly see, expecting two controllers to update the same variable would not work because each of them has the variable in its own isolated scope. What you can do to battle that is to implement a pubsub service that listens to some changes for some properties, use emit and broadcast angular functions, or, the best, rethink why you would need the same controller twice in your app and redesign it.

Resources