I have a content controller and a header controller in my Angular app. The content controller has the ability to add items to array of objects. The header controller puts these items into the navigation.
On load the header controller successfully loads the existing items from the database and adds them to the global scope, they then show up in the nav thanks to an ng-repeat.
When the content controller adds a new item to the global scope though it's not reflected in the header. I can log out the contents of the global scope and the new item is there, it just doesn't want to show up in the dom. Using $apply() doesn't seem to make any difference.
Here's the 2 controllers:
HeaderCtrl:
angular.module('system').controller('HeaderController', ['$scope', 'Global', 'Lists', function ($scope, Global, Lists) {
$scope.global = Global;
$scope.find = function() {
Lists.query(function(lists) {
$scope.global.lists = lists;
});
};
}]);
ContentCtrl:
angular.module('lists').controller('ListsController', ['$scope', '$routeParams', '$location', '$http', 'Global', 'Lists', function ($scope, $routeParams, $location, $http, Global, Lists) {
$scope.global = Global;
$scope.create = function() {
var list = new Lists({
name: this.name,
});
list.$save(function(response) {
$location.path('lists/' + response._id);
$scope.global.lists.push(response);
});
};
}]);
Like I say a console log from the Header Controller shows that the new item is present in $scope.global.lists but it doesn't get reflected in the DOM. Any ideas?
Use a $rootScope event and catch it in the header whenever you update the other controller:
//main ctrl
$rootScope.$broadcast("updateHeader", someInfoToSend);
//header ctrl
$scope.$on("updateHeader", function(e, someInfoReceived){
// do the necessary updates here
});
It was a small data mismatch between existing items and the newly added item that was causing the issue.
The items from the database had a property of
owner: {
"_id" : "213876512461"
}
Whereas the data returned from the adding of a new item had
owner: "213876512461"
Because of this an ng-hide directive was incorrectly hiding the newly added item.
I've marked ruedamanuel's answer up, as I've ended up using his $broadcast method to let the main controller update $scope.lists in the header controller, negating the need to use $rootScope or a global variable.
Related
i am doing one angular project in which i am loading the new view by calling the function . up to this thing it is fine . now the my requirement is i want some data to transferred to the new view from the same same function . using the same controller . i am showing here the demo code.
$scope.passID = function(id){
console.log(id);
$state.go("view", {id: $scope.id });
}
If you are using the same controller, the $scope variables can be accessed in both views. But, if you have 2 views with 2 controllers, it is possible to share the $scope variables(data) between controllers through services and factory.But, the $scope variables are local to the controller itself so set the data to the service or factory to know about that particular variable.I prefer using factory, easy and smooth as butter. If you are using the service of factory in a separate file you need to include the file in index.html.
app.controller('Ctrl1', function($scope, myService, $state) {
$scope.variable1 = "One";
myService.set($scope.variable1);
$state.go('app.thepagewhereyouwanttoshare'); //go to the page where you want to share that variable.
});
app.controller('Ctrl2', function($scope, myService) {
console.log("shared variable " + myService.get());
});
.factory('myService', function() {
function set(data) {
products = data;
}
function get() {
return products;
}
return {
set: set,
get: get
}
})
I am newbie for angularjs.I have list of persons and each person have edit and delete button. when i click to edit button ng-dialog box was open and show person details and person can change and save information on database,behind save button ajax call trigger and update information on database.
Updating information on database work well but on UI side my view doesn't reflect my database changes.
I had tried to apply "$scope.$apply();" method but i got error message "$digest already in progress".
Please help me,how can refresh my scope after ajax call.
You can use shared service for that and broadcast any event through this service. Broadcasted event can be listened in any controller with $scope.$on.
For example:
angular.module("app", []).factory("sharedService", function($rootScope){
var mySharedService = {};
mySharedService.values = {};
mySharedService.personWasUpdated = function(){
$rootScope.$broadcast('update');
}
return mySharedService;
});
Ctrl for person editing.
app.controller('personEditController', ['$scope', 'sharedService', '$http', function ($scope, sharedService, $http) {
$scope.updatePerson = function(newPerson){
$http.post("../some URL/..", {person: newPerson})
.success(function(data){
sharedService.personWasUpdated(); //event broadcasing
})
};
}
Ctrl for displaying list of persons.
app.controller('personController', ['$scope', 'sharedService', '$http', function ($scope, sharedService, $http) {
var loadPersonsData = function(){
$http.get("../some URL/..").
.success(function(data){
$scope.persons = data;
})
};
loadPersonsData(); //first load
$scope.$on('update', function () {
loadPersonsData(); // load after update of any person
});
}
Try with $scope.$digest(); or use $http instead jQuery ajax or others
I have the following controllers:
HeaderCtrl, NewsFeedCtrl, MainCtrl
MainCtrl contains both the other two controllers, which are in the same level.
I'm defining an object in authenticationService and update its value in MainCtrl and I update it frequently in NewsFeedCtrl and I want to display its value in the HTML page controlled by HeaderCtrl.
when I use this line in my HeaderCtrl:
$scope.unreadNum=authenticationService.notificationList.length;
and then I use data binding in my HTML page to display its value:
{{unreadNum}}
I only get the initial value I inserted in authenticationService, not the one after the update in the other controllers.
it seems that my HeaderCtrl is defining all his scope objects only one time and then there's no more use for the Ctrl, but I still want his HTML page to be updated after the update in object values in other controllers.
to sum it up: the value of the object I want is stored in one of my services, and I am unable to display it in my HTML page because I can't seem bind it correctly.
You can send messages between the controllers using a service. The service looks something like this...
aModule.factory('messageService', function ($rootScope) {
var sharedService = {};
sharedService.message = {};
sharedService.prepForBroadcast = function(msg) {
this.message = msg;
this.broadcastItem();
};
sharedService.broadcastItem = function () {
$rootScope.$broadcast('handleBroadcast');
};
return sharedService;
});
In the controller that is sending the message, inject this service...
aModule.controller("sendingController", function ($scope, messageService) {
Then add a method that will broadcast the change to any controller that is listening...
$scope.sendMessage = function (someObject) {
messageService.prepForBroadcast(someObject);
},
In any controller that wants to receive the message, inject the service, and add a handler like this to do something with the message...
$scope.$on('handleBroadcast', function() {
//update what you will..
$scope.something = messageService.message;
});
I've got a potentially really dumb question, but how do I modify variables up in $rootScope in Angular? I've got a slide-in sidebar that I want to change the content on whenever someone clicks on a thumbnail, and I figured the easiest way to handle where the data in the sidebar comes from/the sidebar visibility would either be in global values, or in $rootScope. I'm trying to keep everything as simple as possible, but I just don't know how to handle modifying global variables.
My angular code surrounding this is:
app.run(function($rootScope) {
$rootScope.currentUrl = { value: 'visual/design/1/' };
$rootScope.detail_visible = { value: true };
});
app.controller('navController', ['$scope', '$rootScope',
function ($scope, $rootScope) {
$scope.isDetail = $rootScope.detail_visible.value;
$scope.url = $rootScope.currentUrl.value;
$scope.hide = function($rootScope) {
$rootScope.detail_visible.value = false;
};
}]);
and the connecting HTML is
<div id="detail_box" ng-class="{d_show: isDetail, d_hide: !isDetail}">
<div ng-include="url + 'detail.html'"></div>
</div>
In essence, I'm trying to make it so that when you click on a thumbnail, it changes the currentUrl value from 'visual/design/1/' to whatever they've clicked on (like, 'music/solo/2' or whatever) then changes the value of detail_visible to false, so that the classes on my sidebar switch and I get a nice little slide-in, with fresh content loaded via ng-include which I kind of love a thousand times more than I thought I would. I've been banging my head against this for about three hours now, breaking everything else on this app whenever I get the chance. What am I screwing up here? Alternatively, is there a better way of doing this?
My reason for using global variables is that I have multiple thumbnails in multiple controllers, and I want each one to be able to dynamically change the URL in my ng-include.
For your question, you change the $rootScope variable simple by referencing it with
$rootScope.detail_visible.value = newValue;
but you dont need to inject $rootScope to your function:
$scope.hide = function() { //without $rootScope
$rootScope.detail_visible.value = false;
};
But, I would suggest you to implement a service and not to pollute the rootscope for such task.
https://docs.angularjs.org/guide/services
Object properties of scopes are inherited -- in your controller, you should be able to modify $scope.detail_visible.value and see it affect the $rootScope. You still have to initialize it on the $rootScope in .run() though.
app.run(function($rootScope) {
$rootScope.currentUrl = { value: 'visual/design/1/' };
$rootScope.detail_visible = { value: true };
});
app.controller('navController', ['$scope', function ($scope, $rootScope) {
$scope.hide = function() { // don't need to pass an argument
$scope.detail_visible.value = false;
};
}]);
view:
<div id="detail_box" ng-class="{d_show: currentUrl.value, d_hide: !currentUrl.value}">
<div ng-include="currentUrl.value + 'detail.html'"></div>
</div>
I'm working on an app the uses multiple (but similar) controllers (controllerA) on a page and another controller (controllerB) that could change some properties, but ONLY if the element of controllerA was previously selected.
(in controllerA some properties could have default values, that are passed and applied by using data- attributes)
In this stage, I can only modify the last color property with controllerB (which makes sense, because that's the active scope. My question is, how can I change controllerB scope to be the "active" scope-copy of the selected controllerA?
// Code goes here
var webApp = angular.module("webApp", []);
webApp.controller("controllerA", function($scope, $rootScope, $element, styleFactory){
$scope.selected = false;
var color = angular.element($element[0]).attr("data-style-color");
styleFactory.setColor(color);
$scope.data = styleFactory.getData();
$scope.select = function(){
$scope.selected = !$scope.selected;
}
});
webApp.controller("controllerB", function($scope, $rootScope, $element, styleFactory){
$scope.data = styleFactory.getData();
});
webApp.factory("styleFactory", function(){
var data = {"style":"color:yellow"}
return {
setColor: function(color){
data = {"style":"color:"+color};
},
getData: function(){
return data;
}
};
});
for full example:
http://plnkr.co/edit/He2m7ArfRSur9Igq2eur?p=preview
thanks,
--iM
You can see a modified plnkr that works as you described here.
Personally, I would restructure the architecture of the app and rethink the styleFactory implementation.
Some points for thought:
data = {"style":"color:"+color} replaces the whole data object, thus $scope.data = styleFactory.getData() works only for the last controllerA as the reference is lost for all others.
Try creating a parent controller that will be on top of controllerA and controllerB.
Try solving this problem using directives.