Adding attributes to HTML element in an AngularJS Directive - angularjs

I am new to angular and am attempting to add validation to a directive based on attributes. Here is how I use the directive:
<div sc-textbox data-bind-to="fieldToBind" data-field-name="fieldName" data-required="someValue != 'Office'"></div>
The data-required attribute may or may not be present. The template for the directive is:
<input id="{{fieldName}}" name="{{fieldName}}" type="text" data-ng-model="bindTo" />
When the data-required attribute is present I would like to add data-ng-required="{{required}}" to the input. How would I go about doing this?
The scope for the directive is:
scope: {
'bindTo': '=',
'fieldName': '#',
'required': '='
}
Any help is greatly appreciated.

Why not just leave ng-required there anyway. This is what it's for.

Because you have limited scope, you need a boolean in your directive's scope that represents "someValue != 'Office'".
Or put someValue in scope and do this:
<input id="{{fieldName}}"
data-ng-required="(someValue != 'Office') && required"
name="{{fieldName}}" type="text"
data-ng-model="bindTo" />
Update
The attribute you want to add "data-ng-required" is a directive. You cannot dynamically add and remove directives from the DOM in Angular. If you really don't want "data-ng-required" in parts of your DOM, and you want this to be dynamic, the closest thing you can do is $compile and append this input as 2 templates-> one with required, and one without

Related

Passing An Ng-Model Value to A Directive

