I'm trying to bind model data from a controller to a directive template. The directive template repeats form input types based on a form object from a controller.
The goal is to use this directive in multiple places and bind the appropriate model to the form as opposed to creating a form each time.
I've found examples using transclusion and isolated scopes, however, each of the examples I've come across seem to have the model properties hardcoded.
Here is a jsfiddle that hopefully explains better what I'm trying to achieve.
http://jsfiddle.net/N9rSa/14/
app.controller('FormCtrl',function($scope) {
$scope.form = [
{label:'First',type:'text',name:'first_name'},
{label:'Last',type:'text',name:'last_name'}
];
$scope.person = {first_name:'Jimmy',last_name:'Page'};
});
app.directive('formelements',function() {
return {
restrict: 'E',
scope:false,
template: '<div ng-repeat="elements in form"><input type="text" ng-model="field.name"></div>'
}
});
Thanks for any help.
You can use the following as template:
template: '<div ng-repeat="element in form">' +
'<input type="text" ng-model="person[element.name]"></div>'
Fiddle: http://jsfiddle.net/g5Mzs/
Essentially the name field is used to define the index inside the person object.
Related
I'm trying to understand directive's scope inside ng-repeat, still wondering if it comes from ng-repeat but it looks like.
Here is my code
directive.js
myApp.directive('dirSample', function () {
return {
template: '<input type="text" ng-model="name" />',
replace: true,
restrict: 'AE'
}
});
mainController.js
angular.controller('mainController',function($scope){
$scope.name = 'name'
});
index.htm
<div ng-repeat="i in [1, 2, 3, 4]">
<dir-sample></dir-sample>
</div>
<dir-sample></dir-sample>
<dir-sample></dir-sample>
When i make a change in one of the last two directives (which are not inside ng-repeat) it works well, changes on one are reflected on the other.
Problem :
1 - if i change an input value of a directive generated by ng-repeat , changes are not reflected anywhere else.
2 - if i change value of input on one of the two last directives , the directives inside ng-repeat change too, but if touch ( change input value ) of any directive , changes will not be reflected on that directive but will keep being reflected on the other directives.
Can someone please explain why the scope has that behavior ?
Thanks.
Binding primitives is tricky, as is explained here: Understanding scopes. It has to with how Javascript works. Your 'name' variable will get shadowed once it is altered within the ng-repeat block. The suggested fix (from the link above):
This issue with primitives can be easily avoided by following the
"best practice" of always have a '.' in your ng-models
They also provide a link to a video explaining exactly this problem: AngularJS MTV Meetup
So a fix looks like this:
app.controller('mainController',function($scope){
$scope.attr= {}
$scope.attr.name = 'name'
});
app.directive('dirSample', function () {
return {
template: '<input type="text" ng-model="attr.name" />',
replace: true,
restrict: 'AE'
}
});
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
I would like to access the content of the validation variables provided from AngularJS for the forms.
I need to add forms using a directive like in the code, but if I do that I can't access the $dirty,$error,$invalid variables anymore and I need to access them.
JS:
myApp.directive('test', function() {
return {
scope : {
nameform: "=",
nameinput: "=",
type: "="
},
template: '<form name=nameform></form>',
restrict: 'E',
compile: function (elem,attr){
var form = elem.find("form");
if(attr.type == "text")
form.html('<input type="text" name="nameinput" ng-model="data.text" placeholder="type here..." required />');
}
};
});
HTML:
<test nameform="valform" nameinput="valinput" type="text"/>
{{valform.valinput.$invalid}}
I think that you can't. Because you are using isolated scope for building your directive, so you don't have acces to the information. You can try to build you directive using share scope, and I think that you would be able to access this information.
Change your directive to be ng-form instead of form (because ng-form is nestable). Then wrap your directive inside another form element and give that new form element a name. The outer form element will bind to your outer scope and you can access the properties that way.
Directive template:
"<ng-form name="valform"></ng-form>"
HTML:
<body ng-view>
<div>Valid: {{outerform.$valid}}</div>
<div>Errors: {{outerform.$error}}</div>
<form id="outerform" name="outerform">
<test type="text"/>
</form>
</body>
Side note, form names don't play nice with dynamic names. The plunkr below uses static names so I could help you with your current problem. I found a solution to the dynamic name problem on a different SO post...I think this one...
Dynamic validation and name in a form with AngularJS
Here's a plnkr with your working form...
http://plnkr.co/edit/RFrRXp2kWkP1Mefwl3Kn?p=preview
When you build your HTML for your input controls, make sure you append the name attribute correctly based on 'nameinput' passed in as an attribute:
myApp.directive('test', function() {
return {
scope : {
nameform: "=",
nameinput: "=",
type: "="
},
template: '<form name=nameform></form>',
restrict: 'E',
compile: function (elem,attr){
var form = elem.find("form");
if(attr.type == "text")
form.html('<input type="text" name="' + attr.nameinput +'" ng-model="data.text" placeholder="type here..." required />');
}
};
});
I have a custom directive, and its purpose is to present a widget and bind it to a variable.
Every variable has different data type, so different widgets will be presented depending on the data type.
My problem is that I can pass the data of the variable, but I can't manage to bind the widget to it.
To simplify the problem, my widget is just a simple text input.
When I try to $compile the widget, Angular uses the value of the variable instead of binding to it.
HTML:
<body ng-app="app" ng-controller="myCtrl">
<input type="text" ng-model="resource.name"></div>
<div custom-widget widget-type="widget" bind-to="resource"></div>
</body>
Javascript:
angular.module('app', [])
.directive('customWidget', function($compile) {
return {
replace: true,
template: '<div></div>',
controller: function($scope) {
},
scope: {
bindTo: "=bindTo",
widgetType: "=widgetType"
},
link: function(scope, iElem, iAttrs) {
var html = '<div>' + scope.widgetType.label + ':<input ng-bind="' + scope.bindTo[scope.widgetType.id] + '" /></div>';
iElem.replaceWith($compile(html)(scope));
}
};
})
.controller('myCtrl', function($scope) {
$scope.widget = {
id: 'name',
label: 'Text input',
type: 'text'
};
$scope.resource = {
name: 'John'
};
});
Plunker demo: http://plnkr.co/edit/qhUdNhjSN7NlP4xRVcEA?p=preview
I'm still new to AngularJS and my approach may not be the best, so any different ideas are of course appreciated!
Since you're using an isolate scope one issue is that resource is on the parents scope and not visible within the directive. And I think you're looking for ng-model rather than ng-bind.
Also, since you want to bind to namein resource, we need to tie that in somehow.
So here's one approach to your template html (note the addition of $parent to get around the scope issue and the addition of .name(which you could add programatically using a variable if you preferred, or specify it as part of the attribute))
var html = '<div>' + scope.widgetType.label + ':<input ng-model="' + '$parent.' + iAttrs.bindTo +'.name'+ '" /></div>';
Updated plunker
Well, when you have a isolated scope within your directive and use the "=" operator you already have two-way data binding.
My suggestion would be to use the "template" more like a view so the operations are clearer.
I would change your directive to the following:
Using ng-model instead of ng-bing mainly because as the Documentation reveals:
The ngModel directive binds an input,select, textarea (or custom form control) to a property on the scope using NgModelController, which is created and exposed by this directive. [...]
Changed directive:
angular.module('app', [])
.directive('customWidget', function($compile) {
return {
replace: true,
template: '<div> {{widgetType.label}} <input ng-model="bindTo[widgetType.id]" /></div>',
scope: {
bindTo: "=bindTo",
widgetType: "=widgetType"
}
};
});
EDIT:
Ops forgot the Updated Plunker
Is it possible to specify model for ngInclude so that any changes done inside the included template are reflected on the specified model. For instance:
I have a model such as :
$scope.member = {
name: "Member1",
children:[
{
name:"Child1"
},
{
name:"Child2"
}
]
};
and the template:
<script type="text/ng-template" id="name.html">
<input type="text" ng-model="member.name"/>
</script>
Now is it possible to pass ngInclude either "member" or any child and get their respective name properties modified? I tried passing ng-model to the ng-template but it doesn't work. I tried to dynamically load the scope with the intended model but if the model gets delete, I am left with an orphan template. Following is the jsfiddle code:
http://jsfiddle.net/vaibhavgupta007/p7E5K/
I wish to reuse the template rather than duplicating the same template for different models. I have refered to this question:
How to specify model to a ngInclude directive in AngularJS?
But here models are not getting deleted.
Edit
I have not grasped the concepts of creating custom directives till now. But will creating a new directive in conjuction with ng-include help?
The answer of your last question is: yes. In a directive, you define also a template and a scope. The content of the scope is completely in your hands.
See here: http://jsfiddle.net/vgWQG/1/
Usage:
Member: <member model="member"></member>
<ul>
<li ng-repeat="child in member.children">
Child {{$index}}: <member model="child"></member>
</li>
</ul>
The directive:
app.directive('member', function(){
return {
template : '<input type="text" ng-model="member.name"/>',
replace : true,
restrict: 'E',
scope : {
'member' : '=model'
},
link: function(scope, element, attr) {}
};
});
I've moved the template in an inline variant because I could not getting the template cache getting to work in jsfiddle. In a real world, a templateUrl: 'name.html' should be fine.
This is what you want?