Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can not read value of undefined, when components share data?

I am not able to populate my form with data that I have received from a method called getbyId() from a service, in my console I see that errors: cannot read truckId of undefined, Every solution i have found is saying my form is rendered faster than the object that i want to get with a getById() method and the solution should be *ngIf ="truck" which should ,make the form wait , but it doesn't solve it just refuses to display the form thats all. IN stackblitz below https://stackblitz.com/edit/stackoverflow-49847806-kdjhdc?file=app%2Fservices%2Ftruck.service.ts

But In console i can see the URL and when I open it I see that it actually got the details from my back-end service

getTruckById(id: number): Observable<Truck> {
   const url = `${this.baseUrl}/${id}`;
    return this.http.get(url, {headers: this.headers})
      .pipe(map(this.extractData),
        tap(data => console.log(JSON.stringify(data))),
        catchError(this.handleError));
  } 

This is the edit component completely

export class EditTruckComponent implements OnInit {
  five : number = 5;
  seven: number = 7;

 @Input() truck: Truck;

  truckIdTOUpdate: number;
  processValidation = false;
  dates: string;
  statusCode: number;
  requestProcessing = false;

   truckForm = new FormGroup({
    truckCode: new FormControl(Validators.maxLength(this.seven), Validators.minLength(this.five)),
    date: new FormControl('', Validators.required  ),
    descriptions: new FormControl(Validators.maxLength(this.seven), Validators.minLength(this.five))
  });


  constructor( private route: ActivatedRoute, private truckService: TruckService, private router: Router) {
  }

  ngOnInit() {
     this.getTruckDetail();
  }

  back() {
    this.router.navigate(['/trucks'])
  }

  getTruckDetail() {
    const truckId = +this.route.snapshot.paramMap.get('truckId');
   this.truckService.getTruckById(truckId)
      .subscribe((truck) => this.truck = truck)
      console.log("this is the truck" + this.truck);
  }

  processForm() {
    this.processValidation = true;
    if (this.truckForm.invalid) {
      return; //Validation failed, exit from method.
    }
    // if we are here then all good
    this.preProcessConfigurations()

    let truckCode = this.truckForm.get('truckCode').value.trim();
    let date = this.truckForm.get('date').value.trim();
    let description = this.truckForm.get('descriptions').value.trim();

    if (this.truck.truckId == undefined) {
      let truck = new Truck(null, truckCode,  date , description);

      this.truckService.createTruck(truck).subscribe((truck) => {
        console.log(truck)
        this.router.navigate(['/trucks']);
      }, errorCode => this.statusCode = errorCode);

    } else {
        this.truck = new Truck(this.truck.truckId, truckCode, date, description);
      this.truckService.updateTrucks(this.truck).subscribe((truck)=> {
        console.log(truck);
        this.router.navigate(['/trucks']);
      }, errorCode => this.statusCode = errorCode);
    }
  }

  //Perform preliminary processing configurations
  preProcessConfigurations() {
    this.statusCode = null;
    this.requestProcessing = true;
  }

}


   class="btn btn-light"
           title="Save Truck"
           data-toggle="tooltip"
           data-placement="bottom">
          <i class="fa fa-save"></i> <span class="d-none d-sm-inline" *ngIf="truck?.truckId ==undefined">Save</span>
          <span class="d-none d-sm-inline" *ngIf="truck?.truckId">Update</span>
        </a></li>
    </ul>
  </nav>
</header>

