Reputation: 543
I am working on a small task of creating a Trip Planner using Angular2. The work flow is as follows: The user will input start point and destination along with number of intermediate stop points. Once the user submit "Go" button, the Planner table appears, where user can input the intermediate stop points as well as add/remove the stop points. Somehow I am not getting the desired output as shown in the screen shot.
On adding new stop point, this is what I am getting...
This is how the Tripdetails object looks like. See the difference between the object and the view.
The code are as follows: Trip.ts
export class Trip {
startPoint:string;
destination:string;
stops:number;
}
TripDetails.ts
export class TripDetail {
startPoint:string;
destination:string;
}
app.component.ts
import { Component } from '@angular/core';
import { Trip } from './Trip';
import { TripDetail } from './TripDetails';
@Component({
selector: 'my-app',
moduleId: module.id,
templateUrl: 'app.component.html'
})
export class AppComponent {
trip: Trip = {
startPoint : "",
destination : "",
stops : 0
};
tripDetails: TripDetail[];
clicked(startPoint:string, destination:string, stops:string):void {
let length = parseInt(stops);
this.trip.startPoint = startPoint;
this.trip.destination = destination;
this.trip.stops = length;
this.tripDetails = [];
for (let i=0 ; i< length; i++) {
let tripDetail :TripDetail={
startPoint: "",
destination: ""
}
if( i==0) {
tripDetail.startPoint = this.trip.startPoint;
}
if( i== length-1) {
tripDetail.destination = this.trip.destination;
}
this.tripDetails.push(tripDetail);
}
}
syncData(index:number, locationType:string) {
if(locationType == 'destinationLocation') {
this.tripDetails[index + 1].startPoint = this.tripDetails[index].destination;
}
else if (locationType == 'sourceLocation') {
this.tripDetails[index - 1].destination = this.tripDetails[index].startPoint;
}
}
addStop(index:number):void {
let stop:TripDetail={
startPoint:"newStop",
destination:"newStop"
}
this.tripDetails.splice(index,0,stop);
}
removeStop(index:number):void{
let destination = this.tripDetails[index].destination,
arrlength = this.tripDetails.length;
if(index==0){
return;
}
else if( index== arrlength-1) {
this.tripDetails[index-1].destination = this.tripDetails[index].destination;
this.tripDetails.splice(index,1);
}
else{
this.tripDetails.splice(index,1);
this.tripDetails[index].destination= destination;
}
}
}
app.component.html
<div class="container content">
<div>
<form>
<label for="startPoint">From:</label>
<input class="form-control" type="text" placeholder="Enter start point" name="startPoint" [(ngModel)] = "trip.startPoint" >
<label for="destination">To:</label>
<input class="form-control" type="text" name="destination" placeholder="Enter destiantion" id="destination" [(ngModel)]="trip.destination" >
<label for="Stops">Stops:</label>
<input class="form-control" type="text" placeholder="Enter number of stops" name="stops" id="stops" [(ngModel)]="trip.stops" >
<div class="go-btn">
<button class="btn btn-primary" (click)="clicked(trip.startPoint,trip.destination,trip.stops)">Go</button>
</div>
<div *ngIf = "trip.stops && trip.startPoint && trip.destination" class="trip-table table-bordered">
<table class="table table-stripped">
<thead class="thead">
<tr scope="row">
<th>FROM</th><th>TO</th>
</tr>
</thead>
<tr *ngFor= " let stops of tripDetails; let i=index; let first = first; let last= last; trackBy: index ">
<td>
<input type="text" class="form-control" name="locationFrom-{{i}}" [(ngModel)]= "stops.startPoint" (input)="syncData(i, 'sourceLocation')" >
</td>
<td>
<input type="text" class="form-control" name="locationTo-{{i}}" [(ngModel)]= "stops.destination" (input)="syncData(i, 'destinationLocation')" >
</td>
<td>
<button class="btn btn-primary" (click)="addStop(i)">+</button>
</td>
<td>
<button class="btn btn-primary" (click)="removeStop(i)" >-</button>
</td>
</tr>
</table>
</div>
</form>
</div>
</div>
app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
@NgModule({
imports: [ BrowserModule , FormsModule],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
Upvotes: 1
Views: 420
Reputation: 73387
You are absolutely correct with using trackBy
to track the index, you just had some problems with that. trackby
is needed when there is not an unique ngModel
index to track (variable with property, e.g [(ngModel)]="myItem.myProperty"
) and is therefore needed when dealing with primitive arrays, for example trackBy
would be needed in below example:
<div *ngFor="let item of items">
<input [(ngModel)]="item" />
</div>
If you would try and modify the input fields above, the input field would instantly loose focus when typing something.
But you might say...
My array is not of primitive type and my array
tripDetails
contains objects which can be tracked with the uniquengModel
index?
True, in case you were not using a form, you would be fine without trackBy
.
But since we are dealing with a form, Angular doesn't really care about the unique ngModel
, but looks at the name
attribute, which basically means that field suddenly doesn't have an unique ngModel
with index to track.
So use trackBy
<tr *ngFor= "let stops of tripDetails; let i=index; trackBy:trackByFn">
and the corresponding function in the component:
trackByFn(index: number, stops: string) {
return index;
}
Here's a
Upvotes: 1