Angular 2 nested form group contains null values - angularjs

I'm working with a nested form group structure with Angular 2 beta "FormBuilder" and for the life of me I can't get values into the nested form group. It's weird because binding works just fine. I can send a "user" object to the form and it fills in all the values for the user and their address. But when sending the form value out (say, to an HttpPost), it looks just like the object below...
FWIW I'm following Mosh Hamedani's excellent Angluar 2 course on Udemy and have checked and double checked that the relevant code matches his here: https://github.com/mosh-hamedani/angular2-course
Perhaps what I need is to be told where else to look in my code?
Here's my form group as shown by placing {{ form.value | json }} at the end of my form:
{
"name": "Leanne Graham",
"email": "Sincere#april.biz",
"phone": "1-770-736-8031 x56442",
"address": {
"street": null,
"suite": null,
"city": null,
"zipcode": null
}
}
this is the constructor in my component:
constructor(
fb: FormBuilder,
private router: Router,
private usersService: UsersService,
private params: RouteParams
){
this.form = fb.group({
name: ['', Validators.required],
email: ['', EmailValidators.mustBeEmail], // I hadn't considered the obvious: if it doesn't exist it also doesn't qualify as email.
phone: [],
address: fb.group({
street: [],
suite: [],
city: [],
zipcode: []
})
});
}
This is the form in my template (truncated for brevity):
<div class="col-md-6 well">
<!--the "fieldset" element is needed for this kind of grouped layout.-->
<form [ngFormModel]="form" (ngSubmit)="save()">
<fieldset>
<legend>User</legend>
<div class="form-group">
<label>Name</label>
<input type="text" [(ngModel)]="user.name" ngControl="name" #name="ngForm" class="form-control">
<div class="alert alert-danger" *ngIf="name.errors && name.touched">
The user name is required.
</div>
</div>
*** Email and Phone similar ***
</fieldset>
<fieldset ngControlGroup="address">
<legend>Address</legend>
<div class="form-group">
<label>Street</label>
<input type="text" [(ngModel)]="user.address.street" ng-control="street" class="form-control">
</div>
*** suite, city, and zip similar ***
</fieldset>
{{ form.value | json }}
<button [disabled]="!form.valid" class="btn btn-primary" type="submit">Save</button>
</form>
</div>

ng-control="street"
should be
ngControl="street"
like you did with name

Related

form into ngfor and getting data in array

Problem- Here I'm using a single HTML Form. but in many cases, I'll get multiple counts of rooms and multiple counts of adults according to rooms.so I'm using *ngFor for multiple times of forms according to roomwise adults.
So in case, I have 2 rooms, and in particular rooms, I have 2 adults. so this form will repeat 4 times according to adults.
I have to save the all data in an array. But in FormGroup I'm getting only one form data only even I fill all forms.
this form repeats according to roomdetails.adult
HTML File
<div *ngfor="let item of roomdetails; let i = index" [attr.data-index]="i">
<form [formgroup]="ravelerDetails">
<div class="row">
<mat-card>
<h2 class="titleHeader">ROOM {{i+1}}</h2>
<ng-container formgroupname="customer_detail" *ngfor="let items of item.adult;>
<mat-form-field appearance="outline" color="primary" class="col-md-2">
<mat-label>Salutation</mat-label>
<mat-select [formcontrol]="salutation">
<mat-option value="Mr">Mr.</mat-option>
<mat-option value="Mrs">Mrs.</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline" color="primary" class="col-md-5">
<mat-label>First Name</mat-label>
<input matinput placeholder="First Name" [formcontrol]="first_name" maxlength="25">
<mat-error *ngif="first_name.hasError('required')">
<strong>First Name</strong>is required.
</mat-error>
</mat-form-field>
<mat-form-field appearance="outline" color="primary" class="col-md-5">
<mat-label>Last Name</mat-label>
<input matinput placeholder="Last Name" [formcontrol]="last_name" maxlength="25">
<mat-error *ngif="last_name.hasError('required')">
<strong>Last Name</strong>is required.
</mat-error>
</mat-form-field>
</ng-container>
</mat-card>
</div>
</form>
</div>
TS File
initDos() {
return this.fb.group({
salutation: this.salutation,
first_name: this.first_name,
last_name: this.last_name,
})
}
travellDetiles() {
this.ravelerDetails = this.fb.group({
customer_detail: this.fb.array([this.initDos()]),
checkin_date: this.hotelSearch.checkin_date,
checkout_date: this.hotelSearch.checkout_date,
no_adult: this.hotelSearch.no_adult,
no_children: this.hotelSearch.no_children,
no_room: this.hotelSearch.no_room,
});
}
get dosArray() {
return this.ravelerDetails.get('customer_detail') as FormArray;
}
dos() {
this.dosArray.push(this.initDos());
}
removeDos(index) {
this.dosArray.removeAt(index);
}
Output
{
"no_room": "1",
"checkin_date": "2022-11-5",
"checkout_date": "2022-11-6",
"no_adult": "2",
"customer_detail": [{
"room_number": "1",
"salutation": "Mr",
"first_name": "test",
"last_name": "test",
}, {
"room_number": "1",
"salutation": "Mr",
"first_name": "test2",
"last_name": "test2",
}],
}