<section id="department">
  <div class="container-fluid">
    <div class="row">
      <div class="col">
        <div class="card">
          <div class="card-body">

            <form [formGroup]="truckForm" (ngSubmit)="processForm()"   id="editFormTruck" >
              <!--truck code-->
              <div class="form-group">
                <label class="form-control-label"
                       for="truckCode"></label>
                <input formControlName="truckCode"
                       id="truckCode"
                       class="form-control"
                       type="text"

                       name="truckCode"
                       min="2018-04-11" required
                       [(ngModel)]="truck?.truckCode"/> Truck Code
                <div class="alert alert-danger" role="alert"  *ngIf="truckForm.get('truckCode').invalid && processValidation" required="required" [ngClass] ="'error'">
                  you must have a minimum of 5 chars and maximum of 7
                </div>
                <div *ngIf="statusCode === 409" [ngClass] = "'success'" class="alert alert-danger" role="alert">
                  Truck with such Code already exists try another TruckCode.
                </div>

              </div>

              <!--purchasedDate-->
              <div class="form-group" >
                <label class="form-control-label"  *ngIf="truckForm.get('date').invalid && processValidation"  [ngClass] ="'error'"
                       for="purchasedDate">date is required.</label>
                <input formControlName="date"
                       id="purchasedDate"
                       class="form-control"
                        type="date"
                        name="purchasedDate"
                       [(ngModel)]="truck?.purchasedDate "
                /> Purchased Date
              </div>

I have posted all because the undefined value starts from my button's save and update when I remove them it goes down to truckCode is undefined

For better understanding this is my truckComponent That navigates to EditTruckComponent MY truckComponent shows a list of trucks and edit and delete functions now on my edit function in html i have a i just have a router link

<tr *ngFor="let truck of trucks | async">
              <td>{{truck.truckId}}</td>
              <a routerLink="/truckProfile/">
              <td>{{truck.truckCode}}</td>
              </a>
              <td>{{truck.purchasedDate | date: 'yyyy-MM-dd'}}</td>
              <td>{{truck.descriptions}}</td>
              <td class="text-right">
                <a class="btn-sm btn-outline-secondary"
                   ngbTooltip="Edit Truck"
                   placement="top">
                  <i class="fa fa-bus"></i> <span
                  class="d-none d-md-inline" routerLink="/editsTrucks/{{truck.truckId}}"  >Edit</span></a>

                 <span data-toggle="modal" data-target="#deleteDialog">
                                <a href="#" class="btn-sm btn-outline-secondary"
                                 ngbTooltip="delete truck"
                                 placement="top">
                                    <i class="fa fa-remove"></i>
                                <span class="d-none d-md-inline"  (click)="deleteTruck(truck)">Delete</span></a>
                </span>
              </td>

So i just do a navigate with router link and on my editTruckCompnent im doing a getById on init

like image 434
valik Avatar asked Apr 15 '18 23:04

valik


2 Answers

The issue was in my java controller

 @GetMapping(value = "/trucks/{truckId}")
    @ResponseStatus(HttpStatus.OK)
    public final TruckDto getTruckId(@PathVariable(value = "truckId")
                                         final Integer truckId) {

        LOGGER.debug("test: truckId({})", truckId);
        Truck truck = truckService.getTruckById(truckId);
        return mappingService.map(truck, TruckDto.class);
    }

when i added this annotation it worked @ResponseBody So response body tells the controller that the object returned should be serialized into JSON and passed back into the HttpResponse object. This is why i was getting a HttpResponse Failure

like image 93
valik Avatar answered Oct 22 '22 19:10

valik


It's a design issue. In my opinion, the right way to do it is :

The EditTruckComponent parent should be smart, do the http call and pass the truck to the EditTruckComponent (you could use async so you don't have to subscribe and unsubscribe manualy)

Here is a clue of how I imagine the parent component :

<app-edit-truck [truck]="truck$ | async"></app-edit-truck>

export class EditTruckParentComponent {
    truck$: Observable < Truck > ;

    constructor(private truckService: TruckService) {
        this.truck$ = this.truckService.getTruckById(truckId);
    }
}

Make the EditTruckComponent dumb, no service calls, and only inputs and outputs. Implement OnChanges to handle the truck input changes, and patch the form values.

The edit truck component would look like this :

export class EditTruckParent implements OnChanges {
    @Input() truck: Truck;
    constructor() {
    }

    ngOnChanges() {
        this.truckForm.patchValue(truck);
    }
}
like image 2
ibenjelloun Avatar answered Oct 22 '22 18:10

ibenjelloun