Доступ к родительским @Компонент и vars из * Маршрутизированный * Детский компонент

Я пытаюсь переключить навигационное меню, расположенное в верхней части моего основного шаблона приложения, используя кнопку во вложенном дочернем компоненте. Я не могу понять, как добраться до компонента sidenav в родительском, чтобы сообщить ему sidenav.open().

Я знаю о @Input и @Output для дочернего компонента, но, насколько я понимаю, для использования этого мне нужно иметь какой-то тэг DOM для дочернего компонента, к которому они присоединяются? Например:

<app>
  <sidenav-component #sidenav>...</sidenav-component>

  <child [someInput]="some_parent_var" (childOpensNav)="sidenav.open()"></child>
</app>

Тонны статей о том, как это сделать. Проблема в том, что я направляюсь к этому компоненту, поэтому тег <child> не существует явно в коде. Скорее мой код выглядит так:

<app>
  <sidenav-component #sidenav>...</sidenav-component>

  <router-outlet></router-outlet>
</app>

Если у меня есть дочерний компонент, который перенаправляется, как я могу сделать sidenav.open() или каким-то образом получить доступ к компоненту родителя от дочернего?

Некоторые мысли: я провел некоторое исследование и подумал о нескольких подходах и не уверен, что они верны или даже будут работать... Один подход заключается в использовании услуги Injector и попытке перейти к родительскому, но это кажется неправильным:

// child component
constructor(injector: Injector) {
  this.something = injector.parent.get(Something);
}

Или, возможно, создание службы в родительском объекте, как-то привязанное к компоненту Sidenav, а затем вставляя эту службу в дочерний элемент??

Ответ 1

Самый простой и чистый способ - действительно использовать сервис.

Служба, как она может выглядеть:

export class DomService {
    sidebarVisible: boolean = true;

    showSidebar() {
        sidebarVisible = true;
    }

    hideSidebar() {
        sidebarVisible = false;
    }

    toggleSidebar() {
        sidebarVisible = !sidebarVisible;
    }
}

В своем вызове bootstrap добавьте службу в список поставщиков:

bootstrap(App, [
    // other providers
    DomService
]);

В компонентах (возможно, в app.ts, но также в вашем sidenav.ts), где вы хотите показать/скрыть боковую панель, добавьте службу для инъекции:

constructor(private _domService: DomService) {

}

В вашем шаблоне, где вы хотите переключить/показать/скрыть, вы можете сделать это сейчас:

<sidenav-component *ngIf="_domService.sidebarVisible">...</sidenav-component>

<div id="toggle-sidebar" (click)="_domService.toggleSidebar()">toggle</div>

Ответ 2

Мне нравится принятый ответ, кажется более надежным способом Angular сделать это правильно (особенно если вы собираетесь добавлять дополнительные параметры).

Однако мне нужен быстрый грязный доступ к глобальному элементу по идентификатору. Все говорят об использовании @ViewChild, но я хочу идти по дереву. Я только что вернулся в школу и использовал это в методе компонентов:

document.getElementById('nav-panel').className = 'hide';

Если мы хотим, чтобы несколько копий этого работали (например, левое и правое меню), нам нужно что-то вводить детям, чтобы они знали, для какого идентификатора искать.