I am learning angularJS and while learning found the controller as syntax much easier to read and a little easier for me coming from the OO world to understand. I have read several articles and SO answers that all seem to point to $scope and the 'controller as ' syntax being the same and that the 'controller as' syntax is just syntactical sugar.
However, I posted another question on SO here and the user that answered the question says that I have to still inject $scope to use the 'ui select' directives even though I am using the controller as syntax. Which is it? And if I don't have to use the $scope, what am I missing to get the 'controller as' syntax to work with ui-select?
Sorry this is long with no examples.
A controller in both forms Controller and Controller As are functions. The main difference from my understanding is Controller As when called is called using the new keyword which is why the 'this' syntax works. This is also why you can do the prototype inheritance with Controller As syntax while you can't do that with the normal Controller syntax. The other cool part about Controller As is the namespacing you can place the scope variables in which means in the HTML it is easy to reason which parts go to which controller. If you want I can give examples but that is the core difference from my understanding.
The controllerAs syntax exposes your controller to the template, so rather than binding a bunch of properties to $scope in your controller, you can make them properties of an instance of your controller. (Controllers are JavaScript "class" constructors.)
So if you have the following:
angular.module('myApp')
.controlller('MyController', function() {
var vm = this;
vm.foo = 'bar';
});
...you can access it in your template like this:
<div ng-controller="MyController as vm">
{{ vm.foo }}
</div>
Now, if you want to access scope variables in your controller -- or call a scope method such as $on -- you still need to inject $scope into your controller. Note the $ before scope, which indicates it is a service. This $scope service simply exposes the current scope.
All of that said, if you find yourself injecting $scope into your controller, you should question your approach. Better to create a custom directive and access scope via the link function, or access scope from the template.
Recommended reading: http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/
controller as is nice because you can nest ng-controllers and know which controller you're acting on
<div ng-controller="Ctrl1 as c1">
<--Using c1 here -->
<div ng-controller="Ctrl2 as c2">
<-- Using c2 here -->
</div>
<--Using c1 here -->
</div>
Related
I have a couple questions regarding the use of ng-init - I've see many people online recommend substituting ng-init for running the desired function as soon as the controller is ready.
What if I have two vies that use the same controller, but I only want one of the views to trigger a specific controller function, like so
index.html
<div ng-controller="myController">
{{ someStuff }}
</div>
<div ng-controller="myController" ng-init="run()">
{{ someOtherStuff }}
</div>
app.js
.controller('myController', function($scope){
$scope.someStuff = 'ABC';
$scope.run = function(){
$scope.someOtherStuff = 'XYZ';
}
}
In this scenario, what is the real downside of using ng-init to call run()? Would there be any issues if run() was an asynchronous function?
In my opinion this seems to the the job just fine, but I may have glossed over why this is a bad idea. Any input is appreciated!
I personally avoid using ng-init unless there are NO other ways that I can solve my problem. That's really what ng-init is supposed to be used for, but you'll find a plethora of bad usages of ng-init everywhere you look.
As per the ng-init documentation:
This directive can be abused to add unnecessary amounts of logic into your templates. There are only a few appropriate uses of ngInit, such as for aliasing special properties of ngRepeat, as seen in the demo below; and for injecting data via server side scripting. Besides these few cases, you should use controllers rather than ngInit to initialize values on a scope.
A better idea would be to simply use another controller and map your templates 1:1 with the controllers.
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
i don't know how to listen property of controller through the $scope
http://codepen.io/Tek/pen/fgIkG/ - controller as syntax
http://codepen.io/Tek/pen/zqCpF /?editors=111 - $scope
You need to use $scope and not this inside a controller to bind values.
Here's the edited code
And I believe you were going for this.
In the newest version of Angular JS i.e, 1.2 they have introduces a new keyword controllerAs to make it possible not to have scope inside a controller.
<div ng-controller="testCtrl as test">
{{test.value}}
</div>
And in your controller
app.controller('testCtrl ', function () {
this.value = 'Hello World';
});
See the above controller is generated with out injecting $scope in it.
Here is a good video tutorial explaining this
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.
How to set ng-controller as an expression from the $scope?
According to the documentation:
ngController – {expression} – Name of a globally accessible
constructor function or an expression that on the current scope
evaluates to a constructor function.
But how to evaluate scope expression as a controller for controllers that have been registered with module .controller?
For example:
Layout:
<div ng-controller="myExpr"></div>
JavaScript (define controller):
app.controller('myCtrl', ['$scope', '$timeout', function () { ... }];
JavaScript (parent scope):
$scope.myExpr = ...;
What should be in myExpr to use myCtrl as a controller via expression?
I've tried $controller('myCtrl')... not working...
P.S. If controller has been defined via globally accessible function.. it's possible to provide it as myExpr. But what to do if it has been defined so?
The expressions that ng-controller accept are a bit wierd. So you can do this by writing your controller slightly differently (but read below as for why you probably don't want to).
function myCtrl($scope) {
$scope.value = 'Stuff';
}
This is a controller and will work like normal for this case. Like in this example: http://jsbin.com/ubevel/2/edit
So why not do it?
First of all this is not a good way to define things from a testing perspective. Secondly, this allows you to set the controller dynamically on load but it won't allow you to change things after that. If you change the value of myExpr after the page has loaded the controller will not change.
So what to do?
I would highly suggest looking at using a service instead. Swap out your actions by supplying your outer controller with a service that you then change in the same manner you are now trying to change the inner controller. So something like: http://jsbin.com/ubevel/5/edit
This service can be swapped out on the fly, changing it will change the actions that are available in the scope.
You could also use an include, but this would result in duplicate html. I personalty am fine with this since I am against reusing html for two different types objects (sooner or later you want to change one but not the other and it becomes a mess). But a lot of people would object to that.
An extra note: There are probably nicer ways to do it with controllers, I probably haven't looked at all angles, but I just don't think controllers are the right tool for this case.