Using angular scope directive variable content as an attribute itself - angularjs

I couldn't find the answer to this question anywhere, so I'm gonna ask it here. Is there any way to use an Angular scope directive variable content as an attribute itself?
For example:
View Input:
<custom-directive
attr-one="Atribute value 1"
ng-model="cool.model"
message="Message 1"
extra-attr="variable-attribute"
></custom-directive>
Directive file:
app.directive('customDirective', [
function () {
return {
restrict: 'E',
templateUrl: createuri('/templates/custom-directive'),
require: 'ngModel',
scope: {
message: '#message',
ngModel: '=ngModel',
extraAttr: '#extraAttr',
attrOne '#attrOne'
}
}
}
]);
Directive template file:
<input type="text"
attr-one="attrOne"
class="input-directive"
ng-model="ngModel"
message="message"
{{extraAttr}} %{--something like this--}%
/>
In a way that the output would end up like this:
<input type="text"
attr-one="Atribute value 1"
class="input-directive"
ng-model="cool.model"
message="Message 1"
variable-attribute
/>
Edit: I'm not sure it's a assignment error, because when I try to use a variable that is working ({{label}}), for eg., this is what i get:
The variable gets outputed inside the element's content area, but not inside the element attribute definition area.

As said in here:
"Web browsers are sometimes picky about what values they consider valid for attributes."
Try to use ngAttr for this:
ng-attr-label="{{yourLabelValue}}"
label can be replaced with any attribute name such as ng-attr-variable-attribute="{{attributeValue}}" for "variable-attribute".

I end up finding the answer, and it consists on using the compile directive function as It runs before the link one.
/*[...]*/
priority: 1001,
terminal: true,
compile: function(el, attr) {
var ipt = el[0].childNodes[0].childNodes[1];
/* ^ searching for the element I want to bind the directive attribute to*/
ipt.setAttribute(attr.extraAttr, '');
/* ^ setting the attribute to the value contained in extraAttr */
}
/*[...]*/

Related

Can't get default values into directive from parent component

