Angular UI:Angular Flex-Layout

Angular Flex-Layout

Angular Flex-Layout 原本是依附在 Angular Material2 內的模組,它主要實作了 CSS3 的 Flexbox 排版,因此要注意使用者瀏覽器是否支援此語法。

可以到 Can I use 網站查詢。
為了支援 Angular v4.1 AOT,所以 2.0.0-beta.7 以後版本不支援 Angular 2.4.x 版,因此如果有需要可能就必須將專案版本升級到 4.1 以上。
理論上 Angular 2 與 Angular 4 是 100% 相容,但是升級前還是做好版控或備份。
官方提供了範例網站可以觀看排版效果 https://tburleson-layouts-demos.firebaseapp.com/

透過下列指令安裝,可以得到目前最新版本為 2.0.0-beta.9。
npm install --save @angular/flex-layout@latest

img
FlexLayoutModule 加到 src\app\custom-material.module.ts,要使用時只到將 CustomMaterialModule 對應的模組內就可以直接使用。

custom-material.module.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { NgModule } from '@angular/core';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MdIconModule } from '@angular/material';

@NgModule({
imports: [
FlexLayoutModule,
MdIconModule
],
exports: [
FlexLayoutModule,
MdIconModule
]
})
export class CustomMaterialModule { }

建立網站框架

我們可以看到大部分功能性網站都會如下圖一般,上方會有 header 區塊,側邊會有 aside 區塊,剩下最大區域就是呈現內容的 content
img

重置

md-icon 的圖示改回透過 https://fonts.googleapis.com/ 連結,開啟 src\index.html 修改成最初做法。

index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!doctype html>
<html lang="en">

<head>
<meta charset="utf-8">
<title>FirstApp</title>
<base href="/">

<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!-- <link href="./assets/iconfont/material-icons.css" rel="stylesheet"> -->
</head>

<body>
<app-root></app-root>
</body>

</html>

移除 material-design-icons 套件,執行下面指令:
npm uninstall --save material-design-icons
img
刪除 src\assets\iconfont 資料夾。
img
修改 src\styles.scss,將 P 的樣式拿掉,避免影響。

styles.scss
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* You can add global styles to this file, and also import other style files */

/* Angular Material2 Themes */
// @import "~@angular/material/prebuilt-themes/deeppurple-amber.css";
// @import "~@angular/material/prebuilt-themes/indigo-pink.css";
@import "~@angular/material/prebuilt-themes/pink-bluegrey.css";
// @import "~@angular/material/prebuilt-themes/purple-green.css";

/* Material Design Icon */
// @import '~material-design-icons/iconfont/material-icons.css';

// P {
// border: 1px dashed red;
// margin: 8px;
// }

建立獨立模組與元件

建立一個 HomeModule 模組,指令如下:
ng g m home --routing

--routing:跟 ng new 一樣,此參數會建立一個路由模組-HomeRoutingModule,後續會在說明。

img
HomeModule 下分別建立 HomeComponentHeaderComponentAsideComponent,其中 HomeComponent 不要建立資料夾,指令如下:
ng g c home\home --flat
ng g c home\header
ng g c home\aside

img
img
HomeModule 註冊到 AppModule

app.module.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { Page1Component } from './page1/page1.component';
import { Page2Component } from './page2.component';
import { Page3Component } from './page3.component';
import { Page404Component } from './page404/page404.component';
import { OperationModule } from './operation/operation.module';
import { CustomMaterialModule } from './custom-material.module';
import { HomeModule } from './home/home.module';

