我在stackbliz上创建了这个 https://stackblitz.com/edit/angular-z85mgj
我想知道如何加载sidenav里面的fly组件,我的意思是:
从 NavigatoComponent 我想打开sidenav点击加载 Acomponent 组件(在 NavigatorModule 中声明),而不是从page1我要打开加载 Bcomponent 的sidenav(在 Page1Module 中声明),从subpage1开始,sidenav将以 Ccomponent 打开(在 Page1Module中声明)。
我想根据通过 sidenavService 事件传递的值避免sidenav组件内的ngswitch情况。所以我想在飞行中创建组件或通过服务传递组件的参考。
这可能吗?是一种很好的做法还是有其他更好的做法?
另外你认为路由模块是否正确设置? 而且,有一种更好的方法可以在不使用服务中的Rxjs 主题的情况下,从不同模块中导入的组件打开/关闭sidenav?
答案 0 :(得分:1)
你提出了很多问题。如果您热衷于将组件动态加载到SideNav
内容,那么这是一个解决方案。
<强> sidenav.service.ts 强>
import {
Injectable,
Component,
ComponentRef,
NgModule,
NgModuleRef,
Injector,
Compiler,
ViewContainerRef
} from '@angular/core';
import {Subject} from 'rxjs';
@Injectable()
export class SidenavService {
public subject = new Subject<string>();
private sidenavVCRef: ViewContainerRef;
constructor(private compiler: Compiler, private injector: Injector,
private ngModuleRef: NgModuleRef<any>) {}
// getSidenavVCRef() { return this.sidenavVCRef; }
setSidenavVCRef(vc: ViewContainerRef) { this.sidenavVCRef = vc; }
publish(eventName: string, componentName?: string) {
console.log("qua", this.subject, eventName);
if (componentName) {
this.loadComponent(componentName).then(() => {
this.subject.next(eventName);
});
} else {
this.subject.next(eventName);
}
}
// Create component on the fly, and insert to the view container
private loadComponent(name: string): Promise<boolean> {
// create component and module on the fly
const template = `<span>I am ${name}</span>`;
const tmpComp = Component({template : template})(class {});
const tmpModule = NgModule({declarations : [ tmpComp ]})(class {});
// compile the created module and component,
// and get a hold of it with a componentRef
return new Promise(resolve => {
this.compiler.compileModuleAndAllComponentsAsync(tmpModule)
.then(factories => {
// Compiler will return all factories for corresponding components
// to be created
// Just take the first one as it is the only one.
const f = factories.componentFactories[0];
// create (component) view from its factory and return componentRef
const compRef = f.create(this.injector, [], null, this.ngModuleRef);
// just detach all previous views from view container
this.sidenavVCRef.detach();
// insert it to its view container
this.sidenavVCRef.insert(compRef.hostView);
resolve(true);
})
.catch(error => { console.log(error); });
});
}
}
<强> navigator.component.ts 强>
import {
Component,
OnInit,
ViewChild,
ViewContainerRef,
AfterViewInit
} from '@angular/core';
import {SidenavService} from '../sidenav.service';
@Component({
selector : 'app-navigator',
templateUrl : './navigator.component.html',
styleUrls : [ './navigator.component.css' ]
})
export class NavigatorComponent implements OnInit,
AfterViewInit {
@ViewChild('sidenav') sidenav;
@ViewChild('vcDynamicComp', {read : ViewContainerRef}) vc: ViewContainerRef;
constructor(private sidenavService: SidenavService, ) {}
ngOnInit() {
this.sidenavService.subject.subscribe((value) => {
console.log("value:", value);
switch (value) {
case 'open':
this.openSidenav();
break;
case 'close':
this.closeSidenav();
break;
default:
break;
}
});
}
ngAfterViewInit() {
// set sidenav view container reference at as the first thing when view is
// available
this.sidenavService.setSidenavVCRef(this.vc);
}
private openSidenav() { this.sidenav.toggle(); }
private closeSidenav() { this.sidenav.close(); }
openSN() { this.sidenavService.publish('open', 'Component A'); }
}
<强> navigator.component.html 强>
<mat-sidenav-container class="example-container">
<mat-sidenav #sidenav mode="side" opened="false">
Sidenav content
<div #vcDynamicComp></div>
</mat-sidenav>
<mat-sidenav-content>
<mat-toolbar>My App
<span style=" flex: 1 1 auto"></span>
<button mat-raised-button routerLink="/page1">Page1</button>
<button mat-raised-button routerLink="/page2">Page2</button>
<button mat-raised-button color="primary" (click)="openSN()">Open sidenav</button>
</mat-toolbar>
<main>
<router-outlet></router-outlet>
</main>
</mat-sidenav-content>
</mat-sidenav-container>