How to add cross-field validation in Angular 2 with a complex model - forms

Cross-Field Validation in Template Based Forms
Question: Is it possible to create some sort of validation context that spans multiple fields but does not modify the underlying model? If not, is there a better way to do what I'm doing?
Given a model like this and assuming that changing the model is not possible:
export interface IEvent {
location?: {
address: string;
city: string;
country: string;
};
onlineUrl?: string;
}
What is the best way to build a template-based form that will require either all of the location object (address, city, country) to be populated OR the onlineUrl to be populated?
Here is a working Plunk where I have the custom validation working but there are some challenges:
In order to match the model, we need an ngModelGroup around the location fields, but onlineUrl should not be inside that ngModelGroup since it is not part of the location object. Since onlineUrl is not inside the ngModelGroup, it's difficult to come up with a cohesive validation approach. Notice in this example, that we've added a custom validator on the ngModelGroup, that seems to break some basic validation premises - for example, notice that when the fields are invalid that the red highlighting only shows up next to the location field because the custom validator is on the ngModelGroup and onlineUrl is not inside that group.
Typing in the onlineUrl field does not automatically trigger the re-validation since this field is not in the ngModelGroup that has the custom validator on it. In order to make this work I needed to add this binding: (change)="locationGroup.control.controls.address.updateValueAndValidity()" to the onlineUrl field in order to trigger the validation on a field inside the ngModelGroup. This doesn't seem ideal.
Restating the Question: Is it possible to create some sort of validation context that spans multiple fields but does not modify the underlying model?

You can place 'validateLocation' directive up in the top 'form' tag:
<form #myForm="ngForm" (ngSubmit)="save(myForm)" autocomplete="off" novalidate validateLocation>
<fieldset ngModelGroup="location">
Then validate() method gets access to both location and onlineUrl via 'FormGroup.value' and will look like:
if (control && control.value && control.value.location) {
let g = control.value;
if (g.location.address && g.location.city && g.location.country)
return null;
if (g.onlineUrl)
return null;
}
return {validateLocation: false}
Try this plunker. Form.valid would become true when either location or url is captured.

Related

Input field validation action/filter hook for Gravity Forms

I have a Gravity Forms form configuration that contains an input field for the user to type in a value. For now it's just a single line text field. I need to perform validation on this field - preferably upon leaving the field by way of Ajax.
I'm writing a plugin that uses GFAPI and gform_after_submission to process submitted data.
Does Gravity Forms provide actions/hooks for validation by way of Ajax?
Basically, I'm looking for a way to validate an ID that is to be entered by the user on the frontend. If the ID matches some criteria (which includes a database lookup) then I return true or false in an Ajax response.
I see gform_field_validation, but this is intended for when the entire form is submitted, I think. I need to perform validation upon leaving/entering a field, etc. by Ajax.
How can this be accomplished with a Gravity Form?
As far as i know, gravity forms does not provide client side validation:
see this article: https://www.parorrey.com/blog/front-end-development/wordpress-gravity-forms-validation-on-client-side-using-jquery/
For your specific situation i would suggest the following:
in your javascript file:
(function($){
$.validator.addMethod("validateID", function(value, element) {
//do your db lookup here and run the necessary checks
return value === "stackoverflow";
}, "Please enter in a valid ID");
$("form").validate({
rules:{
customer_id: {
validateID: true
}
}
});
$("form").on('submit',function(){
return false;
});
})(jQuery)
JsFiddle Link:
https://jsfiddle.net/craigiswayne/xpvt214o/214343/

Angular2 model-based parent/children form

