@@ -2,20 +2,26 @@ import { DOCUMENT } from '@angular/common';
2
2
import { Inject , Injectable , Injector , OnDestroy , Optional } from '@angular/core' ;
3
3
import { Title } from '@angular/platform-browser' ;
4
4
import { ActivatedRoute , Router } from '@angular/router' ;
5
- import { Subscription , filter } from 'rxjs' ;
5
+ import { Observable , of , map , delay , isObservable , switchMap , Subject , takeUntil , Subscription } from 'rxjs' ;
6
6
7
7
import type { NzSafeAny } from 'ng-zorro-antd/core/types' ;
8
8
9
9
import { AlainI18NService , ALAIN_I18N_TOKEN } from '../i18n/i18n' ;
10
10
import { MenuService } from '../menu/menu.service' ;
11
11
12
+ export interface RouteTitle {
13
+ title ?: string | Observable < string > ;
14
+ titleI18n ?: string ;
15
+ }
16
+
12
17
@Injectable ( { providedIn : 'root' } )
13
18
export class TitleService implements OnDestroy {
14
19
private _prefix : string = '' ;
15
20
private _suffix : string = '' ;
16
21
private _separator : string = ' - ' ;
17
22
private _reverse : boolean = false ;
18
- private i18n$ : Subscription ;
23
+ private destroy$ = new Subject < void > ( ) ;
24
+ private tit$ ?: Subscription ;
19
25
20
26
readonly DELAY_TIME = 25 ;
21
27
@@ -28,92 +34,126 @@ export class TitleService implements OnDestroy {
28
34
private i18nSrv : AlainI18NService ,
29
35
@Inject ( DOCUMENT ) private doc : NzSafeAny
30
36
) {
31
- this . i18n$ = this . i18nSrv . change . pipe ( filter ( ( ) => ! ! this . i18n $) ) . subscribe ( ( ) => this . setTitle ( ) ) ;
37
+ this . i18nSrv . change . pipe ( takeUntil ( this . destroy $) ) . subscribe ( ( ) => this . setTitle ( ) ) ;
32
38
}
33
39
34
- /** 设置分隔符 */
40
+ /**
41
+ * Set separator
42
+ *
43
+ * 设置分隔符
44
+ */
35
45
set separator ( value : string ) {
36
46
this . _separator = value ;
37
47
}
38
48
39
- /** 设置前缀 */
49
+ /**
50
+ * Set prefix
51
+ *
52
+ * 设置前缀
53
+ */
40
54
set prefix ( value : string ) {
41
55
this . _prefix = value ;
42
56
}
43
57
44
- /** 设置后缀 */
58
+ /**
59
+ * Set suffix
60
+ *
61
+ * 设置后缀
62
+ */
45
63
set suffix ( value : string ) {
46
64
this . _suffix = value ;
47
65
}
48
66
49
- /** 设置是否反转 */
67
+ /**
68
+ * Set whether to reverse
69
+ *
70
+ * 设置是否反转
71
+ */
50
72
set reverse ( value : boolean ) {
51
73
this . _reverse = value ;
52
74
}
53
75
54
- /** 设置默认标题名 */
76
+ /**
77
+ * Set the default CSS selector string
78
+ *
79
+ * 设置默认CSS选择器字符串
80
+ */
81
+ selector ?: string | null ;
82
+
83
+ /**
84
+ * Set default title name
85
+ *
86
+ * 设置默认标题名
87
+ */
55
88
default = `Not Page Name` ;
56
89
57
- private getByElement ( ) : string {
58
- const el = ( this . doc . querySelector ( '.alain-default__content-title h1' ) ||
59
- this . doc . querySelector ( '.page-header__title' ) ) as HTMLElement ;
60
- if ( el ) {
61
- let text = '' ;
62
- el . childNodes . forEach ( val => {
63
- if ( ! text && val . nodeType === 3 ) {
64
- text = val . textContent ! . trim ( ) ;
90
+ private getByElement ( ) : Observable < string > {
91
+ return of ( '' ) . pipe (
92
+ delay ( this . DELAY_TIME ) ,
93
+ map ( ( ) => {
94
+ const el = ( ( this . selector != null ? this . doc . querySelector ( this . selector ) : null ) ||
95
+ this . doc . querySelector ( '.alain-default__content-title h1' ) ||
96
+ this . doc . querySelector ( '.page-header__title' ) ) as HTMLElement ;
97
+ if ( el ) {
98
+ let text = '' ;
99
+ el . childNodes . forEach ( val => {
100
+ if ( ! text && val . nodeType === 3 ) {
101
+ text = val . textContent ! . trim ( ) ;
102
+ }
103
+ } ) ;
104
+ return text || el . firstChild ! . textContent ! . trim ( ) ;
65
105
}
66
- } ) ;
67
- return text || el . firstChild ! . textContent ! . trim ( ) ;
68
- }
69
- return '' ;
106
+ return '' ;
107
+ } )
108
+ ) ;
70
109
}
71
110
72
- private getByRoute ( ) : string {
111
+ private getByRoute ( ) : Observable < string > {
73
112
let next = this . injector . get ( ActivatedRoute ) ;
74
113
while ( next . firstChild ) next = next . firstChild ;
75
- const data = ( next . snapshot && next . snapshot . data ) || { } ;
114
+ const data : RouteTitle = ( next . snapshot && next . snapshot . data ) || { } ;
76
115
if ( data . titleI18n && this . i18nSrv ) data . title = this . i18nSrv . fanyi ( data . titleI18n ) ;
77
- return data . title ;
116
+ return isObservable ( data . title ) ? data . title : of ( data . title ! ) ;
78
117
}
79
118
80
- private getByMenu ( ) : string {
119
+ private getByMenu ( ) : Observable < string > {
81
120
const menus = this . menuSrv . getPathByUrl ( this . injector . get < Router > ( Router ) . url ) ;
82
- if ( ! menus || menus . length <= 0 ) return '' ;
121
+ if ( ! menus || menus . length <= 0 ) return of ( '' ) ;
83
122
84
123
const item = menus [ menus . length - 1 ] ;
85
124
let title ;
86
125
if ( item . i18n && this . i18nSrv ) title = this . i18nSrv . fanyi ( item . i18n ) ;
87
- return title || item . text ! ;
88
- }
89
-
90
- private _setTitle ( title ?: string | string [ ] ) : void {
91
- if ( ! title ) {
92
- title = this . getByRoute ( ) || this . getByMenu ( ) || this . getByElement ( ) || this . default ;
93
- }
94
- if ( title && ! Array . isArray ( title ) ) {
95
- title = [ title ] ;
96
- }
97
-
98
- let newTitles : string [ ] = [ ] ;
99
- if ( this . _prefix ) {
100
- newTitles . push ( this . _prefix ) ;
101
- }
102
- newTitles . push ( ...( title as string [ ] ) ) ;
103
- if ( this . _suffix ) {
104
- newTitles . push ( this . _suffix ) ;
105
- }
106
- if ( this . _reverse ) {
107
- newTitles = newTitles . reverse ( ) ;
108
- }
109
- this . title . setTitle ( newTitles . join ( this . _separator ) ) ;
126
+ return of ( title || item . text ! ) ;
110
127
}
111
128
112
129
/**
113
- * Set the document title, will be delay `25ms`, pls refer to [#1261](https://github.com/ng-alain/ng-alain/issues/1261)
130
+ * Set the document title
114
131
*/
115
132
setTitle ( title ?: string | string [ ] ) : void {
116
- setTimeout ( ( ) => this . _setTitle ( title ) , this . DELAY_TIME ) ;
133
+ this . tit$ ?. unsubscribe ( ) ;
134
+ this . tit$ = of ( title )
135
+ . pipe (
136
+ switchMap ( tit => ( tit ? of ( tit ) : this . getByRoute ( ) ) ) ,
137
+ switchMap ( tit => ( tit ? of ( tit ) : this . getByMenu ( ) ) ) ,
138
+ switchMap ( tit => ( tit ? of ( tit ) : this . getByElement ( ) ) ) ,
139
+ map ( tit => tit || this . default ) ,
140
+ map ( title => ( ! Array . isArray ( title ) ? [ title ] : title ) ) ,
141
+ takeUntil ( this . destroy$ )
142
+ )
143
+ . subscribe ( titles => {
144
+ let newTitles : string [ ] = [ ] ;
145
+ if ( this . _prefix ) {
146
+ newTitles . push ( this . _prefix ) ;
147
+ }
148
+ newTitles . push ( ...( titles as string [ ] ) ) ;
149
+ if ( this . _suffix ) {
150
+ newTitles . push ( this . _suffix ) ;
151
+ }
152
+ if ( this . _reverse ) {
153
+ newTitles = newTitles . reverse ( ) ;
154
+ }
155
+ this . title . setTitle ( newTitles . join ( this . _separator ) ) ;
156
+ } ) ;
117
157
}
118
158
119
159
/**
@@ -124,6 +164,8 @@ export class TitleService implements OnDestroy {
124
164
}
125
165
126
166
ngOnDestroy ( ) : void {
127
- this . i18n$ . unsubscribe ( ) ;
167
+ this . tit$ ?. unsubscribe ( ) ;
168
+ this . destroy$ . next ( ) ;
169
+ this . destroy$ . complete ( ) ;
128
170
}
129
171
}
0 commit comments