Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
139 views
in Technique[技术] by (71.8m points)

typescript - Angular 10 Strange Behaviour with interpolation

I am struggling with some odd behaviour in Angular 10.

When the problem element is removed, everything works fine. The expected behaviour is that when the div.passRide is clicked on, it updates another component using the updateViewer(passRide.id) method which gets and displays a record.

However when I need to get some fields from getCrewDiagram(id) in selector.ts, it behaves as if every div has been clicked on and updates the viewer, showing the last thing in the list of divs gets displayed in viewer.

I have found similar questions on here but they do not address my issue, though suspect it is something basic I may well not be understanding.

selector.htm:

<div id="passes">
  <div class="passRide" *ngFor="let passRide of context.getPassRides();" (click)="updateViewer(passRide.id)">
    
  <p>{{ getCrewDiagram(passRide.id).header.diagNumber }}</p> <<<---- problem element here

    <table>
      <tr *ngFor="let event of passRide.events">
        <td>{{ event.activity.diagramActivity }}</td>
        <td>{{ event.noteLine ? event.note : (event.reliefLine ? event.reliefs : event.location.shortDesc) }}</td>
        <td>{{ event.activity.diagramActivity == "DEP" || event.activity.diagramActivity == "ARR" ? event.eventTime : ""}}</td>
        <td>{{ event.activity.diagramActivity == "DEP" ? event.wttid : ""}}</td>
      </tr>
    </table>
  </div>
</div>

selector.ts

import { Component, OnInit } from '@angular/core';
import { CrewDiagram } from '../../core/crew-diagram';
import { DiagramSetService } from '../../core/current-diagram-set.service';

@Component({
  selector: 'app-selector',
  templateUrl: './selector.component.html',
  styleUrls: ['./selector.component.css']
})
export class SelectorComponent implements OnInit {

  constructor(public context: DiagramSetService) { }

  ngOnInit() {
  }

  updateViewer(index: number) : void {
    this.context.setCrewDiagram(index);
  }

  updateViewerDebug(index: number, $event): void {
    console.log($event)
    this.context.setCrewDiagram(index);
  }

  getCrewDiagram(id: number): CrewDiagram {
    return this.context.getCrewDiagram(id);
  }
}

diagram-set-service.ts

import { Injectable } from '@angular/core';
import { CrewDiagram } from './crew-diagram';
import { CrewPuzzlePiece } from './crewPuzzlePiece/crew-puzzle-piece';

@Injectable({
  providedIn: 'root'
})
export class DiagramSetService {

  constructor() { }

  crewDiagrams: CrewDiagram[] // Original set from which the following are derived
  crewDiagram: CrewDiagram    // Current crew diagram (used by viewer/diagramViewer)
  filteredView: CrewDiagram[] // Diagrams on show at the moment

  passRides: CrewPuzzlePiece[] // Diagram puzzle pieces representing passing moves

  getCrewDiagrams(): CrewDiagram[] {
    return this.crewDiagrams;
  }

  getFilteredDiagrams(): CrewDiagram[] {
    return this.filteredView;
  }

  setCrewDiagrams(crewDiagrams: CrewDiagram[]) {
    this.crewDiagrams = crewDiagrams;
    this.filteredView = crewDiagrams;
  }

  getCrewDiagram(id: number): CrewDiagram{
    console.log("getCrewDiagram" + id);

    return this.crewDiagram = this.crewDiagrams.
      filter(
        function (diagram, index, array) {
          return diagram.id == id;
        }
    )[0];
  }

  setCrewDiagram(id: number): void {
    this.crewDiagram = this.getCrewDiagram(id);
    console.log("setCrewDiagram" + id);
  }

  getNumberCrewDiagrams() {
    if (!this.crewDiagrams) {
      return 0;
    }

    return this.crewDiagrams.length;
  }

  getCurrentCrewDiagram() {
    return this.crewDiagram;
  }

  filterCrewsByDepot(depotString: string): void {
    const depots = depotString.split(' ');

    this.filteredView = this.crewDiagrams.filter(function (diagram) {
      return depots.includes(diagram.header.depot);
    });
  }

  resetView(): void {
    this.filteredView = this.crewDiagrams;
  }

  setPassRides(passRides: CrewPuzzlePiece[]): void {
    this.passRides = passRides;
  }

  getPassRides(): CrewPuzzlePiece[] {
    return this.passRides;
  }
}

Debug output when the problem element is removed: enter image description here

Debug output when the problem element is in use: enter image description here

question from:https://stackoverflow.com/questions/65641901/angular-10-strange-behaviour-with-interpolation

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The problem is because you are binding to a method call.

*ngFor="let passRide of context.getPassRides();"

and

<p>{{ getCrewDiagram(passRide.id).header.diagNumber }}</p>

If you'd notice, if you interact with the section, angular ChangeDetection will fire and angular will reconstruct the whole content again. And since you are binding your ngFor & paragraph with a method, it will essentially reconstruct the whole list again, causing getPassRides() & getCrewDiagram() to be called in a loop.

Hence, the recommended way is to construct/map your model,this.crewDiagrams, then do a property binding, essentially how you are doing for <tr>

<div id="passes">
    <div class="passRide" *ngFor="let passRide of context.getPassRides" (click)="updateViewer(passRide.id)">

        <p>{{ passRide.header.diagNumber }}</p>

        <table>
            <tr *ngFor="let event of passRide.events">
                ...

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...