简体   繁体   English

如何在Angular2中操作特定路径上的组件

[英]How to manipulate a component on specific routes in Angular2

I have a simple TopbarComponent which basically adds a bootstrapish navbar at the top of my view. 我有一个简单的TopbarComponent ,它基本上在我的视图顶部添加了一个bootstrapish导航栏。

Since 90% of my templates should have this directive included, i want to handle it through my app.component which looks like this: 由于90%的模板都应包含此指令,我想通过我的app.component处理它,如下所示:

import ...;

@Component({
    selector: 'my-app',
    templateUrl: 'app/app.component.html',
    directives: [ROUTER_DIRECTIVES, TopbarComponent, ...],
    providers: [ROUTER_PROVIDERS, ...]
})

@RouteConfig([
{
    path: '/login',
    name: 'Login',
    component: LoginComponent
},
{
    path: '/dashboard',
    name: 'Dashboard',
    component: DashboardComponent,
    useAsDefault: true
}
])

with its template looking like this: 其模板看起来像这样:

<my-topbar></my-topbar>

<div class="container-fluid">
    <div class="row">
        <router-outlet></router-outlet>
    </div>
</div>

Now i want use ngIf (or any other way except of hiding it with css) to hide the topbar on several routes, like /login for example. 现在我想使用ngIf (或除了用css隐藏它之外的任何其他方式)来隐藏多个路由上的ngIf ,例如/login I tried several approaches using @CanAcitvate() or implementing OnActivate in my LoginComponent (as well as trying it from my AppComponent ) but with no effects (having problems to even get the functions to fire). 我尝试了几种方法,使用@CanAcitvate()或在我的LoginComponent实现OnActivate (以及从我的AppComponent尝试)但没有任何效果(甚至有问题甚至让函数触发)。 The closest i got was using the Router directly in my AppComponent like this: 我得到的最接近的是直接在我的AppComponent使用Router ,如下所示:

export class AppComponent implements OnInit{
    showTopbar:boolean;

    constructor(private _router:Router) {}

    ngOnInit():any {
        this.showTopbar = this._router.lastNavigationAttempt != '/login';
    }
}

and in my app.component.html i changed the directive to <my-topbar *ngIf="showTopbar"></my-topbar> 在我的app.component.html我将指令更改为<my-topbar *ngIf="showTopbar"></my-topbar>

But this only works on initial load of my app, cause ngOnInit isn't fired on each state change. 但这仅适用于我的应用程序的初始加载,因为每次状态更改都不会触发ngOnInit Is there a similar method i can use (and just can't find) or am i moving in the wrong direction here? 有没有类似的方法我可以使用(并且找不到)或者我在这里走向错误的方向?


Edit: 编辑:

At the moment, the answer of PierreDuc doesn't work for me, but i tried a similar approach using location.path() like this: 目前,PierreDuc的答案对我不起作用,但我尝试使用location.path()这样的类似方法:

constructor(private _location:Location) {}

private get _hideTopbar() : boolean {
    switch(this._location.path()){
        case '/register':
        case '/login':
            return true;
        default:
            return false;
    }
};

Too bad that i can't use the data property in the @RouteConfig in this approach. 太糟糕了,我不能在这种方法中使用@RouteConfig中的data属性。 It would feel better. 感觉会好一些。

I've crashed my head on a similar problem for a while. 我已经在一段时间内遇到了类似的问题。 The solution that i've found could be seen as tricky at first, but can resolve many similar problems of this type for me in the future. 我发现的解决方案起初可能会被视为棘手,但可以在将来为我解决此类问题。

Basically, you have to make the router-outlet emit an event on route change, having your AppComponent listening to that custom event. 基本上,您必须让router-outlet在路由更改时发出事件,让AppComponent侦听该自定义事件。

The first step is to create a custom router-outlet: 第一步是创建自定义路由器插座:

@Directive({
selector: 'custom-router-outlet'
})
export class CustomRouterOutlet extends RouterOutlet {
    private parentRouter:Router;

    constructor(_elementRef: ElementRef,
            _loader: DynamicComponentLoader,
            _parentRouter: Router,
            @Attribute('name') nameAttr: string) {
    super(_elementRef, _loader, _parentRouter, nameAttr);
    this.parentRouter = _parentRouter;
    }

    activate(nextInstruction: ComponentInstruction): Promise<any> {
        //***YOUR CUSTOM LOGIC HERE***
        return super.activate(nextInstruction);
    }
}

