Angular MyCtrl as instead of scope not working for me - angularjs

I've got a form with hundred of fields.
As part of a secondary controller I'm trying to avoid the $scope to bind the data to my view. The way I go is:
angular
.module('app')
.controller('SequencesController', SequencesController) // main controller for the view
.controller('AccordionCtrl', AccordionCtrl); // the one I'm targetting
AccordionCtrl.$inject = ['$scope','$http'];
function AccordionCtrl($scope,$http) {
this.foo = "bar";
}
I'm using this on my view but it's not working.
<div ng-controller="AccordionCtrl as acc">
Acc : {{ acc.foo }}
</div>
Using $scope does work though. Any ideas?
Extra information:
The view is binded to a main controller that's called like that in app.js
.when('/sequences', {
controller: 'SequencesController',
templateUrl: 'app/Sequences/sequences.view.html',
controllerAs: 'vm'
})

When you are trying to access to the object(controller) property: {{ acc.foo }}, which angular is doing behind is: $scope.acc.foo, because this is how angular works, you must define your variables inside the scope.
If you dont want to interfer in the showing variables, create two diferent objects inside the scope: $scope.viewVars and $scope.backVars and add them all properties you need separately.

Related

Automatically instantiate AngularJS controller nested in templateUrl

I'm just learning Angular and have a very basic app set up. When rendering some data via the templateUrl property of a route, what would be the best way to include a sub-controller in the returned template? For example, including a "createOrEditItem" template at the bottom of a "viewItem" template so that the "createOrEditItem" can be reused on its own later?
I've tried putting a div in the template with its ng-controller attribute set to a controller name that I've defined at the app level, but it's not being activated. Should this be done with a directive instead to make it instantiate when the master controller has its contents set, or am I missing something more fundamental?
yes, as mentioned in the later part of the question, you should be using a directive. Or, if using AngularJS >= v1.5, component should be the choice because they are pluggable and works well with nesting too.
Note that for the route also, you can directly use a component like this:
var myMod = angular.module('myMod', ['ngRoute']);
myMod.component('home', {
template: '<h1>Home</h1><p>Hello, {{ $ctrl.user.name }} !</p>',
// ^^^^ other components can be used here
controller: function() {
this.user = {name: 'world'};
}
});
myMod.config(function($routeProvider) {
$routeProvider.when('/', {
template: '<home></home>'
});
});
Now, as the comment suggests, you can freely use other components in the template of home component.
Hope this helps a bit!
A directive can be used.
Another option is to use a seperate view/route. So when you add a ui-view tag, you could define your view and route.
This is explained here:
https://scotch.io/tutorials/angular-routing-using-ui-router

Angular Controller and controllerAs keyword usage in directive

