1

Using the following routing definition:

export const routes: Routes = [
  { path: '', redirectTo: '/home', pathMatch: 'full' },
  { path: 'home', component: GeneralComponent },
  { path: 'test', component: TestComponent }
];

export const routing: ModuleWithProviders = RouterModule.forRoot(routes);

I have the following for loop set up in a module that is loaded as a part of the "home" loading sequence:

<div *ngFor="let row of rows; let i = index">
  <div class="rows">
    <div *ngFor="let coach of row">
      <bio [coach]='coach'></bio>
    </div>
  </div>
</div>

and this is backed by a simple component on the backend:

export class TeamListingComponent implements OnInit {
  rows: Array<Array<Coach>>;

  constructor(private service: CoachListingService) { }

  ngOnInit () {
    this.rows = this.service.get();
  }
}

On initial page load to .../home, everything looks and works great.

If I navigate to .../test and then back to .../home though, the for loop doesn't seem to be fired off. The HTML-only content loads fine but the supplied content from the for loop doesn't render. (No errors reported in JS console).

I've confirmed "this.rows" gets reset properly and has content on the return trip and both the service appears to be working as expected and the non- "for loop" HTML in the component loads up and presents exactly as expected.

Versions:

*ng --version*
angular-cli: 1.0.0-beta.24
node: 7.0.0
os: darwin x64
@angular/common: 2.4.10
@angular/compiler: 2.4.10
@angular/core: 2.4.10
@angular/forms: 2.4.10
@angular/http: 2.4.10
@angular/platform-browser: 2.4.10
@angular/platform-browser-dynamic: 2.4.10
@angular/router: 3.4.10
@angular/compiler-cli: 2.4.10

UPDATE:

Following the comments listed below suggesting approaches with the NgZone module, I reviewed the posts & subsequent NgZone API page and I added the following to the component in question:

constructor(private service: CoachListingService, private zone: NgZone) {
  this.zone.runOutsideAngular(() => {
    this._updateList(() => {
    console.log(this.rows);
      this.zone.run(() => { console.log('Forcing re-draw', this.rows); });
    });
  });
}

ngOnInit () {
  this.rows = this.service.get();
}


_updateList (doneCallback: () => void) {
  this.rows = this.service.get();
}

This seemed to have no effect. I'm sure I'm not thinking this through correctly.

I later altered "_updateList" to call the callback with:

_updateList (doneCallback: () => void) {
  this.rows = this.service.get();
  doneCallback();
}

and it does indeed show the proper output in the javascript console. So data is present.

Thoughts?

TIA

Lorin S.
  • 754
  • 8
  • 29
  • are you using `Observables` – Aravind May 14 '17 at 16:32
  • you can force a rendering of the component which is hosting the ngFor. Please refer to the following link: http://stackoverflow.com/questions/35105374/how-to-force-a-components-re-rendering-in-angular-2 – quirimmo May 14 '17 at 16:32
  • How does the method get() of your service retrieve the data? – bchampion May 14 '17 at 16:59
  • I am not using Observables yet.. Is that the key? Will review other post. Get Service returns an array of arrays for a list of coaches. (Same list every call. nothing dynamic.) – Lorin S. May 14 '17 at 18:41
  • Updated to reflect findings with NgZone – Lorin S. May 15 '17 at 22:06

1 Answers1

4

The service should return an observable, and you should use the async pipe in the template. This will ensure the view is updated whenever the value changes. (Note: '$' suffix is a naming convention I use for observables.)

export class TeamListingComponent implements OnInit {
  rows$: Observable<Array<Array<Coach>>>;

  constructor(private service: CoachListingService) { }

  ngOnInit () {
    this.rows$ = this.service.getRows$();
  }
}

Template:

<div *ngFor="let row of rows$ | async; let i = index">

I don't know what's going on in your service, but if it's simply getting and setting, you could use an rxjs Subject, like this:

@Injectable()
export class CoachListingService {
  private rowsSubject = new Subject<Array<Array<Coach>>>();
  private _rows$ = this.rowsSubject.asObservable();

  getRows$() {
    return this._rows$;
  }

  setRows(rows: Array<Array<Coach>>) {
    this.rowsSubject.next(rows);
  }
}

You definitely shouldn't need to be messing with NgZone and stuff like that.

dbandstra
  • 1,304
  • 9
  • 10