I'm having difficulty getting default values into my directive as set by the parent component.
Here's the instance of my directive, mrc-input-field, which lives in paymentform.html:
<mrc-input-field id="mrc-quantity" label="Quantity" data="$ctrl.quantity" type="number" ></mrc-input-field2>
and here's the resulting (relevant) markup emitted by mrcInputField;
<input id="quantity" type="number" name="quantity" ng-model="$ctrl.data" placeholder="Quantity" >
$ctrl.quantity is set by the parent paymentform.js and I have data bound in the directive;
.directive("mrcInputField", mrcInputField2);
mrcInputField.$inject = ['$compile', '_'];
function mrcInputField($compile, _) {
return {
restrict: 'E',
scope: {
data: "="
},
link: function(scope, element, attributes) {
yet the form has quantity blank when I open it. The good part is that it works for getting the input values out of the directive, i.e. my paymentform.js is getting the entered quantity from the mrc-input-field directive.
The directive is generating and $compile'ing the resulting markup i.e. the input element above.
Thanks for any clues
Update: I didn't mention, the problem is that the value of the "data" inside the directive is the string value "$ctrl.quantity" instead of the value of $ctrl.quantity (which is number 1).

Nested Directives and NgModel

I feel like I'm missing a fundamental concept of Angular directives.
Referring to this Plnkr: http://plnkr.co/edit/WWp9lB6OvxHL8gyBSU5b?p=preview
I have a model of:
{
message: string,
value: number
}
And I have an itemEditor directive to edit that model:
.directive('itemEditor', function() {
return {
replace: true,
templateUrl: 'item.editor.html',
require: 'ngModel',
model: {
item: '=ngModel'
}
};
})
But I want to delegate the editing of value to a custom control:
.directive('valuePicker', function() {
return {
replace: true, // comment this to make it work
templateUrl: 'value.picker.html',
require: 'ngModel',
scope: {
ngModel: '='
},
controller: Controller
};
function Controller($scope, Values) {
$scope.values = Values;
console.log({scope:$scope});
}
})
At current, this code gives the error:
Error: $compile:multidir
Multiple Directive Resource Contention
Commenting the replace: true will allow this code to work. However, I lose the styling instructions from the parent template. I.E: the class form-control is not merged onto the select element.
What is the angular way to make this work?
You are calling value-picker twice here
<value-picker class="form-control" style="width:100%" name="item" value-picker ng-model="item.value"></value-picker>
The value-picker element contains value-picker attribute as well, both being treated as directive which in conflict causing multiple directive error. Remove the attribute value-picker, either call it as element or attribute. Or you can restrict the directive to a specific declaration.
Also remove ng-model from select element of value.picker.html template, which is causing another error:
Multiple directives [ngModel, ngModel] asking for 'ngModel'
So replace: true replaces and appends the current directive attributes to the root level of template element (in your case its select)
Updated Plnkr

angularjs model view update through angular directive

I am trying to update my view using model in angular directive here is the directive I have created
app.directive('aboutOptions', [function() {
return {
restrict: 'C',
scope: {
testing: '='
},
transclude: true,
link: function(scope, elem, attr) {
scope.$watch(attr.ngModel, function() {
console.log(scope.$eval(attr.ngModel));
scope.testing = scope.$eval(attr.ngModel);
});
}
}
}]);
here is html model and file name is ab.html
<input type="text" class="about-options" ng-model="testing" />
Here is the view to be updated and file name is show.html
<h2 class="about-options">{{testing}}</h2>
ab.html file will be loaded as a template inside jquery ui dialog and my show.html file is in main page
If I remove
scope: {
testing: '='
},
Console is showing what I am typing
Update - 1
Tried with the following changes
testing: '=test'
And in html
<input type="text" class="about-options" ng-model="testing" />
<h2 class="about-options">{{test}}</h2>
What your doing at the end there with the scope is isolating the scope, so if an attribute called testing isn't found on the element where that directive is used you should be seeing an error in the console. If you desire an isolate scope (a good idea a lot of the time) then you'll need to provide the testing="someScopeVar" as an attribute of the element the directive is applied to.
Take a look at this repo to see how I'm doing it.
https://github.com/jedininjaster/angular-mask-money/blob/master/demo/js/angular.maskMoney.js
also take a look at the angular-ui mask directive. That is how I built my directive.
https://github.com/angular-ui/ui-utils/blob/master/modules/mask/mask.js
I apologize I don't have time right now to write a full explanation. Will try and update tomorrow

AngularJS: how to compile form in a directive

I'm trying to create a custom directive that set 'required' and 'disabled' attributes to the element (form input). The directive gets this data from the scope object. But clearing of required field doesn't set my form to invalide state. I think it's about form compilation after changing the attribute. I tried to do that but got an infinite loop :(
How to compile my form correctly?
Here is plunker
You could just use ng-disabled and ng-required, instead of adding the attributes in a directive.
<div>
<label for="username">username2</label>
<input ng-model="data.account.username2"
ng-disabled="paintData['data.account.username2'] == 'RO'"
ng-required="paintData['data.account.username2'] == 'R'" />
</div>
Alternatively, you could define a directive template that uses ng-disabled and ng-required, and replace the original element with it:
.directive('metaValidate', function($compile) {
return {
restrict: 'A',
replace: true,
scope: {
model: '=',
paint: '='
},
template: '<input ng-model="model" ng-disabled="readonly" ng-required="required"/>',
link: function(scope, element, attrs) {
scope.required = scope.paint[element.attr('model')] === 'R';
scope.readonly = scope.paint[element.attr('model')] === 'RO';
}
};
});
Then, use it like this:
<input model="data.account.username2" meta-validate paint="paintData"/>
I prefer the first approach, because it can respond to changes to paintData. But, you have to repeat the property name several times.
If you want to try this code, here is an update of your Plunker.
Recompiling the element could work, but in order to avoid the infinite loop you need to first remove the meta-validate attribute (which would cause yet more compiling etc):
/* At the end of the directive's link function */
attrs.$set('metaValidate', undefined);
$compile(element)(scope);
See, also, this short demo.

Angular.js setting the value of ng-show by parameter inside a directive

Sorry if the title isn't clear, here is what I'm trying to do:
I have multiple signup forms and every one of them has a password field. Now, I want to set some requirements to the passwords, ie. I want to get a password that is longer than 5.
I have:
<form name="myForm">
<!-- some elements -->
<input type="password" required ng-model="user.password" name="password" ng-minlength="5">
and right after that:
<div ng-show="myForm.password.$error.minlength">
Password is too short.
</div>
<!-- some other elements -->
</form>
I thought I would refactor this error message into a directive, the only problem is that I can't seem to correctly pass the form's name to the directive.
The directive looks like this:
myApp.directive('passwordLengthError', [function () {
return {
restrict: 'E',
replace: true,
template:'<div ng-show="{{form}}.password.$error.minlength">Password is too short.</div>',
scope: {
form: '#'
}
};
}]);
and I call it like this:
<div>
<password-length-error form="myForm"/>
</div>
If I check in Chrome's web inspector, I see that the parameter is there, I see
<div ng-show="myForm.password.$error.minlength">
however, it doesn't actually work, I don't see the message pop up if the password is shorter than 5 characters.
Is there a way to make this work, or is this not possible? Thanks in advance.
The # in your isolate scope is trying to evaluate an angular expression. You are just passing a string, so you can just set the scope variable directly to the attribute value in your directive, without any isolate scope or evaluation of the attribute.
So:
scope.form = attrs.form;
And the entire directive would be:
app.directive('passwordLengthError', [function () {
return {
restrict: 'E',
replace: true,
template:'<div ng-show="{{form}}.password.$error.minlength">Password is too short.</div>',
link: function(scope, element, attrs){
scope.form = attrs.form // the attribute is a string, so, YAY
}
};
}]);
YOUR DEMO

Resources