因为项目原因又玩上了Angular2(v4.0+),《玩转Angular2》系列用于探索一些灵活或者新的用法。
本文记录制作左侧菜单,并使用路由自动定位的过程。
调整配置
上一篇有些配置不是很合适,故这里我们先进行调整。
全局注入jQuery
上篇我们是这样注入jQuery的:
1 2
| window['$'] = window['jQuery'] = require('./assets/js/jquery.min.js');
|
这样的全局注入其实可能会导致一些问题(不知道是不是配置不正确,导致本骚年的其他jQuery插件失效),所以我们还是用webpack来注入。
首先安装jQuery的依赖:
1
| npm install jquery --save
|
然后在webpack的插件配置plugins
中添加:
1 2 3 4 5 6
| plugins: [ new webpack.ProvidePlugin({ $: "jquery", jQuery: "jquery" }) ]
|
就可以挪掉上面不够优雅的注入方式了。
开启source-map
之前我们的webpack配置中也添加了devtool: 'source-map'
,但是这个需要配合source-map-loader
才能生效:
1
| npm install -D source-map-loader
|
然后在webpack中添加loader:
1 2 3 4 5 6 7 8 9 10 11
| rules: [ { test: /\.js$/, use: ["source-map-loader"], enforce: "pre", exclude: [ path.join(__dirname, 'node_modules', '@angular/compiler'), path.join(__dirname, 'node_modules', 'rxjs') ] } ]
|
这里我们需要排除@angular/compiler
以及rxjs
,可能还有其他一些依赖,不然会有webpack的warning
。详细也可以查看类似的issue-Warnings displayed by webpack when using source-map-loader。
压缩代码
webpack自带了一个压缩插件UglifyJsPlugin
,我们添加以下配置就可以生效:
1 2 3 4 5 6 7
| plugins: [ new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) ]
|
压缩后的代码体积大大减小,但会消耗一定的编译速度,故一般打包到生产环境才会使用。
分离代码
如果说我们需要分离其他的代码,像一些依赖的代码,或者是css代码,也可以通过配置实现。
- 抽离依赖
vender.js
文件
1 2 3 4 5 6 7
| new CommonsChunkPlugin({ name: 'vendors', filename: 'vendors.js', minChunks: function(module) { return isExternal(module); } })
|
关于isExternal()
函数,用了很简单的方式进行:
1 2 3 4 5 6 7
| function isExternal(module) { var userRequest = module.userRequest; if (typeof userRequest !== 'string') { return false; } return userRequest.indexOf('node_modules') >= 0; }
|
- 将样式从js中抽出,生成单独的
.css
样式文件。即把所以的css打包合并:
1 2 3
| new ExtractTextPlugin('style.css', { allChunks: true })
|
这些大家下来可以配置,本骚年就不在项目这使用了。
创建左侧菜单
添加配置文件
这里我们为了方便拓展,使用配置的方式来自定义菜单,这样每次我们需要修改的时候只需要调整配置文件就好了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| export const menus = [{ icon: 'fa-home', text: '页面管理', childMenus: [{ link: '/home/page-setting', text: '页面配置' }, { link: '/home/page-rebuild', text: '页面重现' }] }, { icon: 'fa-cubes', text: '使用说明', link: '/home/page-handbook' }];
|
这里暂时限定我们最多为二级菜单,跟之前搭建管理项目的方式一致。
添加html文件
这里我们可以遍历配置文件生成菜单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <div class="col-md-3 left_col menu_fixed"> <div class="left_col scroll-view"> <div class="main_menu_side hidden-print main_menu"> <div class="menu_section"> <ul class="nav side-menu metismenu" id="sidebar-menu"> <li class="topper-menu" *ngFor="let menu of menus;" [ngClass]="menu.link && isActive(menu.link) ? 'active' : ''"> <a *ngIf="menu.link" [routerLink]="menu.link"><i class="fa" [ngClass]="menu.icon"></i> {{menu.text}}</a> <a *ngIf="!menu.link" class="has-arrow"><i class="fa" [ngClass]="menu.icon"></i> {{menu.text}}</a> <ul class="nav child_menu slide"> <li *ngFor="let childMenu of menu.childMenus;" class="slide-item" routerLinkActive="current-page"> <a [routerLink]="childMenu.link">{{ childMenu.text }}</a> </li> </ul> </li> </ul> </div> </div> </div> </div>
|
这里可以看到,我们使用*ngFor
来进行遍历,然后我们大致可以得到我们的component需要以下功能:
[routerLink]
: 链接跳转
routerLinkActive
: 路由激活时样式
isChildMenuActived
: 判断该菜单下是否有子菜单的路由处于激活状态
这里我们需要注意的是:
- 使用angular自带的常用指令,像
*ngFor
、ngClass
等,需要在注册@NgModule
时引入CommonModule
。
- 使用angular里面路由常用指令,像
[routerLink]
、routerLinkActive
等,需要在注册@NgModule
时引入RouterModule
。
- 使用angular里面表单常用指令,像
[(ngModel)]
等,需要在注册@NgModule
时引入FormModule
。
像我们的HomeModule
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; import { CommonModule } from '@angular/common'; import { HomeRoutes } from './home.routes'; import { HomeComponent } from './home.component'; import { SidebarComponent } from './sidebar/sidebar.component'; @NgModule({ declarations: [ HomeComponent, SidebarComponent ], imports: [ FormsModule, CommonModule, RouterModule.forChild(HomeRoutes) ], providers: [] }) export class HomeModule { }
|
添加component
组件使用了简单的jQuey插件metisMenu,详细说明请参考文档,这里我们只需要知道调用$().metisMenu()
的时候,若有<li class="active">
则自动将该<li>
设置为激活形式,此时我们在路由跳转结束的时候就可以获取对应激活路由然后初始化菜单状态了。
我们直接在代码中说明吧:
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 36 37 38 39 40 41 42 43 44 45 46
| import {Component, ElementRef} from '@angular/core'; import {ActivatedRoute, Router, NavigationEnd} from '@angular/router'; import {menus} from './sidebar.config'; import 'rxjs/Rx'; @Component({ selector: 'home-sidebar', templateUrl: './sidebar.component.html', }) export class SidebarComponent { menus: any[] = menus; constructor(private route: ActivatedRoute, private router: Router, el: ElementRef) { this.router.events.subscribe(event => { if (event instanceof NavigationEnd) { const $menu = $(el.nativeElement).find('#sidebar-menu'); this.menus.forEach((menu, index) => { if (this.isChildMenuActived(menu)) { $menu.find('li.topper-menu').eq(index).addClass('active'); } }); $menu.metisMenu(); } }); } isActive(url: string): boolean { return this.router.isActive(url, false); } isChildMenuActived(menu: any): boolean { let hasOneActive = false; if (menu.childMenus) { menu.childMenus.forEach(child => { hasOneActive = hasOneActive || this.isActive(child.link); }); } return hasOneActive; } }
|
在Angular2-release版本里,一般路由的状态是通过事件监听获取的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import { ActivatedRoute } from '@angular/router'; @Component({ ... }) export class SidebarComponent { constructor(private route: ActivatedRoute) {} ngOnInit() { this.route.params .subscribe((params) => { this.id = parseInt(params['id']); ... }); } ... }
|
这里我们也可以使用filter()
来过滤监听我们想要的事件:
1 2
| router.events.filter(event => event instanceof NavigationEnd).subscribe(event => {});
|
更多有关路由的我们上节也说过,可以点击回顾《玩转Angular2(3)–启用路由和添加静态资源》。
最终效果图:
结束语
这节主要讲了一些基础环境配置的调整,以及制作路由自动定位左侧菜单的过程,主要涉及的可能还是路由相关。
看菜单列表的内容,大家猜猜本骚年接下来想要做什么?就不告诉你,哈哈。
此处查看项目代码
此处查看页面效果
查看Github有更多内容噢:https://github.com/godbasin
更欢迎来被删的前端游乐场边撸猫边学前端噢