Angular: my isolated attribute not binding in simple directive - javascript

I am using this tutorial to understand how to display an image using directives with an isolate scope. What am i missing to get the attribute to bind? In my ide, I can output the image but the directive is not working. The fiddle displays nothing :-(
Fiddle:http://jsfiddle.net/gogirl/Stm3j/
This is outputted in my program:
output:{{photo.url}}
<img ng-src='http://www.w3schools.com/js/pic_bulboff.gif' />
This is not outputted:
<my-d photo-src={{photo.url}} ></my-d>

You have residual data in your jsfiddle configuration
Here is what I did to make it work
Remove the external resources because you use angular 1.0.1 with angular 1.2.1
Remove in options panel the body tag myModule
Remove the call of the undefined variable app and query it with angular.module
jsfiddle updated here
// My Main Application File
angular.module('myApp', ['myDirectives']);
// My Directives File
angular.module('myDirectives', []);
// Controller One File
angular.module('myDirectives', []).directive('myD', function () {
return {
restrict: 'E',
template: '<figure> <img ng-src="{{photoSrc}}"/> </figure>',
replace: true,
// pass these two names from attrs into the template scope
scope: {
photoSrc: '#'
}
}
})

All is working fine now when i switched the original module chaining method with the global app method:
var app = angular.module("myApp", []);
app.directive('myD', function() {
return {
restrict: 'E',
template: '<figure> <img ng-src="{{photoSrc}}"/> </figure>',
replace: true,
// pass this name from attrs into the template scope
scope: {
photoSrc: '#'
}
}
});
But I wont be a happy camper until I figure out what is wrong with the chaining declaration as it works fine elsewhere. Any takers? Fiddle still not working so i think it may be an issue with it being a fiddle.
EDIT: WORKS fine also with chaining method..One baby step at a time :-)

Related

Angular directive inside ng-template with modal

