i have a custom form directive which creates an ng-form inside it. i want to use this form in the transcluded elements, to disable buttons etc, but its not working.. PLUNKER LINK
app.directive("myform", function(){
var templateFn = function(tElement, tAttrs){
var html = '<div ng-form="' + tAttrs.name + '">'
html += '<div ng-transclude=""></div>'
html += '</div>'
return html;
};
return {
restrict: 'E',
template: templateFn,
scope: {list: '='},
transclude: true
}
});
this is how i am using it
<myform name="example">
<input type="text" required ng-model="name2"/>
<button ng-disabled="example.$invalid"> button </button>
</myform>
do i need to compile the template during link?? i thought as the template is used and the linking is happening latter, the $compile would be taking care of that..
the generated html is as expected, except that button is not disabled
<myform name="example" class="ng-isolate-scope">
<div ng-form="example" class="ng-pristine ng-invalid ng-invalid-required">
<div ng-transclude="">
<input type="text" required="" ng-model="name2" class="ng-scope ng-pristine ng-invalid ng-invalid-required">
<button ng-disabled="example.$invalid" class="ng-scope"> button </button>
</div>
</div>
</myform>
When you transclude the contents, have it link to the same isolated scope as ng-form (instead of the parent scope which is the default):
app.directive("myform", function(){
var templateFn = function(tElement, tAttrs){
var html = '<div ng-form="' + tAttrs.name + '">'
html += '<div></div>'
html += '</div>'
return html;
};
return {
restrict: 'E',
template: templateFn,
scope: {list: '='},
transclude: true,
link:function(scope, element, attr, ctrl, transcludeFn) {
var e = element.find('div');
transcludeFn(scope, function(clone) {
angular.element(e[1]).append(clone);
});
}
}
});
Demo Plunker
Related
I'm trying to re-use code as much as I can so I'm wrapping ng-messages in a directive. However, the messages are not shown..
<form name="elementForm" novalidate>
<md-input-container>
<label>Category</label>
<input ng-required="true" ng-model="item.category" name="category" type="text">
<administration-input-validation control="category" formname="elementForm"></administration-input-validation>
</md-input-container>
</form>
Directive:
angular.module('app.analytics')
.directive('administrationInputValidation', function () {
return {
template:
'<div ng-messages>' +
'<div ng-message="required">This is required</div>' +
'</div>',
restrict: 'E',
replace: true,
require: '^form',
scope: {
control: '=',
formname: '='
},
link: function(scope, element, attrs) {
var formStr = attrs.formname + "." + attrs.control;
element.attr("ng-messages", formStr + "." + "$error");
}
};
});
I also tried hardcoding but it's still not showing
<div ng-messages="elementForm.category.$error">
UPDATE
Binding works now, but it's not showing the messages
I have the following directive code (AngularJS 1.4.8). The issue I have is using template replacements inside the ng-repeat.
(function() {
'use strict';
angular.module('app').directive('formGroup', formGroup);
var template = '<div class="form-group">' +
'<label class="control-label">{{label}}</label>' +
'<div data-ng-transclude></div>' +
'<span class="text-danger field-validation-error" data-ng-repeat="errorMessage in {{form}}.$error.{{model}}.messages">' +
'{{errorMessage}}' +
'</span>' +
'</div>';
function formGroup () {
return {
restrict: 'A',
require: '^form',
transclude: true,
replace: true,
scope: {
label: '#',
model: '#'
},
template: template,
link: link
};
}
function link(scope, element, attrs, ctrl) {
scope.form = element.closest('form').attr('name');
}
})();
It is used in HTML in the following way (server-validate is another directive that puts in-line validation errors returned from the server into the ng-repeat in the directive).
<form name="myForm" data-ng-submit="submit()">
<div class="col-lg-4 col-md-6">
<div data-form-group data-label="Memeber number" data-model="memberNumber">
<input type="text"
class="form-control"
data-ng-model="model.memberNumber"
data-server-validate />
</div>
</form>
I want it to produce the following output HTML.
<form name="myForm" data-ng-submit="submit()">
<div class="form-group">
<label class="control-label">Member number</label>
<div>
<input type="text"
class="form-control"
data-ng-model="model.memberNumber"
data-server-validate" />
<span class="text-danger field-validation-error"
data-ng-repeat="errorMessage in myForm.$error.memberNumber.messages">
{{errorMessage}}
</span>
</div>
</div>
</form>
However, it gets the following error.
[Error] [$parse:syntax] Syntax Error: Token '{' invalid key at column 2 of the expression [{{form}}.$error.{{model}}.messages] starting at [{form}}.$error.{{model}}.messages]
Since form controller is injected as required, using the formcontroller, the error messages can be handled like below.
function link(scope, element, attrs, ctrl) {
var unbinder = scope.$watch(function () {return ctrl[scope.model] }, function(value) {
if (!value) return;
scope.errorMessagesList = ctrl[scope.model].$error;
unbinder();
})
}
The complete working sample is availabe in the fiddle:
http://jsfiddle.net/acb3c8n7/5/
In the link function use:
function link(scope, element, attrs, ctrl) {
scope.formString = element.closest('form').attr('name');
scope.form = scope.$eval(scope.formString);
}
In the template use:
ng-repeat="errorMessage in form.$error[model].messages"
The $eval method evaluates the string as an Angular expression and will return a reference to the myForm object.
closest() is not present in the elementargument (check this).
Also you have several errors, by example you don't provide a name attribute in the input, this causes that the scope.myForm object don't recognize the model string provided how a property.
<input type="text" name="memberNumber"
class="form-control"
data-ng-model="model.memberNumber"
data-server-validate" />
In your ng-repeat you shouldn't provide a expression like this:
{{form}}.$error.{{model}}.messages
you should be create a property to attach the form object and the use some like this --> http://codepen.io/gpincheiraa/pen/eZGZPX
I have a scenario where i need to know the value and name of model passed in to the function.
I tried the following
$scope.rad='fff';
app.directive('kmRadio', function() {
return{
restrict:'E',
compile: function(element,attrs)
{
var model = {l:''};
model.l = attrs.kmModel;
var str1="n in ";
var str2=attrs.kmOption;
var repeat=str1.concat(str2);
var htmlText='<div><div ng-switch on="format">'+
'<div ng-switch-when="kmForm">'+
'<div>'+
'<div class="floatLeft">'+
''+attrs.title+''+
'</div>'+
'<ul>'+
'<li class="rowlist" ng-repeat="'+repeat+'"> {{n}}<input type="radio" ng-model="'+attrs.kmModel+'" name="a" ng-click="radioValueChanged(n,'+attrs.kmModel+')"/></option>'+
'</ul>'+
'{{'+attrs.kmModel+'}}'+
''+attrs.kmModel+''+
''+model.l+''+
'</div>'+
'</div>'+
'<div ng-switch-when="kmPreview">'+
'<div>'+attrs.title+'<input type="radio" ng-model="'+attrs.kmModel+'" disabled="true"/></div>'+
'</div>'+
'</div></div>';
element.replaceWith(htmlText);
}
}
})
The following code is responsible for calling the function
'<li class="rowlist" ng-repeat="'+repeat+'"> {{n}}<input type="radio" ng-model="'+attrs.kmModel+'" name="a" ng-click="radioValueChanged(n,'+attrs.kmModel+')"/></option>'+
$scope.radioValueChanged = function (value,model) {
//alert("Changed value"+value + model);
alert(value +" and " + model);
//alert("model "+ model );
}
In HTML , i have a code like below
<km-radio km-model="rad" title="Gender" km-option="['De','CM','PM']"></km-radio>
When i tap on radio button in html, i am getting the o/p De and fff, but what i need is o/p De and rad
See the plunker code and check the directive kmRadio
I rewrote your directive to get the binding from the attributes parameter of the link function:
app.directive('kmRadio', function($parse) {
return {
restrict: 'E',
replace: true,
templateUrl: 'kmRadio.html',
scope: true,
link: function(scope, element, attr) {
scope.kmModel = scope.$eval(attr.kmModel);
scope.binding = attr.kmModel;
scope.title = attr.title;
scope.kmOption = scope.$eval(attr.kmOption);
}
}
})
where kmRadio.html is:
<div>
<div ng-switch on="format">
<div ng-switch-when="kmForm">
<div>
<div class="floatLeft">{{title}}</div>
<ul>
<li class="rowlist" ng-repeat="n in kmOption">
{{n}} <input type="radio" ng-model="$parent.kmModel" ng-click="radioValueChanged(n, binding)" ng-value="n"/>
</li>
</ul>
bound to: {{binding}}, value: {{kmModel}}
</div>
</div>
<div ng-switch-when="kmPreview">
<div>{{title}}
<input type="radio" ng-model="kmModel" disabled="true" />
</div>
</div>
</div>
</div>
Here is a working demo: http://plnkr.co/edit/2q8a8B4zNGpQ0C5V1x8D?p=preview
I have a controller AppCtrl as
scope.transaction = {}
The index looks like
<div class="control-group">
<label class="control-label">Date</label>
<div class="controls">
<div class="control-group input-append date form_datetime">
<date-time-picker data-ng-model="transaction.date"></date-time-picker>
</div>
</div>
</div>
<div class="control-group">
<label class="control-label">Amount</label>
<div class="controls">
<div class="input-append">
<input type="text" name="transactionAmount" ng-model="transaction.amount" required>
</div>
and my custom directive looks like
angular.module('customDirectives', []).directive('dateTimePicker', function() {
return {
restrict: 'E',
replace: true,
scope: {
transaction['date']: '=' # COMPILATION ERROR HERE
},
template: '<div class="control-group input-append date form_datetime">'+
'<input type="text" readonly data-date-format="yyyy-mm-dd hh:ii" name="transactionDate" ng-model="transaction.date" data-date-time required>'+
'<span class="add-on"><em class="icon-remove"></em></span>'+
'<span class="add-on"><em class="icon-th"></em></span>'+
'</div>',
link: function(scope, element, attrs, ngModel) {
var input = element.find('input');
element.datetimepicker({
format: "yyyy-mm-ddThh:ii:ssZ",
showMeridian: true,
autoclose: true,
todayBtn: true,
pickerPosition: 'bottom-left'
});
element.bind('blur keyup change', function(){
console.log('binding element');
scope.$apply(date);
});
function date() {
console.log('setting date',input.val());
scope.ngModel = input.val();
}
date(); // initialize
}
}
});
I want to assign the date value from my directive to $scope.transaction.date but it is failing as compilation error, how can I achieve this?
scope: {
transaction['date']: '=' # COMPILATION ERROR HERE
},
Should be
scope: {
transactionDate: '='
},
And
<date-time-picker data-ng-model="transaction.date"></date-time-picker>
Should be
<date-time-picker transaction-date="transaction.date"></date-time-picker>
Then within your directive you can call scope.transactionDate = myValue;
within scope.$apply();
EDIT: If you want to use ng-model within your directive then you can use
....
restrict: 'E',
require: '?ngModel',
....
And
controller.$setViewValue(value); //this will in directive code where you want set the value of the ng-model bound variable.
In Html
<date-time-picker data-ng-model="transaction.date"></date-time-picker>
I try to create custom html form elements with angular.js. The idea is that I have one maintemplate for the formfield-layout and paste in the template needed for textfield, datefield or whatever.
var app = angular.module("myApp", []);
app.controller("MainCtrl", function ($scope) {
$scope.window = window;
});
// create general formfield-layout
function buildFormTemplate(innerTemplate) {
var t = '<div class="control-group">'
+ '<label class="control-label" for="{{x.id}}">{{x.label}}';
t += '<span ng-show="x.required" class="required">*</span>',
t += '</label><div class="controlls">';
t += innerTemplate;
t += '</div>';
t += '</div>';
return t;
}
app.directive("textfield", function() {
return {
restrict: "E",
scope: {},
replace: true,
template: buildFormTemplate('<input id="{{x.id}}" type="text" name="{{x.name}}" value="{{x.value}}" />'),
link: function(scope, elm, attrs) {
scope.x = attrs;
}
};
});
This code works like I expect, but if I have multiple textfield-elements only one is rendered. The other textfield-elements are gone.
<textfield id="myLabel" label="label1" name="mytext1" value="with label"/>
<textfield id="anotherOne" label="label2" name="mytext2" value=""/>
renders
<div class="control-group" id="myLabel" label="label1" name="mytext1" value="with label">
<label class="control-label ng-binding" for="myLabel">
label1<span ng-show="x.required" class="required" style="display: none;">*</span></label>
<div class="controlls">
<input id="myLabel" type="text" name="mytext1" value="with label">
</div>
</div>
The second textfield is gone. What am I missing?
Close the tags and it works fine
<textfield id="anotherOne" label="label2" name="mytext2" value=""></textfield>
DEMO: http://plnkr.co/edit/ky0Tpt8qudSLSGho2QAk?p=preview