i'm current starting to wrap my head around Angular, and i'm struggling with 2 things:
The first problem:
I have an controller 'AboutCtrl', which uses variables from a service 'Abstract'. Now what i want to do is set some basic variables in this service like: 'mobileMenuOpen'.
Now what i want is that 'AboutCtrl' uses the value from 'Abstract', now i've been searching around and i found this way to do it:
constructor (private $scope: IAboutScope, $rootScope, Abstract) {
console.log(Abstract);
$scope.mobileMenuOpen = Abstract.mobileMenuOpen;
}
Now what i'm wondering is, is there a way to automatically inherit all of these variables?
The second problem:
Is there a way to display a variable from 'Abstract' service in a view/index.html?
Example: in my about view i want to show mobileMenuOpen's value, now when i inherit the variable in the AboutCtrl like shown above it works, but how would i be able to do this without inheritance?
I've done some research about best practise and using services/factory seems best practise, if not some other examples would be greatly appreciated.
Let's say your service looks like this:
.service('abstract', function abstract(){
this.mobileMenuOpen = false;
this.someMethod = function(){
//do the magic
return 'cowabunga';
};
})
If you assign the service to $scope by reference like this:
$scope.myAbstract = abstract
then all the values of your service would be assigned to myAbstract and accessible in the templates as well, also it will be two-way binded
<p> Is the menu open: {{ myAbstract.mobileMenuOpen }} </p>
<p> {{ myAbstract.someMethod() }} was the ninja turtles war cry! </p>
Assignment like
$scope.mobileMenuOpen = abstract.mobileMenuOpen;
Would be assigning it by value so whatever change you make later in service will have no effect on $scope.mobileMenuOpen
Related
Now, I know this is an off-the-wall question and that there is probably not going to be any API level access for doing this. I also understand that doing this is completely unnecessary. However, the implementation that I am aiming for requires for me to be able to make {{ variable }} look inside of the $scope.object instead of the $scope itself.
For example:
Controller($scope) {
$scope.active = ...;
}
In this case you can get the active object through {{active}} and any child elements through {{active.broken}} however for the sake of humoring me, lets assume that all of the variables I'm ever going to have to obtain is going to be part of that active object. So I'll be typing things like.. (Data not related)
{{active.title}}
{{active.author}}
{{active.content}}
You could just say "Well why not just move the title/author/content into the $scope and keep it outside of the active object, as that would achieve the desired result of this:
{{title}}
{{author}}
{{content}}
Well, that's where the problem comes in. This is a controller that is not exposed to my end-user, however the end-user does have a completely mutable object (in this example: active) that they can modify. This object has many [optional] listeners and callbacks that are invoked by the application controller when necessary.
The user's that have used my application during testing have commented on what a drag it is to have to type in active. before everything in order to get the data they wanted. Considering data from the $scope is never rendered to the screen, and only data from active is, I was wondering if perhaps there was a way to change where AngularJS looks when parsing/binding data.
If the goal is to evaluate expressions in a different context than $scope, that can be done with the $parse service.
app.controller("myVm", function($scope, $parse) {
var vm = $scope;
vm.active = { a: 5,
b: 3
};
var.myFn = $parse("a+b");
vm.total = myFn(vm.active);
console.log(vm.total);
});
The above example shows how to evaluate the expression a+b using the properties of the $scope.active object.
The DEMO on JSFiddle
I created a directive, I´m using in my template:
<data-input> </data-input>
In my directive (dataInput) I have the template (...data-input.html).
In that template the html-code says:
<input ng-change="dataChanged()" ... > ...
In the JavaScript it says:
scope.dataChanged= function(){ //change the data }
Works perfectly, but know I need to safe the changed data to a variable in my controller (that has a different scope of course).
The template, where I put the <data-input> </data-input> belongs to the final scope, I´m trying to reach.
I tried it via parameter, but it didnt work.
Thanks
Here are your options:
You can nest controllers if possible. This will create scope inheritance and you will be able to share variables of the parent.
You can use $rootscope from both the controllers to share data. Your dataChanged function can save anything to the $rootscope
You can use angular.element to get the scope of any element. angular.element('data-input').scope() returns it's cope.
This is not recommended. But there are circumstances in which people use global space to communicate between angular and non-angular code. But I don't think this is your case.
You can use Angular Watches to see changes is some variable, like this
eg:
$scope.$watch('age + name', function () {
//called when variables 'name' or 'age' changed
//Or you can use just 'age'
});
A few questions regarding structuring Angular code and the behavior of JavaScript when using variable bound vs private method naming function conventions. Is there a performance or stylistic reason for using variable bound functions / first class functions in AngularJS over private method naming? How is hoisting affected in each method? Would the second method below reduce the amount of hoisting performed and would this have a noticeable affect on application performance?
An example of private method naming. Is this a recommended way to structure Angular code?
(function () {
'use strict'
function modalController(dataItemsService, $scope) {
var vm = this;
function _getSelectedItems() {
return dataItemsService.SelectedItems();
}
function _deleteSelectedItems() {
dataItemService.DeleteItem();
$("#existConfirmDialog").modal('hide');
}
vm.SelectedItems = _getSelectedItems;
vm.deleteItemRecord = _deleteItemRecord;
}
angular.module('app').controller('modalController', ['dataItemService', '$scope', modalController]
})();
An example of variable bound functions. What about this method of structuring angular code within the controller - is there any disadvantage or advantage in terms of performance/style?
angular.module('appname').controller("NameCtrl", ["$scope", "$log", "$window", "$http", "$timeout", "SomeService",
function ($scope, $log, $window, $http, $timeout, TabService) {
//your controller code
$scope.tab = 0;
$scope.changeTab = function(newTab){
$scope.tab = newTab;
};
$scope.isActiveTab = function(tab){
return $scope.tab === tab;
};
}
]);
The first method, using "private" methods and exposing them via public aliases, is referred to as the Revealing Module Pattern, although in the example the methods aren't actually private.
The latter is a pretty standard Constructor Pattern, using $scope as context.
Is there a performance or stylistic reason for using variable bound functions / first class functions in AngularJS over private method naming?
Is [there] a recommended way to structure Angular code?
TL;DR
Fundamentally, there isn't much difference between the two styles
above. One uses $scope, the other this. One Constructor function is defined in a closure, one is defined inline.
There are scenarios where you may want a private method or value.
There are also stylistic and (probably insignificant) performance
reasons for using the variable this/vm over $scope. These are not mutually exclusive.
You'll probably want to use a
basic, bare bones, old school Constructor Pattern, and a lot of
people are exposing state and behavior via this instead of $scope.
You can allow yourself data privacy in the Controller, but most of
the time this should be leveraged by a Service/Factory. The main exception is data representative of the state of the View.
Don't use jQuery in your Controller, please.
References:
AngularJS Style Guide by Todd Motto.
AngularJS Up & Running
To answer your question thoroughly, I think it important to understand the responsibility of the Controller. Every controller's job is to expose a strict set of state and behavior to a View. Put simply, only assign to this or $scope the things you don't mind your user seeing or playing with in your View.
The variable in question (vm, $scope) is the context (this) of the instance being created by the Controller function.
$scope is Angular's "special" context; it has some behaviors already defined on it for you to use (e.g. $scope.$watch). $scopes also follow an inheritance chain, i.e. a $scope inherits the state and behaviors assigned to its parent $scope.
Take these two controllers:
angular.module("Module")
.controller("Controller", ["$scope", function($scope) {
$scope.tab = 0;
$scope.incrementTab = function() {
$scope.tab++;
};
}])
.controller("OtherController", ["$scope", function($scope) {
// nothing
}]);
And a view
<div ng-controller="Controller">
<p>{{ tab }}</p>
<button ng-click="incrementTab();">Increment</button>
<div ng-controller="OtherController">
<p>{{ tab }}</p>
<button ng-click="incrementTab();">Increment</button>
</div>
</div>
Example here
What you'll notice is that even though we didn't define $scope.tab in OtherController, it inherits it from Controller because Controller is it's parent in the DOM. In both places where tab is displayed, you should see "0". This may be the "hoisting" you're referring to, although that is an entirely different concept.
What's going to happen when you click on the first button? In both places we've exposed "tab", they will now display "1". Both will also update and increment when you press the second button.
Of course, I may very well not want my child tab to be the same tab value as the parent. If you change OtherController to this:
.controller("OtherController", ["$scope", function($scope) {
$scope.tab = 42;
}]);
You'll notice that this behavior has changed - the values for tab are no longer in sync.
But now it's confusing: I have two things called "tab" that aren't the same. Someone else may write some code later down the line using "tab" and break my code inadvertently.
We used to resolve this by using a namespace on the $scope, e.g. $scope.vm and assign everything to the namespace: $scope.vm.tab = 0;
<div ng-controller="OtherController">
<p>{{ vm.tab }}</p>
<button ng-click="vm.incrementTab();">Increment</button>
</div>
Another approach is to use the simplicity and brevity of this and take advantage of the controllerAs syntax.
.controller("OtherController", function() {
this.tab = 0;
});
<div ng-controller="OtherController as oc">
<p>{{ oc.tab }}</p>
</div>
This may be more comfortable for people who are used to using plain JS, and it's also easier to avoid conflicts with other Angular sources this way. You can always change the namespace on the fly. It's also a bit "lighter" on performance since you're not creating a new $scope instance, but I'm not sure there's much gain.
In order to achieve privacy, I would recommend encapsulating your data in a Service or Factory. Remember, Controllers aren't always singletons; there is a 1:1 relationship between a View and a Controller and you may instantiate the same controller more than once! Factories and Service objects are, however, singletons. They're really good at storing shared data.
Let all Controllers get a copy of the state from the singleton, and make sure all Controllers are modifying the singleton state using behaviors defined on the Service/Factory.
function modalController(dataItemsService) {
var vm = this;
vm.selectedItems = dataItemsService.SelectedItems(); // get a copy of my data
vm.updateItem = dataItemService.UpdateItem; // update the source
}
But wait, how do I know when another part of my app has changed my private data? How do I know when to get a new copy of SelectedItems? This is where $scope.$watch comes into play:
function modalController(dataItemsService, $scope) {
var vm = this;
vm.updateItem = dataItemService.UpdateItem; // update the source
// periodically check the selectedItems and get a fresh copy.
$scope.$watch(dataItemsService.SelectedItems, function(items) {
vm.items = items;
});
// thanks $scope!
}
If your data is not shared, or if your private data is representative of the View layer and not the Model layer, then it's totally OK to keep that in the controller.
function Controller() {
var buttonClicked = false;
this.click = function() {
buttonClicked = true; // User can not lie and say they didn't.
};
}
Lastly, DO NOT USE JQUERY IN YOUR CONTROLLER, as your reference did!
$("#existConfirmDialog").modal('hide');
This example might not be purely evil, but avoid accessing and modifying the DOM outside a Directive, you don't want to break other parts of your app by modifying the DOM underneath it.
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.
In angular, following the demos, I can define a controller as:
function TodoCtrl($scope) {
$scope.todos = [
{text:'learn angular', done:true},
{text:'build an angular app', done:false},
{text:'empty dishwasher', done:false}];
$scope.oldtodos = [];
var oldtodos2 = [];
...
You'll note that I have two oldtodos. One on the $scope and one that is just a local var. I think the latter approach is the way to go if you want to encapsulate this variable - i.e. no-one other than the controller has any interest in it and the former is good if you want to pass the variable back to the model
Am I correct?
It looks like you just want to keep a private copy of oldTodos around in the event you have to either refer back to them or resurrect one of them or something. In that case, it probably makes sense not to put the oldTodos into the scope. As Maxim said, scope is relevant if you want to bind values to the view. If you just want to save the oldTodos so keep a reference to them, a normal variable is fine.
Then, if you want to bring one of them back, just copy it into the $scope.todos and back it comes.
Note that you can also abstract all of the todos into a service and inject the service into your controller for another level of encapsulation and better testing strategy. It will also enable you to share the todos across controllers if that's necessary.
Long story short, if you want AngularJS app to interact with the application scope, then you need to add the data in the scope. That's it.
$scope.oldtodos is the correct way to add data to the scope and it can be referred by name oldtodos in HTML template. While var oldtodos2 is private in your controller, so angular will not be able to access this data in the template since it is not in the scope.