$scope.some_random_variable = "random_value";
$scope.some_random_function = function(random_param){
console.log("randomness");
}
I want to know the difference in the context of digest cycle.
As far as I can understand scope_function changes the visibility of Angular Function.
basically, in your html, or whatever that interacts with your $scope, a $scope.something that is declared as a function will be launched when it is called, just like how a javascript function works, same as a variable, only difference between a $scope and a var would be that $scope is a global call.
Related
I've got the following code where I'm trying to show/hide a text box based on if a key was pressed in the global window scope. However, every time a key is pressed, it does not seem to fire the watch service. Why is this?
Plnkr here http://plnkr.co/edit/qL9ShNKegqJfnyMvichk
app.controller('MainCtrl', function($scope, $window) {
$scope.name = '';
angular.element($window).on('keypress', function(e) {
//this changes the name variable
$scope.name = String.fromCharCode(e.which);
console.log($scope.name)
})
$scope.$watch('name', function() {
console.log('hey, name has changed!');
});
});
It is because you are handling the keypress event outside of the digest cycle. I would strongly encourage you to let angular do its thing with databinding or using ngKeypress
Otherwise, in your handler, call $scope.$digest().
angular.element($window).on('keypress', function(e) {
//this changes the name variable
$scope.name = String.fromCharCode(e.which);
console.log($scope.name);
$scope.$digest();
})
On a high level view, watching a value on a scope needs two parts:
First: the watcher - like you created one. Every watcher has two parts, the watch function (or like here the value) and the listener function. The watch function returns the watched object, the listener function is called when the object has changed.
Second: the $digest cycle. The $digest loops over all watchers on a scope, calls the watch function, compares the returned newValue with the oldValue and calls the corresponding listener function if these two do not match. This is called dirty-checking.
But someone has to kick the $digest. Angular does it inside its directives for you, so you don't care. Also all build-in services start the digest. But if you change the object outside of angular's control you have to call $digest yourself, or the preferred way, use $apply.
$scope.$apply(function(newName) {
$scope.name = newName;
});
$apply first evaluates the function and then starts the $digest.
In your special case, I would suggest to use ngKeypress to do it the angular way.
I have a custom directive (this is just a simplified example). When the directive is clicked, a variable is updated and a function is executed. Both the variable and the function is from the parent scope, accessed via two-way binding and method/behavior binding respectively.
In some cases the parent scope might overrule the directive and force the variable to be a different value than the one set by the directive.
My problem is that the function is called before the variable is updated, probably because the asynchronous behaviour. That means that the variable gets overwritten by the directive.
Is there some way to force the function to be called after the variable has been updated?
This is from the directive's link function:
element.bind('click', function(){
scope.val = 'someValue'; // This should alway be executed first
scope.directiveClicked(); // This should always be executed last
});
The variable and method are accessed like this:
scope: {
val: '=value',
directiveClicked: '&onDirectiveClick'
}
Turned out that it was not called in the wrong order after all, but somehow the updated value was not displayed/stored. Adding a scope.$apply() before notifying the parent function did the trick.
element.bind('click', function(){
scope.val = 'someValue';
scope.$apply();
scope.directiveClicked();
});
So if in a lot of my controllers I have this:
$scope.error = false;
$scope.errorData = {};
$scope.working = false;
$scope.workingMessage = "Doing something, please wait..."; // best UI message ever
Is there a way of having these permanently defined on every $scope given to my controllers?
So I do need inheritence, so where can apply these scope variables to the $rootScope so that it will be picked up in all my controllers?
Update
So after much reading of the documentation, it appears that use ngApp auto bootstraps the angular app which is when the $injector service runs. I need to have the variables put on before this service runs, so I'm guessing that I need to manually bootstrap the application.
More Work
.run()
This runs after all the modules have been initialised. So here I can inject the $rootScope and put my variables on that. Then they can be used on my all my $scopes, obviously if you data bind to it then you can simply use the property names because the binder will first look at the $scope then all subsequent parent scopes until it finds the property.
.run(['$rootScope', rootScopeConfigure]);
function rootScopeConfigure($rootScope) {
$rootScope.myCustomProperty = "Hey There";
}
As per a comment on my question, this can get dangerous if you are adding properties randomly. But as I have only a few properties, it doesn't really matter or add that much overhead. (I hope).
However, all this poses one problem, if I want to modify this custom property in the controller then I have to inject $rootScope. I don't particularly want to do this, I want to be able to access these on the $scope
In response to my above paragraph, I should be able to do $scope.$root.myCustomProperty because I'm guessing that the $rootScope is the root of all $scopes.
Dirty Answer
$rootScope.$oldNew = $rootScope.$new;
$rootScope.$new = function () {
var childScope = $rootScope.$oldNew();
childScope.myCustomerProp = "Hey There";
return childScope;
}
Do this in the run() function. Basically overriding the factory to return a new child scope that has my property on it :).
Yes, scopes can be inherited from parents. You can create a parent controller for your controllers.
A scope can inherit from a parent scope, as in this example:
var parent = $rootScope;
var child = parent.$new();
parent.salutation = "Hello";
child.name = "World";
expect(child.salutation).toEqual('Hello');
child.salutation = "Welcome";
expect(child.salutation).toEqual('Welcome');
expect(parent.salutation).toEqual('Hello');
https://docs.angularjs.org/api/ng/type/$rootScope.Scope
I recommend not using scope but instead using $cacheFactory or a service/factory of your own to persist this type of state.
So I have the answer now.
.run()
This runs after all the modules have been initialised. So here I can inject the $rootScope override is $new function that I believe is just a simple scope factory.
Then any properties or methods put on that new scope can be used on my all my $scopes.
.run(['$rootScope', runtimeConfigure]);
function runtimeConfigure() {
$rootScope.$oldNew = $rootScope.$new;
$rootScope.$new = function(isolate, parent) {
var child = $rootScope.$oldNew(isolate, parent);
child.title = "Hey There";
return child;
}
}
I believe there to be no issues with doing this.
Firstly, I'm merely ghosting the original scope factory $new
Secondly, I'm just adding properties to the new scope.
What scope is a function defined inside an angular js controller, part of?
.controller('MyCtrl', ['$scope', '$location', function($scope, $location) {
function log() {
console.log('Vanakkam Ulagam');
}
var functionToCall = 'log';
????????[functionToCall]();
}])
I tried MyCtrl, this and $window. I can access the function by namespacing it like so functions.print = function() and then using functions[functionToCall]().
I am more interested in accessing the function without namespacing it. Is it possible? If not, why?
i think this is solution for this question
var f;
var a = angular.module('MyApp',[]);
a.controller('MyCtrl', ['$scope', function ($scope) {
$scope.log=function(){
console.log('test');
}
f=$scope.log;
});
and this function useable to element html for example
<button ng-click='log()'></button>
or
<button onclick='f()'></button>
If you're just trying to access global functions in your controller code, window (or $window if it's properly injected) should work fine:
$scope.alert = function(text) { window.alert(text) }
To be extra sure this works in other environments you could manually bind it in a self-invoking anonymous function:
(function(global){ //make the global scope assigned to the variable global
app.controller('MyCtrl',['$scope',function($scope){
$scope.alert = global.alert;
}]);
)(this); //this is the global object when this is evaluated
If you want to do anything inside an expression on a DOM element (such as ng-click), it's important to realise that angular expressions are evaluated by Angular's own expression evaluator (namely through $scope.$eval). So if something is not attached to a scope, it cannot be used in an angular expression.
However, if you were to do something like attach the global object to the scope, then everything is fair game. This really isn't recommended (I think there's no real reason to do this, and its better to manually define functions you need), but :
$scope.global = window
Inside your controller definition should work. You can then do things like ng-click="global.alert('Yo SO!')" in your DOM without issues.
For better testability you could use $window (you said you tried it and it didn't work... are you sure you injected it properly? Look at the example in the documentation carefully). But like I said, if you're attaching the global object to the scope you're opening Pandora's Box.
Cannot access to $scope and Food inside destroy function.
foodProject.controller('FoodsCtrl', function($scope, Food) {
$scope.foods = Food.index();
$scope.destroy = function(food){
debugger;
console.log(Food, $scope);
food.$destroy();
};
});
The local scope variables are only
food: Resource
this: $get.e.$new.a
I can't find $scope and Food
http://jsfiddle.net/fW2EA/1/
http://jsfiddle.net/wizztjh/fW2EA/3/
Include this line in your destroy function (it is missing from your fiddle):
console.log(Food, $scope);
Then in your debugger, check the Closure section of Scope Variables (I was using Chrome), when stopped on your break point. Food and $scope are in there (as one would expect!).
this in the context of the destroy function is a new scope within the ng-repeat so is not the same as $scope, although both are scopes.
Within your function, this is the scope, e.g. this.foods.
I believe if you want Food to be accessible add it to the scope, i.e., $scope.Food = Food;.
I'm still a noob and am not sure if adding Food to $scope is the right thing to do.