Angular5如何在里面加载组件打开sidenav

时间:2018-05-22 23:08:03

标签: angular rxjs angular-material angular5 angular-services

我在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?

1 个答案:

答案 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>

以下是working example at stackblitz