简体   繁体   中英

Angular 6 using ContentChildren/ViewChildren in AngularElements - CustomComponents

I am trying out the new AngularElements feature. ( https://angular.io/guide/elements )

First tests were successfull but as soon as I integrate @ContentChildren it stops working. This seems logic to me, as the newly created Component as the CustomElement cannot get the reference via Angular-Context as it lives outside of the app.

My aim was to make a minimal Tab-Wrapper / Tab - Component like structure as they use in angular.io

<custom-tab-wrapper>
  <anb-tab [title]="'Test'">
    Content
  </anb-tab>
</custom-tab-wrapper>

I also created a Stackblitz but this seems to be even worse as HMR defines the component multiple times which results in an error. But this should give you a better idea on what I am trying to achieve.

https://stackblitz.com/edit/angular-byvpdz?file=src%2Fapp%2Ftab-wrapper%2Ftab-wrapper.component.ts

Here are the main files:

index.html

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>AngularBlog</title>
  <base href="/">

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<anb-root></anb-root>
<custom-tab-wrapper>
  <anb-tab [title]="'Test'">
    Content
  </anb-tab>
</custom-tab-wrapper>
</body>
</html>

tab-wrapper.component.ts

import {AfterContentInit, Component, ContentChildren, OnInit, QueryList, ViewEncapsulation} from '@angular/core';
import {TabComponent} from '../tab/tab.component';

   import {AfterContentInit, Component, ContentChildren, OnInit, QueryList, ViewEncapsulation} from '@angular/core';
import {TabComponent} from '../tab/tab.component';

@Component({
  selector: 'anb-tab-wrapper',
  template: `
    <div class="tab-selection-wrapper">
      <h1>Test</h1>
      <div class="tab-selection" (click)="enableTab(tab)" *ngFor="let tab of tabs.toArray()">
        {{tab.title}}
      </div>
    </div>
    <div class="tab-content">
      <ng-content></ng-content>
    </div>
  `,
  encapsulation: ViewEncapsulation.Native
})
export class TabWrapperComponent implements OnInit, AfterContentInit {
  @ContentChildren(TabComponent) tabs: QueryList<TabComponent>;

  constructor() {
    setInterval(() => {
      console.log(this.tabs);
    }, 2000);
  }

  public enableTab(tab: TabComponent) {
    tab.active = true;
    this.tabs.toArray().forEach(tabToCheck => {
      if (tab !== tabToCheck) {
        tabToCheck.active = false;
      }
    });
  }

  ngAfterContentInit(): void {
  }


  ngOnInit() {
  }

}

And then I would have a Tab-Component. Which gets rendered correctly if I use it within the angular-root-element but not as a WebComponent.

app.module.ts

import {BrowserModule} from '@angular/platform-browser';
import {Injector, NgModule} from '@angular/core';

import {AppComponent} from './app.component';
import {TabWrapperComponent} from './tab-wrapper/tab-wrapper.component';
import {TabComponent} from './tab/tab.component';
import {createCustomElement} from '@angular/elements';

@NgModule({
  declarations: [
    AppComponent,
    TabWrapperComponent,
    TabComponent
  ],
  entryComponents: [
    TabWrapperComponent,
    TabComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {

  constructor(private injector: Injector) {
    const tabWrapper = createCustomElement(TabWrapperComponent, {injector: injector});
    customElements.define('custom-tab-wrapper', tabWrapper);
    const tabComponent = createCustomElement(TabComponent, {injector: injector});
    customElements.define('anb-tab', tabComponent);
  }
}

I am aware that I can solve this without @ContentChildren but I would like to use this feature in CustomComponent. So my question is: Is it possible to use @ContentChildren / ViewChildren. If no, what are the alternatives?

Thanks for helping

Daniel

Rob Worlmald answered my question which I sent him as a Twitter-Message. I would like to share the results with the community:

He answers the question if Directives and ContentChild / ContentChildren works as following:

Nope, they do not - in the current view engine queries are static. In general, because you're likely to be projecting either vanilla DOM nodes or other (Angular) Custom Elements, you can just use querySelector(All)

Which basically means we can select the children within an angular-livecyclce or outside with document.querySelector(). In my personal opinion fetching it outside Angular seems to be the cleaner approach as the few additional lines wouldn't be used but still executed within an angular-application. I will do some research.

Further discussions on this topic and a possible workaround can be found here: https://twitter.com/robwormald/status/1005613409486848000

He continues:

Which you can combine with https://developer.mozilla.org/en-US/docs/Web/Events/slotchange … to approximate the same behaviors

And ends with a possibly cleaner solution in the future:

In ivy (v7 or shortly thereafter) we'll likely make the query system more flexible and dynamic

Will update my solution with querySelectorAll in the near future.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM