【问题标题】:Angular - Dynamic components loading after API data come - flickeringAngular - API数据到来后加载动态组件 - 闪烁
【发布时间】:2018-09-13 17:06:10
【问题描述】:

我正在构建将与外部 CMS 集成的 Angular 5 应用程序。该 CMS 具有被重写为我的 Angular 应用程序的页面模板。它的工作方式如下面的屏幕所示:

几乎一切都很好,只有我有闪烁的问题。当我第一次进入页面时,我可以看到容器和布局加载之间闪烁。请参阅下面的屏幕:

这是我的代码:

app.module.ts(路由配置)

const routes: Routes = [
  {
    path: '',
    pathMatch: 'full',
    redirectTo: '/pl'
  },
  {
    path: ':lang',
    component: ContainerComponent
  },
  {
    path: ':lang/:index',
    component: ContainerComponent,
  },
  {
    path: '**',
    component: NotfoundComponent
  }
];

container.component.ts

@Component({
  selector: 'app-container',
  templateUrl: './container.component.html',
  providers: [DownloadService, ServerService, Config, SeoService, LinkService],

})

export class ContainerComponent implements OnDestroy, AfterContentInit {
  subscription: ISubscription;
  lang: string;

  @ViewChild('container', { read: ViewContainerRef }) _vcr;


  constructor(@Inject(PLATFORM_ID) private platformId: Object, private link: LinkService, private componentFactoryResolver: ComponentFactoryResolver, private route: ActivatedRoute, private dl: DownloadService, private service: ServerService, private config: Config, private seo: SeoService) {
    if (isPlatformBrowser(this.platformId))
      this.getData();
  }

  ngOnInit() {
  }

  ngAfterContentInit() {
    this.subscription = this.route.params.subscribe((params) => {
      this.getLayoutData(params);
    })
  }

  ngOnDestroy() {
    if (this.subscription) this.subscription.unsubscribe();
  }

  generateSeo(seoData: Seo, langURL, index) {
    let title = '';

    if (seoData.metaTitle == '')
      title = seoData.title;
    else
      title = seoData.metaTitle;

    this.link.addTag({ rel: 'canonical', href: `${this.config.URL}/${langURL}/${index}` });
    this.seo.generateTags({ lang: langURL, title: title, description: seoData.description, keywords: seoData.keywords, image: seoData.banner, slug: index })
  }

  getLayout(index): Type<any> {
    switch (index) {
      case 'HomeComponent': return HomeComponent
      case 'MainComponent': return MainComponent
      case 'NewsComponent': return NewsComponent
      default: return MainComponent
    }
  }

  getLayoutData(params) {
    let index = '';

    if (typeof params.index === 'undefined') index = 'home';
    else index = params.index;

    if (typeof params.lang === 'undefined') this.lang = this.config.getLanguage();
    else this.lang = params.lang;

    this.subscription = this.service.getResponse(`${this.config.impressURL}/api/seo/${index}/${this.lang}`).subscribe(response => {
      this.generateSeo(response, this.lang, index);
    }, (error) => {
      console.log(error);
    });


    if (isPlatformBrowser(this.platformId))
      this.subscription = this.service.getResponse(`${this.config.impressURL}/api/layout/${index}/${this.lang}`).subscribe(res => {
        this.getComponent(res);
      }, (error) => {
        this.dl.getLayout(URL, index, params.lang).then((res: any) => {
          this.getComponent(res);
        });
      });

    if (isPlatformServer(this.platformId))
      this.subscription = this.service.getResponse(`${this.config.impressURL}/api/layout/${index}/${this.lang}`).subscribe(res => {
        this.getComponent(res);
      });
  }

  getComponent(layout) {
    let component = this.getLayout(layout);
    let componentFactory = this.componentFactoryResolver.resolveComponentFactory(component);
    let viewContainerRef = this._vcr;
    viewContainerRef.clear();
    let componentRef = viewContainerRef.createComponent(componentFactory);
  }

  async getData() {
    await this.dl.downloadDataInBackground(this.config.impressURL);
  }

}

container.component.html

<div class="mainContainer">
  <div #container></div>
</div>

