How to check if all form groups on separate components are valid before submitting. Angular 7, Reactive Forms - save

We are building an application that is essentially one giant form. There are several, child, parent and sibling components with their own form groups. Before the user is able to submit the application, all form groups in each component must be valid.
How can I check to see if each form group in each component is valid before the user can submit their application. If any form group contains an invalid field, the user must not be able to submit their application.
I will show an example of how we have set up form groups for sibling components and a "submit page" component that should check each component with form groups.
Component one TS file:
export enum DemographicsSection {
SECTION_ONE,
SECTION_TWO,
}
demographicsSectionEnum = DemographicsSection;
selectedSectionGroup = {
sectionOne: false,
sectionTwo: true,
};
// From Group.
public demographicsSectionOne: FormGroup;
public demographicsSectionTwo: FormGroup;
ngOnInit() {
this.initForm();
}
initForm() {
// Section 1
this.demographicsSectionOne = this.formBuilder.group({
parentsCurrentMaritalStatus: ['', [Validators.required]],
parentsNotSingleDate: ['', [Validators.required,
CustomValidators.pastMonthYearFormat]]
});
// Section 2
this.demographicsSectionTwo = this.formBuilder.group({
parentOneSsn: ['', [Validators.required, CustomValidators.numeric]],
parentOneLastName: ['', [Validators.required,
CustomValidators.onlyAlphabet]],
});
}
get sectionOne() { return this.demographicsSectionOne.controls; }
get sectionTwo() { return this.demographicsSectionTwo.controls; }
Here is a snippet of the HTML for that component:
<div>
<!-- parent-demographics-setiion-1 -->
<div [hidden]="selectedSectionGroup.sectionOne" id="
{{demographicsSectionEnum.SECTION_ONE}}">
<form [formGroup]="demographicsSectionOne">
<label for="parent-demographics-section-1" class="col-lg-3 sr-
only">Parent Status</label>
<select required formControlName="parentsCurrentMaritalStatus"
id="parentsCurrentMaritalStatus"
class="form-control" data-hint="yes"
(change)="parentMaritalStatusChange(demographicsSectionFive
,$event.target.value)">
<option selected="selected" value="">-- SELECT --</option>
<option value="1">Married / Remarried</option>
<option value="2">Never married</option>
</select>
</div>
</form>
</div>
<div [hidden]="selectedSectionGroup.sectionTwo" id="
{{demographicsSectionEnum.SECTION_TWO}}">
<form [formGroup]="demographicsSectionTwo">
<div [hidden]="sectionOne.parentsCurrentMaritalStatus.value === ''">
<div class="form-group">
<div class="col-lg-4">
<label for="parent-demographics-section-2-1" class="col-lg-3 sr-
only">Parent 1 ITIN</label>
<input formControlName="parentOneSsn" minlength=11 maxlength=11
id="parentOneSsn" type="text" class="form-control">
<div *ngIf="sectionTwo.parentOneSsn.touched &&
sectionTwo.parentOneSsn.invalid"
class="alert text-danger m-0 p-0 col-md-12">
Enter parent1 SSN or ITIN
</div>
</div>
</div>
</div>
</form>
</div>
Each component is set up the same way. We havent begun to work on the submit application, it essentially blank at this time while we try to figure out a plan.
What we expect to happen is, if there is an invalid field on any of the other components form groups, the user should not be able to submit the application. If all fields are valid on all form groups on all components, then, the user should be able to submit the application.

Use custom validators
this is Instruction :
Angular Custom validators

Related

Angular 2 Reactive Forms - Binding select drop down value in form array of a reactive form

