Can't get default values into directive from parent component - angularjs

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).

Related

Dynamic add attributes on component in Angular pre compile

I have component that is used multiple times, 58 times to be exakt. The only thing that differs between them is unique attributes to add validations. What I want to do is to add a array of attributes to my template before is compiled. Is this possible to achieve when working with a Angular component?
component.js
(function () {
'use strict';
angular
.module('upaApp')
.component('component', {
bindings: {
inputAttributes: '#',
},
controller: controller,
restrict: 'E',
templateUrl: 'app/component/component.html'
});
function controller() {
var $ctrl = this;
}
})();
component.html
<input {{ $ctrl.inputAttributes }}
class="form-control"
type="text" />
When I use the component <component input-attributes="directive1, directive2"></component> it doesn't render out my string and even if it did I would not be sure that it would work. So is there a way to dynamically be able to set the attributes in AngularJS?
Is this angular 1 or 2?
Ill assume the former.
I dont know of a way to place a string as an attribute. What you could do as a workaround is conditionally insert attributes with the ng-attr- attribute. This will insert the attribute if the variable is not undefined.
maybe something like this:
$scope.ctrl.inputAttributes = {
directive1:undefined, //this one wont show
directive2:"this one will show"// as directive2="this one will show"
}
then in your markup:
<input ng-attr-directive1="ctrl.inputAttributes.directive1"
ng-attr-directive2="ctrl.inputAttributes.directive2"
class="form-control"
type="text" />
https://www.thinkingmedia.ca/2015/03/conditionally-add-a-html-element-attribute-value-in-angularjs/
EDIT: it may not be clean, but you could create a directive that compiles html.
app.directive('dynamicAttributes', function ($compile) {
return {
restrict: 'E',
scope: {
attributes: '#'
},
link: function (scope, elem, attrs) {
var h = '<input '+scope.attributes + ' class="form-control" type="text" />';
elem.replaceWith($compile(h)(scope));
}
}
});
then in your DOM
<dynamic-attributes attributes="1 2 3"></dynamic-attributes>
fiddle: http://jsfiddle.net/brhardwick/nx16zdrL/1/
There was actually a very simple solution on my problem. You could use ng-model to send the value to the component. And when I placed my directives on the component it validates accordingly since it can access the value from ng-model.

Changing placeholder via AngularJS directive?

I have a text input field whose placeholder I want to change every few seconds. However I don't want to pollute my controller with this so I want to encapsulate this functionality into a directive.
This is what my directive looks like:
myApp.directive('searchBox', ['$interval', function($interval) {
return {
restrict: 'A',
link(scope, element, attrs) {
$interval(function() {
attrs.placeholder = 'New';
}, 1000);
}
}
}])
And the html:
<input type="text" class="form-control" placeholder="Old" ng-model="search" search-box>
However the placeholder stubbornly doesn't change, even though in the console attrs.placeholder can be seen to change to 'Test' from 'Hello'. Any ideas?
PLUNKR: https://plnkr.co/edit/Oy1M8FPTXxzB9oYMJqlx?p=preview
You cannot change attributes values via the attr object (it's just a static reflection of your element attributes). Instead, update your element using element.attr('placeholder', 'Test') or attrs.$set('placeholder', 'Test').

Using angular scope directive variable content as an attribute itself

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 */
}
/*[...]*/

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

watching value of inner element in directive

How can I scope some inner element's value to an arbitrary variable and watch for its changes?
//search.html
<div>
<input type="search" class="input-medium search-query" />
</div>
angular.module('search',[])
.directive "searchBar", ->
restrict: "E"
templateUrl: 'search.html'
replace: true
Now I'd like to $watch for input's value change ('keyup') and access (get and set) current value from "outside". Can I have some arbitrary attribute and access it, like:
<div>
<search-bar value='{{searchTerm}}' />
</div>
I hope it's clear what I'm trying to do.
You can set the value of searchTerm inside your directives linking function.
link: function(scope, element, attrs) {
// input element
var input= angular.element(element.children()[0]);
input.bind('keyup', function(){
scope.searchTerm = input.val();
});
}
Jonathan's answer will create a "searchTerm" variable in the parent scope. This is unreliable, because if you put your directive in another scope (say a form) it will not be accessible outside of that form. What you should do is create "searchTerm" where you need it, and then pass it to "searchBar". If you are not using isolated scope for "searchBar" then you don't have to even pass it. If you do use isolated scope then passing would looks something like this.
angular.module('search',[])
.directive("searchBar", function () {
restrict: "E"
scope: {
searchTermInside: '=searchTerm'
},
templateUrl: 'search.html'
replace: true});
And you use the directive like so:
<search-bar searchTerm="searchTermOutside" />
And in template
//search.html
<div>
<input type="search" ng-model="searchTermInside" class="input-medium search-query" />
</div>

Resources