在確認此使用者的相關資訊後會進入核心模塊,也是佈局畫面的底。
很多功能模塊會以延遲子路由的方式,掛在核心模塊裡。
<!--core.component.html-->
<div id="side" [@slideInOut]="animationState">
<app-menu></app-menu>
</div>
<div class="core-container">
<header class="flex space-between"></header>
<main class="flex straight-flex">
<app-tab></app-tab>
<router-outlet></router-outlet>
</main>
<footer class="flex"></footer>
</div>
路由守衛
做判斷,CoreComponent
。@Injectable()
export class GuardService implements CanActivate {
constructor(
private router: Router,
private service: AppService,
private loginService: LoginService
) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean> | Promise<boolean> | boolean {
return this.loginService.loginState.pipe(
map(e => {
if (e) return true;
}),
catchError(e => {
this.service.backLogin();
return Observable.throw(e);
})
);
}
}
Menu
、tab
跟其他子模塊router-outlet
,那除了上述其他加載的事情,
CoreComponent 本身要做的事情大概如下:
-src
|-app
|-app.component.css
|-app.component.html
|-app.component.ts
|-app.module.ts
|-core
|-menu
...
|-animations.ts
|-core-routing.module.ts
|-core.component.css
|-core.component.html
|-core.component.ts
|-core.module.ts
|-core.service.ts
|-guard.service.ts
在右上角有個切換語系,所以如果語系有更動,
全局都要更換翻譯。
core.service.ts
:@Injectable()
export class CoreService {
constructor(
private service: AppService,
private storageService: StorageService,
public translate: TranslateService
) {}
useLanguage(lang: string) {
return this.translate.use(lang);
}
getNowLang() {
return this.storageService.getLangStorage();
}
logout(): void {
this.service.backLogin();
}
}
--
core.component.ts
:export class CoreComponent implements OnInit, OnDestroy {
COPYRIGHT = COPYRIGHT;
LANG = LANG;
TOGGLELANG = TOGGLELANG;
constructor(
private service: CoreService,
private userService: UserService,
...
) {}
...
useLanguage($event) {
if (!!$event) {
this.service.useLanguage($event);
}
}
getUserAccount(): string {
if (
!!this.userService.getUser() &&
!!this.userService.getUser().account
) {
return this.userService.getUser().account;
}
}
}
--
core.component.html
:<div class="flex align-center text-red" *ngIf="TOGGLELANG">
<div class="base-icons">
<mat-icon svgIcon="global" (click)="select.open()"></mat-icon>
<mat-select
#select
[value]="service.getNowLang()"
(valueChange)="useLanguage($event)"
color="red"
style="width:100px;"
class="b"
>
<mat-option *ngFor="let lang of LANG"
[value]="lang.short" class="b">
{{ lang.long | translate }}
</mat-option>
</mat-select>
</div>
</div>
通常如果版面有做RWD,則 Menu 就會有收放。
變成平板尺寸的時候,就會出現漢堡按鈕,
點擊就會有動畫開菜單。
需要做到以下幾件事情:
hamburgers
套件animations.ts
(Menu收放特效)--
hamburgers
套件npm install hamburgers -g
style.css
@import "~hamburgers/dist/hamburgers.min.css";
animations.ts
import { trigger, state, style, transition, animate, group } from "@angular/animations";
export const SlideInOutAnimation = [
trigger("slideInOut", [
state(
"in",
style({
"max-height": "100%",
"max-width": "100%",
opacity: "1",
visibility: "visible"
})
),
state(
"out",
style({
"max-height": "0%",
"max-width": "0%",
opacity: "0",
visibility: "hidden"
})
),
transition("in => out", [
group([
animate(
"200ms ease-in-out",
style({
visibility: "hidden"
})
),
animate(
"300ms ease-in-out",
style({
"max-height": "0%",
"max-width": "0%"
})
)
])
]),
transition("out => in", [
group([
animate(
"1ms ease-in-out",
style({
visibility: "visible"
})
),
animate(
"400ms ease-in-out",
style({
"max-height": "100%",
"max-width": "100%"
})
),
animate(
"500ms ease-in-out",
style({
opacity: "1"
})
)
])
])
])
];
並註冊在core.component.ts
@Component({
selector: "app-core",
templateUrl: "./core.component.html",
styleUrls: ["./core.component.css"],
animations: [SlideInOutAnimation]
})
export class CoreComponent implements OnInit, OnDestroy {
...
subscription: Subscription;
animationState = "in";
isHamburger = false;
isDevice = "";
constructor(
private breakpointObserver: BreakpointObserver,
...
) {}
ngOnInit() {
if (!!this.service.isHamburgerIn()) {
this.subscription = this.service.isHamburgerIn()
.subscribe(val => {
if (this.isDevice==='mb') {
this.isHamburger = val !== "";
this.toggleMenu();
}
});
}
this.breakpointObserver.observe("(max-width: 1199px)")
.subscribe(r => {
this.isHamburger = r.matches;
this.isDevice = r.matches ? "mb" : "pc";
this.toggleMenu();
});
this.goHome();//預設頁面為首頁
}
ngOnDestroy() {
if (!!this.subscription) {
this.subscription.unsubscribe();
}
}
toggle() {
this.isHamburger = !this.isHamburger;
this.toggleMenu();
}
toggleMenu() {
this.animationState = this.isHamburger ? "out" : "in";
}
...
}
breakpointObserver
會偵測目前螢幕的尺寸,this.isHamburger = true;
this.isToggle = true;
isHamburger
是指 是否出現漢堡選單按鈕。isDevice
是指 目前螢幕尺寸是否為 mb 版。
當螢幕為 mb 版時 isHamburger
才會為true。
則Menu會消失 animationState為'out'。
需要注意的是isHamburgerIn()
為什麼這邊會出現Observable?
原因是因為當Menu出現,點擊頁面項目後Menu就會關閉,
並且要出現漢堡選單按鈕。
core.service.ts
@Injectable()
export class CoreService {
private hamburgerSubject = new BehaviorSubject<string>("");
constructor(
...
) {}
nextHamburger(next: string) {
this.hamburgerSubject.next(next);
}
isHamburgerIn(): Observable<string> {
if (!!this.hamburgerSubject) {
return this.hamburgerSubject.asObservable();
}
return null;
}
...
}
--
Menu內的頁面項目被點擊則會觸發:
(扮演推送值)
//menu.component.ts
this.coreService.nextHamburger("next");
--
則CoreComponent訂閱:
(扮演接值)
//core.component.ts
ngOnInit() {
if (!!this.service.isHamburgerIn()) {
this.subscription = this.service.isHamburgerIn().subscribe(val => {
if (this.isDevice==='mb') {
this.isHamburger = val !== "";
this.toggleMenu();
}
});
}
...
}
...
toggleMenu() {
this.animationState = this.isHamburger ? "out" : "in";
}
...
https://stackblitz.com/edit/ngcms-corecomp
一開始會跳出提示視窗顯示fail為正常,
請先從範例專案裡下載或是複製db.json
到本地端,
並下指令:
json-server db.json
json-server開啟成功後請連結此網址:
https://ngcms-corecomp.stackblitz.io/cms?token=bc6e113d26ce620066237d5e43f14690
您好:
照著您的範例實作,但您的範例中,好像少放入了model/tab.ts
所以有點卡住了,不知您是否可以把它們補全,或是另外把這檔案回給我也可以
import { ITabMain, ITabBase } from "../../model/tabs";
真的很不好意思,還要這樣煩您
謝謝您
Archer
謝謝您,還沒有看到一篇,所以不知道後續有說明,真的太感謝您了
您好,
看完了您的範例,真的是對我幫助很大,有很多的啟發,
尢其是 webService 及 dataService,
把資料的存取全部放入到這兩個泛型服務中,真的是太棒了,
對程式碼可以減少的非常多,
謝謝您的無私分享.
good luck
Archer
感謝你的回覆~
很開心有幫到你