create objects on form submit in typescript angular 7

I am trying to create 2 separate objects on form submission. Below is my code:
test.html
<form [formGroup]="gamificationForm" (ngSubmit)="onSubmit()">
<div *ngFor="let medal of medalResponse; let iMedal=index" class="row col-lg-12 mtop-custom-10 medal-level-point-config">
<input type="hidden" class="point-text" [(ngModel)]="medal.medalId" formControlName="medalId" />
<div class="col-lg-4 col-6">
{{medal.medalName}}
<input type="hidden" class="point-text" [(ngModel)]="medal.medalName" formControlName="medalName" />
</div>
<div class="col-lg-4 col-6">
<input type="number" class="point-text" [(ngModel)]="medal.points" formControlName="points" />
</div>
</div>
<button class="custom-btn-class" name="submit" type="submit">Save</button>
</form>
Typescript:
gamificationForm = this.fb.group({
medals: this.fb.group({
medalId: [''],
medalName: [''],
points: ['']
})
});
onSubmit() {
console.warn(this.gamificationForm.value);
}
JSON:
It is printing last values from the loop on html page and in json too.
You can convert this JSON value to your required format,
format(value) {
return [
{
Level: [
{
"levelId-0":value.levelId-0,
"level-0":value.level-0,
"points-0":value.points-0
},
....
],
Medal: [
.....
]
}
]
}
Unfortunately I haven't done this via Template-driven Forms, but here my ideas:
You will need to group your fields into the Level and Medal arrays by wrapping your *ngFor into a <ng-container formArrayName="Level"> container and try it.
Each *ngFor should set the position FormArray, but you will need to experiment and see if the formArrayName directive does it for you.
Happy ng-coding!

Angular 5 Reactive Forms - pass checkbox Id's for FormArray

I am using Reactive Forms FormGroup, FormBuilder and FormArray. I have a FormArray in which I would like to loop through an external array, create a list of checkboxes and then pass the id of those checkboxes to my FormArray.
My components typescript looks like:
export class AppComponent {
form: FormGroup;
sites = [ "site-1", "site-2", "site-3", "site-4" ];
constructor(private fb: FormBuilder){
}
ngOnInit() {
this.form = this.fb.group({
type: [ '', Validators.required ],
sites: this.fb.array([
this.fb.group({
siteId: [ '', Validators.required ]
})
])
})
}
}
And my html...
<form [formGroup]="form">
<input type="checkbox" formControlName="type" id="type">
<label for="type">Type</label>
<div class="checkbox-group" style="margin-top:40px">
<div class="checkbox-wrap" id="productsContainer">
<input type="checkbox" id="all">
<label for="all">All mills</label>
<div *ngFor="let site of sites; let i = index">
<input type="checkbox" [id]="i" />
<label [for]="i"> {{ site }} </label>
</div>
</div>
</div>
</form>
I know I need to pass the formArrayName into the html but I am getting console errors when trying to pass in the formArrayName="sites" and then using the "i" from my loop as the formGroupName. What am I doing wrong?
Here's a StackBlitz with the full setup...
https://stackblitz.com/edit/angular-rcfnwi?file=app%2Fapp.component.html
You need to populate the FormArray with the individual FormControls that correspond to the items in your sites list. In my fork example, I did this in the initialization of the FormGroup (https://stackblitz.com/edit/angular-hilz9h) by mapping over the sites array and generating FormControls with a default value of false (unchecked). I also removed the embedded FormGroup that was surrounding the sites FormArray to make it simpler. I then added the formArrayName directive to the wrapping div with the string attribute corresponding to the 'sites' name of the FormArray from the ts file. Finally, I added a [formControlName] directive and passed it the ngFor index of each input to correspond to the array index of the FormArray controls. FormArrays internally keep track of their controls by index.
So, your ngOnInit now looks like:
ngOnInit() {
this.form = this.fb.group({
type: [ '', Validators.required ],
sites: this.fb.array(this.sites.map(s => this.fb.control(false)),
[ Validators.required ])
})
}
and your html file now looks like:
<form [formGroup]="form">
<input type="checkbox" formControlName="type" id="type">
<label for="type">Type</label>
<div class="checkbox-group" style="margin-top:40px">
<div class="checkbox-wrap" id="productsContainer" formArrayName="sites">
<input type="checkbox" id="all">
<label for="all">All mills</label>
<div *ngFor="let site of sites; let i = index">
<input type="checkbox" [id]="i" [formControlName]="i"/>
<label [for]="i"> {{ site }} </label>
</div>
</div>
</div>
</form>
<pre> {{ form.value | json }}</pre>
Really the answer work, but, in my opinion is not clearly a Form Array.
When we have a serie of checks, we must to understand that there are two separate concept: the form and the data. Yes this two concepts are not necesary equal.
I choose create the form like
//we create a FormGroup array transform the array "this.sites"
const controls:FormGroup[]=this.sites.map(x=>{
return this.fb.group({
site:false
})
});
//in this.form I use this array of FormGroup
this.form = this.fb.group({
type: ['', Validators.required],
sites: this.fb.array(controls)
})
Well, when we make the form, the form.value can be like
{
"type": "",
"sites": [
{ "site": false }, { "site": true }, { "site": false }, { "site": true }
]
}
Then, in submit we must transform the form.value to a data we like
submit(form: FormGroup) {
if (form.valid) {
//create an array of sites
let sites: string[] = [];
let i: number = 0;
form.value.sites.forEach((x:any) => {
if (x.site)
sites.push(this.sites[i]);
i++;
});
//Our data will be
let data = {
type: form.value.type,
sites: sites
}
console.log(data);
}
}
Finally, the form:
<form [formGroup]="form" (submit)="submit(form)" novalidate">
<input formControlName="type">
<div formArrayName="sites" *ngFor="let site of form?.get('sites').controls;let i=index">
<div [formGroupName]="i">
<--!see that formControlName is "site"
and the labels of the check use the array this.sites -->
<input type="checkbox" formControlName="site" /> {{sites[i]}}
</div>
</div>
<button>send</button>
</form>
All are asking Why on earth and What is it? All the things must be too complex? My answer is that it is not so complex. FurtherMore, imagine how can populate the form with data.
//if data={type:"a",sites:["site-2","site-4"], we create the form like
const controls:FormGroup[]=this.sites.map(x=>{
return this.fb.group({
site:data.sites.indexOf(x)>=0; //true or false
})
});
this.form = this.fb.group({
type: [data.type, Validators.required],
sites: this.fb.array(controls)
})

Binding to correct model - with same name

I have an ng-repeat that displays 2 forms (the array has 2 items)
I have an empty input for each of these, that has the same model, something like the following:
<div ng-repeat=“group in groups”>
<form>
<input type=“text” ng-model=“group.name” placeholder=“name” />
</form>
</div>
Now this works well, until I try to enter text into the first input, it automatically enters into the second as well.
Is there an easy way to bind to the input i’m actually typing in?
Thanks
Your PLNKR example doesn't match your question.
Simplifying your code:
<!-- Top loop doesn't repeat -->
<div ng-repeat="group in groups">
<div ng-repeat="g in group">
<div ng-repeat="info in g.info.data">
<input ng-model="info.name" placeholder="Name" />
</div>
<!-- This is the problem -->
<input ng-model="test.name" placeholder="Name">
<button ng-click="nameAdd($parent.$index)">
Add
</button>
</div>
</div>
The input with the Add button uses the same ng-model name. Also here are three nested loops when only two are needed.
The Data
$scope.groups = {
"data": [{
"id": 1,
"name": "Group 1",
"site_id": 3,
"info": {
"data": [{
"id": 1,
"name": "Jim"
}]
}
}, {
"id": 2,
"name": "Group 2",
"site_id": 3,
"info": {
"data": [{
"id": 10,
"name": "Bob"
}, {
"id": 11,
"name": "Terry"
}]
}
}]
}
Change the template to:
<!-- remove top loop
<div ng-repeat="group in groups">
-->
<div ng-repeat="groupDatum in groups.data" ng-cloak>
<div ng-repeat="infoDatum in groupDatum.info.data">
<input ng-model="infoDatum.name" placeholder="Name" />
</div>
<!-- use groupDatum to store new name -->
<input ng-model="groupDatum.newName" placeholder="Name">
<!-- use groupDatum as arg to ng-click -->
<button ng-click="nameAdd(groupDatum)">
Add
</button>
</div>
The nameAdd function:
$scope.nameAdd = function(groupDatum) {
var newId = uniqueId();
var newName = groupDatum.newName;
groupDatum.info.data.push({id: newId, name: newName});
}
try like this,
<div ng-repeat=“group in groups”>
<form>
<input type=“text” ng-model=“groups[$index].name” placeholder=“name” />
</form>
</div>
you also probably put the form tag outside of the repeater unless you want to create a new from for every input tag

Why is my AngularFire push( ) not working?

I'm trying to learn Angular with Firebase by running through a typical to-do tutorial, but I'm sort of modifying the app as I go. Instead of list of to-dos, I'm actually creating 'Users'.
Firebase is set up correctly, pulling object data down from Firebase works as well and I can see everything populating as expected via ng-repeat.
But I'm trying to 'add a new member' using the .push() method, and I'm receiving an error. Please take a look:
Here's an example of the JSON data that I uploaded to Firebase (just one user)
{
"members": {
"-JWT5y43YFy1mGirVVS2": {
"date": 1410328158691,
"firstname": "Michael",
"lastname": "Jordan",
"project": "sample project description",
"image": "http://telehealth.org/wp-content/images/user-placeholder.jpg",
"upcoming": "PTO on Thursday",
"status": {
"color": "red",
"contact": {
"email": "test#email.com",
"yahoo": "yahooIM"
},
"projects": {
"projectone": "project one",
"projecttwo": "project two",
"projectthree": "project three"
}
}
}
}
}
Here's my .html code
<h1 id="teamTitle">Team Members</h1>
<div class="addMember">
<p>add member</p>
<form class="formmember"
name="memberform"
ng-submit="addMember()"
novalidate>
<div class="inputwrapper">
<input type="text" name="firstname" placeholder"First Name" ng-required="true">
<button type="submit" class="btn"
ng-disabled="memberform.$invalid">Add</button>
</div>
</form>
</div>
<ul class="cbp-rfgrid" ng-repeat="member in members">
<li class="{{member.status.color}}">
<img src="{{member.image}}">
<div>
<h3>{{member.firstname}}</h3>
<button>View</button>
</div>
</li>
</ul>
And here's my Controller.js
myApp.controller('MembersController', function($scope, $firebase){
var ref = new Firebase('https://scrumcheck.firebaseio.com/members');
var members = $firebase(ref);
$scope.members = members.$asObject();
$scope.addMember = function(){
members.$push({
firstname: $scope.firstname,
date: Firebase.ServerValue.TIMESTAMP
});
}
});
The console log shows this error:
Error: Firebase.set failed: First argument contains undefined in property 'firstname'
I was hoping that by pushing to Members, an object with just the firstname key, it would allow me to do so. Am I only allowed push() an entire object, with all key-values in it?
Thanks!
Checkout your $scope.firstname model, that's where the issue is. make sure it's not a null value and properly defined.

Resources