I am using the angularJS for validation purpose in my project and i know to validate the normal fields. Now, i have the date field by the parameter passing in the name attribute. In some cases user can click the 'Add Date' button for adding more dates. So, in this case i have written the code by passing the parameter with the name attribute. Because of that i couldn't get the name field to validate.
Even if user add more date fields to enter i want to validate the all date fields.
Here is my code i am using,
<div class="col-md-12 col-sm-12 col-xs-12 table-responsive">
<table class="table table-striped table-hover">
<tr ng-repeat="dates in hall.hallDates">
<td>
<div class="col-md-6 col-sm-6 p-0 p-t-10">
Select Date<span style="color: red">*</span>
</div>
<div class="col-md-6 col-sm-6 p-0 p-t-10">
<input type="text" ng-model="dates.hallDate"
name="hallDate{{ $index }}" class="form-control"
placeholder="DD-MM-YYYY" mydatepicker readonly="true"
ng-class="{ validateFields: submitted && createHallForm.date.$invalid }"
required>
<div ng-show="submitted && createHallForm.date.$invalid">
<span class="validateFields" ng-show="createHallForm.date.$error.required" >Please enter the date</span>
</div>
</div>
How to get the name field with parameter to apply validation for n number of fields?
Can anyone help me to know about this?
It's likely that you're running Angular 1.2 or below. In those versions you cannot dynamically set the name attribute of an element. You can confirm this by looking in the rendered DOM and still seeing
name="hallDate{{ $index }}" after render.
See the related GitHub issue here: https://github.com/angular/angular.js/issues/1404
Solution 1
Upgrade to 1.3.x
Solution 2
Use the following workaround as posted in the GitHub issue referenced above.
// Your app module
angular.module('app', [])
// Workaround for bug #1404
// https://github.com/angular/angular.js/issues/1404
// Source: http://plnkr.co/edit/hSMzWC?p=preview
.config(['$provide', function($provide) {
$provide.decorator('ngModelDirective', ['$delegate', function($delegate) {
var ngModel = $delegate[0], controller = ngModel.controller;
ngModel.controller = ['$scope', '$element', '$attrs', '$injector', function(scope, element, attrs, $injector) {
var $interpolate = $injector.get('$interpolate');
attrs.$set('name', $interpolate(attrs.name || '')(scope));
$injector.invoke(controller, this, {
'$scope': scope,
'$element': element,
'$attrs': attrs
});
}];
return $delegate;
}]);
$provide.decorator('formDirective', ['$delegate', function($delegate) {
var form = $delegate[0], controller = form.controller;
form.controller = ['$scope', '$element', '$attrs', '$injector', function(scope, element, attrs, $injector) {
var $interpolate = $injector.get('$interpolate');
attrs.$set('name', $interpolate(attrs.name || attrs.ngForm || '')(scope));
$injector.invoke(controller, this, {
'$scope': scope,
'$element': element,
'$attrs': attrs
});
}];
return $delegate;
}]);
}]);
Related
I am dynamically adding a text field in DOM (from directive link function) & want to grab entered value and push it to controller scope object, but its always giving me undefined, below is my code:
<div class="input-group">
<span class="fieldIcon input-group-addon"><i class="fa fa-sticky-note" aria-hidden="true"></i></span>
<select name="addlist[]" multiple="multiple">
<option ng-repeat="options in optionList">{{options.label}}</option>
</select>
</div>
<script type="text/javascript">
angular.module('myapp')
.controller('AddContactController',[ '$scope', function ($scope) {
$scope.optionList = [{label: 'NewList'}];
$scope.addOption = function(optionList) {
console.log('List:', optionList); // its giving undefined
scope.optionList.push(optionList);
}
}])
.directive('optionList', ['$compile', function ($compile) {
return {
restrict: 'E',
templateUrl: '/templates/int_optionList.html',
controller: 'AddContactController',
link: function(scope, element, attrs) {
// Adding input field and on click of a button controllers addOption function should be called with the text field value
var addListField = '<input class="form-control" type="text" ng-modal="addList" name="addList" placeholder="Add new list...">'+
+'<button class="btn btn-default" type="button" ng-click="addOption()">';
addListField = $compile(addListField)(scope);
$(element).find('.multiselect-container').prepend(addListField);
}
}
}]);
</script>
Now here in addOption function I am getting optionList value as undefined.
Error is human :).
Replace ng-modal by ng-model in your directive
AngularJS expects models to be in object form, setting ng-modal="addList" to ng-modal="data.addList" might help.
I am creating one form using Bootstrap & AngularJS. I am using CK editor in my page as textarea. But I am not able to retrieve the value of the textarea while the value of the input text field is easily captured in my AngularJS controller. Following is the code snippet:
HTML page:
<div class="container">
<div ng-controller="controller">
<form role="form">
<label for="sd"><b>Short Description: </b></label>
<input ng-model="sdesc" class = "form-control input-xxlarge" type = "text" placeholder ="Provide a short description here."/>
<br/>
<label for="dt"><b>Details: </b></label>
<textarea ng-model="details" class="form-control" name="details_editor" id="details_editor"></textarea>
<br/>
<button class = "btn btn-primary" ng-click="submitted()">Ask It!</button>
<script>
CKEDITOR.replace('details_editor');
</script>
</form>
</div>
<br/>
<hr>
</div>
JS
app.controller('controller', ['$scope', function($scope){
$scope.submitted = function(){
var sdesc = $scope.sdesc;
var details = $scope.details;
alert($scope.details);
};
}]);
The alert shows undefined for the text area value.
Please help me solve the issue.
You are using the plain Javascript version of CK editor and hence Angular is not getting notified to update the ng-model value of that textarea.
Basically, Angular runs a digest cycle to update all views and models but since in this case the values being changed in the CK editor is happening outside the Angular.s context which is not updating the ng-model value.
To fix this, we added a small directive and notifying the change in the ng-model to the Angular by using the $timeout. (We can also use the $apply directive, but it may fail sometimes if the digest cycle is already in progress)
Example directive:
var app = angular.module("your-app-name", []);
app.directive("ckEditor", ["$timeout", function($timeout) {
return {
require: '?ngModel',
link: function ($scope, element, attr, ngModelCtrl) {
var editor = CKEDITOR.replace(element[0]);
console.log(element[0], editor);
editor.on("change", function() {
$timeout(function() {
ngModelCtrl.$setViewValue(editor.getData());
});
});
ngModelCtrl.$render = function (value) {
editor.setData(ngModelCtrl.$modelValue);
};
}
};
}]);
Remove, your following code:
<script>
CKEDITOR.replace('details_editor');
</script>
And, modify your text-editor like:
<textarea ng-model="details" class="form-control" ck-editor name="details_editor" id="details_editor"></textarea>
I found ng-ckeditor to implement ckeditor in angularjs.
Please refer this :https://github.com/esvit/ng-ckeditor. I tried it, It is easy to implement and working as expected
I am working on a directive and I had form validation working by hard-coding the ng-show to form.birthdate.$touched && form.birthdate.$error.required. After getting this working I set out to removing the portion of that string where i'd hardcoded the name of the input to birthdate. See below in the code where I have two spans with the this field is required message. The bottom one works, the top one doesn't. If I look at it in the HTML elements panel they look identical so it seems like it should work but things are not being bound correctly. Any idea how to solve this problem?
angular.module('MyModule').directive('vrDatepicker', function() {
return {
scope: {
dateModel: '='
},
link: function(scope, element, attrs, ctrls) {
scope.id = attrs.id;
scope.name = attrs.name;
scope.required = attrs.required;
scope.form = ctrls[0];
},
controller: ['$scope', function($scope) {
// Removed b/c it doesn't matter
}],
template: '<p class="input-group">\
<input type="text" \
class="form-control"\
datepicker-popup="MMM d, yyyy"\
ng-model="dateModel"\
is-open="opened"\
show-weeks="false"\
close-text="Close" />\
<span class="input-group-btn">\
<button type="button"\
class="btn btn-default"\
ng-click="open($event)">\
<i class="glyphicon glyphicon-calendar"></i>\
</button>\
</span>\
<span ng-show="form[\'{{name}}\'].$touched && form[\'{{name}}\'].$error.required" class="text-danger">This field is required</span>\ // ### This one doesn't work
<span ng-show="form.birthdate.$touched && form.birthdate.$error.required" class="text-danger">This field is required</span>'\ // ### This one works
</p>'
}
});
This should work
<span ng-show="form[name].$touched && form[name].$error.required" class="text-danger">This field is required</span>
Everything within the string assigned to ng-show will be interpolated so you don't need all that extra syntax.
I am new to Angular. It is a very simple question -
in my index.html I am defining two models on two text boxes :-
<html><head><script...></head><body ng-app="myApp"ng-controller="MainController" >
<input ng-model="tb1" type="text" name="numberofusers"/>
<input ng-model="tb2" type="text"></input>
</body></html>
And in my app.js I am defining like this
var app = angular.module('myApp', []);
app.controller('MainController', ['$scope', function($scope){
$scope.tb1 = $scope.tb2;
}]);
Now, what I want is that whatever I type in first text box (tb1) automatically typed to second text box (tb2) and vise-versa, but that is not happening.
Any guess ?
Your code in controller $scope.tb1 = $scope.tb2; would only be executed once (when controller initializes), that's why it doesn't work.
You need to bind input elements to the same model then Angular will handle two-way binding for you automatically.
<input ng-model="tb1" type="text" name="numberofusers"/>
<input ng-model="tb1" type="text"></input>
Or if you want to use two different models for different elements, you can add a hook to input's ng-change event listener like
<input ng-model="tb1" type="text" name="numberofusers" ng-change="tb2 = tb1"/>
<input ng-model="tb2" type="text" ng-change="tb1 = tb2"></input>
Then these two elements would sync automatically. But you know what, ng-change can only monitor user input change, that means, if you change tb1 or tb2 programmably, ng-change will not be triggered.
In this case, you should monitor model's change using $scope.$watch
$scope.$watch('tb1', function(newValue) {
$scope.tb2 = newValue;
}));
Currently it's beyond your requirement.
This is because controller will only execute once and if there is any value in $scope.tb2 will assign to $scope.tb1 but intially both of them are blank .
So you need to $watch the changes and assign value to each other like :-
$scope.$watch('tb1',function(newVal){
$scope.tb2=newVal;
})
$scope.$watch('tb2',function(newVal){
$scope.tb1=newVal;
})
And if you want to manage it on front end you can use ng-change directive like
<input ng-model="tb1" type="text" ng-change="tb2=tb1" name="numberofusers"/>
<input ng-model="tb2" type="text" ng-change="tb1=tb2"></input>
You can use two-way binding to achieve that. An example is: JSFiddle
Create your directive:
var app = angular.module('myApp', []);
app.controller("myCtrl", function($scope) {
$scope.myName = 'Carl';
}).directive("myDirective", function() {
return {
restrict: 'AE',
scope: {
twowayBindingProp: '=myName'
}
}
});
And bind it through:
<div ng-app="myApp">
<div ng-controller="myCtrl">
<h1>From parent: </h1>
<h3>parentProp2: <input ng-model="myName"></h3>
<div my-directive my-name="myName">
<h1>From child: </h1>
<h3>twowayBindingProp: {{ twowayBindingProp }}</h3>
<h1>Set from child:</h1>
twowayBindingProp: <input ng-model="twowayBindingProp">
</div>
</div>
</div>
For my project, i'm currently developping custom form / inputs directive.
For example, I have the following directive :
angular.module('myApp').directive("textField", function() {
return {
restrict: 'E',
replace: true,
templateUrl : "/common/tpl/form/text-field.html",
scope : {
label : "#",
model : "="
}
};
});
with the associated template :
<div class="form-group">
<label for="{{fieldId}}" class="col-lg-2 control-label">{{label |translate}}</label>
<div class="col-lg-10">
<input type="text" class="form-control" id="{{fieldId}}" ng-model="model" placeholder="{{label|translate}}">
</div>
</div>
I have many more custom fields ( date, select, double select, and so on... )
The usage is simple :
<text-field label="app.myLabel" model="myObj.myAttribute"></text-field>
The idea is to cleanup the main template by avoiding to verbosely add labels on every fields. Very common need i believe.
Now the problem :
Now I need to add custom validation to my input models.
I did a naive approach which was to create a validation directive :
directive('myValidation', function (){
return {
require: 'ngModel',
link: function(scope, elem, attr, ngModel) {
ngModel.$parsers.unshift(function (value) {
// do some validation
return value;
});
}
};
});
and then to use it like this :
<text-field label="app.myLabel" model="myObj.myAttribute" myValidation="validationOptions"></text-field>
But of course this doesnt work, simple because the text-field directive which is replace=true "erases" the validation directive on it's element.
Can someone tell one what is the correct approach to do "custom input with presentation" directive, while allowing the validation part to be declared on the directive ( here text-field ) and used on the directive's input ?
Per example, is there a way to say "attributes on my element directive will be 'copied' to inside my directive ?"
aka :
<text-field label="app.myLabel" model="myObj.myAttribute" **myValidation="validationOptions"**></text-field>
would result in :
<div class="form-group">
<label for="{{fieldId}}" class="col-lg-2 control-label">{{label |translate}}</label>
<div class="col-lg-10">
<input type="text" class="form-control" id="{{fieldId}}" ng-model="model" **myValidation="validationOptions"** placeholder="{{label|translate}}">
</div>
</div>
Or am I simply missing something ?
I would like to avoid using transclusion to resolve this issue, because this would oblige the form template to look like this :
<field label="myLabel">
<input type="text" class="form-control" id="{{fieldId}}" ng-model="model" placeholder= {{label|translate}}">
</field>
which is just uselessly verbose in my opinion. But i'm starting to ask myself if there really is another option ?
Maybe the trick can be done in the pre (or post ?) directve link function, where I would copy attributes/ directive from the text-field tag to it's child (input) tag ?
Could someone just light the way for me there ?
Could you try this:
Write a validate directive. This will have a controller that exposes an addValidationFunction(fn) and a getValidationFunction() methods.
Have the myValidation directive require the validate controller and call ctrl.addValidationFunction(myValidationImplementation) where myValidationImplementation is a function implementing the validation logic for this specific directive.
Write another directive, validateInner. This will require optionally the validate controller from its parent. This directive will also require the ngModel controller. If it finds the validate controller, it calls ctrl.getValidationFunction() and registers the function with the ngModel i.e.:
require: ["^?validate", "ngModel"],
link: function(scope,el,attrs,ctrls) {
if( ctrls[0] != null ) {
var validationFn = ctrls[0].getValidationFunction();
// register validationFn with ngModel = ctrls[1]
}
...
}
In the template of your textField:
<input validate-inner type="text" class="form-control" id="{{fieldId}}" ng-model="model" placeholder="{{label|translate}}">
Usage:
<text-field label="app.myLabel" model="myObj.myAttribute"
validate my-validation="validationOptions"></text-field>
NOTE 1: I am not sure if a replace:true directive wipes the other directives. If so, it is not consistent behaviour.
NOTE 2: The myValidation directive gets called as <xxx my-validation> (note camelCase → dash-case). If your code above is not a typo, then this is why <text-field> seems to wipe myValidation.