F
F
Franked2020-09-24 11:58:36
Angular
Franked, 2020-09-24 11:58:36

How to implement one common header for all pages in Angular?

Hello! I am learning Angular and it became necessary to have one common site header for all pages, where, depending on the current route, the header title and available buttons would change. Moreover, the buttons perform the functions of a component that was loaded through a <router-outlet></router-outlet>particular route.

I read somewhere that in such situations the best solution would be to create a service, but I don’t quite understand how to implement it, in the sense that when I go to other pages, my buttons and title would also be automatically updated ... I used BehaivorSubject 's, but this does not help me much, and now I can't hang on any of my BehaivorSubject whims ..?!

Here is what my implementation and structure looks like at the moment:

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;
    }
}



Actually, why my implementation does not solve my problem much:

The fact is that in the implementation of my service I did not manage to make sure that the data is always updated when I move through the history of page visits, back, for example, or forward. I have loaded the data once and that's it. They only change when I directly go to the page for the first time, well, or forced by the URL in the browser.

Tell me, please, how can I achieve what I want?) Thank you for taking the time to my question!

Answer the question

In order to leave comments, you need to log in

1 answer(s)
A
Anton Shvets, 2020-09-24
@Xuxicheta

step 1. You make a service where you store data

private data = new BehaviorSubject(null);

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

Is it clear how to deal with it?
step 2. Display data in the component And that's it, no extra subscriptions and scribbles are needed. Everything will update itself when you call setData. And do not cram a lot of data into one service, many small services are better. In order not to write the same, you can make an abstract class with these methods and inherit from it. Or you can take Akita where everything is already ready and much more for convenient work with data
data = this.myService.selectData()
<p> {{ data | async }} <p>

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question