-
Notifications
You must be signed in to change notification settings - Fork 26.2k
Change Detection Not Working with Routing in Zone-Less Application #39296
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Labels
area: core
Issues related to the framework runtime
area: zones
Issues related to zone.js
core: change detection
P3
An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
state: confirmed
state: has PR
Milestone
Comments
This seems to be a bug that when |
JiaLiPassion
added a commit
to JiaLiPassion/angular
that referenced
this issue
Oct 20, 2020
…tick. Close angular#39296 Fix an issue that `markDirty()` will not trigger change detection. The case is for example we have the following component. ``` export class AppComponent implements OnInit { constructor(private router: Router) {} ngOnInit() { this.router.events .pipe(filter((e) => e instanceof NavigationEnd)) .subscribe(() => ɵmarkDirty(this)); } } export class CounterComponent implements OnInit, OnDestroy { ngOnInit() { this.countSubject.pipe(takeUntil(this.destroy)).subscribe((count) => { this.count = count; ɵmarkDirty(this); }); } ``` Then the app navigate from `AppComponent` to `CounterComponent`, so there are 2 `markDirty()` call at in a row. The `1st` call is from `AppComponent` when router changed, the `2nd` call is from `CounterComponent.ngOnInit()`. And the `markDirty()->scheduleTick()` code look like this ``` function scheduleTick(rootContext, flags) { const nothingScheduled = rootContext.flags === 0 /* Empty */; rootContext.flags |= flags; if (nothingScheduled && rootContext.clean == _CLEAN_PROMISE) { rootContext.schedule(() => { ... if (rootContext.flags & RootContextFlags.DetectChanges) rootContext.flags &= ~RootContextFlags.DetectChanges; tickContext(); rootContext.clean = _CLEAN_PROMISE; ... }); ``` So in this case, the `1st` markDirty() will 1. set rootContext.flags = 1 2. before `tickContext()`, reset rootContext.flags = 0 3. inside `tickContext()`, it will call `CounterComponent.ngOnint()`, so the `2nd` markDirty() is called. 4. and the `2nd` scheduleTick is called, `nothingScheduled` is true, but rootContext.clean is not `_CLEAN_PROMISE` yet, since the `1st` markDirty tick is still running. 5. So nowhere will reset the `rootContext.flags`. 6. then in the future, any other `markDirty()` call will not trigger the tick, since `nothingScheduled` is always false. So `nothingScheduled` means no tick is scheduled, `rootContext.clean === _CLEAN_PROMISE` means no tick is running. So we should set the flags to `rootContext` only when `no tick is scheudled or running`.
josephperrott
pushed a commit
that referenced
this issue
Oct 29, 2020
…tick. (#39316) Close #39296 Fix an issue that `markDirty()` will not trigger change detection. The case is for example we have the following component. ``` export class AppComponent implements OnInit { constructor(private router: Router) {} ngOnInit() { this.router.events .pipe(filter((e) => e instanceof NavigationEnd)) .subscribe(() => ɵmarkDirty(this)); } } export class CounterComponent implements OnInit, OnDestroy { ngOnInit() { this.countSubject.pipe(takeUntil(this.destroy)).subscribe((count) => { this.count = count; ɵmarkDirty(this); }); } ``` Then the app navigate from `AppComponent` to `CounterComponent`, so there are 2 `markDirty()` call at in a row. The `1st` call is from `AppComponent` when router changed, the `2nd` call is from `CounterComponent.ngOnInit()`. And the `markDirty()->scheduleTick()` code look like this ``` function scheduleTick(rootContext, flags) { const nothingScheduled = rootContext.flags === 0 /* Empty */; rootContext.flags |= flags; if (nothingScheduled && rootContext.clean == _CLEAN_PROMISE) { rootContext.schedule(() => { ... if (rootContext.flags & RootContextFlags.DetectChanges) rootContext.flags &= ~RootContextFlags.DetectChanges; tickContext(); rootContext.clean = _CLEAN_PROMISE; ... }); ``` So in this case, the `1st` markDirty() will 1. set rootContext.flags = 1 2. before `tickContext()`, reset rootContext.flags = 0 3. inside `tickContext()`, it will call `CounterComponent.ngOnint()`, so the `2nd` markDirty() is called. 4. and the `2nd` scheduleTick is called, `nothingScheduled` is true, but rootContext.clean is not `_CLEAN_PROMISE` yet, since the `1st` markDirty tick is still running. 5. So nowhere will reset the `rootContext.flags`. 6. then in the future, any other `markDirty()` call will not trigger the tick, since `nothingScheduled` is always false. So `nothingScheduled` means no tick is scheduled, `rootContext.clean === _CLEAN_PROMISE` means no tick is running. So we should set the flags to `rootContext` only when `no tick is scheudled or running`. PR Close #39316
josephperrott
pushed a commit
that referenced
this issue
Oct 29, 2020
…tick. (#39316) Close #39296 Fix an issue that `markDirty()` will not trigger change detection. The case is for example we have the following component. ``` export class AppComponent implements OnInit { constructor(private router: Router) {} ngOnInit() { this.router.events .pipe(filter((e) => e instanceof NavigationEnd)) .subscribe(() => ɵmarkDirty(this)); } } export class CounterComponent implements OnInit, OnDestroy { ngOnInit() { this.countSubject.pipe(takeUntil(this.destroy)).subscribe((count) => { this.count = count; ɵmarkDirty(this); }); } ``` Then the app navigate from `AppComponent` to `CounterComponent`, so there are 2 `markDirty()` call at in a row. The `1st` call is from `AppComponent` when router changed, the `2nd` call is from `CounterComponent.ngOnInit()`. And the `markDirty()->scheduleTick()` code look like this ``` function scheduleTick(rootContext, flags) { const nothingScheduled = rootContext.flags === 0 /* Empty */; rootContext.flags |= flags; if (nothingScheduled && rootContext.clean == _CLEAN_PROMISE) { rootContext.schedule(() => { ... if (rootContext.flags & RootContextFlags.DetectChanges) rootContext.flags &= ~RootContextFlags.DetectChanges; tickContext(); rootContext.clean = _CLEAN_PROMISE; ... }); ``` So in this case, the `1st` markDirty() will 1. set rootContext.flags = 1 2. before `tickContext()`, reset rootContext.flags = 0 3. inside `tickContext()`, it will call `CounterComponent.ngOnint()`, so the `2nd` markDirty() is called. 4. and the `2nd` scheduleTick is called, `nothingScheduled` is true, but rootContext.clean is not `_CLEAN_PROMISE` yet, since the `1st` markDirty tick is still running. 5. So nowhere will reset the `rootContext.flags`. 6. then in the future, any other `markDirty()` call will not trigger the tick, since `nothingScheduled` is always false. So `nothingScheduled` means no tick is scheduled, `rootContext.clean === _CLEAN_PROMISE` means no tick is running. So we should set the flags to `rootContext` only when `no tick is scheudled or running`. PR Close #39316
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Labels
area: core
Issues related to the framework runtime
area: zones
Issues related to zone.js
core: change detection
P3
An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
state: confirmed
state: has PR
🐞 bug report
Affected Package
The issue is caused by package @angular/coreIs this a regression?
NoDescription
Issue happens in zone-less environment when routing is used. If you remove
<router-outlet></router-outlet>
from app component template and put<app-counter></app-counter>
, everything works fine.Problem makes
ɵmarkDirty(this)
that is called immediately inngOnInit
method of counter component (see bellow Workaround 1). Then, when you click on increment or decrement button,rootContext.flags
is equal to1 (DetectChanges)
instead of0 (Empty)
, and because of that, tick is never scheduled:🔬 Minimal Reproduction
npm install
npm start
Workaround 1
counter.component.ts
fileWorkaround 2
app.component.ts
fileɵmarkDirty(this)
withɵdetectChanges(this)
on line 21🌍 Your Environment
Angular Version: 10.1.6
The text was updated successfully, but these errors were encountered: