1

I have to create a multi select filter that would accept multi option values to be clicked, in order to refine the response of some get API endpoint on the backend.

Whenever a user click on a option, a "chip" component would be dynamically render to aware the user: "hey you just filter the result by this, and that filter option"

Looking around in internet I found this stackblitz

In this code sample I understand that somehow in this lines:

let componentFactory = this.CFR.resolveComponentFactory(ChildComponent);
let childComponentRef = this.VCR.createComponent(componentFactory);

we insert an instance of a given child component inside a ViewContainerRef. find a object somewhat like this:

_data: Object { renderElement: <!--  -->, componentView: undefined, viewContainer: {…}, … }
_elDef: Object { nodeIndex: 4, bindingIndex: 0, outputIndex: 1, … }
_embeddedViews: Array(5) [ {…}, {…}, {…}, … ] //here   
_view: Object { def: {…}, parent: {…}, state: 1036, … }

where under __embeddedViews object the dynamically generated views will be stack up

Later to decide which views will be deleted, the creator of this stackblitz just get the component and make a ViewContainerRef.indexOf(component) to get the index in which the component is stored and verify that that the dynamically generated component exist in that array. Then he/she just delete the view calling this.ViewContainerRef.remove(index);

Funny thing, in my implementation when I log my ViewContainerRef I get this object as response:

​_hostTNode: Object { type: 0, index: 23, injectorIndex: 34, … }
_hostView: Array(94) [ ..., {…}, 147, … ]
_lContainer: Array(12) [ <!-- 

The chips are successfully added dynamically as expected, but there is no _embeddedViews, so I cannot remove them dynamically because ViewContainerRef.indexOf(chip) will always return -1 as "no, I don't have a "chip" here" Please can someone enlighten me and show what I'm doing wrong here?

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Rod Ramírez
  • 1,138
  • 11
  • 22
  • I quickly took a look at all this, and whyu not just use a `ngFor` that render your chips depending on the user selected filter (that you probably store somewhere)? Your current approach sound so complicated for what you want to achieve – Crocsx Oct 02 '20 at 02:59
  • Wow, I have not tought it that way at all. It shows that im still strugling with angular lol. If you post this answer I will gladly tick as correct. Thanks for your help! – Rod Ramírez Oct 02 '20 at 03:35
  • is not really an answer to your problem, so I don't think it s worth posting, is just a much simpler way to achieve the same result ^^ take a look at this https://stackblitz.com/edit/angular-filter-list-of-items-yafewh?file=src/app/app.component.html – Crocsx Oct 02 '20 at 05:03

1 Answers1

5

You have such inconsistency because you're using ViewContainerRef API in a wrong way.

Here's the signature of indexOf method:

abstract indexOf(viewRef: ViewRef): number;

This signature never changed during Angular updates.

The stackblitz you're referring to uses Angular 6 version that leverage ViewEngine under the hood but in your code you're using Angular 9 and above version where Ivy compiler comes into play.

In stackblitz you have:

this.VCR.indexOf(componentRef as any);

Meaning that you're passing ComponentRef instance not ViewRef instance. It works by accident because indexOf method looks like:

ViewContainerRef_.prototype.indexOf = function (viewRef) {
  return this._embeddedViews.indexOf(viewRef._view);
};

and

ComponentRef._view === ViewRef._view in ViewEngine.

You should be passing ViewRef instance instead:

this.VCR.indexOf(componentRef.hostView)

Forked Stackblitz

The demo works with Ivy(your particular case) but it will also work in ViewEngine.

yurzui
  • 205,937
  • 32
  • 433
  • 399