app.component.html

<app-menu></app-menu>

<main class="main-content">
  <router-outlet></router-outlet>
</main>

<app-footer></app-footer>

编辑 11:04 04.04.2018 - container.component.html

现在 container.component.html 看起来像

<div class="mainContainer">
  <ng-template #container [ngIf]="layout"></ng-template>
</div>

你知道怎么解决吗?

【问题讨论】:

  • 发布您的 html。考虑使用 *ngIf 或微调器。如果您的数据尚未加载,ngIf 将阻止页面呈现。
  • 好吧,我在主帖中添加了 html。嗯..我没有那样想。也许您的简单解决方案可以解决问题。我去看看。
  • 我不能在 ViewContainerRef 甚至在同一个标​​签中使用 ngIf。
  • 在它周围再放一个 div 吗?
  • 我不能。会出现core.js:1448 ERROR TypeError: Cannot read property 'clear' of undefined 之类的错误。 clear() 是来自 ViewContainerRef 的方法

标签: javascript angular animation components pageload


【解决方案1】:

我终于找到了解决办法。

那部分代码:

 getComponent(layout) {
    let component = this.getLayout(layout);
    let componentFactory = this.componentFactoryResolver.resolveComponentFactory(component);
    let viewContainerRef = this._vcr;
    viewContainerRef.clear();
    let componentRef = viewContainerRef.createComponent(componentFactory);

改为:

  getComponent(layout) {
    let component = this.getLayout(layout);
    let componentFactory = this.componentFactoryResolver.resolveComponentFactory(component);
    let componentRef = this._vcr.createComponent(componentFactory);
  }

在路由器用户内部,我正在清除View Container,所以我之前的代码:

  ngAfterContentInit() {
    this.subscription = this.route.params.subscribe((params) => {
      this.getLayoutData(params);
    })
  }

现在看起来:

  ngAfterContentInit() {
    this.subscription = this.route.params.subscribe((params) => {
      this._vcr.clear();
      this.getLayoutData(params);
    })
  }

【讨论】:

    【解决方案2】:

    几天前我做了一个模态,遇到了你在评论部分描述的类似问题。

    当你在ViewContainerRef 上使用*ngIf 时,它是未定义的,你会得到错误:

    ERROR TypeError: Cannot read property 'clear' of undefined.

    但是,您可以做的是在两者之间添加一个组件。

    在我的例子中,我的对话框容器看起来像这样

    <div class="modal" *ngIf="isOpen">
        <ng-template dialogHostApp></ng-template>
    </div >
    

    我不得不这样做

    <!-- we have to use another component to wrap the template else the viewcontainer is gonna be undefined-->
    <dialog-modal-app [isOpen]="isOpen">
        <ng-template dialogHostApp></ng-template>
    </dialog-modal-app>
    

    在我的模态组件中

    <div *ngIf="isOpen" class="modal">
        <ng-content></ng-content>
    <div>
    

    在你的情况下,这将转化为,而不是这个:

    <div class="mainContainer">
      <ng-template #container [ngIf]="layout"></ng-template>
    </div>
    

    你可以拥有这个

    <main-container-app [isShown]="layout">
      <ng-template #container></ng-template>
    </main-container-app>
    

    在主容器应用中

    <div *ngIf="isOpen" class="mainContainer">
        <ng-content></ng-content>
    <div>
    

    【讨论】:

    • @PatrykPanek 在我的组件中决定是否显示它的布尔值
    • main-container-app 是 app.component 对吗?所以你的意思是删除路由器插座?你能用 TS 文件展示完整的例子吗?
    • @PatrykPanek no no 不要删除路由器,我正在向您提供有关如何解决您在评论部分中所说的问题的信息
    • 我说过ngIf 可以很好地与ng-template 配合使用,如果您编写像[ngIf] 这样的语法。我需要你的组合吗?但是当我让 ngIf 语句工作时,布局组件在我第一次进入页面时不显示,这是我在第一篇文章的评论部分中写的。
    • + 页面还在闪烁
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-19
    • 2012-08-24
    • 1970-01-01
    • 2016-06-05
    相关资源
    最近更新 更多