@NgModule({
declarations: [
AppComponent,
Page1Component,
Page2Component,
Page3Component,
Page404Component
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
CustomMaterialModule,
OperationModule,
HomeModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

修改 src\app\app-routing.module.ts,加入路由規則 home 對應到 HomeComponent,並將 空白的路由規則導引到 home 路徑。

app-routing.module.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { Page1Component } from './page1/page1.component';
import { Page2Component } from './page2.component';
import { Page3Component } from './page3.component';
import { Page404Component } from './page404/page404.component';
import { Opt1Component } from './operation/opt1/opt1.component';
import { HomeComponent } from './home/home.component';

const routes: Routes = [
// { path: '', children: [] },
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{ path: 'p1', component: Page1Component },
{ path: 'p2', component: Page2Component },
{ path: 'p3', component: Page3Component },
{ path: 'opt1', component: Opt1Component },
{ path: '404', component: Page404Component },
{ path: '**', redirectTo: '404' }
// { path: '**', redirectTo: ''}
// { path: '**', component: Page404Component }
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

pathMatch:表示路由規則比對模式,full 表示須完全相同。
正常情況下如果路由規則是重新導向 (redirectTo),都會加上 pathMatch: 'full' 的參數,萬用路由因為無法明確比對,所以可以不加,最簡單的方式就是一律加上去。

修改 src\app\app.component.html,只保留路由插座 (router-outlet)。

app.component.html
1
2
3
4
5
6
7
<!-- <a routerLink="/p1">Page 1</a>
<button routerLink="/p2">Page 2</button>
<span routerLink="/p3">Page 3</span>
<a routerLink="/opt1">Option 1</a>
<hr> -->
<router-outlet></router-outlet>

執行測試,會發現 http://localhost:4200/ 會被自動引導到 http://localhost:4200/home,整個頁面只會出現 HeaderComponent 的樣板。
img

切版

因為會使用到 Angular Flex-Layout 所以將 CustomMaterialModule 註冊到 HomeModule (src\app\home\home.module.ts) 內。

home.module.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { HomeRoutingModule } from './home-routing.module';
import { HomeComponent } from './home.component';
import { HeaderComponent } from './header/header.component';
import { AsideComponent } from './aside/aside.component';
import { CustomMaterialModule } from '../custom-material.module';

@NgModule({
imports: [
CommonModule,
HomeRoutingModule,
CustomMaterialModule
],
declarations: [
HomeComponent,
HeaderComponent,
AsideComponent
]
})
export class HomeModule { }

修改 src\styles.scss,將 htmlbody tag 高度設為 100%,並取消邊界。

styles.scss
1
2
3
4
5
6
7
8
9
10
11
12
html,
body {
height: 100%;
margin: 0px;
}

/* Angular Material2 Themes */
// @import "~@angular/material/prebuilt-themes/deeppurple-amber.css";
// @import "~@angular/material/prebuilt-themes/indigo-pink.css";
@import "~@angular/material/prebuilt-themes/pink-bluegrey.css";
// @import "~@angular/material/prebuilt-themes/purple-green.css";

修改 src\app\home\home.component.html,將其切成3個區塊,並在樣式檔 src\app\home\home.component.scss 內加入背景顏色以檢視版面是否正常。

home.component.scss
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.layout {
background: black;
.header {
background: blue;
}
.main {
background: grey;
.aside {
background: green;
}
.content {
background: orange;
}
}
}

home.component.html
1
2
3
4
5
6
7
<div fxFill fxLayout="column" class="layout">
<div fxFlex="68px" class="header">Header</div>
<div fxFlex fxLayout="row" class="main">
<div fxFlex="200px" class="aside">Aside</div>
<div fxFlex class="content">Content</div>
</div>
</div>

fxFillfxFlexFill 的縮寫,表示填滿區域。
fxLayout:內容項目排列方式,row 表示優先以水平排列;colunm 表示優先以垂直排列。
fxFlex:有值時會以該值作為設定,無值時表示會佔用剩餘空間,其值會受到父元素的 fxLayout 屬性影響,當父元素為水平優先排列時(fxLayout='row'),fxFlex 會影響目前元素的寬度(width);當父元素為垂直優先排列時(fxLayout='colunm'),fxFlex 會影響目前元素的高度(height)。

檢視執行結果。
img
最後再將 HeaderComponent 的 tag-app-headerAsideComponent 的 tag-app-aside 加入到 home.component.html

home.component.html
1
2
3
4
5
6
7
8
9
10
11
<div fxFill fxLayout="column" class="layout">
<div fxFlex="68px" class="header">
<app-header></app-header>
</div>
<div fxFlex fxLayout="row" class="main">
<div fxFlex="200px" class="aside">
<app-aside></app-aside>
</div>
<div fxFlex class="content">Content</div>
</div>
</div>

再觀察結果應該就會發現2個元件的樣板已經套用進來了。
img

[**first-app_2017-09-01_2.zip**](/uploads/first-app_2017-09-01_2.zip)