I've looked at the previous answers, but I'm not sure if they are the answer I need or not.
I have a directive, let's call it "selectValue". A value can have a default, let's call it "$scope.default".
The directive will look like this in one place:
<select-value ng-model="data.input" controlId="inputSelector" />
But it will look like this in another:
<select-value ng-model="myValue" controlId="inputSelector" />
I do not have the option of making the ng-model inputs the same; they are used in different places and this is a legacy code base.
Here is the definition of the directive:
.directive('selectValue', ['$timeout', function($timeout) {
const directive = {
restrict: 'E',
scope: {
controlId: '#',
model: '=?'
},
controller: 'selectValueCtrl',
template: '<input id="{{controlId}}" name="{{controlId}}" placeholder="Enter Value" type="text" ng-model="model" />'
};
return directive;
}
The question: what do I do to be able to enter different inputs in the "model" attribute of the <select-value> to have it access different scope variables?
Edit: the referenced "duplicate" question refers to setting a value for ng-click, not referencing ng-model in a form control.
From what I can tell, it looks as though you are trying to pass defaults with preset values. The problem you are facing is your select-value element is using the ng-model directive in an attempt to pass the data however your binding in your directive is 'model'.
In order to fix this issue, simply change the 'ng-model' to 'model' and your bindings should then work.
In the end, your element should look like so:
<select-value model="myValue" controlId="inputSelector" />
as opposed to:
<select-value ng-model="myValue" controlId="inputSelector" />

having optional attributes in an angular directive

let's say that I want to create directive to mimic a number input field (doing so just to learn about directives in general)
so, I create a directive like this
angular.module('myApp')
.directive('inputNumber',function(){
return {
replace: true,
restrict: 'E',
scope: {
label: '#',
property: '=',
readonly: '#',
disabled: '#'
},
templateUrl: '/directives/numberInput.html'
};
})
and the templateUrl is this
<md-input-container>
<label>{{ label }}</label>
<input ng-model="property" type="number">
</md-input-container>
and my html text is this
<input-number label="Columns" property="controller.data.cols">
</input-number>
oddly enough this worked ;) I was quite impressed for a first attempt ..
Now, sometimes I want to add the min and max attributes for a number
as these are optional, what do I add to the directive js and html ?
I presume that I add
min: '#',
max: '#',
to the directive js, but what do I put into the html ?
<input ng-model="property" type="number" min="{{min}}" max="{{max}}">
but then what happens if min / max is not supplied ?
thanks
You need to append them with a question mark.
scope: {
label: '#',
property: '=',
readonly: '#',
disabled: '#',
min: '#?',
max: '#?'
}
The question mark declares the scope bindings as optional, allowing you to not include them at all in the markup if you don't need them. If you don't include them then the properties on your isolate scope won't be defined.
<input ng-model="property" type="number" min="{{min}}" max="{{max}}">
Yes that is the correct way to pass values into those attributes, using the {{}} binding syntax. This is because the # binding is a literal binding. It will take the raw string value of that attribute and assign it to that isolate scope variable. By using the binding syntax, Angular will have resolved those bindings to actual values before your directive is parsed and reads those values in.
If you want two-way binding, then you would use the = binding symbol. That one tells angular that whatever you pass to that attribute should be a variable on the parent scope surrounding the directive. Angular knows at that point to create a two-way binding between that outer-scope variable and the variable on your directive's isolate scope. This way, if the variable in your isolate scope changes, so does the outer-scope variable, and vice versa.
<input ng-model="property" type="number" min="minValue" max="maxValue">
In that example your directive would expect minValue and maxValue to be variables already defined on the parent scope surrounding your directive.
I wrote a detailed blog post on isolate scope bindings last year. Hope it helps!

Binding stops working in input within ng-if

For some reason binding doesn't work on an input within ng-if block in a directive
so this, doesn't work:
app.directive 'foo', ->
restrict: 'E'
scope:
type:'='
template: "<input ng-if=\"type === 'string'\" ng-model='filterText'>
<div> {{filterText}} </div>"
<foo type="'string'" />
it works fine outside of directive or without ng-if. Wrapping input inside of a div with ng-if not helping. Is it a bug?
jsbin link
It is caused by the ng-if introducing a new scope combined with the fact that you ng-model "has not dot in it".
This works:
template: "<div ng-init='holder={}'> <input ng-if=\"type === 'string'\" ng-model='holder.filterText'></div>
<div> {{holder.filterText}}</div>"
See the directive info at https://docs.angularjs.org/api/ng/directive/ngIf and notice the text "This directive creates new scope."
For the "dot-in-model", see for example
Does my ng-model really need to have a dot to avoid child $scope problems?
or
https://egghead.io/lessons/angularjs-the-dot
Basically when reading the value it will be read correctly traversing scope prototypes, but when modifying the value it will be written to the very own scope.
Since ng-if creates a new scope, you just need to do this
ng-model='$parent.filterText'
Also, please check this answer.
The <div> tag isn't closed in your example, nor is the ng-if applied to that node.
Try this:
template: "<input ng-model='filterText'>
<div ng-if=\"type === 'string'\"> {{filterText}}"</div>"

Angular directive: using ng-model within isolate scope

I'm having trouble working out how I can define a custom directive that both:
Uses isolate scope, and
Uses the ng-model directive in a new scope within in its template.
Here's an example:
HTML:
<body ng-app="app">
<div ng-controller="ctrl">
<dir model="foo.bar"></dir>
Outside directive: {{foo.bar}}
</div>
</body>
JS:
var app = angular.module('app',[])
.controller('ctrl', function($scope){
$scope.foo = { bar: 'baz' };
})
.directive('dir', function(){
return {
restrict: 'E',
scope: {
model: '='
},
template: '<div ng-if="true"><input type="text" ng-model="model" /><br/></div>'
}
});
The desired behaviour here is that the input's value is bound to the outer scope's foo.bar property, via the the directive's (isolate) scope model property. That doesn't happen, because the ng-if directive on the template's enclosing div creates a new scope, so it's that scope's model that gets updated, not the directive's scope's.
Ordinarily you solve these ng-model issues by making sure there's a dot in the expression, but I can't see any way to do that here. I wondered if I might be able to use something like this for my directive:
{
restrict: 'E',
scope: {
model: {
value: '=model'
}
},
template: '<div ng-if="true"><input type="text" ng-model="model.value" /><br/></div>'
}
but that doesn't work...
Plunker
You are right - ng-if creates a child scope which is causing a problem when text is entered in the input text field. It creates a shadow property named 'model' in child scope which is a copy of the parent scope variable with the same name - effectively breaking the two-way model binding.
The fix for this is simple. In your template, specify the $parent prefix:
template: '<div ng-if="true">
<input type="text" ng-model="$parent.model" /><br/>
</div>'
This ensures that it will resolve 'model' from the $parent scope, which you've already setup for two-way model binding through the isolated scope.
In the end, the '.' in ng-model saves the day. I find it useful to think about anything left of the dot as a way for Angular to resolve the property through scope inheritance. Without the dot, resolving the property only becomes an issue when we're assigning scope variables (otherwise, lookups are fine, including read-only {{model}} binding expressions).
ng-if creates an additional prototypally inheriting scope, so ng-model="model" binds to the inherited property of the new scope and not to the 2-way binded property of the directive scope.
Change it to ng-show and it will work.
You can use a small Firebug extension i've written to inspect angular scopes.

Declaring and using angular directives that map id, name, and ng-model to a single field

I'd like to create/use an AngularJS directive to avoid creating some redundant HTML and also to simplify how I use Bootstrap elements. I'd like to use a directive this way:
<myDirective id="person.lname" label="Last Name"></myDirective>
The template I'd like AngularJS to write is:
<label for="person.lname">Last Name</label>
<input id="person.lname" name="person.lname" ng-model="person.lname">
How would I declare a directive to be able to create this template and have the binding with ng-model still work? Are there any reasons why this would not be a good idea?
Thanks.
UPDATED
I added the label tag to reflect how the id/name would be used for the input element. The generated template would allow you to click on the label and have the focus be placed on the input element.
This should do what you want:
.directive('myDirective', function() {
return {
scope: {
model: '=identifier',
id: '#identifier',
label: '#',
},
restrict: 'E',
template: '<label for="{{id}}">{{label}}</label><input id="{{id}}" name="{{id}}" ng-model="model">'
};
})
View:
<my-directive label='Last name' identifier="person.lname"></my-directive>
Fiddle

Resources