This is a very basic implementation of a custom router-outlet. 这是自定义路由器插座的一个非常基本的实现。 Your logic have to be implemented in the activate method, called on each route change. 您的逻辑必须在activate方法中实现,在每次路由更改时调用。 In this method you can check the data property in your route. 在此方法中,您可以检查路径中的data属性。

The main problem is that, right now, Directives can't emit events... They accept only inputs, no outputs can be fired... 主要问题是,目前, 指令不能发出事件......它们只接受输入,不能触发任何输出......

I've found a workaround to this problem: creating a Service for communication between Components and Directives using EventEmitter : 我找到了解决此问题的方法:使用EventEmitter为组件和指令之间的通信创建服务

@Injectable()
export class PubsubService {
    private _pubSubber: EventEmitter<any> = new EventEmitter();

    routeisChanging(obj:any) {
        this._pubSubber.emit(obj);
    }

    onRouteChanged() {
        return this._pubSubber;
    }
}

The AppComponent subscribes to the onRouteChanged event: AppComponent订阅了onRouteChanged事件:

subscription:any;
constructor(private _pubsubService:PubsubService) {
    this.subscription = this._pubsubService.onRouteChanged().subscribe(data => {
        /**YOUR CUSTOM LOGIC HERE*/});
}

The CustomRouterOutlet fires the event in the activate method when needed: CustomRouterOutlet在需要时在activate方法中触发事件:

activate(nextInstruction: ComponentInstruction): Promise<any> {
    //***YOUR CUSTOM LOGIC HERE***
    this._pubsubService.routeisChanging(nextInstruction.routeData.data['hideTopbar']);
    return super.activate(nextInstruction);
}

In this way you can easily implement a communication between the router and the AppComponent , with any logic. 通过这种方式,您可以使用任何逻辑轻松实现路由器与AppComponent之间的通信。

You have to remember to inject the communication Service in the RootComponent of your app. 您必须记住在应用程序的RootComponent中注入通信服务。

Not sure if it is the right way, but you can add any data in the data parameter of the @RouteConfig() object. 不确定它是否是正确的方法,但您可以在@RouteConfig()对象的data参数中添加任何数据。 For instance you can place in the RouteDefinition of Login an object with a hideTopbar setting. 例如,您可以在这个地方RouteDefinitionLogin的对象与hideTopbar设置。 You would only have to place this in a route if it should be set to true: 如果应将其设置为true,则只需将其放在路径中:

warning, untested code ahead :) 警告,未经测试的代码:)

@RouteConfig([{
    path: '/login',
    name: 'Login',
    component: LoginComponent,
    data : {hideTopbar : true}
},
//...
])

You can access this data in the AppComponent class like: 您可以在AppComponent类中访问此数据,如:

export class AppComponent {

    private get _hideTopbar() : boolean {
       return this._data.get('hideTopbar');
    };

    constructor(private _data:RouteData) {}

}

And change the AppComponent template to: 并将AppComponent模板更改为:

<my-topbar *ngIf="!_hideTopbar"></my-topbar>

<div class="container-fluid">
    <div class="row">
        <router-outlet></router-outlet>
    </div>
</div>

Almost there, try this 几乎在那里,试试这个

Template 模板

<my-topbar *ngIf="shouldShowTopbar()">
</my-topbar>

App Component 应用组件

export class AppComponent implements OnInit{

    hideWhen: Array<string> = ['Login', 'Help', 'SomeOtherRoute'];
       // put all the route names where you want it hidden in above array

    constructor(private _router:Router) {}

    shouldShowTopbar() {
     return (hideWhen.indexOf(this._router.currentInstruction.component.routeName) > -1);
    }

}

I has a similar problem. 我有类似的问题。 Maybe this is not the best way. 也许这不是最好的方式。 But I solved it this way: 但我这样解决了:

I created a new component. 我创建了一个新组件。 This component handles all the routes that need the navbar. 此组件处理需要导航栏的所有路径。 The other routes are handled by app.component . 其他路由由app.component处理。

Here is some code to understand: 以下是一些需要了解的代码:

app.component

@RouteConfig([
{
    path: '/...',
    name: '...',
    component: MyComponentWhoAddsNavbar,
    useAsDefault: true
},
{   path: '/login',
    name: 'Login',
    component: LoginComponent
}
])


@Component({
selector: 'my-app',
template: '<router-outlet></router-outlet>', 
})

export class AppComponent {}

Here the MyComponentWhoAddsNavbar 这里是MyComponentWhoAddsNavbar

@RouteConfig([
    put your routes here who need a navbar
])

@Component({
    selector: '...',
    template: `
        <navbar></navbar>
        <router-outlet></router-outlet> 
    `,

 })

export class MyComponentWhoAddsNavbar{} 

