Angular - 在 API 数据到来后加载动态组件 - 闪烁
Angular - Dynamic components loading after API data come - flickering
我正在构建 Angular 5 将与外部 CMS 集成的应用程序。该 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>
你知道怎么解决吗?
几天前我做了一个模态,遇到了你在评论部分描述的类似问题。
当你在 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>
并在 main-container-app
<div *ngIf="isOpen" class="mainContainer">
<ng-content></ng-content>
<div>
我终于找到了解决办法。
那部分代码:
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);
})
}
我正在构建 Angular 5 将与外部 CMS 集成的应用程序。该 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>
你知道怎么解决吗?
几天前我做了一个模态,遇到了你在评论部分描述的类似问题。
当你在 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>
并在 main-container-app
<div *ngIf="isOpen" class="mainContainer">
<ng-content></ng-content>
<div>
我终于找到了解决办法。
那部分代码:
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);
})
}