I have created a reactive form which has a form array. A Form Group is pushed into the form array on a button click. So every button click generates a section of html on screen.
This form group contains 2 drop down lists having parent-child relation i.e. selection of item in parent drop down decides values to be populated in child drop down.
When I select a value in parent drop down change event fetches results for child but this has to be limited to the scope of the specific form array element. Currently change event fetches data and sets values for child drop downs in each form array element. This is a basic structure of my mark up
<form [formGroup] = "mainForm" (ngSubmit)="savedata()">
<div>some form control</div>
<div formArrayName = "myArray" *ngFor="let arrayitem of myArray.controls";let i = index">
<div [formGroupName] = "i">
<div>
<select class="form-control" (change)="onSelect($event.target.value)" id="{{'header' + i}}" formControlName="parentdrpdown">
<option value="" disabled selected hidden>Select a Parent...</option>
<option *ngFor="let pitem of parentdrpdown"
value={{pitem.id}}>
{{pitem.name}}
</option>
</select>
</div>
<div>
<select class="form-control" (change)="onSelect($event.target.value)" id="{{'header' + i}}" formControlName="childdrpdown">
<option value="" disabled selected hidden>Select a Child...</option>
<option *ngFor="let citem of childdrpdown"
value={{citem.id}}>
{{citem.name}}
</option>
</select>
</div>
</div>
</div>
constructor(private fb:
FormBuilder, private _segService: SegmentService) {
this.parentdrpdown= this._segService.getparentValues();
onSelect(pid) {
this.childdrpdown= this._segService.getChildSegment()
.filter((item) => item.parentid == pid);
}
parentdrpdown and childdrpdown are properties declared in component and they are loaded with values using http get request to a web server
Use below snippet for the code in reactive form in case of select tag.
<h1>My Application</h1>
<select formControlName="selectedValue">
<option *ngFor="let c of countries" [ngValue]="c">{{c.name}}</option>
</select>

onchange not binding to editorfor mvc

I am having an issue with binding an onChange event to an EditorFor thats being dynamically loaded a dropdown list of countries in a partial view. I have spent a few hours looking for a solution as to what i am doing wrong and why the prop/attribute onChange event is not binding to the div element when the page loads.
The EditorFor block:
<div class="row">
<div class="col-md-2 labelText">#Html.GetResourceString("BuyNow.Step5", "CountryLabel")</div>`enter code here`
<div class="col-md-10">
#Html.EditorFor(m => m.Country, "DropDownSelect", new { DisplayLabel = "empty", List = BaseController.CountryList, CssClass = "tealInput", onChange = "OnChange()" })
</div>
</div>
Now when i view source on the page once its loaded to check if the onChange is present in the div it is not.
Viewed Source after page load:
<select class="tealInput" data-bind="value: DefaultBilling.Country" data-val="true" data-val-length="The Country value cannot exceed 50 characters. " data-val-length-max="50" id="DefaultBilling_Country" name="DefaultBilling.Country"><option value="">Select</option>
As you can see the onchange isnt present and so far i have not been able to debug the issue. Any information on this and possible resources to go study to find out why this is happening would be greatly appreciated.
I have changed the way I am displaying the dropdown:
<div class="row">
<div class="col-md-2 labelText" style="white-space:nowrap;">#Html.GetResourceString("BuyNow.Step5", "CountryLabel")</div>
<div class="col-md-10">
<select class="tealInput" name="country" data-bind='value: DefaultBilling.Country' id="ddlCountry_#Model.AddressTypeId" style="text-align:right;" onchange="OnChange()">
#foreach (var countries in BaseController.CountryList)
{
<option value="#countries.Value">#countries.Text</option>
}
</select>
</div>
</div>
Same issue here as well, on my watch its showing OnChange() is unavailable

ngMessages and Nested Scopes - Datepicker in an ngForm on a Tab

OP Update
I've figured out the problem and this issue is now closed. Would one day like to find the time to post the solution as an answer here.
I'm having a bit of an issue working with ngMessages inside an ng-form on an Angular Bootstrap tab. Further complicating things, the thing I am validating is a Angular Bootstrap datepicker. So the code is as follows, and as you can see, I've got a datepicker on an ng-form which is on a Tab.
<uib-tab index="5" heading="{{tabs[5].title}}" select="changeCheck($event)">
<ng-form name="{{tabs[5].form}}">
<div class="col-md-9">
...
<div class="col-md-6">
<h4>Add Followup</h4>
<div class="form-group">
<label for="addFollowupDate" class="control-label">Date:</label>
<div class="input-group">
<input type="text" id="addFollowupDate"
class="form-control"
uib-datepicker-popup="{{format}}"
ng-required="true"
ng-model="newFollowup.date"
is-open="addFollowupPopup.opened"
datepicker-options="dateOptions"
close-text="Close"
alt-input-formats="altInputFormats" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="openAddFollowupPopup()"><i class="fa fa-calendar"></i></button>
</span>
<div class="help-block" ng-messages="followupsForm.newFollowup.date.$error" ng-if="followupsForm.newFollowup.date.$invalid && followupsForm.newFollowup.date.$touched">
<div ng-message="required">This field is required</div>
</div>
</div>
</div>
I've assigned that form to the scope using the following line:
$scope.followupsForm = $('ng-form[name="followupsForm"]').data('$formController');
And I have successfully set the control to touched on a submit which does make the border go red:
if ($scope.followupsForm.$invalid) {
angular.forEach($scope.followupsForm.$error, function (formErrorField) {
angular.forEach(formErrorField, function (errorField) {
errorField.$setTouched(); // by setting to touched, the relevant message will display if the field is invalid.
});
});
}
But the ng-message for required does not display.
Any ideas why? Could be a tough one with nested scopes etc. (of the tab and the picker).

Is there a way to place a prepended a select menu beside a standard text input?

I am getting started with JQueryMobile and love it. I am wondering if there is a way to place a prepended a select menu beside a standard text input.
For example, if the text input were to be for a name, then the prepended select would contain Mr, Mrs, etc.
Edit 1:
I've looked at the control group docs and for a case like buttons it works e.g.
<div data-role="controlgroup" data-type="horizontal">
Icon only
Icon only
</div>
However if I use an input like
<div data-role="controlgroup" data-type="horizontal">
Icon only
<input type = "text" name = "some_input" id = "some_input">
</div>
It gives a weird result with the input overlapping the button.
Edit 2:
I've looked at grids but cannot see a way to have a div span 2 grids. e.g.
<div class="ui-grid-b">
<div class="ui-block-a">Block A</div>
<div class="ui-block-b ui-block-c">Block C</div>
</div>
I've tried the above, but it doesn't work as desired, for example
<div class="ui-grid-c">
<div class="ui-block-a">
<label for = "full_name">
Name
<select id = "honorific" data-mini="true" data-inline="true">
<option value = "mr">Mr</option>
<option value = "mrs">Mrs</option>
<option value = "miss">Miss</option>
</select>
</label>
</div>
<div class="ui-block-b ui-block-c ui-block-d">
<input type="tel" data-clear-btn="true" id = "full_name" name = "full_name">
</div>
</div>
simply yeilds the select box below the full name label, and the input only spans what appears to be one cell/block - rather than the 3 cells/blocks of the grid.
From what I've seen control group works best with buttons, check boxes, etc. Not with the combination you're expecting to do. So here are two solutions for you.
Without using grids
You'll just have to make the two elements as an inline-block, like this :
.ui-select, .ui-input-text {
display:inline-block
}
Demo with No grids, No control group.
Using grids
The way you're using grids is wrong. You'll have to use two-column grid, which will have these two two classes alone: ui-block-a & ui-block-b, like this :
<div class="ui-grid-b">
<div class="ui-block-a">Block A</div>
<div class="ui-block-b">Block B</div>
</div>
This will scale your markup accordingly. Here's how the HTML looks like :
<div class="ui-grid-a">
<div class="ui-block-a" style="width:100px">
<select name="select-choice-min" id="select-choice-min" data-mini="true">
<option>Mr.</option>
<option>Mrs.</option>
<option>Ms.</option>
</select>
</div>
<div class="ui-block-b">
<input type="text" name="name" id="name" value="" placeholder="Enter Name" />
</div>
</div>
Demo with grids

Why am I getting this Javascript HIERARCHY_REQUEST_ERR with jQuery Mobile and Razor?

Dazed and confused here... The field in question is a boolean, and I want the UI to be a checkbox (or a yes/no or on/off jQuery slider). Each time I try to add in this checkbox input, I end up getting a
Microsoft JScript runtime error: DOM Exception: HIERARCHY_REQUEST_ERR (3)
error.
Here's the HTML+Razor
<div data-role="fieldcontain">
<fieldset data-role="controlgroup">
<legend>End Game:</legend>
#Html.TextBoxFor(model => model.HandicapSession.WinByTwo, new { #type="checkbox" })
<label for="WinByTwo">Win By Two?</label>
</fieldset>
</div>
Here's the generated HTML:
<div data-role="fieldcontain">
<fieldset data-role="controlgroup">
<legend>End Game:</legend>
<input data-val="true" data-val-required="The WinByTwo field is required." id="HandicapSession_WinByTwo" name="HandicapSession.WinByTwo" type="checkbox" value="False" />
<label for="WinByTwo">Win By Two?</label>
</fieldset>
</div>
Apparently this error occurs when there are conflicting or overlapping id's as jQuery Mobile creates its checkbox widget
$.widget( "mobile.checkboxradio", $.mobile.widget, { ...etc...
But how do I use the HTML and/or razor to make a simple checkbox work in jQuery Mobile?
If the problem with jQuery Mobile really is duplicate names for the HTML tags, then you'll have to render your own input type=checkbox tag in HTML, as the ASP.NET MVC Html.CheckBoxFor helper method will render an input type=hidden tag with a duplicate name value. See this post for a discussion.
The hidden form tag is there because if you submit an unchecked checkbox to the server, the form value for that field isn't included. So a hidden input tag is included with value=false so that if the checkbox is unchecked, the value false is still submitted. The model binding process in MVC will filter out the duplicate form values for you, but if you're having a client-side problem with the duplicate name attributes, you'll have to render your own checkbox and label in HTML, then handle the fact that no value will be submitted for the HandicapSession.WinByTwo property when the box is unchecked. If no other property for HandicapSession is submitted, then that whole object will be null.
So, you can manually create the checkbox input and still load the checked and value attributes from your model, as desired, but you can run into model binding problems where the value for WinByTwo will still be false even when the box is checked.
Note also that the for attribute on your label doesn't match the ID of the checkbox in your sample. You need the full HandicapSession_WinByTwo.
The following manually creates the input tags:
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.css" />
<script type="text/javascript" src="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.js"></script>
<div data-role="page">
<div data-role="content">
<div data-role="fieldcontain">
<fieldset data-role="controlgroup">
<legend>End Game:</legend>
<input type="checkbox" id="HandicapSession_WinByTwo" name="HandicapSession.WinByTwo" #(Model.HandicapSession.WinByTwo ? "checked=checked" : "") value="#(Model.HandicapSession.WinByTwo.ToString().ToLower())" />
<label for="HandicapSession_WinByTwo">Win By Two?</label>
</fieldset>
</div>
</div>
</div>
The HTML output is as follows for a checked checkbox on load:
<div data-role="page">
<div data-role="content">
<div data-role="fieldcontain">
<fieldset data-role="controlgroup">
<legend>End Game:</legend>
<input type="checkbox" id="HandicapSession_WinByTwo" name="HandicapSession.WinByTwo" checked=checked value="true" />
<label for="HandicapSession_WinByTwo">Win By Two?</label>
</fieldset>
</div>
</div>
</div>
The best would be just to use the MVC helper methods, so I'm not sure if you'd tried the following. The default Html.CheckBoxFor and Html.LabelFor helper methods work with jQuery Mobile 1.0a4.1 or 1.1.0 just fine. The following works for me:
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.css" />
<script type="text/javascript" src="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.js"></script>
<div data-role="page">
<div data-role="content">
#using (Html.BeginForm())
{
<div data-role="fieldcontain">
<fieldset data-role="controlgroup">
<legend>End Game:</legend>
#Html.CheckBoxFor(m => m.HandicapSession.WinByTwo)
#Html.LabelFor(m => m.HandicapSession.WinByTwo, "Win By Two?")
<input type="submit" id="SubmitForm" value="submit" />
</fieldset>
</div>
}
</div>
</div>
This produces the HTML output:
<div data-role="page">
<div data-role="content">
<form action="/Mobile/Mobile" method="post">
<div data-role="fieldcontain">
<fieldset data-role="controlgroup">
<legend>End Game:</legend>
<input checked="checked" data-val="true" data-val-required="The WinByTwo field is required." id="HandicapSession_WinByTwo" name="HandicapSession.WinByTwo" type="checkbox" value="true" />
<input name="HandicapSession.WinByTwo" type="hidden" value="false" />
<label for="HandicapSession_WinByTwo">Win By Two?</label>
<input type="submit" id="SubmitForm" value="submit" />
</fieldset>
</div>
</form>
</div>
</div>
Fix might be pretty simple.
Choice 1: Go scorched earth and turn off Ajax based navigation. This will ensure that unique IDs stay unique. This will ensure that you never encounter this issue again. Only downside is that you lose the pretty little transitions from page to page (someone call the whambulance). You can do this by setting up a global configuration script...
script src="jquery.js"
script src="custom-scripting.js"
script src="jquery-mobile.js"
inside that you'll override the ajax settings to disable them...
$(document).bind("mobileinit", function(){
$.mobile.ajaxEnabled = false;
});
Choice 2: Call pages that are going to require this uniqueness with a link that has an attribute of rel='external' or target='_black' which will accomplish the same thing without disabling all ajax based navigation. If you are reaching the page as a results of a form post, you can use data-ajax='false' on the form tag to accomplish a clean load.

Resources