Detect markForCheck() when using IVY's ɵrenderComponent

So IVY is out and with it comes some great private APIs, which help create smaller Custom Elements.

  • ɵrenderComponent
  • createCustomElement -> using ɵRender3ComponentFactory

The problem that I have run into is using third-party structural directives that use the ChangeDetectorRef’s method markForCheck().

We know this simple marks a component’s view (and parent’s view) to be checked in the next change detection cycle.

When hand cranking a component with IVY we lose this and have to manually call change detection.

So how does a component detect a change that is done by a directive. (being that we can’t change the directive implementation…)

I have created an example on branch directiveCD of the below repo.


(either project shows of the problem)

Things I have played with:

  • ChangeDetectorRef is shared by the directive and the component, so can we monkey patch an observable to emit when lView[2] changes.
  • Try and add some of the functionality of NgZone and ApplicationRef so we can hook into scheduleTick()

So I am opening a discussion on how would be the best way to approach this problem (obviously I am aware that these are private APIs etc and none of this is standard).

Thanks

One attempt I would make is to inspect the 3rd party child component’s view data structure in ngDoCheck of our parent component and run detectChanges on the third party component if it is marked for check.

Hey, thanks for the question, let’s start a discussion :slight_smile:.

Yes, before Angular Ivy it simply updated the view state, however here’s the comment I’ve found in the sources in Angular 9:

Marks a view and all of its ancestors dirty.

It also triggers change detection by calling scheduleTick internally, which coalesces
multiple markForCheck calls to into one change detection run.

This can be used to ensure an {@link ChangeDetectionStrategy#OnPush OnPush} component is
checked when it needs to be re-rendered but the two normal triggers haven’t marked it
dirty (i.e. inputs haven’t changed and events haven’t fired in the view).

Yet, I checked the code and it doesn’t schedule a change detection run yet as opposed to markDirty which is still private API

Here’s how I tested it with zone.js disabled:

    constructor(private ref: ChangeDetectorRef) {
        setTimeout(() => {
            this.title = 'updated';
            console.log('updated');
            // this.ref.markForCheck(); <--- doesn't schedule change detection
            // ɵmarkDirty(this);        <---- schedules change detection
        }, 2000);
    }

What’s the end goal?

Thanks @LayZee for the direction.

Gave this a shot…to no avail.

My thinking is as the component is the root component and no other change detection is triggered the ngDoCheck() only gets called once.

I have updated the provided repo (project renderComponentCE) to show this in action.

@m.koretskyi

Thanks for taking the time to look at this problem.

Yea I found the same comment and noticed the implementation wasn’t there. So headed down the private API root too.

ɵmarkDirty() does call scheduleTick() so we see a change detection which is great! :slight_smile:

I suppose the end goal here is:

As IVY is new and naturally angular Libraries won’t want to start using private APIs, how can we use these libraries now, and get some of the benefits of Angular, IVY and Custom Elements.

I have a concrete example where we are making a POC and want to use third-party libraries and Custom Elements using angular :slight_smile:

As Angular evolves with IVY I am sure a lot of this work will become redundant…

I have updated the provided repo (project renderComponentCE) to show a solution to this change detection problem.

Does the 3rd party component expose some component event or DOM event that you can hook into?

So to clarify, say you have an Angular Element implemented by 3rd party, and they use markForCheck in their implementation expecting it to schedule a change detection. In the current Angular release it doesn’t schedule it, so you want to detect the usage of markForCheck and enable scheduling for it somehow, ie replace it with markDirty? That’s an interesting question.

What about providing some service that these Angular Components could use instead of markForCheck and inside this service you’ll abstract away the internals of Angular?

Ah it is more the other way around :slight_smile:

I have created an Angular Element using ɵrenderComponent.
This Angular Element uses a third-party directive.
This third-party directive is using markForCheck which doesn’t trigger any change detection in my Angular Element.

I have got it working by overriding the ViewRef prototype in my Angular Element when it renders.

Changing the markForCheck function to call ɵmarkDirty.

This way when the Angular creates/injects a ViewRef for the directive, it injects one that will use ɵmarkDirty.

As ɵmarkDirty uses scheduleTick I dont think this should have a performance impact?

1 Like

Nice, I didn’t think it was possible to override the ChangeDetectorRef! ɵdetectChanges will be more performant since it only dirty checks from the specific part lf the component tree and its descendants. But it is synchronous, so beware of infinite loop change detection cycles.

To me it seems like you’ve implemented the function in line with what I suggested :

by

I have got it working by overriding the ViewRef prototype in my Angular Element when it renders. Changing the markForCheck function to call ɵmarkDirty.

So what is your question now? :slight_smile:

And it would be great if you wrote an article on how you’ve done the overriding part! we could publish it on indepth.

more performant that what implementation?

what implementation are you talking about here?

detectChanges is more performant than markDirty when controlled properly since it narrows the scope of dirty checking to a sub component tree.

detectChanges runs synchronously. If the 3rd party component somehow synchonously notifies us of it’s dirty state and we initiate a synchronous dirty check using detectChanges which results in it getting dirty and notifying us which results in a synchronous dirty check, and so on.

Cool.

I have started the structure of an article around using IVY and custom elements.
Once I have fleshed it out etc, where would I send it to get proofed/approved?

1 Like

Awesome! Put a draft on Medium and send it to m.koretskyi@gmail.com. I’ll read it and get back with feedback.