I am creating a generic component (in Angular 5.1.0). We call this a multipart component. It allows the user to create and move parts up/down (such as employment history). I used this post as my starter and it works: Angular pass multiple templates to Component
However my component stops working when I put my component inside a form tag. I have studied it in Augery and from what I can tell, the data looks correct. However, on the initial display, all the input fields are displayed with the last item in the array! Once I correct manually in the UI, it works fine so it is an initial display problem of some sort. I put debugging bindings in to show the difference between the input fields and non-input fields.
Here is my generic multipart component:
import { Component, TemplateRef, ElementRef, ContentChild, OnInit, Input, ViewChild,AfterViewInit } from '@angular/core';
@Component({
selector: 'vue-multipart',
templateUrl: './vue-multipart.component.html',
styleUrls: ['./vue-multipart.component.css']
})
export class VueMultipartComponent implements OnInit, AfterViewInit {
//@Input()
//@ViewChild('theForm') theForm;
@Input()
private label: string;
@Input() itemsData: any[];
@ContentChild(TemplateRef) itemTemplate: TemplateRef;
constructor() {
}
ngOnInit() {
}
ngAfterViewInit() {
console.log(this.itemsData);
}
getTitleTranslation() {
return this.label;
}
add() {
this.itemsData.push({});
}
remove(index: number) {
this.itemsData.splice(index, 1);
this.markForm();
}
down(index: number) {
var temp = this.itemsData[index + 1];
this.itemsData[index + 1] = this.itemsData[index];
this.itemsData[index] = temp;
this.markForm();
}
up(index: number) {
var temp = this.itemsData[index - 1];
this.itemsData[index - 1] = this.itemsData[index];
this.itemsData[index] = temp;
this.markForm();
}
markForm() {
//this.theForm.form.touched = true;
//this.theForm.form.pristine = false;
}
}
Here is the template for the multipart component:
<span *ngFor="let item of itemsData; let i = index" >
<hr/>
<b>{{i + 1}} {{getTitleTranslation()}}</b>
<span *ngIf="i === 0" >
<button (click)="down(i)">Down</button>
</span>
<span *ngIf="i > 0 && (i < (itemsData.length -1))" >
<button (click)="down(i)">Down</button> <button (click)="up(i)">Up</button>
</span>
<span *ngIf="i > 0 && (i === (itemsData.length -1))" >
<button (click)="up(i)">Up</button>
</span>
<button (click)="remove(i)">Remove</button><br/>
<ng-template
ngFor let-item [ngForOf]="[item]" [ngForTemplate]="itemTemplate">
</ng-template>
</span>
<button (click)="add()">Add</button><br/>
Here is a component that uses the above multipart component:
import { Component, OnInit, ViewChild } from '@angular/core';
import { Hero } from '../hero';
@Component({
selector: 'app-component-tester',
templateUrl: './component-tester.component.html',
styleUrls: ['./component-tester.component.css']
})
export class ComponentTesterComponent implements OnInit {
@ViewChild('myForm') myForm;
heroes: Hero[] = [];
multipartLabel = "Hero Detail";
isInvalidFormSubmitted: boolean = false;
constructor() {
this.heroes = [
{ id: 11, name: 'Mr. Nice', power: 'Nice Guy' },
{ id: 12, name: 'Narco',power: 'Narco Guy'},
{ id: 13, name: 'Bombasto', power: 'Bombasto Guy' }
];
}
ngOnInit() {
}
}
And lastly here is that components template:
<form #myForm="ngForm" novalidate>
<!-- beginning of multipart test -->
<vue-multipart [itemsData]="heroes" [label] = "multipartLabel" >
<ng-template let-item>
{{item.name}}, {{item.power}}
<input #inputElement id="heroName" name="heroName" class="form-control" [(ngModel)]="item.name" placeholder="Name" required #heroName="ngModel">
<input #inputElement id="heroPower" name="heroPower" class="form-control" [(ngModel)]="item.power" placeholder="Hero Power" required #heroPower="ngModel">
</ng-template>
</vue-multipart>
<!-- end of multipart test -->
</form>
When the above component has the form tag, this is what I get:
Notice the bindings outside of the input field are correct but the input fields are always showing the last item. Inside of Augury, everything looks correct to me, just the UI input fields are not reflected. With the form tag removed, it looks great.