In the "Create Components" section of AngularJS's homepage, there is this example:
controller: function($scope, $element) {
var panes = $scope.panes = [];
$scope.select = function(pane) {
angular.forEach(panes, function(pane) {
pane.selected = false;
});
pane.selected = true;
}
this.addPane = function(pane) {
if (panes.length == 0) $scope.select(pane);
panes.push(pane);
}
}
Notice how the select method is added to $scope, but the addPane method is added to this. If I change it to $scope.addPane, the code breaks.
The documentation says that there in fact is a difference, but it doesn't mention what the difference is:
Previous versions of Angular (pre 1.0 RC) allowed you to use this interchangeably with the $scope method, but this is no longer the case. Inside of methods defined on the scope this and $scope are interchangeable (angular sets this to $scope), but not otherwise inside your controller constructor.
How does this and $scope work in AngularJS controllers?
"How does this and $scope work in AngularJS controllers?"
Short answer:
this
When the controller constructor function is called, this is the controller.
When a function defined on a $scope object is called, this is the "scope in effect when the function was called". This may (or may not!) be the $scope that the function is defined on. So, inside the function, this and $scope may not be the same.
$scope
Every controller has an associated $scope object.
A controller (constructor) function is responsible for setting model properties and functions/behaviour on its associated $scope.
Only methods defined on this $scope object (and parent scope objects, if prototypical inheritance is in play) are accessible from the HTML/view. E.g., from ng-click, filters, etc.
Long answer:
A controller function is a JavaScript constructor function. When the constructor function executes (e.g., when a view loads), this (i.e., the "function context") is set to the controller object. So in the "tabs" controller constructor function, when the addPane function is created
this.addPane = function(pane) { ... }
it is created on the controller object, not on $scope. Views cannot see the addPane function -- they only have access to functions defined on $scope. In other words, in the HTML, this won't work:
<a ng-click="addPane(newPane)">won't work</a>
After the "tabs" controller constructor function executes, we have the following:
The dashed black line indicates prototypal inheritance -- an isolate scope prototypically inherits from Scope. (It does not prototypically inherit from the scope in effect where the directive was encountered in the HTML.)
Now, the pane directive's link function wants to communicate with the tabs directive (which really means it needs to affect the tabs isolate $scope in some way). Events could be used, but another mechanism is to have the pane directive require the tabs controller. (There appears to be no mechanism for the pane directive to require the tabs $scope.)
So, this begs the question: if we only have access to the tabs controller, how do we get access to the tabs isolate $scope (which is what we really want)?
Well, the red dotted line is the answer. The addPane() function's "scope" (I'm referring to JavaScript's function scope/closures here) gives the function access to the tabs isolate $scope. I.e., addPane() has access to the "tabs IsolateScope" in the diagram above because of a closure that was created when addPane() was defined. (If we instead defined addPane() on the tabs $scope object, the pane directive would not have access to this function, and hence it would have no way to communicate with the tabs $scope.)
To answer the other part of your question: how does $scope work in controllers?:
Within functions defined on $scope, this is set to "the $scope in effect where/when the function was called". Suppose we have the following HTML:
<div ng-controller="ParentCtrl">
<a ng-click="logThisAndScope()">log "this" and $scope</a> - parent scope
<div ng-controller="ChildCtrl">
<a ng-click="logThisAndScope()">log "this" and $scope</a> - child scope
</div>
</div>
And the ParentCtrl (Solely) has
$scope.logThisAndScope = function() {
console.log(this, $scope)
}
Clicking the first link will show that this and $scope are the same, since "the scope in effect when the function was called" is the scope associated with the ParentCtrl.
Clicking the second link will reveal this and $scope are not the same, since "the scope in effect when the function was called" is the scope associated with the ChildCtrl. So here, this is set to ChildCtrl's $scope. Inside the method, $scope is still the ParentCtrl's $scope.
Fiddle
I try to not use this inside of a function defined on $scope, as it becomes confusing which $scope is being affected, especially considering that ng-repeat, ng-include, ng-switch, and directives can all create their own child scopes.
The reason 'addPane' is assigned to this is because of the <pane> directive.
The pane directive does require: '^tabs', which puts the tabs controller object from a parent directive, into the link function.
addPane is assigned to this so that the pane link function can see it. Then in the pane link function, addPane is just a property of the tabs controller, and it's just tabsControllerObject.addPane. So the pane directive's linking function can access the tabs controller object and therefore access the addPane method.
I hope my explanation is clear enough.. it's kind of hard to explain.
I just read a pretty interesting explanation on the difference between the two, and a growing preference to attach models to the controller and alias the controller to bind models to the view. http://toddmotto.com/digging-into-angulars-controller-as-syntax/ is the article.
NOTE: The original link still exists, but changes in formatting have made it hard to read. It's easier to view in the original.
He doesn't mention it but when defining directives, if you need to share something between multiple directives and don't want a service (there are legitimate cases where services are a hassle) then attach the data to the parent directive's controller.
The $scope service provides plenty of useful things, $watch being the most obvious, but if all you need to bind data to the view, using the plain controller and 'controller as' in the template is fine and arguably preferable.
I recommend you to read the following post:
AngularJS: "Controller as" or "$scope"?
It describes very well the advantages of using "Controller as" to expose variables over "$scope".
I know you asked specifically about methods and not variables, but I think that it's better to stick to one technique and be consistent with it.
So for my opinion, because of the variables issue discussed in the post, it's better to just use the "Controller as" technique and also apply it to the methods.
In this course(https://www.codeschool.com/courses/shaping-up-with-angular-js) they explain how to use "this" and many other stuff.
If you add method to the controller through "this" method, you have to call it in the view with controller's name "dot" your property or method.
For example using your controller in the view you may have code like this:
<div data-ng-controller="YourController as aliasOfYourController">
Your first pane is {{aliasOfYourController.panes[0]}}
</div>
Previous versions of Angular (pre 1.0 RC) allowed you to use this
interchangeably with the $scope method, but this is no longer the
case. Inside of methods defined on the scope this and $scope are
interchangeable (angular sets this to $scope), but not otherwise
inside your controller constructor.
To bring back this behaviour (does anyone know why was it changed?) you can add:
return angular.extend($scope, this);
at the end of your controller function (provided that $scope was injected to this controller function).
This has a nice effect of having access to parent scope via controller object that you can get in child with require: '^myParentDirective'
$scope has a different 'this' then the controller 'this'.Thus if you put a console.log(this) inside controller it gives you a object(controller) and this.addPane() adds addPane Method to the controller Object. But the $scope has different scope and all method in its scope need to be accesed by $scope.methodName().
this.methodName() inside controller means to add methos inside controller object.$scope.functionName() is in HTML and inside
$scope.functionName(){
this.name="Name";
//or
$scope.myname="myname"//are same}
Paste this code in your editor and open console to see...
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>this $sope vs controller</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular.min.js"></script>
<script>
var app=angular.module("myApp",[]);
app.controller("ctrlExample",function($scope){
console.log("ctrl 'this'",this);
//this(object) of controller different then $scope
$scope.firstName="Andy";
$scope.lastName="Bot";
this.nickName="ABot";
this.controllerMethod=function(){
console.log("controllerMethod ",this);
}
$scope.show=function(){
console.log("$scope 'this",this);
//this of $scope
$scope.message="Welcome User";
}
});
</script>
</head>
<body ng-app="myApp" >
<div ng-controller="ctrlExample">
Comming From $SCOPE :{{firstName}}
<br><br>
Comming from $SCOPE:{{lastName}}
<br><br>
Should Come From Controller:{{nickName}}
<p>
Blank nickName is because nickName is attached to
'this' of controller.
</p>
<br><br>
<button ng-click="controllerMethod()">Controller Method</button>
<br><br>
<button ng-click="show()">Show</button>
<p>{{message}}</p>
</div>
</body>
</html>
Related
It is clear that a method should be set to scope in order to be visible or available for the view (in html) or in a directive or in any other place where the method should be accessed, so that the method can be accessed through the $scope. My question is to know whether $scope is always necessary or a good practice to use when a method is defined. For instance, following are different method declarations:
Scenario 1. $scope.myMethod = function(){};
Scenario 2. var myMethod= function(){};
if 'myMethod' is only used in one controller, is it required to set it to the $scope? What are the advantages or why scenario 1 or 2 is good ?
What if someone has declared it as $scope.myMethod = function(){} ? is it not good or an unnecessary load to the $scope ? What can be the best practice?
NB: I don't need to make any poll here, please let me know any pros and cons
You mainly use the first scenario for things like binding click events. If you will only call the myMethod you don't really need to define it in scope.
For example following will need the first definition:
<button ng-click="myMethod()">My Button</button>
But following can use the second:
angular.module('myCtrl', [])
.controller('myController', function($scope) {
var myMethod = function (text) {alert(text)};
$scope.mySecondMethod = function () { myMethod('second'); }
$scope.myThirdMethod = function () { myMethod('third'); }
In second case you can use mySecondMethod and myThirdMethod in event binding.
Scenario 1. $scope.myMethod = function(){};
Scenario 2. var myMethod= function(){};
Scope is the glue between the controller and the view. If you really
need any variable and methods for the current view, then this should
be added to the scope variable. Please see the controller as property
if you don't want to add the methods to scope.
Scenario 1
If you declare a method using the scope, then this will be available/accessed from the view.
Scenario 2
If you really don't need this method to be accessed from the view, then you can remove this method from the scope.
You need $scope to define a method only if you are calling that function from your html code.
You can use this or some_names also instead of $scope.
$scope is just to mean that the function's (mehtod's) scope is inside that controller function and can be accessible from the html code that written inside the controller
If the function is calling inside the javascript (controller) only, then use normal definition
function myMethod(){}
OR
var myMethod = function(){}
Declaring a method or variable as $scope variable is only for accessing from DOM. If you are creating a new variable in $scope. It just adding that variable to the clousure of $scope as $scope: {first, seccond, third}
When ever you are calling a $scope function that just returns from the closure. There is not much load to the $scope I guess
Scope is the glue between application controller and the view. During
the template linking phase the directives set up $watch expressions on
the scope. The $watch allows the directives to be notified of property
changes, which allows the directive to render the updated value to the
DOM.
Both controllers and directives have reference to the scope, but not
to each other. This arrangement isolates the controller from the
directive as well as from the DOM. This is an important point since it
makes the controllers view agnostic, which greatly improves the
testing story of the applications.
From the documentation
When I try to call one controller method from another controller in the angular javascript, an error occurs. I think something is missing there. If anyone has the complete method to do this, please give me the answer.
Being able to share a variables throught different controllers it's a common issue in angular.
Your function can be seen as an object: you'll need to share it's reference throught scopes.
To perform that, you can either:
.
Use a service:
You can see services as singletons object that are injected in your controllers: a common object shared throught your controllers.
You can use this wonderful guide to understand how to share your scope variables by using services (this guide uses factory and not services, but for your needs will be ok).
If you still wants to use services, follow this answer.
.
Use scope hierarchy:
Sometimes you have a hierarchy between controllers (and thus, scopes).
Let's assume you have an HTML formatted this way:
<div ng-controller="parentController">
<!-- some content here...-->
<div ng-controller="childController">
<!-- some content here...-->
</div>
</div>
This structure will produces two different scopes: one for parentController, and one for childController.
So, you childController scope will be able to access parentController scope by simply using $parent object. For example:
myApp.controller('parentController', function($scope) {
$scope.functionExample = function(){
console.log('hey there');
};
})
myApp.controller('childController', function($scope) {
$scope.functionExample();
});
Note: the parent object is accesible directly in your child scope, even if declared in your parent.
But beware of this method: you can't never really sure of your scope hierarchy, so a check if the variable is defined should be always needed.
.
Use $rootScope:
You can use rootscope as a common shared object to share yours. But beware: this way, you'll risk to pollute your $rootScope of variables when $rootScope should be used for other uses (see message broadcasting).
Anyway, see this answer for an example use case.
.
Use Messages:
By using messages, you can share objects references, as shown in this example:
myApp.controller('parentController', function($scope, $rootScope) {
var object = { test: 'test'}
$rootScope.$broadcast('message', message)
console.log('broadcasting');
});
myApp.controller('childController', function($scope, $rootScope) {
$scope.$on('message', function(message){
console.log(message);
});
});
.
In the end, i suggest you to use a common service for all your variables or, where you can, use the scope hierarchy method.
If two controllers are nested in one controller,
then you can simply call the other by using:
$scope.parentMethod();
Angular will search for parentMethod() function starting with the current scope up to it reach the $rootscope
View:
<div ng-controller="MainPageController as mpc">
<h1>{{mpc.city}}</h1>
</div>
Controller:
app.controller('MainPageController',['GetCityService', function(GetCityService){
this.city = null;
this.getCityService = GetCityService;
this.getCityService.getCity().success(function(data){
this.city = data.city
console.log(this.city); // Prints the city correctly.
});
}]);
I am trying to use "controller as" method to access values from controller. My service returns the city name correctly which is assigned to controller variable "this.city". but I am unable to bind the value in the View" (I see nothing displayed in the view)
Please let me know if I'm doing it wrong.
PS: I tried to use $scope, it perfectly works. I am unable to understand why I am unable to bind the controller variable" and If possible suggest when $scope and this variables should be used.
Thanks in advance :)
Since the original question has been answered, I'll try to explain the answer. It's just a matter of understanding 'this' in javascript.
Within your controller function 'this' refers to the controller itself. With in your success callback 'this' no longer refers to the controller (I'm guessing the window if you're not in strict mode?).
By saving 'this' to a variable, such as 'vm', whenever you refer to vm you know that you are referencing the controller, and not whatever 'this' refers to in that context (which can change).
On another note, now that we have all these features in 1.5, it's best to not use $scope anymore and to use 'controllerAs' all the time. I also suggest moving to components instead of using stand-alone controllers.
I'm a bit confused with the use of $scope in controllers and of scope in directives. Please verify if my understanding is correct (and also provide some alternative ways how to do this).
Let's say I have an html:
<div ng-controller="app1_Ctrl">
.
.
.
<input type="text" ng-model="value"/>
<input type="checkbox" />
<button ng-click="submit()"></button>
</div>
And my main.js
(function() {
angular.module('mainApp', ['app1']);
})();
And my app1 looks like this (based on official AngularJS documentation here)
(function() {
var app = angular.module('app1', []);
app.controller('app1_Ctrl', ["$scope", function($scope) {
.
.
.
}]);
app.directive('app1_Dir1', [function() {
function link(scope, element, attr) {
scope.$watch(attr.someAttrOfCheckBox, function() {
// some logic here
});
function submit() {
// some logic here
}
}
return link;
}]);
})();
How does $scope.value passed in scope in directive so that I can do some manipulations there? Will ng-click fire the function submit() in the directive link? Is it correct to use scope.$watch to listen for an action (ticked or unticked of course) in checkbox element?
Many thanks to those who can explain.
By default, directive scope is controller $scope; but it means the directive is directly dependent on your controller and you need a different controller for each instance of the directive you want to use. It is usually considered a best practice to isolate your directive scope and specifically define the variables you wish to pass it from your controller.
For this, you will need to add a scope statement to your directive :
scope {
label :'#',
context : '=',
function : '&'
}
and update your view :
<my-directive label="labelFromController" context="ctxtFromController" function="myFunction()" ></my-directive>
The symbols denote the kind of thing you wish to pass through : # is for one-way binding (as a string in your directive), = is for two-way binding of an object (which enables the directive to update something in your controller), and & is for passing a function.
There are a lot of additional options and subtleties that are best explained by the Angular doc https://docs.angularjs.org/guide/directive. There are also some nice tutorials out there (e.g. http://www.sitepoint.com/practical-guide-angularjs-directives/)
Your submit() function is not attached to anything, so you won't be able to call if from your viewer. You need to define it as scope.submit = function() ... in your link function if you wish to access it.
You can use $watch for this kind of thing, but there are usually other more elegant ways to achieve this by leveraging the fact that angular already "watches" the variables it is aware of and monitors any changes he can (this can be an issue when some external service changes data for exemple, because angular cannot listen to events it is not made aware of). Here, you can probably simply associate the ng-model directive to your input checkbox to store its true/fale (checked/unchecked) value, and the ng-change or ng-click directives to act on it. The optimal solution will mostly depend on the exact nature of your business logic.
Some additional thoughts :
The HTML insides of your directive should be packaged in an inline template field, or in a separate HTML file referenced by the templateUrl field in your directive.
In your HTML code above, your directive is not referenced anywhere. It should be an element, attribute or class (and your directive definition should reflect the way it can be called, with the restrict field). Maybe you have omitted the line containing the directive HTML, but as it stands, your directive doesn't do anything.
To my knowledge, you don't need to return link. Think of it as the "body" of your directive, where you define the variables and functions you will call in the HTML.
Your directive doesn't actually need HTML code and the above thoughts might be irrelevant if you are going in a different direction, but encapsulating some kind of view behaviour that you want to reuse is probably the most common use of directives.
I read about the new syntax from angularJS regarding controller as xxx
The syntax InvoiceController as invoice tells Angular to instantiate
the controller and save it in the variable invoice in the current
scope.
Visualization :
Ok , so I wont have the parameter $scope in my controller and the code will be much cleaner in the controller.
But
I will have to specify another alias in the view
So Until now I could do :
<input type="number" ng-model="qty" />
....controller('InvoiceController', function($scope) {
// do something with $scope.qty <--notice
And now I can do :
<input type="number" ng-model="invoic.qty" /> <-- notice
....controller('InvoiceController', function() {
// do something with this.qty <--notice
Question
What is the goal of doing it ? removing from one place and add to another place ?
I will be glad to see what am I missing.
There are several things about it.
Some people don't like the $scope syntax (don't ask me why). They say that they could just use this. That was one of the goals.
Making it clear where a property comes from is really useful too.
You can nest controllers and when reading the html it is pretty clear where every property comes.
You can also avoid some of the dot rule problems.
For example, having two controllers, both with the same name 'name', You can do this:
<body ng-controller="ParentCtrl">
<input ng-model="name" /> {{name}}
<div ng-controller="ChildCtrl">
<input ng-model="name" /> {{name}} - {{$parent.name}}
</div>
</body>
You can modify both parent and child, no problem about that. But you need to use $parent to see the parent's name, because you shadowed it in your child controller. In massive html code $parent could be problematic, you don't know where that name comes from.
With controller as you can do:
<body ng-controller="ParentCtrl as parent">
<input ng-model="parent.name" /> {{parent.name}}
<div ng-controller="ChildCtrl as child">
<input ng-model="child.name" /> {{child.name}} - {{parent.name}}
</div>
</body>
Same example, but it is much much clearer to read.
$scope plunker
controller as plunker
The main advantage with controller as syntax I see is that you can work with controllers as classes, not just some $scope-decorating functions, and take advantage of inheritence. I often run into a situation when there's a functionality which is very similar to a number of controllers, and the most obvious thing to do is to create a BaseController class and inherit from it.
Even though there's is $scope inheritence, which partially solves this problem, some folks prefer to write code in a more OOP manner, which in my opinion, makes the code easier to reason about and test.
Here's a fiddle to demonstrate: http://jsfiddle.net/HB7LU/5796/
I believe one particular advantage is clear when you have nested scopes. It will now be completely clear exactly what scope a property reference comes from.
Source
Difference between Creating a controller using the $scope object and Using the “controller as” syntax and vm
Creating a controller using the $scope object
Usually we create a controller using the $scope object as shown in the listing below:
myApp.controller("AddController", function ($scope) {
$scope.number1;
$scope.number2;
$scope.result;
$scope.add = function () {
$scope.result = $scope.number1 + $scope.number2;
}
});
Above we are creating the AddController with three variables and one behaviour, using the $scope object controller and view, which talk to each other. The $scope object is used to pass data and behaviour to the view. It glues the view and controller together.
Essentially the $scope object performs the following tasks:
Pass data from the controller to the view
Pass behaviour from the controller to the view
Glues the controller and view together
The $scope object gets modified when a view changes and a view gets modified when the properties of the $scope object change
We attach properties to a $scope object to pass data and behaviour to the view. Before using the $scope object in the controller, we need to pass it in the controller function as dependencies.
Using the “controller as” syntax and vm
We can rewrite the above controller using the controller as syntax and the vm variable as shown in the listing below:
myApp.controller("AddVMController", function () {
var vm = this;
vm.number1 = undefined;
vm.number2=undefined;
vm.result =undefined;
vm.add = function () {
vm.result = vm.number1 + vm.number2;
}
});
Essentially we are assigning this to a variable vm and then attaching a property and behaviour to that. On the view we can access the AddVmController using controller as syntax. This is shown in the listing below:
<div ng-controller="AddVMController as vm">
<input ng-model="vm.number1" type="number" />
<input ng-model="vm.number2" type="number" />
<button class="btn btn-default" ng-click="vm.add()">Add</button>
<h3>{{vm.result}}</h3>
</div>
Ofcourse we can use another name than “vm” in the controller as syntax. Under the hood, AngularJS creates the $scope object and attaches the properties and behaviour. However by using the controller as syntax, the code is very clean at the controller and only the alias name is visible on the view.
Here are some steps to use the controller as syntax:
Create a controller without $scope object.
Assign this to a local variable. I preferred variable name as vm, you can choose any name of your choice.
Attach data and behaviour to the vm variable.
On the view, give an alias to the controller using the controller as syntax.
You can give any name to the alias. I prefer to use vm unless I’m not working with nested controllers.
In creating the controller, there are no direct advantages or disadvantages of using the $scope object approach or the controller as syntax. It is purely a matter of choice, however, using the controller as syntax makes the controller’s JavaScript code more readable and prevents any issues related to this context.
Nested controllers in $scope object approach
We have two controllers as shown in the listing below:
myApp.controller("ParentController", function ($scope) {
$scope.name = "DJ";
$scope.age = 32;
});
myApp.controller("ChildController", function ($scope) {
$scope.age = 22;
$scope.country = "India";
});
The property “age” is inside both controllers, and on the view these two controllers can be nested as shown in the listing below:
<div ng-controller="ParentController">
<h2>Name :{{name}} </h2>
<h3>Age:{{age}}</h3>
<div ng-controller="ChildController">
<h2>Parent Name :{{name}} </h2>
<h3>Parent Age:{{$parent.age}}</h3>
<h3>Child Age:{{age}}</h3>
<h3>Country:{{country}}</h3>
</div>
</div>
As you see, to access the age property of the parent controller we are using the $parent.age. Context separation is not very clear here. But using the controller as syntax, we can work with nested controllers in a more elegant way. Let’s say we have controllers as shown in the listing below:
myApp.controller("ParentVMController", function () {
var vm = this;
vm.name = "DJ";
vm.age = 32;
});
myApp.controller("ChildVMController", function () {
var vm = this;
vm.age = 22;
vm.country = "India";
});
On the view these two controllers can be nested as shown in the listing below:
<div ng-controller="ParentVMController as parent">
<h2>Name :{{parent.name}} </h2>
<h3>Age:{{parent.age}}</h3>
<div ng-controller="ChildVMController as child">
<h2>Parent Name :{{parent.name}} </h2>
<h3>Parent Age:{{parent.age}}</h3>
<h3>Child Age:{{child.age}}</h3>
<h3>Country:{{child.country}}</h3>
</div>
</div>
In the controller as syntax, we have more readable code and the parent property can be accessed using the alias name of the parent controller instead of using the $parent syntax.
I will conclude this post by saying that it’s purely your choice whether you want to use the controller as syntax or the $scope object. There is no huge advantage or disadvantage to either, simply that the controller as syntax you have control on the context is a bit easier to work with, given the clear separation in the nested controllers on the view.
I find the main advantage is a more intuitive api since the methods/properties are associated with the controller instance directly and not the scope object. Basically, with the old approach, the controller becomes just a decorate for building up the scope object.
Here are some more info on this: http://www.syntaxsuccess.com/viewarticle/551798f20c5f3f3c0ffcc9ff
From what I've read, $scope will be removed in Angular 2.0, or at least how we view the use of $scope. It might be good to start using controller as as the release of 2.0 nears.
Video link here for more discussion on it.