Pass ng-model attribute to custom directive - javascript

So, I have a form, where I need to use a custom directive.
What i need: pass the user model to the directive.
<form>
<input type="text" ng-model="user.login">
<input type="password" ng-model="user.password">
<span ng-custom-directive ng-model="user.testfield"></span>
</form>
Directive template looks like this:
<span><input type="checkbox" ng-model="[HERE I NEED user.testfield TO WORK WITH user]"> </span>
How I can pass the user model to directive template?
After form submit I need user.testfield to be avaliable in the $scope.user like:
console.log($scope.user)
{
login: 'test',
password: 'test',
testfield: true|false
}

You can solve it in the other way plunker
In brief:
scope: {
bindedModel: "=ngModel"
},
template: '<input type="text" ng-model="bindedModel">'

Well, I found similar question and resolved my problem in this way:
angular.module("myApp")
.directive "ngCustomDirective", () ->
restrict: 'A',
scope:
field: '#',
model: '='
template: '<span><input type="checkbox" ng-model="model[field]"></span>'
And directive usage will be:
<span ng-custom-directive
ng-bind-model="user"
ng-bind-field="testfield">
</span>

Related

Form element directive with error message manage AngularJS

I'm trying to create a input directive element with validation.
I'd like to manage error status in this directive.
There are 3 files
Index.html: uses this directive
textValid.js: contains directive code
textValid.html: contains the directive template
I create this directive
textValid.js
App.directive("textValid", function() {
return {
restrict: "E",
templateUrl: "tpl/textValid.html",
require: "?ngModel",
scope: {
name: "#",
element: "=",
model: "="
}
};
});
index.html
<form name="edit_form_ctrl.contract_edit_form" action="#" novalidate >
<div class="row">
<text-valid name="ncontract"model="edit_form_ctrl.contract.ncontract"
element="edit_form_ctrl.contract_edit_form.ncontract">
</text-valid>
</div>
</form>
and template textValid.html
<input type="text" name="name" ng-model="model" class="form-control" ng-required="true" value="{{model}}" />
<div>pristine: {{element.$pristine}}</div> <!--is always undefined-->
<div>Invalid: {{element.$error}}</div> <!--is always undefined-->
<span class="color-red" ng-if="element.$error.required &&!element.$pristine">
{{curLang.field_mandatory}}
</span>
I'm trying to get input control to check $error and $pristine value, but I cannot to achieve it.
I read all documentation and the book too, but with any results.
Does someone try to do that?
Thanks in advance
If you add {{double-curlies}} around the name attribute of the input element like so:
<input type="text" name="{{name}}" ng-model="model" class="form-control" ng-required="true" />
You should be able to access the ngModelController for it from inside text-valid with $scope.form[$scope.name]
A couple other notes:
using a value attribute on and element with ng-model attribute will not have an effect.
you have require: '?ngModel' in your directive definition, but there is no ng-model attribute on the text-valid element. This is ok, but you won't get any ngModelController being injected unless the text-valid element has an ng-model attribute.
Edit:
Because text-valid has isolate scope, to access the FormController, you would need to require it and bind it:
App.directive("textValid", function() {
return {
restrict: "E",
templateUrl: "tpl/textValid.html",
require: "^^form",
scope: {
name: "#",
element: "=",
model: "="
},
link: function($scope, elem, attr, FormCtrl){
$scope.form = FormCtrl;
// now you should be able to access $scope.form[$scope.name]
// (you *might* need to put any initialization that accesses it inside a $timeout to wait for it to be rendered/bound)
}
};
});
In template, you should then be able to access it like:
<div>pristine: {{form[name].$pristine}}</div>
<div>Invalid: {{form[name].$error}}</div>
Try this:
<input type="text" name="name" ng-model="model" class="form-control" ng-required="true" value="{{model}}" />
<div>pristine: {{form.name.$pristine}}</div> <!--is always undefined-->
<div>Invalid: {{form.name.$error}}</div> <!--is always undefined-->
<span class="color-red" ng-if="element.$error.required &&!element.$pristine">
{{curLang.field_mandatory}}
</span>

How to keep parent scope property and isolated scope property in sync?