I'm a newbie with Angular2 (beta1) and I'd like to implement a sort of simple editable grid, built of 2 components. Here I use two fake-data components to keep things simple. They are (see this Plunker: http://plnkr.co/edit/5cZfLTIlhLc82wWV4PQI):
the parent component, named contact. Say it represents a contact with a name.
the child component, named entry. Say it represents an entry for a contact, where each contact can include 0 or more entries. Each entry has an address and a zip code.
I'd like to create a form where the user can edit the contact's properties, and also its children entries: he could add a new entry, delete an existing entry, or edit an existing entry.
To this end, the views for both these components provide a form-based template.
I can think of this data flow:
contact: the user edits the form and then clicks a submit button to save
the whole thing. Thus, I can just have some code handling the submit button
and emitting an event as the component output. The contact has an entries
array property: I can thus use an ngFor directive in its template to render
an entry component for each of them.
entry: the entry has properties addressCtl and zipCtl which represent
the control directives included in the ControlGroup representing the whole
form. Also, I need a couple of properties to be bound as the input of the
component (address and zip), so that in the parent template I can do something like:
<tr *ngFor="#e of entries">
<td><my-entry [address]="e.address" [zip]="e.zip"></my-entry></td>
</tr>
Now, it's not clear to me how to shape the relation between the "model" properties representing the control's input, and the "form" directives properties. I should be able to get the address and zip values from the parent component through the [...] binding, and pass the updated values up through an event fired by the child component (e.g. blur?). Does this make sense in the NG2 world? Anyway, I'm missing a piece here: how can I connect the form controls values to the model properties values? Could anyone make this clearer or point to some good docs?
In fact, using the [...] binding only corresponds to a one-way binding. When the parent property is updated in the parent component, the value is also updated in the child component.
But if you want to update parent attributes from the child, you need to leverage events and #Ouput attribute.
Here is a sample with a labels component:
export class LabelsComponent implements OnInit {
#Input()
labels:string[];
#Output()
labelsChange: EventEmitter;
(...)
removeLabel(label:string) {
var index = this.labels.indexOf(label, 0);
if (index != undefined) {
this.labels.splice(index, 1);
this.labelsChange.emit(this.labels);
}
}
addLabel(label:string) {
this.labels.push(this.labelToAdd);
this.labelsChange.emit(this.labels);
this.labelToAdd = '';
this.addAreaDisplayed = false;
}
}
This way you can leverage two way binding on this component:
<labels [(labels)]="company.labels"></labels>
Hope it answers your question,
Thierry
Just moved the comment to answer...
You can pass the object e, instead of passing string.
i.e
<my-entry [entry] = "e"></my-entry>
then in your my-entry component, use ng-model for each input. so you automatically gets 2 way bindings.

Can a lift form accept attributes for multiple models?

In Rails I can use accepts_nested_attributes_for to allow a single form to create two different but related objects. Now, I'm working on a Scala Lift project and I want to do something similar. I have a User model and an Address model. I want to have a single form that creates the User and their Address. How does this work in Lift?
In general, Lift approaches form processing by binding a handler function to each input which is called on form submission. In each of those functions, you would define the logic you need to set the appropriate fields in your model.
Using something like the example below you can instantiate your classes and then perform the appropriate action on submission. You will see this creates a User and an Address class and then sets a field on each of them. The function bound to the submit button will take care of persisting them both. Since the actions happen in a function, you can include as much logic as necessary to make your application work (data transformation, setting multiple fields, etc...). For example, in the submit logic I associate the id of the Address with the User to define how they are related.
In your Snippet
val user = new User()
val address = new Address()
".nameField" #> SHtml.input(user.name, (txt) => {
user.name = txt
}) &
".addressField" #> SHtml.input(address.address1, (txt) => {
address.address1 = txt
}) &
".submit" #> SHtml.submit("Save", () => {
//persist address
user.addressId = address.id
//persist user
})
In your HTML
<form data-lift="form">
<input class="nameField"></input>
<input type="submit" class="submit"></input>
</form>
In general, that is how you would accomplish what you are looking to do. In addition to handling everything yourself, Lift includes Mapper which is pretty much a database ORM. I believe that can automate a lot of the relation mapping and make the creation of some forms easier. I don't really use it myself, so I can't give you a more concrete example. But, if you decide to check that out, you can find more information on Mapper here and here.

How to remove a validator from a Select?

I have a form where I need to add/remove validators dynamically. Based on a dropdown selection, other form fields may have different validation rules.
For other kinds of inputs, I've used replace(methodThatCreatesTheInput()) to get rid of a previously added validator. (Not knowing of a better way. Specifically, there doesn't seem to be any way to directly remove a validator from a component...)
With Select, from wicket-extensions, this approach fails with something like:
WicketMessage: submitted http post value [[Ljava.lang.String;#5b4bf56d]
for SelectOption component [8:myForm:targetInput] contains an
illegal relative path element [targetConsortiums:1:option] which does not
point to an SelectOption component. Due to this the Select component cannot
resolve the selected SelectOption component pointed to by the illegal value.
A possible reason is that component hierarchy changed between rendering and
form submission.
The method that creates the Select:
private FormComponent<?> targetSelection() {
Map<Class<? extends Target>, List<Target>> targets = targetService.getAllAsMap();
SelectOptions<Target> propertyOptions = new SelectOptions<Target>("targetConsortiums",
targets.get(Consortium.class), new TargetRenderer());
SelectOptions<Target> consortiumOptions = new SelectOptions<Target>("targetProperties",
targets.get(Property.class), new TargetRenderer());
Select select = new Select(ID_TARGET, new PropertyModel<Target>(model, "target"));
select.add(propertyOptions);
select.add(consortiumOptions);
select.setRequired(true);
select.setMarkupId(ID_TARGET);
return select;
}
(Why use a Select instead of normal DropDownChoice? We want the two types of choices to be clearly separated, as documented in this question.)
Any ideas how to solve this? What I'm trying to achieve is, of course, very simple. Unfortunately Wicket disagrees, or I'm using it wrong.
Wicket 1.4.
I don't know how to do this on Wicket 1.4, but on Wicket 1.5 there is a remove method for validators on FormComponent (see javadoc)

JQuery validation based on class?

I have a form that will have dynamically created elements, one of those will be of date type. There can also be many fields of this type on my form, all which must be validated.
I am using a strongly typed view in Asp MVC, so the name will change based on various factors. What I would like to do is validate based on a class name instead, since that will be constant for that type of field.
Ie:
<%= Html.TextBox("questionAnswers[" + index + "].AnswerValue", qa.AnswerValue, new { #class = "DateTypeClass" })%>
So then I would need JQuery validation based on the classname DateTypeClass versus the Name.
Any ideas?
In your validation function you could check the value like so:
var dateFieldValue = $(".DateTypeClass").val();
//validate dateFieldValue here
Also you don't have to resort to selecting by class if you don't want to. If no other fields share this validation then you could select by partial id in jquery. That would look like this:
var dateFieldValue = $("[id^=someId]").val();
//validate dateFieldValue here
So you would if set someId on the field and ASP.Net adds some additional stuff to the id it will still select it properly. I don't use ASP.Net MVC so not sure where you are setting the ID (if at all)
Also if you want inline validation you can wireup the onBlur event like so:
$(".DateTypeClass").blur(function(){
//Call field validation function here
});