dhilt
dhilt

Reputation: 20734

Wrap Angular ngFor directive with another structural directive

My AppComponent's template (bootstraped in the AppModule) is

<div *myDir="let item of mySource">
  My Directive #{{item}}
</div>

where mySource is an object with list property

mySource = { list: [1, 2, 3, 4, 5] };

So, replacing myDir with ngFor (*ngFor="let item of mySource.list") would give me

My Directive #1
My Directive #2
My Directive #3
My Directive #4
My Directive #5

I'm trying to implement myDir in pair with myComponent to provide a templated wrapper over ngFor Angular core directive to be able to pass mySource (instead of mySource.list) and achive the same result. Of course, the final goal is much more complex, but I need to solve this task in terms of minimal example... What is done?

my.directive.ts

import { MyComponent } from './my.component';

@Directive({ selector: '[myDir][myDirOf]' })
export class MyDirective {
  private source;
  constructor(/* ... */) { }
  @Input() set myDirOf(source) {
    this.source = source;
  }
  ngOnInit() {
    // dynamic wrapping with MyComponent
    const templateView = this.templateRef.createEmbeddedView({});
    const compFactory = this.resolver.resolveComponentFactory(MyComponent);
    const componentRef = this.viewContainer.createComponent(compFactory, null, this.viewContainer.injector, [templateView.rootNodes]);
    // passing MyDir params to Mycomponent's instance
    componentRef.instance.source = this.source;
    componentRef.instance.template = this.templateRef;
  }
}

my.component.ts

@Component({
  selector: 'app-my',
  template: `
<div *ngFor="let item of source.list">
  <ng-template
    [ngTemplateOutlet]="template"
    [ngTemplateOutletContext]="{
      item: item
    }">
  </ng-template>
</div>`
})
export class MyComponent implements OnInit {
  source;
  template;
  constructor() { }
}

The problem is that I can't pass item back to the host component's template, so currently I'm getting just 5 same lines containing "My Directive #" and {{item}} doesn't work. Looks like something's wrong with MyComponent's ng-template. It accepts the template but won't pass the item.

I created an editable DEMO with my current situation. Also, here's a simple diagram of what I need:

enter image description here

Upvotes: 1

Views: 639

Answers (1)

yurzui
yurzui

Reputation: 214007

Use $implicit to pass data to item

my.component.html

<div *ngFor="let item of source.list">
  <ng-template
    [ngTemplateOutlet]="template"
    [ngTemplateOutletContext]="{
      $implicit: item
    }">
  </ng-template>

</div>

Your template

<div *myDir="let item of mySource">
  My Directive #{{item}}
</div>

is equivalent to

<ng-template myDir let-item [myDirOf]="mySource">
  <div>
    My Directive #{{item}}
  </div>
</ng-template>

but if template variable (let-item) doesn't have value then it takes $implicit by default

<ng-template myDir let-item="$implicit" [myDirOf]="mySource">
  <div>
    My Directive #{{item}}
  </div>
</ng-template>

DEMO

See also

Upvotes: 1

Related Questions