I am trying to persist the whole scope to a service so that it is available and already set up after user navigates away and then returns to the screen.
I have created a service and am storing the current scope there. Later I set the $scope variable passed into the controller with the one stored in my service, but the after inspecting the DOM, I see that it's bound scope is still the scope object that existed before replacing.
How can I replace the scope so that it will also be used for the DOM elements?
Thanks for any help!
the below code tries to see if the local scope variable is initialized and if so it sets the $scope to it, otherwise it continues and wires it all up normally. this.scope is a member variable defined and set in the controller's super class (not shown).
function xyzController($scope, stateService) {
_super.call(this, $scope, stateService);
if (this.scope.hasBeenInitialized) {
$scope = this.scope; // $scope is updated but the DOM's scope never changed
return;
}
$scope.hasBeenInitialized = true;
...
}
You could try:
if (this.scope.hasBeenInitialized) {
angular.extend($scope, this.scope);
return;
}
This would merge the values from this.scope onto your $scope without replacing the variable.
This won't work. Scope is wired up deep inside Angular. To give you an idea, on any element, you can call:
angular.element(someDomElement).scope();
And get its scope. It's really not do-able to replace scopes like you're trying to do. But the more immediate problem is you're just overwriting that particular variable. It's an object passed in. Imagine you have this code:
var myObject = { a: 1 };
function f(obj) {
obj = { a: 2 };
}
f(myObject);
Clearly this doesn't change myObject. It'll replace obj within your function, but the thing about scopes is they're set up for you for all the expression in your views (it's the this in any scope functions for example). You'd need to change it through and through, and I don't see a way to do that.
Related
I want to access this in only one controller if I move to another component I want to remove this value and make it null
this.$rootScope.selectedPage = page;
You can call $onDestroy inside your controller, and remove that value there
$onDestroy() - Called on a controller when its containing scope is destroyed. Use this hook for releasing external resources, watches and event handlers
e.g.
$scope.$onDestroy = function() {
this.$rootScope.selectedPage = null; // or delete it altogether
};
EDIT:
However, if you just need that one variable in just one controller you should just bind it to $scope and avoid $rootScope, that way angularjs will free it by itself.
$scope.selectedPage = ...
General rule of thumb is to avoid $rootScope if you can.
I need to clear all the $scope values while performing some operations.
For eg: If I Click a "Signout" button to redirect to "signin" page, then all the $scope or $rootScope values in the session should be cleared.
How can I achieve this?
You can do the following:
$rootScope = $rootScope.$new(true);
$scope = $scope.$new(true);
The function $new is creating a new scope inheriting the variables from the parent. true prevents the inheritance.
But this is not the correct approach, because if you use the thing above, you should bootstrap the controllers functions manually and recreating the tree of scopes.
This might be useful though, where the idea is to store the initialized data is stored in some variables and then, when assigned copied to the displayed variables.
The correct solution is to clear manually every property in each scope on the logout event like this:
Logout event:
$rootScope.$broadcast("logout");
Catching the event:
$rootScope.$on("logout", function(){
$rootScope.myData = undefined;
});
Or as suggested in the comments, to use a service and then be cleaned.
You not want delete scope
var authScope =['authLogo','currentPath','pageTitle','app'];
for (var prop in $rootScope) {
if (prop.substring(0,1) !== '$') {
if(authScope.indexOf(prop) ==-1)
delete $rootScope[prop];
}
}
I know you may have seen this question before, but is there really an answer for it? I started to doubt it.
Simply, I am using controlleras syntax as recommended, I have no problem accessing parent controller members form within the view, but I cannot do it from the constructor function of my child scope. And here is some code from what I am having right now:
myapp.controller("ParentController", function() {
this.selectedItem = {
Id: 1,
name: 'item1'
};
this.setSelectedItem = function(item) {
this.selectedItem = item;
//do other stuff
}
});
myapp.controller("ChildController", function() {
this.onItemChanged = function(newItem) {
//How can I call the parent controller instance from here
}
});
Also please notice that I want to call the 'updateSelectedItem' function from my child controller in away that the 'this' keyword will refer to the parent controller instance not the child, because I want to change the parent controller instance, so how should I do this?
To answer your question as clearly as possible, you first must have a bit of background on how the Controller-As syntax actually works.
Using Controller-As does not mean that you are not using $scope. In reality, $scope still exists. Controller-As is shorthand which creates an object on $scope and attaches the properties assigned via this to that object. On the view side, this object is explicitly bound to all the controls. you could still reference $scope.vm.property. However, since $scope is implicit in this scenario, it is not necessary to create a dependency to it.
Accessing the properties of the vm object of the parent controller in a nested scenario is still possible, but only if each controller is referenced by a different name. If your objects are outerScope and innerScope, then inside the HTML template of innerScope, you can still refer to outerScope.someProperty.
If, however, all controllers are named the same (i.e. vm), then the only way to access the parent controller would be through a property of the child scope which is aliased to a $scope property, introducing the $scope dependency.
In practice, whenever you have a controller within another controller, it's much cleaner for the innermost item to be a directive which wraps its own content, and explicitly defines which variables it needs through an Isolate Scope. However, whenever this is not necessary, the fallback should be for inner controllers to be named uniquely from outer controllers.
Is it possilbe to introspect/reflect of the model in Angular app, where you can change the scope and traverse it? Something like batarang have but that will allow to change the values.
If not is it possilbe to monkey patch Angular code (by including another script on the page) that will make it possilbe?
1. Accessing/Modyfing scope properties
Angular's $scope objects are plain old JS objects, so thay can be manipulated the standard way.
E.g. someScope.someProp retrieves the value of a property, while someScope.someProp = someValue sets the value of a property.
2. Letting Angular know
Modifying the object's properties is one thing - making Angular aware of the change is another.
Angular will not know about our modifications, until it runs a $digest cycle. If we want to apply the changes right away, we can explicitely trigger a $digest cycle, by using someScope.$apply().
3. Getting hold of a scope
In order to get a reference to the scope associated with a DOM element, we need to have a reference to the corresponding DOM Node object, wrap it in angular.element and execute its scope() method.
Something like this:
<body class="ng-scope">
var someScope = angular.element(document.body).scope();
Furthermore, if we want to access the $rootScope (the parent scope of all scopes), we can use the injector to inject the $rootScope service:
<html ng-app="myApp">
var injector = angular.element(document.documentElement).injector();
var rootScope = injector.get('$rootScope');
4. Traversing the scope-tree
Once we get hold of a scope object, we might want to traverse the scope-tree. Every scope has the following properties (among others):
$parent: The parent scope of this scope (if any).
$$nextSibling: The next sibling scope of this scope (if any).
(Sibling scopes are scopes that have the same $parent)
$$childHead: The first child scope of this scope (if any).
In order to traverse the branch of the scope-tree with someScope as its root:
var scopes = [someScope];
while (scopes.length) {
var scope = scopes.shift();
console.log(scope);
if (scope.$$nextSibling) { scopes.unshift(scope.$$nextSibling); }
if (scope.$$childHead) { scopes.unshift(scope.$$childHead); }
}
E.g. to traverse the whole scope-tree, you can use the following snippet:
var injector = angular.element(document.body).injector();
var rootScope = injector.get('$rootScope');
var scopes = [rootScope];
while (scopes.length) {
var scope = scopes.shift();
report(scope);
if (scope.$$nextSibling) { scopes.unshift(scope.$$nextSibling); }
if (scope.$$childHead) { scopes.unshift(scope.$$childHead); }
}
function report(scope) {
var str = '' + scope.$id;
while (scope.$parent) {
str = scope.$parent.$id + ' => ' + str;
scope = scope.$parent;
}
console.log(str);
}
Found the way, here is a code that change the scope outside of Angular:
var $scope = angular.element($('.section:eq(0)')).scope();
$scope.$apply(function() {
scope.color = "blue";
});
the angular.element().scope() will return scope and can be investigated. Not sure about how to traverse scope tree, but this is good starting point. If there is no scope traverse you could just traverse the DOM and check if there is new scope on that DOM element.
You can get the scope by calling the scope() method on an object returned by angular.element() (or $(), if you also use jQuery), e.g. angular.element(".foo").scope(). There's no direct way of accessing child scopes. But knowing that all elements that have a new scope associated with them have the ng-scope class, you can traverse the element tree instead.
$(".foo").find(".ng-scope").each(function() {
var scope = $(this).scope();
});
This, however can be a bit tricky, as find() will find all nodes below .foo, regardless of their depth. You could use children() instead but it may not yield any results as direct descendants of .foo may not create a new scope.
I have the following $rootScope variable which I use to save the current logged in user privilege level, then I access this variable from other controllers. Is there a way I can watch the rootScope variable for changes in order to update controllers specific variables with any changes to the root scope variable? Below is the code I am using so far, can someone please tell me what I am doing wrong and how to fix it? Thanks
In app.js under .run:
$rootScope.uPLevel = 0;
.controller
$scope.$watch($rootScope.uPLevel, function() {
$scope.userPLevel = $rootScope.uPLevel;
}, true);
The first parameter to $watch should either be a string or a function (docs). Right now you're passing it the value of $rootScope.uPLevel on controller initialization.
$scope.$watch(function() {
return $rootScope.uPLevel;
}, function() {
$scope.userPLevel = $rootScope.uPLevel;
}, true);
Two sidenotes:
It may be prudent to store this value in a service instead of $rootScope.
If uPLevel is only an integer (as your example suggests) then you don't need to pass true as the third parameter - that's only for arrays and objects. If you do want to watch a collection, then I suggest using $watchCollection instead.
I recommend watching $rootScope variables like that:
$scope.$watch('$root.uPLevel', function() {
$scope.userPLevel = $rootScope.uPLevel;
});
This way, When current directive/controller is destroyed. It clears the watch as well. In $rootScope.$watch case, the watch stays forever.