Directives are not working in Angular 1.2.20 - javascript

In a previous question, I was having problems getting a complex widget to properly work.
After waking up with a clear head, I decided that I should start with a sanity check: can I get a basic directive to properly work? Given that I have written directives in the past, I foresaw no difficulty.
This is the basic plunker I wrote, with only two very basic use cases.
app.directive('TestDirective',
function () {
return {
template: '<strong>This is a test directive.</strong>'
};
}
);
app.directive('SecondTestDirective',
function () {
templateUrl: 'directiveTest.html'
}
);
This is not a sane case, apparently. I'm using Angular 1.2.20, but apparently, neither a very basic directive with a hardcoded template, or a basic directive with a URL reference to a hardcoded template, are working properly. As this is a very basic case, my question: am I doing anything wrong? Should I open a bug on Angular's GitHub project?

Your problem is simple: first letter of directive name must be lowercase.
For example, instead of SecondTestDirective use secondTestDirective
While matching directives, Angular strips the prefix x- or data- from element/attribute names. Then it converts - or : delimited strings to camelCase and matches with the registered directives. That’s why we have used the secondTestDirective directive as second-test-directive in the HTML.

There were several things wrong with your code, this is the fixed version of it:
app.directive('testDirective',
function () {
return {
restrict: 'AE',
template: '<strong>This is a test directive.</strong>'
};
}
);
app.directive('secondTestDirective',
function () {
return{
restrict: 'AE',
templateUrl: 'directiveTest.html'
}
}
);
Example
Things that were wrong:
The names of the directives should have a camel-case format (the first character should be lowercase)
The second directive was not returning an object
If you are going to use your directive as Elements, you should specify that in the restrict attribute.

Related

Angular Directive: Make submit button directive listen to form validation

I have a bunch of forms in my project and all of them have a set of specific validation expressions. On top of that I have also a loading element attached to it so it makes pretty hard to reuse that button throughout the project. With that in mind I figured I could use a directive instead. The problem is that when I pass in my expressions through attributes it does not update/validate as user inputs data. Basically it just sits with the attribute like if it was a string and not an expression.
In a nutshell what I've been trying to achieve is something like this:
<submit-button label="My Label" validate="!myForm.$valid"></submit-button>
Which will return something like that:
<button ng-disabled="!myForm.$valid">My Label</button>
Here's a basic isolated template on jsfiddle of what I have so far: https://jsfiddle.net/lucasbittar/8m992bet/3/
Thank you so much!
The easiest way to achieve this in that version of Angular is to utilize a two-way attribute binding on your directive. Bind the validate attribute/scope property to your expression, and reference that in the ngDisabled directive.
An important thing to note is that ngDisabled (and other ng-prefixed directives, IIRC) operate on expressions directly, so you shouldn't use double-brackets to interpolate them.
Directive code becomes:
function submitButton () {
var submit = {
restrict: 'E',
require: ['^form'],
template: '<button ng-disabled="validate">{{ label }}</button>',
scope: {
validate: '='
},
link: link
}
return submit;
function link (scope, el, attr, formCtrl) {
scope.label = attr.label;
}
}
Check out the working fiddle here: https://jsfiddle.net/8m992bet/6/
Note that if you wanted, you could also mimic the above using an inherited scope on your directive combined with scope.$watch, but using a two-way binding is easier.
Also note that on Angular 1.5 you can utilize a one-way binding on a directive using < instead of =.

How to create reusable components with angularjs directives?

I want to create a reusable html component with angulajrs, so I'd probably have to go for directives. I created a simple one, which does not work:
<my-fn info="test"></my-fn>
app.directive('my-fn', function() {
return {
restrict: 'E',
templateUrl: 'templates/my-fn.html',
scope: {
info: "=info"
}
};
});
my-fn.html:
<p>the value is: {{info}}</p>
Result: I don't see anything, and there is no error in the console.
How can I ensure that the directive loads, and that the template is found?
Your declaration is wrong, it should be without the hyphen:
app.directive('myFn', ...)
Every camel case is split into hyphens, this is why the directive didn't work.
You didn't see an error because it is otherwise just an element without semantic meaning, but is syntactically correct.