I have the following template:
<dynamic-field name="Name" type="input" ng-model="temp.product.name"></dynamic-field>
<dynamic-field name="Price" type="input" ng-model="temp.product.price"></dynamic-field>
<dynamic-field name="Qty" type="input" ng-model="temp.product.qty"></dynamic-field>
Custom directive code:
app.directive('dynamicField', function() {
return {
restrict: 'E',
templateUrl: getTemplate('templates/single-field.html'),
scope: {
ngModel: '='
},
link: function($scope, $element, $attrs) {
// console.log($scope);
}
};
});
In directive template, the input field is displayed:
<div class="field">
<input type="text" ng-model="ngModel" /> <!-- if $temp.product.post_text in parent scope is set to "Test", it's displayed -->
</div>
The problem is, when I modify something inside the input (isolated scope) changes are not applied to the parent scope. I think the problem is that I use primitive here:
<input type="text" ng-model="ngModel" />
... but I'm not sure how to resolve this. Any suggestions?
You must be doing something wrong. If it's two-way bound, it's two-way bound.
function MainController() {
this.name = "test";
this.logCtrlName = function() {
alert(this.name);
}
}
function dynamicField() {
return {
restrict: 'E',
template: `
<div class="field">
<input type="text" ng-model="ngModel" />
</div>
`,
scope: {
ngModel: '='
}
};
}
angular.module('app', []);
angular.module('app')
.controller('MainController', MainController)
.directive('dynamicField', dynamicField);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="MainController as ctrl">
<dynamic-field name="Name" type="input" ng-model="ctrl.name"></dynamic-field>
<a href ng-click="ctrl.logCtrlName();">Name inside parent controller</a>: {{ ctrl.name | json }}
</div>
</div>
Like you said since your model is primitive value, so whenever you change the value inside isolated scope will create its copy of scope variable.
A simple and quick way to fix that is create a function to update parent scope value $scope.$parent.ngModel directly then add ng-change to input element and call that function whenever input value is changed.

Dynamically setting ng-model is breaking

I have a directive HTML template like this:
textinput.html
<label for="{{name}}">{{label}}</label>
<input type="{{type}}" placeholder="{{placeholder}}" id="{{name}}" ng-model="{{name}}">
The label is outputting correctly, however everything inside of the input field is outputing as static {{varname}}
If I remove the ng-model like this:
<input type="{{type}}" placeholder="{{placeholder}}" id="{{name}}">
It outputs the variables correctly, but the moment I place the ng-model in and dynamically assign it a value, it breaks the entire input.
Why is this happening?
My goal is to be able to create simple 1 line text inputs which I can mass change with the directive, like so:
<textinput name="username" label="Username" type="text" placeholder="Enter Username"></textinput>
Finally figured it out, you have to pass the ng-model through using 2 way binding like this:
<textinput type="email" name="userEmail" ng-model="userEmail" placeholder="Email Address..." label="Email"></textinput>
-
app.directive('textinput', function() {
return {
restrict: 'E',
scope: {
label:'#label',
placeholder:'#placeholder',
name: '#name',
type: '#type',
ngModel: '=ngModel'
},
transclude: true,
templateUrl: 'directives/textinput.html'
};
});
Now within the template HTML file you can map properties without the {{ }} which works with ng-model.
example:
<label for="{{name}}">{{label}}</label>
<input type="{{type}}" placeholder="{{placeholder}}" id="{{name}}" ng-model="ngModel">
Now it'll be super easy to quickly generate re-usable form code.

AngularJS : Pass $scope variable as directive attribute

