AngularJS isolated directives inheriting surrounding or parent scope - angularjs

I have put up my code at jsbin: http://jsbin.com/fewom/1/edit
If any one can guide me, what am I doing wrong there. I have specified scope to be isolated inside myDirective with scope: {} but still when i write myProperty inside my directive in html I am able to read myProperty.
I was using AngularJS https://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.min.js
When i changed my library to it started to work,
https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0-rc.3/angular.js
then I have tried,
Angular JS version 1.2.16
https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js
and
Angular JS version 1.3.0-beta.5
https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.5/angular.min.js
and the problem for isolated scoping shows up again.

You don't have a controller, so nothing ties your directive to your html.
Change your js to this (I just added a blank controller):
angular.module('myApp', [])
.directive('myDirective', function() {
return {
restrict: 'A',
scope: {}
};
})
.controller('ctrl', function (){
});
and in your html, change to this (referenced the controller):
<html ng-app="myApp" ng-controller='ctrl'>

Related

Unable to execute a custom directive in angularjs

I am trying to build a simple custom directive, but it is constantly throwing me an error. I have included my sample code in the below jsFiddle link. Can anyone help me with where exactly I went wrong.
[https://jsfiddle.net/sridharspeaks/65vnj4dz/][1]
Thanks,
Sridhar
this is fixed plunker :
http://plnkr.co/edit/KTFFLc0QdmunQ4i8AT8o?p=preview
i think there is a problem with
the case of your directive and controller name ( i put all to lower case, don't have too much time sorry)
you didn't use ng-controller="mycontainercontroller" to tell angular with controller to use
don't inject $scope on directive only in controller's directive :
html :
<head>
<script src="http://code.angularjs.org/1.2.1/angular.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-controller="mycontainercontroller">
<mycontainer></mycontainer>
</div>
</body>
and JS :
angular.module('myApp', []).
directive('mycontainer', function() {
return {
restrict: 'E',
scope: {},
controller: 'mycontainercontroller',
template: '<div><input ng-model="container"></div><div>output : {{container}}</div>'
}
}).controller('mycontainercontroller', ['$scope', function($scope) {
$scope.container = 123;
}]) ;
There are two issues. One is with the fiddle, the script must run before window load, so set the following:
Second, don't inject $scope into the directive. You only have access to the directive scope in the link function.
Updated fiddle: https://jsfiddle.net/65vnj4dz/5/

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.

Angular custom directive not being called

I am new to angular and am currently converting my conventional html/javascript website into an Angular application.
I am stuck on a custom directive where the problem is that it is not rendering in the view.
From reading the angular docs, I understand the camel-casing convention and that if you don't have the 'restrict' property set, Angular will automatically assume that its an attribute so I know that there are no issues there.
Here is how I have structured my directive:
var directives = angular.module('app.directives', []);
directives.directive("dataPercent", [function() {
return {
restrict: 'E',
template: 'Click me to go to Google '
}
}]);
How it is in the DOM:
<data-percentage></data-percentage>
And the reference to the directive in the header of index.html
<script src="assets/directives/dataPercentDirective.js"></script>
Also just in case this is how I init my controllers, directives and services in app.js:
angular.module('app', ['app.controllers', 'app.directives', 'app.services']);
The strange part is that there are no errors displayed in dev tools
Use datPercent as data is reserved keyword
data is reserved keyword.
jsbin http://jsbin.com/yexonayoho/edit?html,js,output

AngularJS - $templateCache is not defined

I am trying to load a template file in an AngularStrap popover, however I am having trouble using $templateCache. I seem to be a step further back than the other SO questions, hence this seemingly double one.
Following the API docs I added a <script type="text/ng-template" id="popoverTemplate.html"></script> right before the closing </body> tag. When I use <div ng-include="'popoverTemplate.html'"></div> on my page, I get nothing. If I try using console.log($templateCache.get("popoverTemplate.html")) I get "$templateCache is not defined", which leads me to assume I am missing a crucial step. However, I can't find how to do it in the docs or other SO questions.
EDIT:
Injecting the service was the missing link. However, when I inject the service, the controller's other function no longer works, but if you inject al the function's parameters the working code becomes:
(function() {
"use strict";
angular.module("app").controller("managerController", ["$scope", "imageHierarchyRepository", "$templateCache", function ($scope, imageHierarchyRepository, $templateCache) {
imageHierarchyRepository.query(function(data) {
$scope.hierarchies = data;
});
var template = $templateCache.get("popoverTemplate.html");
console.log(template);
}]);
})();
To use the template script tag . You have to insert it inside the angular application. That is inside the element with the ng-app attribute or the element used to bootstrap the app if you don't use the ng-app tag.
<body ng-app="myapp">
<div ng-template="'myTemplate.html'"></div>
<script type="text/ng-template" id="myTemplate.html">
// whate ever
</script>
</body>
If you want to retrieve the template on a component of the application then you need to inject the service where you want to consume it:
controller('FooCtrl', ['$templateCache', function ($templateCache) {
var template = $templateCache.get('myTemplate.html');
}]);
Or
controller('FooCtlr', FooCtrl);
FooCtrl ($templateCache) {};
FooCtrl.$inject = ['$templateCache'];
EDIT
Do not register two controllers with the same name because then you override the first one with the last one.
(function() {
"use strict";
angular.module("app").controller("managerController",["$scope", "imageHierarchyRepository", "$templateCache", function ($scope, imageHierarchyRepository, $templateCache) {
var template = $templateCache.get("popoverTemplate.html");
console.log(template);
imageHierarchyRepository.query(function(data) {
$scope.hierarchies = data;
});
}]);
})();
Small addition: Although there are few ways to achieve your goals, like wrapping your whole HTML in <script> tags and all that, the best approach for me was to add the $templateCache logic into each Angular directive. This way, I could avoid using any external packages like grunt angular-templates (which is excellent but overkill for my app).
angular.module('MyApp')
.directive('MyDirective', ['$templateCache', function($templateCache) {
return {
restrict: 'E',
template: $templateCache.get('MyTemplate').data,
controller: 'MyController',
controllerAs: 'MyController'
};
}]).run(function($templateCache, $http) {
$http.get('templates/MyTemplate.html').then(function(response) {
$templateCache.put('MyTemplate', response);
})
});
Hope this helps!

Angularjs directive private scope/model

I've got an issue with a directive that is becoming a pain to resolve, which appears to be from a change in angular between version 1.0.8 and version 1.2.0.
In the first jsfiddle using version 1.0.8 you can see that upon changing an input file the fileChanged fn is called and the $scope.file is changed.
Yet in the second fiddle using version 1.2.0, the same code just doesn't update the directive
It looks like it has something to do with the a directive having an isolate scope, but not being able to have it's own scope variables. For instance, the controller of a directive:
controller: function ($scope, $timeout, $upload) {
$scope.fileCount = 0;
}
Am I missing something pretty key here?
The reason this doesn't work is due to no template being set for the directive:
...
scope: {
uploadPath: '#'
},
templateUrl: 'file-uploader.html',
controller: function ($scope, $timeout, $upload)
...
I'm sure using template: 'html here' would also work, but the reality is there much more html.
Working fiddle
There was a change with regard to isolated scopes. The expression $scope.file = element.files[0]; changes file in the directive's scope, not the scope outside. What you need to do is to establish a connection between file in the directive and file outside of it.
<div file-upload file="file">
<input type="file" />
<hr />
File selected: {{file.name}}
</div>
...
myApp.directive('fileUpload', function(){
return {
restrict: 'A',
scope: {
file: '='
},

Resources