Angular — это MVC-фреймворк для создания одностраничных приложений в Javascript.
В этой статье мы узнаем, как установить заголовок браузера (страницы/документа) для всего приложения и как изменить заголовок браузера при перемещении по приложению.
Пример проекта
Пример проекта для демонстрации этой функции. Вы можете скопировать этот проект и запустить его на своем компьютере.
git clone https://github.com/bbachi/angular_title_example.git
// install dependencies and run the project
npm install
npm start
Как установить заголовок браузера для приложения
В каждом одностраничном приложении есть только один файл index.html
, который загружается в браузер до появления в нем элементов фреймворка. В Angular нужно загрузить файл index.html
, который содержит компонент root, минимизированные js-файлы и файлы CSS.
Это первый файл, который загружается в браузер для приложения Angular. Устанавливаем заголовок для всего приложения в теге title
.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Title for the whole app</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>
Это снимок браузера. Заголовок не меняется при изменении страницы или перемещении по приложению.
Что такое TitleService
Привязка данных или интерполяция в Angular не работает, поскольку тег title находится вне тела, а Angular не имеет к нему доступа. Доступ к объекту Document можно получить напрямую и установить title вручную. Однако Angular не рекомендует использовать этот способ.
Angular предоставляет TitleService для управления заголовком документа. Это простой API для получения и установки заголовка. Сервис предоставляет два метода.
class Title {
// получить заголовок
getTitle(): string
// установить заголовок
setTitle(newTitle: string)
}
TitleService в действии
Рассмотрим использование TitleService для чтения и установки заголовка текущего документа.
Сервис Title
нужно импортировать из angular/platform-browser
и добавить в массив Providers. После добавления сервис доступен для использования в любом из компонентов.
import { BrowserModule, Title } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [
Title
],
bootstrap: [AppComponent]
})
export class AppModule { }
Теперь импортируем TitleService в компонент app и добавляем сервис в конструктор, чтобы он был доступен для использования в компоненте.
import { Component } from '@angular/core';
import { Title } from '@angular/platform-browser';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'angulartitle';
constructor(private titleService: Title) {}
setDocTitle(title: string) {
console.log('current title:::::' + this.titleService.getTitle());
this.titleService.setTitle(title);
}
}
Теперь помещаем несколько ссылок в файл шаблона компонента приложения. Метод setDocTitle
используется для чтения и установки заголовка документа.
<h2>Quick Links: </h2>
<ul>
<li><a (click)="setDocTitle( 'About' )">About</a>.</li>
<li><a (click)="setDocTitle( 'Dashboard' )">Dashboard</a>.</li>
<li><a (click)="setDocTitle( 'Profile' )">Profile</a>.</li>
<li><a (click)="setDocTitle( 'My Account' )">My Account</a>.</li>
</ul>
Теперь заголовок изменяется, а в консоли появляются выходные данные при нажатии на ссылки в разделе Quick Links. Вы можете проверить этот результат, используя копию указанного выше проекта.
Как установить заголовок браузера для каждой страницы
Проблема с вышеупомянутым подходом заключается в том, что эти методы нужно поместить в компонент и вызвать для каждой ссылки, находящейся в шаблонах компонентов.
Однако есть более удобный способ сделать это с помощью модуля Angular Routing и Router API. В примере ниже название браузера изменяется по мере загрузки определенных страниц при нажатии на ссылки в заголовке.
Посмотрим, как можно это реализовать. Во-первых, нужно определить модуль маршрутизации для приложения с объектом data. В этом месте нужно разместить заголовок.
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AboutComponent } from './about/about.component';
import { MyprofileComponent } from './myprofile/myprofile.component';
import { MyaccountComponent } from './myaccount/myaccount.component';
import { DashboardComponent } from './dashboard/dashboard.component';
const routes: Routes = [
{ path: 'about', component: AboutComponent, data: {title: 'About'}},
{ path: 'profile', component: MyprofileComponent, data: {title: 'Profile'} },
{ path: 'myaccount', component: MyaccountComponent, data: {title: 'My Account'} },
{ path: 'dashboard', component: DashboardComponent, data: {title: 'Dashboard'} }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Теперь в компоненте App нужно импортировать Router и ActivatedRoute, чтобы подписаться на события маршрутизатора и изменить заголовок с помощью Title service.
Подписываемся на события маршрутизатора, получаем доступ к данным с помощью ActivatedRoute и устанавливаем заголовок с помощью TitleService.
import { Component, OnInit } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { Router, NavigationEnd, ActivatedRoute } from '@angular/router';
import { filter, map } from 'rxjs/operators';
// Опущено для краткости
export class AppComponent implements OnInit {
title = 'angulartitle';
// Опущено для краткости
ngOnInit() {
const appTitle = this.titleService.getTitle();
this.router
.events.pipe(
filter(event => event instanceof NavigationEnd),
map(() => {
const child = this.activatedRoute.firstChild;
if (child.snapshot.data['title']) {
return child.snapshot.data['title'];
}
return appTitle;
})
).subscribe((ttl: string) => {
this.titleService.setTitle(ttl);
});
}
}
В файле выше мы также читаем текущий заголовок, если заголовок не определен в маршрутах. Это можно проверить, удалив объект data в одном из маршрутов в примере проекта.
С модулями
Большую часть времени выполняется отложенная загрузка модулей в целях повышения производительности. Нужно внести небольшие изменения из-за наличия вложенных дочерних элементов.
Я создал модуль Users для отложенной загрузки. Загружаем этот модуль для пути users и ProfileComponent для пути profile. Получаем следующий маршрут: /users/profile.
const routes: Routes = [
{ path: 'about', component: AboutComponent },
{ path: 'profile', component: MyprofileComponent, data: {title: 'Profile'} },
{ path: 'myaccount', component: MyaccountComponent, data: {title: 'My Account'} },
{ path: 'dashboard', component: DashboardComponent, data: {title: 'Dashboard'} },
{ path: 'users', loadChildren: './users/users.module#UsersModule' }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ProfileComponent } from '../profile/profile.component';
import { ListusersComponent } from '../listusers/listusers.component';
const routes: Routes = [
{ path: 'profile', component: ProfileComponent, data: {title: 'User Profile'} },
{ path: 'myaccount', component: ListusersComponent, data: {title: 'List Users'} }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class UsersRoutingModule { }
Цикл while должен выполняться до тех пор, пока не будет найден последний дочерний элемент.
ngOnInit() {
const appTitle = this.titleService.getTitle();
this.router
.events.pipe(
filter(event => event instanceof NavigationEnd),
map(() => {
let child = this.activatedRoute.firstChild;
while (child.firstChild) {
child = child.firstChild;
}
if (child.snapshot.data['title']) {
return child.snapshot.data['title'];
}
return appTitle;
})
).subscribe((ttl: string) => {
this.titleService.setTitle(ttl);
});
}
Вывод в браузере:
Выводы:
- В каждом одностраничном приложении есть index.html.
- Заголовок для всего приложения можно установить с тегом title в разделе head файла index.html.
- Тег title не доступен через привязку данных Angular.
- Angular предоставляет TitleService для получения и установки текущего заголовка документа.
- Получить и установить заголовок для каждой страницы можно в одном месте, подписавшись на маршруты Angular.
- Для вложенных модулей необходимо выполнять цикл до нахождения последнего дочернего элемента.
Заключение
TitleService — это удобный метод для чтения и установки заголовка документа. Angular не рекомендует обращаться к объекту Document вручную для получения и установки заголовка.
Читайте также:
- ANGULAR v7: подсказки командной строки, виртуальная прокрутка, перетаскивание и др.
- Битва трендов: React vs Angular vs Vue
- Визуализация данных и веб-отчёты в Angular
Перевод статьи Bhargav Bachina: Dynamic Page Titles in Angular