Manage ng-if from another controller - angularjs

I have prepared a Plunkr that provides the code:
https://plnkr.co/edit/WXtQxVcdgyvnaajbs4eJ?p=preview
I have assigned the object $scope.menu.show in controller toolbarCtrl that controls whether the menu is created or not.
<md-menu id="menu" ng-controller="toolbarCtrl" ng-if="true"> ...
When toolbarCtrl gets initialized I can control whether the menu is shown but I would like to control it from another controller. In this case I created two buttons in mainCtrl that produce true or false and I'm trying to assign the value to the $rootScope but that doesn't work.
How can I do that?
UPDATE:
Although jtmingus solution works perfectly I came up with another solution that doesn't need events.
I created this factory service:
app.factory('menuState', function () {
var state = {val: false};
function getState() {
return state;
}
function setState(newState) {
state.val = newState;
}
return {
getState: getState,
setState: setState
}
});
Then at the toolbarCtrl controller I initially hide the menu
app.controller('toolbarCtrl', function($scope, menuState) {
// hide the menu
menuState.setState(false);
// pass the value to the menu
$scope.state = menuState.getState();
});
on the mainCtrl I define the button clicks like that:
app.controller('mainCtrl', function($scope, $rootScope, menuState) {
$scope.state = menuState.setState;
});
and 2 buttons on the the main.html
<md-button class="md-primary md-raised" ng-click="setState(true)">Show</md-button>
<md-button class="md-primary md-raised" ng-click="setState(false)">Hide</md-button>

The way that data generally should be shared through controllers is by using a service. Because you need both controllers to watch the same state, you can use Angular's events, specifically the $on event.
Here is an article that will walk you through having multiple controllers watch the same state:
https://variadic.me/posts/2013-10-15-share-state-between-controllers-in-angularjs.html
Edit: After looking through this article some more, I realized it's not the best explanation, and there are some things in the code that don't work. For example, if you try to pass arguments in with a $broadcast call, the $on function needs two paramaters–event and args.

On the controller from which you want to initiate showing the menu,
app
.controller('MainCtrl', ['$scope', function($scope) {
$scope.$broadcast('menu:show');
}])
.controller('ToolbarCtrl', ['$scope', function($scope) {
var ToolbarCtrl = this;
$scope.$on('menu:show', function() {
ToolbarCtrl.showMenu = true;
});
}])
In the markup switch using this value
<md-menu id="menu" ng-controller="ToolbarCtrl" ng-if="ToolbarCtrl.showMenu"> ...

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

AngularJS: How to pass button clicked ng-model value from one controller to another controller

I have a button where I am passing the ng-model value as a parameter to its ng-click function, Now after this ng-click function executes, I would like to send that ng-model value to another controller, how can I do this using angular?
Is there a way to do this?
There are many ways to communicate between controllers.
use $broadcast like mentioned in other comments, or
use $emit
Something like this...
function Controller1($scope)
{
$scope.$on('myEvent', function(event, args) {
// do the event
});
// another controller, directive
}
function Controller2($scope)
{
$scope.$emit('myEvent', args);
}
1.In first Controller, you can do:
$rootScope.$broadcast('eventName', value);
and listen to the event in second Controller:
$scope.$on('eventName', function (event, value) {//your code});
2.In first controller assign the values to the $rootScope variable then access from the other controller.for example
in first controller
$rootScope.TestValue="10";
in second controller
$scope.receiveValue=$rootScope.TestValue;
3.AngularJS- How to pass data from one controller to another on ng-click()?
Use angular service to communicate between 2 controller.
Watch this Video
Yes $broadcast will work for you.
Here are some essential parts:
Create service that implements message bus functionality (e.g. $scope instance)
Inject service to both controllers
$broadcast() event from one controller
subscribe to event with $on() in another
Example:
HTML
<div ng-controller="ctrl1 as ctrl1">
<button ng-click="ctrl1.hello()">Say hello</button>
</div>
<div ng-controller="ctrl2 as ctrl2">{{ctrl2.messages}}</div>
JavaScript
angular.module('app', [])
.service('messageBus', ['$rootScope', function($rootScope) {
return $rootScope.$new();
}])
.controller('ctrl1', ['messageBus', function(messageBus) {
this.hello = function() {
messageBus.$broadcast('hello', 'Hi! '); // <= send message
}
}])
.controller('ctrl2', ['messageBus', function(messageBus) {
var ctrl2 = this;
this.messages = '';
console.log(messageBus.mb)
messageBus.$on('hello', function(evt, msg) { // <= receive message
ctrl2.messages += msg;
});
}]);

AngularJS - update a directive outside the view

I want to update the scope inside a directive that is outside of my main view, here's my code:
index.html
<!-- the directive I want to update -->
<nav sitepicker></nav>
<section id="content-wrapper">
<!-- main content-->
<div ui-view></div>
</section>
sitepicker is essentialy just a dropdown menu that contains some html structure.
sitepicker.html
<span>{{currentWebsite}}</span> <-- this is the one I want to update
<ul>
<li ng-repeat="website in websites">{{website.name}}</li>
</ul>
and the JS:
.controller('sitepicker', function($scope, websiteService)
$scope.website = websiteService.currentWebsite; // not updating eventhough I update this in overview.js
});
overview.js
.controller('OverviewCtrl', function($scope, websiteService) {
websiteService.currentWebsite = website; // assume that this value is dynamic
});
but currentWebsite is not changing. How can I work around this? I want to avoid using $rootScope because I know it's bad.
Here's my service:
.factory('websiteService', function() {
var currentWebsite;
return {
currentWebsite: currentWebsite
};
});
Edit: Adding a watch like this works but i'm not sure if its good
.controller('sitepicker', function($scope, websiteService)
$scope.$watch(function() {
$scope.website = websiteService.currentWebsite;
});
});
Solution #1
We can add $watch to makes this possible
.controller('sitepicker', function($scope, websiteService)
$scope.$watch(function(){
return websiteService.currentWebsite;
}, function(newValue){
// Do something with the new value
});
});
Solution #2
We can also define websites in our main controller. Then, we can update it in our overview child controller like so:
.controller('sitepicker', function($scope, websiteService)
$scope.$parent.website = websiteService.currentWebsite;
});
it is important to use $parent
Since we update the controller that wraps up the whole app, we will be able to access it from anywhere, any directive, controller, view, etc.

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