learning angular so some time things not clear when read article on angular. here i stuck to understand what is the usage or importance of this keywords Controller and controllerAs in directive.
code taken from here http://blog.thoughtram.io/angularjs/2015/01/02/exploring-angular-1.3-bindToController.html
app.controller('SomeController', function () {
this.foo = 'bar';
});
app.directive('someDirective', function () {
return {
restrict: 'A',
controller: 'SomeController',
controllerAs: 'ctrl',
template: '{{ctrl.foo}}'
};
});
i like to know understand the importance of this two keywords in directive and they are controller: 'SomeController', and controllerAs: 'ctrl',
please tell me if we do not use these two keyword controller: 'SomeController', and controllerAs: 'ctrl', then what would happen or what would be worse ?
please help me to understand the usage or importance of this keywords controller: 'SomeController', and controllerAs: 'ctrl', in directive. thanks
You need the controller if you plan on referencing a controller object. This is how you hook it up.
The controllerAs allows you to create a variable that you can reference the controller with in lieu of using the $scope.
Refined answer:
<html ng-app="app">
<head></head>
<body>
<script src="node_modules/angular/angular.js"></script>
<script>
var app = angular.module('app', []);
app.directive('fooDirective', function() {
return {
restrict: 'A',
controller: function($scope) {
// No 'controllerAs' is defined, so we need another way
// to expose this controller's API.
// We can use $scope instead.
$scope.foo = 'Hello from foo';
},
template: '{{foo}}'
};
});
app.directive('barDirective', function() {
return {
restrict: 'A',
controller: function() {
// We define a 'vm' variable and set it to this instance.
// Note, the name 'vm' is not important here. It's not public outside this controller.
// The fact that the 'controllerAs' is also called 'vm' is just a coincidence/convention.
// You could simply use 'this.bar' if you prefer.
var vm = this;
vm.bar = 'Hello from bar';
},
// This allows us to reference objects on the controller's instance by
// a variable called 'vm'.
controllerAs: 'vm',
// Now we can reference objects on the controller using the 'controllerAs' 'vm' variable.
template: '{{vm.bar}}'
};
});
</script>
<div foo-directive></div>
<div bar-directive></div>
</body>
</html>
One of its main advantages, especially if you're new to AngularJS, is that it ensures proper data binding between child scopes.
Just play around with this code sample and try to notice something strange:
angular
.module('myApp', [])
.controller('MainCtrl', ['$scope',
function($scope) {
$scope.truthyValue = true;
$scope.foo = 'hello';
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MainCtrl">
<p>Start by writing something in the first input. Then write something in the second one. Good job, you've broke AngularJS!</p>
1.
<input type="text" ng-model="foo">
<div ng-if="truthyValue">
2.
<input type="text" ng-model="foo">
</div>
<div>$scope.foo: {{ foo }}</div>
</div>
The reason behind it is that ngIf creates a child scope which inherits from the parent scope. You're basically changing the value inside ngIf's scope which doesn't affect the value from its parent scope.
Finally, I consider controllerAs syntax an important AngularJS best practice. If you get accustomed to it early in your learning process, you'd be avoiding a lot of head-scratching wondering why your code doesn't work, especially when everything seems in order.
You don't need to use both controller and controllerAs. You can use the shorthand:
controller: 'SomeController as ctrl'
The relationship is that a new instance of the controller is created and exposed to the template using the instance handle you provide as ctrl.
Where this comes in handy is if you are using nested controllers -- or using multiple instances of a controller in a view.
UPDATE TO ANSWER COMMENTS
You do not need to use controllers with AngularJS directives. Infact as of AngularJS 1.5 you should probably only use controllers when creating components rather than directives.
Directives and Components are conceptually similar. Up until AngularJS they all components would be defined as a directive.
In many ways a directive interacts with an element (like ng-href) or events (like ng-click).
The simplest way to differentiate Components and Directives is a Component will have a template.
Can't I just create a component using the directive link method?
You can, but I wouldn't recommend it unless you have a good reason. Using controllers allows you to use object oriented classes or prototypes to define the action behaviors with the template and user.
As well these controllers are extremely more easy to unit test than the directive link functions.
Check out this plunkr code
Here is my simple Directive code:
angular.module('app', [])
.directive('someDirective', function () {
return {
scope: {},
controller: function ($scope) {
this.name = 'Pascal';
$scope.color = 'blue';
},
controllerAs: 'ctrl',
template: '<div>name: {{ctrl.name}} and Color: {{color}}</div>'
};
});
And The HTML
<body ng-app="app">
<some-directive />
</body>
So, as you can see, if you need to access some variable which were defined against this keyword in the controller, you have to use controllerAs. But if it was defined against $scope object you can just access it with its name.
For example, you can get the variable color just by using {{color}} as it was defined against $scope but you have to use {{ctrl.name}} as "name" was defined against this.
I don't think there really is much difference, as this answer says,
Some people don't like the $scope syntax (don't ask me why). They say
that they could just use this
Also from their own website you can read the about the motivation behind this design choice,
Using controller as makes it obvious which controller you are
accessing in the template when multiple controllers apply to an
element
Hope it helps.

Using Globalize methods within ng-bind

I want to use the the jquery-globalize format function within a ng-bind to format a date value in a $scope field according to the current culture.
Something like this:
<div>{{Globalize.format(test.testDate, Globalize.culture().calendar.patterns.d)}}</div>
But it doesn't seem to work.
How do I accomplish this the easiest way?
Thank you
Your question mentions ng-bind but I don't see any use of it in your code. In any event, you can always use a controller to bind a variable to you views.
For example:
function HomeController() {
var vm = this;
// Any other variables here...
vm.formattedDate = Globalize.format(test.testDate, Globalize.culture().calendar.patterns.d);
}
Then in your html you could do something like:
<div ng-controller="HomeController as homeCtrl">
<p>{{ homeCtrl.formattedDate }}</p>
</div>
Or if you are using something like ui-router, you could do:
$stateProvider
.state('home', {
url: '/home',
controller: 'HomeController as homeCtrl',
template: '<p>{{ homeCtrl.formattedDate }}</p>' // Or use templateUrl.
});
Note: If you are using $scope instead of the this method, it's basically the same process except you'll just trade the vm. syntax with $scope. and you can change HomeController as homeCtrl to just HomeController.

Angular: injecting state into controllers (or binding "models" to controllers)

Suppose I have a general purpose controller, TableController, that can be used in multiple places in the app to display a table of Key x Value pairs via a custom directive, ui-table, that generates an HTML table.
angular.module('ui.table', [])
.controller('TableController', ['$scope', 'data',
function($scope, data) { $scope.data = data; }])
.directive('uiTable', function() {
return { templateUrl: 'table.html' }
});
I could use the controller in the following template:
<div ng:controller="TableController">
<div ui-table></div>
</div>
And create a factory to pass data to this controller.
.factory('data', function() {
return [{'App':'Demo', 'Version':'0.0.1'}];
})
But, I have multiple controllers (sometimes in the same views), so I need to "bind" a particular factory to a particular controller (e.g., UserProfile, AppData, etc.)
I have started to look at angular-ui-router's $stateProvider, but it seems too complicated for what must be a typical use case? What I'd really like to be able to do is use the template to annotate which factory (what I think of as a model) should be used for that particular controller. E.g., something like:
<div ng:controller="TableController" ng:model="AppData"></div>
What is the right approach?
EDIT:
I've figured out $stateProvider and "resolve" allows me to map provider services onto injected values for the state's main controller -- but the controller I want to influence is a child of this controller.
$stateProvider
.state('home', {
url: '/home',
templateUrl: '/home/view.html',
controller: 'MainViewController',
resolve: {
'data': 'AppData'
}
});
So, I still can't figure out how to influence the controllers inside the state's view.
I think what you are looking for is simply passing your data into the directive through attributes. Then use an isolated scope in directive so you can have multiple instances active at the same time
<div ng-controller="ViewController">
<div ui-table dataSource="tableData"></div>
</div>
Then your directive would be written in a generic way to be re-usable regardless of the data passed in.
.factory('SomeService', function(){
var data ={
headings: ['ID','Age','Name'],
rows:[
{id:1, 33,'Bill'}......
]
};
return {
get:function(){ return data;}
};
})
.controller('ViewController', function($scope, SomeService){
$scope.tableData = SomeService.get();
})
.directive.directive('uiTable', function () {
return {
scope: {
dataSource: '=' // isolated scope, 2 way binding
}
templateUrl: 'table.html',
controller: 'TableController', // this controller can be injected in children directives using `require`
}
});
In essence this is just reversing your layout of controller/directive. Instead of TableController wrapping the directive, it is used internally within directive. The only reason it is a controller in the directive is to allow it to be require'd by child directives such as perhaps row directive or headings directive and even cell directive. Otherwise if not needing to expose it for injection you can use link and put all sorts of table specific operations in there
As mentioned in my comments there are various approaches to creating a table directive. One is with heavy configuration objects, the other is with a lots of declarative view html that use many child directives. I would suggest analyzing the source of several different grid/table modules to see what best suits your coding style
Thanks in part to #charlietfl (above) I have an answer:
<ui-graph model="SomeGraphModel"></ui-graph>
Then:
angular.module('ui.graph', [])
.directive('uiGraph', [function() {
return {
controller: 'GraphController',
scope: {
model: '#model' // Bind the element's attribute to the scope.
}
}
}]);
.controller('GraphController', ['$injector', '$scope', '$element',
function($injector, $scope, $element) {
// Use the directive's attribute to inject the model.
var model = $scope.model && $injector.get($scope.model);
$scope.graph = new GraphControl($element).setModel(model);
}])
Then somewhere else in the app (i.e., not necessarily in the directive/controller's module):
angular.module('main', [])
.factory('SomeGraphModel', function() {
return new GraphModel();
})

two-way binding in angular not updating

I have created a plnkr which demonstrates a problem that I am trying to solve. When you click the link in the plnkr, you will see a textfield. This textfield is bound with ng-model to myCtrl.foo, and in that controller is a $watch looking at the controller's foo property and then setting $scope.num to a random number. You will notice the random number never changes even though the watcher is clearly firing (via a console.log).
http://plnkr.co/edit/wpFPFeRC6CFFjLOa9QQw
Can anyone explain why this is not working, and what I can do to fix it?
Here is what happens
When you define your routes:
app.config(function ($stateProvider) { $stateProvider
.state('items', {
url: '/items/:item_id',
views: {
'my-view': {
controller: 'myController as myCtrl',
templateUrl: 'my-view.html'
},
'main#': {
controller: 'myController',
templateUrl: 'main.html'
},
}
})
});
you assign 2 different views to use the same controller, which is OK, but a controller in Angular is not a singleton. It is a constructor function. Meaning that both controllers (and their scope) will not be the same instance, but 2 different instances.
So the controller and the scope in view 1 will not be the same controller and scope as in view 2.
The controller will be instantiated twice with a different scope so the changes made in the scope of view 1 will not reflect the changes made in the scope of view 2 (as they have a different scope).
You can see this if you add the following lines to your controller:
app.controller('myController', function($scope) {
console.log('myController scope id: ' + $scope.$id);
console.dir($scope);
// Your code here
});
The log will show:
myController scope id: 003
myController scope id: 004
Possible solutions
Avoiding this boils down to personal preference. Here are some valid options:
use events to communicate between scopes and send an event when num is updated
use a service to store num centrally
store num in the $rootScope
Hope that helps!
Here is it working: http://plnkr.co/edit/tagldRNsgLXUhoGfZ2Un?p=preview
Did two things, first assigned all primitive bindings to an object called test. You can see why it is best to do this here: https://egghead.io/lessons/angularjs-the-dot
Second, put the controller around the views to ensure they share a scope and pulled it out of ui-router (as I know next to nothing about ui-router and whatever magic it does)

Resources