I am building a component (html, css, spec.ts, ts) in angular in which I always want endDate > startDate. I have followed this link https://material.angular.io/components/datepicker/overview in order make multiple datepickers.
This is the html which I have used for startDate and endDate.
startDate:
<div class="item item-1" fxFlex="50%" fxFlexOrder="1">
<mat-form-field>
<input matInput [matDatepicker]="picker1" placeholder="{{'PORTAL.STARTDATE' | translate}}" type="text" formControlName="startDate" [(ngModel)]="unavailability.startDate" [readonly]="!componentPermission.writePermission">
<mat-datepicker-toggle matSuffix [for]="picker1"></mat-datepicker-toggle>
<mat-datepicker #picker1></mat-datepicker>
</mat-form-field>
</div>
endDate:
<div class="item item-2" fxFlex="50%" fxFlexOrder="2">
<mat-form-field>
<input matInput [matDatepicker]="picker2" placeholder="{{'PORTAL.ENDDATE' | translate}}" type="text" formControlName="endDate" [(ngModel)]="unavailability.endDate" [readonly]="!componentPermission.writePermission">
<mat-datepicker-toggle matSuffix [for]="picker2"></mat-datepicker-toggle>
<mat-datepicker #picker2></mat-datepicker>
</mat-form-field>
</div>
The validateForm() code which I have used in the ts is:
validateForm() {
this.unavailabilityForm = this.formBuilder.group({
'startDate': [''],
'endDate': ['']
});
}
I am pretty sure I have to make some changes in the validateForm() code but I am not sure what changes I have to make.
Since you seem to be going with a mix of reactive form and template driven, I would choose a reactive form completely. Implementing Validation is also in my opinion easier and cleaner. This also then means, you can drop ngModel altogether, which I would then strongly suggest, since it can cause unexpected issues to have two bindings (form control and ngModel). EDIT 2/2019: ngModel together with reactive forms are now also luckily not allowed since v7. That is in my opinion good, since it was misused way too much!
So build your form and attach a custom validator. In case you have a large form, I would attach the custom validator to a nested group of your dates, as it sits in this example, the validator is firing whenever there is any change in the form, doesn't matter if it's the date fields, or some other field.
constructor(private formBuilder: FormBuilder) {
this.unavailabilityForm = this.formBuilder.group({
startDate: [''],
endDate: ['']
}, {validator: this.checkDates});
}
If you are getting your values for variable unavailability at a later stage, you can use patchValue or setValue to assign the values to your form controls:
this.unavailabilityForm.setValue({
startDate: this.unavailability.startDate;
endDate: this.unavailability.endDate;
})
The custom validator simply checks that the end date is a later date than the startdate and returns null if it is valid, or then a custom validation error:
checkDates(group: FormGroup) {
if(group.controls.endDate.value < group.controls.startDate.value) {
return { notValid:true }
}
return null;
}
and then you can display this custom error in template with:
<small *ngIf="unavailabilityForm.hasError('notValid')">Not valid</small>
Also remember to mark this formgroup in your form tag:
<form [formGroup]="unavailabilityForm">
Related
I was playing around with aurelia-validatejs and was able to validate simple fields on a page. On the same page, I tried initializing a object and bind validation to that object and it stops working:
user_data:IUserData = {
Login:"",
Id : null,
UserProfile: {
UserId: null
},
Permissions: null
};
constructor(){
this.reporter = ValidationEngine.getValidationReporter(this.user_data);
// if I just use this and not user_data object,
// it works and error message is displayed in UI
this.validator = new Validator(this.user_data)
// when I use this.user_data (user_data object), it does validate
// and I can see validation result on console, but it's not shown in UI
.ensure('Login').required().length({minimum: 3, maximum:10});
this.observer = this.reporter.subscribe(result => {
console.info(result);
});
}
Someone mentioned that validatejs looks for a label, and I do have it, it worked when I initialize on this object but as soon as I want to validate on this.user_data property, it just doesn't display in UI, but I can see it on console.
<form action="" submit.delegate="createUser()">
<div class="input-group ${user_data.Login | loginClass}">
<label></label>
<input placeholder="Login" type="text" class="form-control"
keyup.delegate="validate()" value.bind="user_data.Login & validate">
</div>
</form>
this doesn't work, but if I clear user_data like:
user_data:IUserData = {
Login:"",
Id : null,
UserProfile: {
UserId: null
},
Permissions: null
};
constructor(){
this.reporter = ValidationEngine.getValidationReporter(this);
//if I just use this and not user_data object,
//it works and error message is displayed in UI
this.validator = new Validator(this)
.ensure('Login').required().length({minimum: 3, maximum:10});
this.observer = this.reporter.subscribe(result => {
console.info(result);
});
}
and
<form action="" submit.delegate="createUser()">
<div class="input-group ${user_data.Login | loginClass}">
<label></label>
<input placeholder="Login" type="text" class="form-control"
keyup.delegate="validate()" value.bind="Login & validate">
</div>
</form>
This works and is displayed in UI. I searched a little bit on stackoverflow and got few results similar (My problem is similar. I have error messages on UI if I don't use it on a object but when I try to use it on an object, it logs on console but it doesn't display in UI)
I also found this but it wasn't of much help.
I found one more which wasn't of much help either.
I understand there are a lot of articles which might make this question marked duplicate, the reason I'm asking is aurelia is quite new technology and stuffs are changing quite frequently.
Thanks in advanced!
So I dug around a little bit and asked developers in Gitter. As it turns out, it's a bug in validatejs plugin.
Recent changes were made to make something available on all classes, and when that change was done, validatejs looks for validationError Reporter on same model even if we provide different model.
A small hacky workaround is to have
this.__validationReporter__ = ValidationEngine
.getValidationReporter(this.user_data);
Instead of:
this.reporter = ValidationEngine.getValidationReporter(this.user_data);
I have reported this issue and will update once they come up with something.
For now I'm gonna use
this.__validationReporter__ = ValidationEngine
.getValidationReporter(this.user_data);
Or one other workaround is to downgrade to 0.3.x which is not very recommended as there might have been some important changes.
Have you used form-group? Usually our validation problems come down to HTML structure not conforming to the library.
<form action="" submit.delegate="createUser()">
<div class="form-group ${user_data.Login | loginClass}">
<label></label>
<input placeholder="Login" type="text" class="form-control"
keyup.delegate="validate()" value.bind="Login & validate">
</div>
</form>
I have a simple form that needs to validate if the beginning and the end of the input is not space.
In HTML5, I will do this:
<input type="text" pattern="^(?!\s|.*\s$).*$">
What is the right property for validation pattern in the new Angular 2 ngControl directive? The official Beta API is still lacking documentation on this issue.
Now, you don't need to use FormBuilder and all this complicated valiation angular stuff. I put more details from this (Angular 2.0.8 - 3march2016):
https://github.com/angular/angular/commit/38cb526
Example from repo :
<input [ngControl]="fullName" pattern="[a-zA-Z ]*">
I test it and it works :) - here is my code:
<form (ngSubmit)="onSubmit(room)" #roomForm='ngForm' >
...
<input
id='room-capacity'
type="text"
class="form-control"
[(ngModel)]='room.capacity'
ngControl="capacity"
required
pattern="[0-9]+"
#capacity='ngForm'>
Alternative approach (UPDATE June 2017)
Validation is ONLY on server side. If something is wrong then server return error code e.g HTTP 400 and following json object in response body (as example):
this.err = {
"capacity" : "too_small"
"filed_name" : "error_name",
"field2_name" : "other_error_name",
...
}
In html template I use separate tag (div/span/small etc.)
<input [(ngModel)]='room.capacity' ...>
<small *ngIf="err.capacity" ...>{{ translate(err.capacity) }}</small>
If in 'capacity' is error then tag with msg translation will be visible. This approach have following advantages:
it is very simple
avoid backend validation code duplication on frontend (for regexp validation this can either prevent or complicate ReDoS attacks)
control on way the error is shown (e.g. <small> tag)
backend return error_name which is easy to translate to proper language in frontend
Of course sometimes I make exception if validation is needed on frontend side (e.g. retypePassword field on registration is never send to server).
Since version 2.0.0-beta.8 (2016-03-02), Angular now includes a Validators.pattern regex validator.
See the CHANGELOG
You could build your form using FormBuilder as it let you more flexible way to configure form.
export class MyComp {
form: ControlGroup;
constructor(#Inject()fb: FormBuilder) {
this.form = fb.group({
foo: ['', MyValidators.regex(/^(?!\s|.*\s$).*$/)]
});
}
Then in your template :
<input type="text" ngControl="foo" />
<div *ngIf="!form.foo.valid">Please correct foo entry !</div>
You can also customize ng-invalid CSS class.
As there is actually no validators for regex, you have to write your own. It is a simple function that takes a control in input, and return null if valid or a StringMap if invalid.
export class MyValidators {
static regex(pattern: string): Function {
return (control: Control): {[key: string]: any} => {
return control.value.match(pattern) ? null : {pattern: true};
};
}
}
Hope that it help you.
custom validation step by step
Html template
<form [ngFormModel]="demoForm">
<input
name="NotAllowSpecialCharacters"
type="text"
#demo="ngForm"
[ngFormControl] ="demoForm.controls['spec']"
>
<div class='error' *ngIf="demo.control.touched">
<div *ngIf="demo.control.hasError('required')"> field is required.</div>
<div *ngIf="demo.control.hasError('invalidChar')">Special Characters are not Allowed</div>
</div>
</form>
Component App.ts
import {Control, ControlGroup, FormBuilder, Validators, NgForm, NgClass} from 'angular2/common';
import {CustomValidator} from '../../yourServices/validatorService';
under class
define
demoForm: ControlGroup;
constructor( #Inject(FormBuilder) private Fb: FormBuilder ) {
this.demoForm = Fb.group({
spec: new Control('', Validators.compose([Validators.required, CustomValidator.specialCharValidator])),
})
}
under {../../yourServices/validatorService.ts}
export class CustomValidator {
static specialCharValidator(control: Control): { [key: string]: any } {
if (control.value) {
if (!control.value.match(/[-!$%^&*()_+|~=`{}\[\]:";##'<>?,.\/]/)) {
return null;
}
else {
return { 'invalidChar': true };
}
}
}
}
My solution with Angular 4.0.1: Just showing the UI for required CVC input - where the CVC must be exactly 3 digits:
<form #paymentCardForm="ngForm">
...
<md-input-container align="start">
<input #cvc2="ngModel" mdInput type="text" id="cvc2" name="cvc2" minlength="3" maxlength="3" placeholder="CVC" [(ngModel)]="paymentCard.cvc2" [disabled]="isBusy" pattern="\d{3}" required />
<md-hint *ngIf="cvc2.errors && (cvc2.touched || submitted)" class="validation-result">
<span [hidden]="!cvc2.errors.required && cvc2.dirty">
CVC is required.
</span>
<span [hidden]="!cvc2.errors.minlength && !cvc2.errors.maxlength && !cvc2.errors.pattern">
CVC must be 3 numbers.
</span>
</md-hint>
</md-input-container>
...
<button type="submit" md-raised-button color="primary" (click)="confirm($event, paymentCardForm.value)" [disabled]="isBusy || !paymentCardForm.valid">Confirm</button>
</form>
Without make validation patterns, You can easily trim begin and end spaces using these modules.Try this.
https://www.npmjs.com/package/ngx-trim-directive
https://www.npmjs.com/package/ng2-trim-directive
Thank you.
In a ASP.NET MVC5, I'm using the chosen JS library for a multi-dropdown select. How Can I do to use Data Annotation to validate the field?
Actually I use [Required] on all fields, this multi-dropdown select too, but it isn't working.
Code:
[MinLength(1)]
public int[] fields{ get; set; }
Here is my Code in the cshtml:
#Html.ListBoxFor(x => x.fields, Model.fieldsSelect, new { data_placeholder = "pholder" })
#Html.ValidationMessageFor(model => model.fields, "", new { #class = "text-danger" })
Without the plugin I currently use (chosen) there is no validation , Here is the HTML rendered without chosen:
<div class="col-md-10">
<select data-placeholder="Enter multiple fields" data-val="true" data-val-minlength="The field fieldsmust be a string or array type with a minimum length of '1'." data-val-minlength-min="1" id="fields" multiple="multiple" name="fields">
<option value="944454">WARUYFJGHIE</option>
<option value="33033095">WEBJKHGJHGVHGAN</option>
</select>
<span class="field-validation-valid text-danger" data-valmsg-for="fields" data-valmsg-replace="true"></span>
</div>
Validation works for all my string but not this one: when I select nothing on the form, all [Required] for strings works: an error message is apparing and submit is not hitting the controller/server, but not the [MinLength(1)]... No error message and when I fill all except [MinLength(1)], the form is submitting and error occurs in the controller/server because of null.
Versions of JS validations:
jQuery Validation Plugin - v1.11.1 - 3/22/2013
jquery.validate.unobtrusive.min.js : no version (neither in the
jquery.validate.unobtrusive..js)
You can use the MinLengthAttribute
[MinLength(1)]
public int[] fields{ get; set; }
Edit
Based on additional comments, a jquery plugin is being used that hides the <select>. By default hidden fields are not validated. To include hidden fields, add the following
$.validator.setDefaults({
ignore: []
});
When I create multiple view instances of the Marionette view which is linked with a template html with ids, these would get duplicated for multiple instances of these views.
While it works correctly, I feel that there ought to be more architecturally correct way of doing this.
The example code is like below.
Template:
<script id="myTemplate" type="text/template">
<div id="myDiv">
<input type="text" id="myText"/>
<input type="button" id="myBtn" value="Click me!"/>
</div>
</script>
View:
MyView = Backbone.Marionette.ItemView.extend({
template: '#myTemplate',
events: {
'click #myBtn' : 'myFunc' //Correctly identifies its own 'myBtn'
},
myFunc : function() {
alert($('myText').val()); //Again, picks own 'myText'
}
});
var v1= new MyView();
v1.render();
var v2= new MyView();
v2.render(); //Duplicate IDs now present in DOM
I need some unique identification of these DOM elements and hence the ids.
Even when tying the model to this view, we need some way to identify these DOM elements.
What is the correct way of doing this without duplicating the ids.
Just pass the id to the view when you create it:
Template:
<script id="myTemplate" type="text/template">
<input type="text" class="js-myText"/>
<input type="button" class="js-myBtn" value="Click me!"/>
</script>
View def:
MyView = Backbone.Marionette.ItemView.extend({
template: '#myTemplate',
events: {
'click #myBtn' : 'myFunc' //Correctly identifies its own 'myBtn'
},
myFunc : function() {
alert($('myText').val()); //Again, picks own 'myText'
}
});
Instanciation:
var v1= new MyView({ id: "view" + number});
v1.render();
Then you can provide dynamic id values for your views (e.g. by using a model id).
That said, when using Marionette you shouldn't need to call render: you should instead show a view within a region. Take a look at the free sample to my Marionette book to get you up to speed.
If you must go for unique IDs to make sure no one accidentally duplicates a class name inside a view, you can use:
Underscore's uniqueId method to generate a unique ID for each DOM element inside the view, like: <input type="text" id= <%= _.uniqueId("myText_") %> /> This will just make sure that IDs are not duplicated. But they're not very helpful if you need to identify the elements by these IDs.
Marionette's TemplateHelpers which allow you to use helper functions from inside the templates:
//Define this inside your view:
templateHelpers: function() {
var that = this;
return {
getIdSuffix : function() { return that.idSuffix; }
/*Where idSuffix is passed to the view during instantiation
and assigned to this.idSuffix */
};
}
//In the template:
<input type="text" id= <%= "myText_" + getIdSuffix() %> />
You now know before runtime what DOM IDs you will have, provided care is taken not to give the same idSuffix to more than one view instance.
Simply put, don't use an id if it's not unique. Use a class or some other way of identifying the element.
You can use any jQuery selector to locate the element you want, ranging from the insane and brittle:
this.$('div > input:first'); // don't actually do this!
to the slower but semantically better:
this.$('[data-element-type="some-text-box-descriptive-name"]');
Although in reality, using a class is best, because that's what a class is for - for identifying a type of element. I can see that a maintainer might not know not to change your class in the template, so a data-attribute might be acceptable, or maybe even (in this case):
this.$('input[type=text]');
I try to made nested form with validation. All works fine, but when I remove one of nested form, validation continue to use removed form. I made jsfiddle example http://jsfiddle.net/sokolov_stas/VAyXu/
When example runs, form are valid. If click "+" button, nested form will be added and valid will be false. Then click "-" button, and valid will be false all the same.
The question is: How to remove dynamic created form from validation processing.
Well, for one thing, a <form> inside of a <form> is not valid HTML.
Second, you're not supposed to be doing DOM manipulation from inside the controller. The controller is for "business" logic. See the section on controllers here
For what you're doing, you'd probably be better off using one form, with an ng-repeat inside of it, and adding additional elements to an array:
<form name="myForm" ng-controller="FormCtrl" ng-submit="doSomething()">
<div ng-repeat="item in items">
<input ng-model="item" type="text" required/>
</div>
<a ng-click="addItem()">+</a>
<a ng-click="removeItem()">-</a>
<button type="submit">Submit</button>
<div>Form valid: {{myForm.$valid}}</div>
</form>
and the controller:
function FormCtrl($scope) {
$scope.items = [];
$scope.addItem = function() {
$scope.items.push(null);
};
$scope.removeItem = function() {
$scope.items.pop();
};
$scope.doSomething = function () {
//your submission stuff goes here.
};
}