我在stackbliz
上创建了这个 https://stackblitz.com/edit/angular-z85mgj
我想知道如何在侧导航内加载动态组件,我的意思是:
从导航组件中,我想在加载组件Acomponent 时单击打开侧导航(在导航模块中声明(,而不是从第 1 页我想打开加载 Bcomponent 的侧导航(在Page1Module中声明(,从子页面 1 开始,侧导航将使用Ccomponent打开(在第1 页模块中声明(). 我想避免基于侧导航服务的值传递的低谷事件在侧导航组件内出现 ngswitch 情况。所以我想动态创建组件或通过服务传递组件的引用。
有可能这样做吗?是一个很好的做法,或者还有其他更好的方法?
此外,您认为路由模块的设置是否正确? 而且,还有更好的方法可以从不同模块中导入的组件打开/关闭侧导航,而无需在服务中使用 Rxjs主题?
你问了很多问题。如果您热衷于动态加载组件以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>
这是堆栈闪电战的工作示例