Can we use directives dynamically in AngularJS app

I am trying to call (or use) few custom directives in ionic framework, dynamic is like <mydir-{{type}} where {{type}} will come from services and scope variable, having values radio, checkbox, select etc, and created my directives as mydirRadio, MydirCheckbox, mydirSelect, But its not working.
Is their any good approach to get the dynamic html as per {{type}} in scope?
Long story short; no you can't load directives dynamically in that way.
There are a few options for what you can do. You can, as other answers have mentioned, pass your context as an attribute (mydir type="checkbox"). You could make a directive that dynamically loads another directive, as also mentioned by others. Neither of these options are imo every good.
The first option only works if you write the directive yourself, not when using something like ionic. It also requires you to write multiple directives as one, which can get very messy very quickly. This mega directive will become hard to test and easy to mess up when maintaining it in the future. Note that this is the correct way to pass data to a directive from the view, it's just not good for this specific use case.
The second option is problematic because obfuscates things a bit too much. If someone reads your html and sees a directive called dynamic that is given dynamic data... they have no idea what is going to happen. If they see a directive called dropdown that is given a list they have a fair idea of what the result will be. Readability is important, don't skimp on it.
So I would suggest something simpler that requires much less work from you. Just use a switch:
<div ng-switch="type">
<mydir-select ng-switch-when="select"></mydir-select>
<mydir-checkbox ng-switch-when="checkbox"></mydir-checkbox>
</div>
I dont understand why do you need dynamic directives.
Simple use single directive and change the template accordingly.
For example -
angular.module('testApp')
.directive('dynamicDirective', function($compile,$templateCache,$http) {
return {
restrict: 'C',
link: function($scope,el) {
//get template
if(radio){
$http.get('radio.html', {cache: $templateCache}).success(function(html){
//do the things
el.replaceWith($compile(html)($scope));
});
} else if(checkbox){
//load checkbox template
} //vice-versa
}
};
});
You can inject service variable in directive also.
a bit more code would help. I don't know, if its possible to do dynamic directives like the ones in a tag
<{dyntag}></{dyntag}>
but you also can use an expression like
<your-tag dynamic_element="{type}">...</your-tag>
which should have exactly the same functionality. In your case it would be like:
Your JSObject ($scope.dynamics):
{"radio", "checkbox", "select"}
and your HTML:
<div ng-repeat="dyn in dynamics">
<your-tag dynamic_element="{dyn}"></your-tag>
</div>
Yes, that's not a problem. You can interpolate your data using {{}} and in your directive compile a new element using that data:
myApp.directive('dynamic', function($compile, $timeout) {
return {
restrict: "E",
scope: {
data: "#var" // say data is `my-directive`
},
template: '<div></div>',
link: function (scope, element, attr) {
var dynamicDirective = '<' + scope.data + ' var="this works!"><' + scope.data + '>';
var el = $compile(dynamicDirective)(scope);
element.parent().append( el );
}
}
});
HTML:
<div ng-controller="MyCtrl">
<dynamic var="{{test}}"></dynamic>
</div>
Fiddle

Where should I place common code when writing angular directives?

I'm in a situation where I would like to have many directives with different
tag names, but which are nearly identical in their behaviour. My first idea
was to just capture the directive as a function like this:
function standardDirective(template,extract) {
return function() {
return {
restrict: 'E',
... }
}
}
and then use this function to 'stamp out' the required directives.
angular.module('MCQ', [])
.directive('mcq'
, standardDirective("MCQTemplate.html"
, function(scope){return scope.userSelection;}
)
);
After writing this, I'm pretty sure that there is some common, and possibly better
idiom, for doing this. For example, in my 'solution' I probably can't make angular load the standardDirective when it is needed.
(I know that I could make a single directive and use attributes to differentiate behaviour, but for now, let's assume that I really need different directives..)
You could use the require option on the directive.
This essentially ensures that your directive "inherits" another directive, like a base class.
Your 'base' directive can contain all of your common logic and then then other directive that is requiring it has it's own spin on whatever it needs to do.
you can read more about it in the Angular Docs - see the Creating Directives that Communicate section

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