I am curious about the way angular works with preloading directives since I have a problem with a directive that resides in a <script> tag and ng-template.
When I pause execution in chrome dev-tools, during the initial document load, I can clearly see that the code inside the directive's controller does not get called if my directive lies in some arbitrary template. Here is the example code, when e.g. myDirective is included in index.html as a part of myModule.js module, also included both in index and in the main app module:
This is some other directive's html containing the problematic myDirective
<script type="text/ng-template" id="callThis">
<myDirective></myDirective>
</script>`
and I call it on click with ngDialog like this
ngDialog.open({
template: 'callThis',
scope: $scope
});
and it can't run the directive since it doesn't have any html to work with (thats the error, about some html element missing).
Finally here is the code for the module which holds myDirective
angular.module('myModule', ['myDirectiveTemplates', 'myDirectiveDirective'])
angular.module('myDirectiveTemplates', []).run(["$templateCache", function($templateCache) {$templateCache.put("myDirectiveTemplate.html","<h1></h1>");}]);
angular.module('myDirectiveDirective', []).directive('myDirective', function ($window, $templateCache) {
return {
restrict: 'E',
template: $templateCache.get('myDirectiveTemplate.html'),
controller: function($scope) {
//some arbitrary code
}
};
})
Interestingly if i put <my-directive></my-directive> right in index.html file it works ok, and the code inside the controller gets loaded on startup. I'm unsure how to solve this.
From what I understand of this problem you need to use the $compile service. There is a tutorial here that might help : $compile
The reason given in the tutorial is:
"The newly minted template has not been endued with AngularJS powers
yet. This is where we use the $compile service..."
And quoted from the Angular Docs:
Compiles a piece of HTML string or DOM into a template and produces a
template function, which can then be used to link scope and the
template together.
Here is a brief code example as per the tutorial :
app.directive('contentItem', function ($compile) {
/* EDITED FOR BREVITY */
var linker = function(scope, element, attrs) {
scope.rootDirectory = 'images/';
element.html(getTemplate(scope.content.content_type)).show();
$compile(element.contents())(scope);
}
/* EDITED FOR BREVITY */
});

why ng-repeat not working in angular js (using directives)?

I am using ng-repeat with custom directive but i am getting error .can you please tell me how to remove this error
here is my plunker
http://plnkr.co/edit/uj8b3hL8T6MjoKSZyjsc?p=preview
custom directive
// Code goes here
angular.module('ui.directive',[]).directive('newDir',function(){
return{
restrict:'E',
scope:{
data:'='
},
replace:true,
templateUrl:"pop.html",
controller:function($scope){
console.log($scope.data)
},
link :function(scope,element,attr){
element.click(function(){
})
}
}
})
The error generated is pretty self explanatory:
Error: [$compile:tplrt] Template for directive 'newDir' must have exactly one root element. pop.html
Essentially you need to make sure your template has one root node. You've appended a br tag on the end.
Change:
<div><h1>{{str.name}}</h1><p>{{str.category}}</p></div></br>
To:
<div><h1>{{str.name}}</h1><p>{{str.category}}</p></div>
Furthermore,
Your template is referencing str however the scope variable is data. Change your template:
<div><h1>{{data.name}}</h1><p>{{data.category}}</p></div>
http://plnkr.co/edit/eUgmU45quL0VoNLY19ek?p=preview

Angularjs Multiple directives [gridsection, gridsection] asking for template on: <div gridsection="">

I'm receiving the error: Multiple directives [gridsection, gridsection] asking for templateon : <div gridsection=""> with this code.
I don't see how i'm using nested directives or what is causing this.
html page
<div gridsection ></div>
directive
angular.module('web').directive('gridsection', function() {
return {
restrict: 'A',
replace: false,
scope: {
patient: "=patient"
},
templateUrl: 'directive/section.html',
link: function(scope, element, attrs, fn) {
}
};
});
directive/section.html
<div>
here?
</div>
It seems like you are declaring the gridsection multiple times in your angular code.
I have seen this before when I have a copy of a directive script file in a folder.
i.e. my file structure was
* myDirective.js
* myDirective - copy.js
So essentially I had two directives with the same name.
Doh!
Note originally posted this as a comment but created as an answer in response to comment from #jayjayjay
For posterity, I was getting this exception because I was trying to create a directive named pager and that was colliding with Bootstrap's pager.
Make sure you didn't include the script tag twice.
I had this issue but only declared the directive once in the markup, turns out it was because I included the script twice.
Note: I saw this in one of the comments for another answer and posted it as an answer for easier access/to prevent it getting lost.
I was getting this error for a reason not specified in other answers.
I was using declaration for xyz directive as <xyz xyz="xyz"></xyz>
my definition was:
angular.module('app')
.directive('xyz', function () {
return {
templateUrl: '..../xyz.html',
restrict: 'EA',
scope: {
xyz: '='
},
link: function (scope, element, attrs) {
}
};
});
The problem here is that I allowed directive to be used as element and attribute. so <xyz xyz="xyz"></xyz> contained both the declaration which was causing the issue.
Solution is to either restrict the directive to be used as Element only restrict: 'E' OR change the name of the directive to something like xyzView and use it like <xyz-view xyz="xyz"></xyz-view>.

How to define an angular directive inside an angular directive's link function?

I want to create an angular directive inside of a link function, however; the directive created is not able to be compiled.
See this JSFiddle: http://jsfiddle.net/v47uvsj5/5/
Uncommenting this directive in the global space works as expected.
app.directive('test', function () {
return {
templateUrl: 'myform', // wraps script tag with id 'myform'
restrict: 'E',
require: "^mydir",
replace: true,
scope: {
},
link: function (scope, element, attrs, mydirCtrl) {
scope.remove = function () {
element.remove();
mydirCtrl.remove();
}
}
}
});
But the exact same code inside the link function fails.
The reason I want to do this is because I want the user (who is going to be myself) to be able to provide only a script tag's id via an id attribute to my main directive which will in turn create a 'wrapper' directive with a 'remove' method. This way, in the script tag, all one needs to do is implement the 'remove'.
Check out this fiddle:
http://jsfiddle.net/v47uvsj5/8/
What I did in this fiddle was daisy chain your directives, which is the correct thing to do. When your app runs, it does a binding of each of your directive and builds your html as it's being compiled, then it links events to it. Links and compilation happen after binding all directives to the DOM.
So <test></test> becomes <div></div> if you give it a template. If there is no template, nothing really builds your directive against the DOM, it just becomes empty, but you can still run a jquery script if you want.
Think of it like this, when your app loads up, it registers all the directives to be binded with the associated templates. Afterwards, the app then "compiles" those directives by binding any kind of events to the newly established DOM. At this point, if no directives are registered during app load, the compile function ignores it. In your case, you tried to bind the 'test' directive after the app load, and during the compilation.
This mechanism is analogous to how jquery's "on" works. When you do a "click" event on an already loaded DOM element, this fires up. But when you load html AFTER the DOM is finished, nothing works unless you use "on".
To be fair, the developers of angular did mention how there's a steep learning curve for handling directions, and will be revised to make it much easier in 2.0. You can read about it in this blog here: Angular-2.0
Anyways,
This is how your html should look like:
<mydir><test></test></mydir>
and this is how you daisy chain:
var app = angular.module('app', []);
app.directive('mydir', function ($compile, $templateCache) {
return {
template: '',
restrict: 'E',
controller: function () {
console.log("got it!");
}
}
}).directive('test', function () {
return {
templateUrl: 'myform',
restrict: 'E',
require: "^mydir",
replace: true,
scope: {
},
link: function (scope, element, attrs, mydirCtrl) {
scope.remove = function () {
element.remove();
mydirCtrl.remove();
}
}
}
});

AngularJS directive not showing up on template

I've got a tiny problem with an angular directive that's now working and I don't know why. I think it's a fairly simple issue that I'm overlooking, maybe you can help me out.
Directive is defined like this:
angular.module('directives', [])
.directive('my-directive', function () {
return {
restrict: 'AE',
scope: {
name: '=name'
},
template: '<h1>{{name}}</h1>'
};
});
Then index.cshtml:
<my-directive name="test"></my-directive>
Application.js:
var app = angular.module('MyApp', [
...,
'directives'
]);
And here's controllers.js
angular.module('controllers', ['apiServices', 'directives'])
.controller('homecontroller', function($scope, $resource, webApiService, $log, $translate, $localStorage, $sessionStorage) {
Ok confirmed that directives.js is loaded, otherwise application.js nags about 'unknown module'. There are no error messages in the console, the thing just doesn't show. Any ideas?
EDIT
So as pointed out, I changed the directive name to camelCase, but still no luck:
<my-directive name="John Doe"></my-directive>
And
.directive('myDirective', function () {
But nothing is showing yet.
EDIT
Problem is that angular expects an object to be passed into the attribute, not a string literal. If you create an object person = { name: 'John' }, pass the person in, then write {{ person.name }} ( assuming we named the attribute person + scope var person too ).
During normalization, Angular converts - delimited name to camelCase.
So use camelCase while specifying the directive inside JS:
.directive('myDirective', function () {
Fiddle
I'm sure you've figured this out already, but if you change your scope definition for name to be
scope: {
name: '#'
}
you will then be able to pass a string. The '#' interpolates the attribute while the '=' binds it. Additionally, you don't need to include an attribute name if it is the same as the scope variable.
The problem appears to be in the directive definition. You note in your question that Angular expects an object; this is true for the "=" scope, but not for the "#" scope. In the "#" scope, Angular expects a string only. I have created a snippet below.
Too many modules
Unless you are reusing the directive in multiple applications, do not create a new module for it. Add the directive definition to the module that you created for the application. In my example below, I called the module back by using "angular.module( moduleName )"... When only one argument is used, Angular returns the existing object rather than creating a new one. This is how we can separate the code into many files.
Things to Note
You will notice the following:
You do not need to load the module into the app variable. Calling the Singleton each time is actually safer and easier on memory management.
The directive is in camel case, as you have already noted.
I am setting the name attribute to a string value and not an object; this works because of the "#" scope setting.
The div is set to ng-app='MyApp'. This usually is set to the html element, but I did not want to mess with the DOM on Stack Exchange. The ng-app directive can be set on any element, and the directives associated with that module will be applied on all elements that are within that element's scope. Without the ng-app directive, Angular does not know which module to run on the page.
//app.js - this defines the module, it uses two parameters to tell the injector what to do.
angular.module('MyApp',[]);
//directive.js stored elsewhere
//this calls back the module that has been created. It uses one parameter because the injector is no longer needed.
angular.module('MyApp').directive('myDirective', function () {
return {
restrict: 'AE',
scope: {
name: '#'
},
template: '<h1>{{name}}</h1>'
};
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp">
<h1>Successful Load</h1>
<my-directive name="test"></my-directive>
<p>By applying the directive definition to the MyApp module, the MyApp module knows to activate the directive within this scope. In this form, it does not get injected.</p>
</div>
Using Injection
When you have a different module for each and every directive or controller, each one must be injected into the application's module definition. This leaves a lot of room for error. As a best practice, only create a new module when necessary, and make the module a container for a group of related functionality and not a single item.
The code below demonstrates proper injection.
angular.module( "MyApp", ['ReusableDirectives'] );
angular.module( "MyApp" ).directive( "myDirective", function(){
return {
restrict: "AE",
scope: { name: "#" },
template: "<p>This is the directive I defined in the example above. It uses <u>the same module</u> as my main application, because it is not going to be reused over and over again. In fact, I will need it just for this application, so I don't need to complicate things with a new module. This directive takes an attribute called 'name' and if it is a string allows me to manipulate the string within my templates scope to do things like this: {{'hello ' + name + '!'}}</p>"
};
} );
angular.module( "ReusableDirectives", [] );
angular.module( "ReusableDirectives" ).directive("reusableDirective", function(){
return {
restrict: "E",
template: "<p>This is a directive that I intend to use in many, many applications. Because I will reuse it so much, I am putting it in a separate module from my main application, and I will inject this directive. This is the only reason that this directive is not in the same module as the one I defined above.</p>"
};
} ).directive("reusableDirective2", function(){
return {
restrict: "E",
template: "<p>This is a second directive that I intend to use in multiple applications. I have stored it in a module with the first directive so that I can freely inject it into as many apps as I like.</p>"
};
} )
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp">
<h1>Successful Load</h1>
<my-directive name="Johnny"></my-directive>
<p>By applying the directive definition to the MyApp module, the MyApp module knows to activate the directive within this scope. In this form, it does not get injected.</p>
<h3>Injected Directives</h3>
<reusable-directive></reusable-directive>
<reusable-directive2></reusable-directive2>
</div>
Keep it simple. Define your directives on a single module for your application. Once you have that done and working, if you need the directives again in another application, refactor and experiment with injections at that time after you have some more Angular practice under your belt.
You have a bright future with Angular, keep up the good work!
Your directive must be camel-cased
.directive('myDirective', function () {
then in your html, your are free whether to call it my-directive or myDirective
Both are valid
<my-directive name="test"></my-directive>
<myDirective name="test"></myDirective>
Just to follow up on this, I had to use the following way to get my directive to work.
<my-directive name="test"></my-directive>

Categories

Resources