I'm trying to pass the $scope variable values to a custom directive as attribute, but it's not working.
Here is the HTML code:
<ul ng-repeat="q in questions">
<li>
{{q.question}}
<check-list name="{{q.id}}"></check-list>
</li>
</ul>
The directive is <check-list name={{q.id}}></check-list>, and here is the directive code :
app.directive('checkList',function(){
return {
restrict:'E',
template: function(elem,attrs){
console.log(attrs.name);
return '</br> <input type="radio" /> Yes </br> <input type="radio" /> No'
},
link:function(scope,elem,attrs){
}
};
})
I'm logging the attribute attrs.name but the value I'm getting is "{{q.id}}" instead of the actual value of q.id
I suppose what you want to do is injecting scope object from controller to your directive. So you can define your directive as
app.directive('checkList',function(){
return {
restrict:'E',
scope: {
name: "="
}
template: '{{name}}</br> <input type="radio" /> Yes </br> <input type="radio" /> No',
link:function(scope,elem,attrs){
}
};
}
And in your view, you can reference your directive as
<check-list name="q.id"></check-list>
In directives, attributes are just strings.
In a template function, all you can do is use the string value of the attribute. If you want to use the evaluated or interpolated value of the attribute, you have a few options:
1) Use an isolated scope
app.directive('checkList', function() {
return {
restrict:'E',
scope: {
name: '&'
}
template: '</br> <input type="radio" /> Yes </br>{{name()}} <input type="radio" /> No'
link: function(scope, elem, attrs) {
}
};
});
<ul ng-repeat="q in questions">
<li>
{{q.question}}
<check-list name="q.id"></check-list>
</li>
</ul>
2) Inject $interpolate or $parse to evaluate the interpolation or expression manually in the link function
app.directive('checkList', function($interpolate) {
return {
restrict:'E',
template: '</br> <input type="radio" /> Yes </br>{{name}} <input type="radio" /> No'
link:function(scope,elem,attrs){
scope.name = $interpolate(attrs.name)(scope);
}
};
});
<ul ng-repeat="q in questions">
<li>
{{q.question}}
<check-list name="{{q.id}}"></check-list>
</li>
</ul>
2a) And finally, $parse
app.directive('checkList',function($parse){
return {
restrict:'E',
template: '</br> <input type="radio" /> Yes </br>{{name}} <input type="radio" /> No'
link:function(scope,elem,attrs){
scope.name = $parse(attrs.name)(scope);
}
};
});
<ul ng-repeat="q in questions">
<li>
{{q.question}}
<check-list name="q.id"></check-list>
</li>
</ul>
I think you need to pass "q.id" instead of name={{q.id}} provided $scope.q.id is defined in your corresponding controller.
<check-list name="q.id"></check-list>
Or pass the entire scope to your directive:
app.directive('checkList',function(){
return {
restrict:'E',
scope: true, //scope
template: function(elem,attrs){
console.log(attrs.name);
return '</br> <input type="radio" /> Yes </br> <input type="radio" /> No'
},
link:function(scope,elem,attrs){
var question = scope.q; //get your question here
}
};
})
I recommend you pass only reference type as argument to your directive. Do not pass primitive types (q.id may be an integer). Pass question instead. It's all about how angularjs utilizes prototypical inheritance.
Scope is a complex topic in angularjs. See this: https://github.com/angular/angular.js/wiki/Understanding-Scopes

angularjs: (bootstrap) popover directive. How to close it?

I have a pattern related question. I build a directive to "load" content into a bootstrap popover.
HTML:
<span data-ng-show="user.provider === 'local'" class="label label-warning" data-popover="" data-placement="bottom" data-content-selector="#passwordchangeform">change password</span>
<div id="passwordchangeform" style="display:none;">
<h3>Change password</h3>
<h4>(Not finished yet!)</h4>
<form>
Old password:
<input type="text" ng-model="username" name="text"/><br/>
New password:
<input type="text" ng-model="password1" name="text"/><br/>
New password again:
<input type="text" ng-model="password2" name="text"/><br/>
<input type="button" data-ng-click="login()" value="login"/>
</form>
</div>
Directive:
angular.module('app').directive('popover', function ($compile) {
return {
restrict: 'A',
link: function (scope, el, attrs) {
var content;
if (attrs.content) {
content = attrs.content;
} else {
content = $compile("<div>" + $(attrs.contentSelector).html() + "</div>")(scope);
}
$(el).popover({
trigger: 'click',
html: true,
content: content,
placement: attrs.placement
});
}
};
});
In my controller, I implemented the function login()
Let's say in the controller login() function I get a success response to a HTTP/POST
What would be the best-practice then on actually closing the popover?? I can think of "dirty hacks" to force the closure, but I guess it could be done in a more appropriate way...
The "problem" I may have maybe related to the fact I'm using $compile in the directive to tie the content to the scope of the controller so I'm unable to close it from the link function.
Thanks,
Sander

Categories