I'm working in 我在工作

"@angular/common": "~2.1.0"

The way that I solved this issue was to created an application-wide data object global and a service for it GlobalService . 我解决这个问题的方法是创建一个global应用程序范围的数据对象和一个GlobalService服务。 Any component can subscribe() to all or part of the global object. 任何组件都可以subscribe()全局或部分全局对象。 For your purposes, you should create a boolean property isTopBar: true . 出于您的目的,您应该创建一个布尔属性isTopBar: true You can subscribe your <topBar> component to it and use that value to toggle it. 您可以订阅<topBar>组件并使用该值进行切换。

isTopBar: boolean;

ngOnInit(){
  this._globalService.getIsTopBar().subscribe(isTopBar => this.isTopBar = isTopBar)
}

<my-topbar *ngIf="isTopBar"></my-topbar>

A big advantage that I've found to this kind of 'GlobalService' is that I can save a lot of state information to my backend. 我发现这种“全球服务”的一大优势是我可以将很多状态信息保存到我的后端。 It's less useful in you example, but imaging that you have an internal set of tab; 在你的例子中它不太有用,但你有一套内部标签的成像; you can save which tab the user last selected and reload to that view when they revisit the page at a later date. 您可以保存用户上次选择的选项卡,并在以后重新访问该页面时重新加载到该视图。

This is probably not the right way to do it at all, but it worked for me. 这根本不是完成它的正确方法,但它对我有用。 It's not changing the visibility depending on the route itself, but on the Component shown in that route. 它不是根据路径本身改变可见性,而是根据路线中显示的组件改变可见性。

I was already using a service to feed the Navigation Bar with all the sections, and so on, so I thought it could also tell the navigation bar when to appear or not. 我已经在使用服务来为导航栏提供所有部分,等等,所以我认为它也可以告诉导航栏何时出现。

I basically created a subject in this service determining if it should be visible or not, with a series of methods to change this visibility. 我基本上在这个服务中创建了一个主题,通过一系列方法来确定它是否可见,以改变这种可见性。

@Injectable()
export class NavigationService {

  mobileNavbarIsVisible = true;
  mobileNavbarIsVisibleChanged = new Subject<boolean>();

  constructor() { }

  getSections() {
    return this.sections;
  }

  getMobileNavbarIsVisible() {
    return this.mobileNavbarIsVisible;
  }

  hideMobileNavbar() {
    this.mobileNavbarIsVisible = false;
    this.mobileNavbarIsVisibleChanged.next(this.mobileNavbarIsVisible);
  }

  showMobileNavbar() {
    this.mobileNavbarIsVisible = true;
    this.mobileNavbarIsVisibleChanged.next(this.mobileNavbarIsVisible);
  }

} }

Then, I call those methods in the components that affect visibility -in my case, everytime I edit an item in a list of items, I want the navigation bar to disappear-. 然后,我在影响可见性的组件中调用那些方法 - 在我的情况下,每当我编辑项目列表中的项目时,我希望导航栏消失 - 。 I call the hide method when this component is initiated, and the show method when it is destroyed. 我启动此组件时调用hide方法,并在销毁时调用show方法。

@Component({
  selector: 'app-item-edit',
  templateUrl: './item-edit.component.html',
  styleUrls: ['./item-edit.component.css']
})
export class ItemEditComponent implements OnInit, OnDestroy {

  constructor(private navigationService: NavigationService) {}
  ngOnInit() {
    this.navigationService.hideMobileNavbar();
  }
  ngOnDestroy() {
    this.navigationService.showMobileNavbar();
  }
}

Then, my NavbarComponent is listening to that subject, and simply appears or not depending on that value: 然后,我的NavbarComponent正在监听该主题,并根据该值显示或不显示:

@Component({
  selector: 'app-mobile-navbar',
  templateUrl: './mobile-navbar.component.html',
  styleUrls: ['./mobile-navbar.component.css']
})
export class MobileNavbarComponent implements OnInit {
  isVisible = true;
  ngOnInit() {
    this.isVisible = this.navigationService.getMobileNavbarIsVisible();
    this.navigationService.mobileNavbarIsVIsibleChanged.subscribe(
      (visibilityValue) => this.isVisible = visibilityValue
    );
  }
}

As I said, it's most likely not the right or the most efficient way to do it, but it works if you only want to hide the navigation bar when other component is initiated, because they simply use the Navigation Service as a way of communicating. 正如我所说,它很可能不是正确或最有效的方式,但如果您只想在启动其他组件时隐藏导航栏,它就可以工作,因为它们只是使用导航服务作为通信方式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM