@Franked

Как реализовать одну общую шапку для всех страниц в Angular?

Всем привет! Изучаю Angular и возникла необходимость иметь одну общую шапку сайта для всех страниц, где, в зависимости от текущего роута менялся бы заголовок шапки и доступные кнопки. Причем, кнопки выполняют функции компонента, который был загружен через <router-outlet></router-outlet> по тому или иному роуту.

Где-то вычитал, что в таких ситуациях лучшим решением будет создать service, однако я не совсем понимаю как его реализовать, в том смысле, чтобы при переходе на другие страницы, у меня также автоматически обновлялись бы кнопки и заголовок... Я использовал BehaivorSubject'ы, но это мне не сильно помогает, и не вешать же мне теперь на любой мой каприз BehaivorSubject..?!

Вот как выглядит моя реализация и структура на данный момент:
spoiler

1. Имеется общий компонент MainComponent, чей шаблон выглядит следующим образом:
<!-- ===== [MAIN] ===== -->
<div class="main">
        <!-- ========== [Общая шапка] ========== -->
        <div *ngIf="headerVisibility">
            <div class="header">
              <h2 class="heading_text">{{ЗДЕСЬ_БЫ_ПОДСТАВЛЯЛСЯ_ЗАГОЛОВОК}}</h2>
              <div class="header_buttons">
                  <button
                      *ngFor="let btn of МАССИВ_ВСЕХ_ДОСТУПНЫХ_КНОПОК_ПО_ТЕКУЩЕМУ_РОУТУ(editUser(), addUser())"
                      class="{{btn.НАЗВАНИЕ_КЛАССА_КНОПКИ}}"
                        
                      (click)="btn.ФУНКЦИЯ_ИЗ_ВСТАВЛЯЕМОГО_ПО_РОУТУ_КОМПОНЕНТА()"
                       title="{{btn.НАЗВАНИЕ_КНОПКИ}}"
                    ></button>
              </div>
            </div>
          </div>

        <!-- Вставляемый по роуту компонент -->
        <router-outlet></router-outlet>
</div>


2. Сам код общего компонента MainComponent:
import { Component, OnInit } from '@angular/core';
import { HeaderService } from '@app/shared/services/header.service';

@Component({
  selector: 'app-main'
  templateUrl: './main.component.html',
  styleUrls: ['./main.component.less']
})
export class MainComponent implements OnInit {
  title: string; // текущий заголовок хэдэра
  headerVisibility: boolean; // текущее состояние отображения хэдэра
  headerButtons = new Array<Object>(); // текущий набор кнопок хэдэра

  constructor ( private headerService: HeaderService  ) {  }

  ngOnInit() {
    // подписываемся на объекты, чтобы отлавливать изменения
    this.headerService.visibility.subscribe((newMode: boolean) => {this.headerVisibility = newMode});
    this.headerService.title.subscribe((newTitle: string) => {this.title = newTitle});

    this.headerButtons = this.headerService.getButtons(); // получаем массив кнопок
  }
}


3. Код компонента UsersComponent, который грузится по роуту '/users/'.
import { Component, OnInit } from '@angular/core';
import { HeaderService } from '@app/shared/services/header.service';

@Component({
  selector: 'app-users',
  templateUrl: './users.component.html',
  styleUrls: ['./users.component.less']
})
export class UsersComponent implements OnInit {
    constructor ( private headerService: HeaderService  ) {  }

    ngOnInit() {
        this.headerService.addButton(this.addUser.bind(this), 'add_button_class', 'MyButtonTItle');
    }

    editUser(id: number) {
        console.log('EDITING_USER_HERE');
    }

    addUser() {
        console.log('ADDING_USER_HERE');
    }
}

В нем как раз есть функции editUser, addUser которые должны заполнить массив headerButtons в компоненте MainComponent и тем самым попасть в директиву (click) в шаблон компонента MainComponent

4. Мой вариант реализации сервиса:
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable({
    providedIn: 'root'
})
export class HeaderService
{
    constructor(){}

    visibility = new BehaviorSubject<Boolean>(true); // видимость header'a как такового
    title = new BehaviorSubject<String>('Заголовок хэдэра по умолчанию'); // заголовок
    
    buttons = new Array<Object>(); // кнопки

    addButton(buttonFunc: Function, className: string, title: string) {
        this.buttons.push({
            buttonFunc,
            className,
            title
        });
    }

    getButtons() { // чтобы получить массив объектов с информацией о кнопках
        return this.buttons;
    }
}



Собственно, почему моя реализация не сильно решает мою проблему:

Дело в том, что в реализации моего сервиса у меня не получилось сделать так чтобы всегда данные обновлялись, когда я перемещаюсь по истории посещений страниц, назад, например, или вперед. У меня один раз подгрузились данные и все. Меняются они только когда я непосредственно зайду на страницу в первый раз, ну или принудительно по URL'у в браузере.

Подскажите, пожалуйста, как можно добиться того, что я хочу?) Спасибо, что уделили время моему вопросу!
  • Вопрос задан
  • 373 просмотра
Пригласить эксперта
Ответы на вопрос 1
Xuxicheta
@Xuxicheta Куратор тега Angular
инженер
шаг 1. Делаешь сервис где хранишь данные
private data = new BehaviorSubject(null);

  setData(data) { this.data.next(data) }
  selectData() { return this.data.asObsevable() }

Понятно да, как с этим обращаться?

шаг 2. Выводишь данные в компоненте
data = this.myService.selectData()

<p> {{ data | async }} <p>

И все, никаких лишних подписок и писанины не нада. Все само будет обновляться, когда вызовешь setData.
И не напихивай много данных в один сервис, лучше много маленьких сервисов.
Чтоб одинаковое не писать, можно сделать абстрактный класс с этими методами и наследоваться от него.
А можно взять Akita где уже все готово и много чего еще для удобной работы с данными
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы