以前focus on 移动开发商没怎么接触过重量级的前端Pc框架,此次接手一个Angular的项目,在重构前趁机学习一下。
在书中说道“Angular的整个应用都是由模型驱动的,是同中所展示的内容是模型,被存储起来的内容也是模型,几乎所有的一切都是模型”。Angular的概念比较多有 module, confroller, Template, Filter , Directors , Service。以下逐个了解下。
controller
他有三个公用
- 为应用中的模型设置初始状态
- 通过$scope对象把数据模型和函数暴露给视图管理模块
- 监视模型其余部分的变量并采取相应的动作
关于controller拆分粒度,个人认为应该向拆组件那么拆。
controller可以嵌套,并且通过嵌套可以继承$scope.
使用注意事项: - 不要试图复用controller,一个controller一般只负责一小块视图
- 不要再Controller中操作DOM,这不是控制器的职责
- 不要再Controller里面做数据格式化,ng有很好用的表单控件
- 不要在controller里面做数据过滤操作,ng有Filter服务
- 一般来说controller不会互相调用的,controller之间的交互通过事件进行 emit向
Filter
数据格式转换,作为数据在模板上展示的辅助工具
- 内置9个filter:
currency,date,filter,json,limitTo,lowercase,number,orderBy,uppercase - filter 使用|符号嵌套使用
- filter可以传递参数
- 可以自定义filter
{{11234567789 | data:"MM/DD/YYYY @ h:mma"}}
model.filter('filtera',function(){
return function(item){
return item +'hahahhah';
}
})
Template
没什么好说的 就是View层用来展示的模板
Directors
指令 有点像是组件,特点是可以做一定的DOM操作,如事件绑定,返回指定模板到容器等。
angular 没有组件的概念,视图的复用使用directory来实现的。
指令的四种类型
restrict 匹配模式
restrict | 元素 | 例子 |
---|---|---|
A | 标签(默认) | |
E | 元素 | |
C | 样式类 | |
M | 注释方式 |
注意
当需要创建带有自己的模板的指令时,使元素名称的方式创建指令
当需要为已有的HTML标签增加功能的时候,使用属性的方式创建指令
template 模板
templateUrl 模板路径 支持异步请求
templateCatch 模板缓存
module.run(function($templateCatch){
// run 方法在所有依赖注入加载完成后执行,且仅执行一次
$templateCatch.put('hello.html',"....")
});
module.director('hello', function($templateCatch){
return {
restrict: 'AECM',
template:$templateCatch.get('hello.html'),
replace: true
}
})
link
使用link来操作dom,绑定事件,类似组件。
model.director('loader', function(){
return {
restrict:'AE',
link: function(scope, element, attr){
element.bind('mouseenter', function(){
scope.loadData();//controller中的scope
or scope.$apply('loadData')
})
}
}
})
通过属性来获取不同的处理方法,达到指令复用的目的。
hahaha
model.director('loader', function(){
return {
restrict:'AE',
link: function(scope, element, attrs){
element.bind('mouseenter', function(){
scope.$apply(attrs.howtoload)//html 不区分大小写 统一使用小写
})
}
}
})
指令间通信
通过暴露controller,其他指令依赖该指令,调用controller上暴露出来的方法
动感超人---力量
var mydoel = angular.module('model',[]);
myModule.directive('superman', function(){
return {
scope: {},//创建独立作用域,多指令不共享作用域。 link controller 操作基于这个scope
restrict: 'AE',
controller: function($scope){
// 指令暴露出去的方法写在这里
$scope.ablities = [];
this.addStrengh = function() {
$scope.ablities.push('strength');
}
this.addSpeed = function() {
$scope.ablities.push('addSpeed');
}
this.addLight = function() {
$scope.ablities.push('addLight');
}
},
link: function(scope, element, attrs){
// 指令内部绑定事件和数据
element.addClass('btn btn-primary');
element.bind('mousenter', function(){
console.log(scope.abilities);
})
}
}
});
myModule.directive('strength',function(){
return {
require: '^superman',// 指令依赖
link: function(scope, element, attrs, supermanCtrl) {//第四个参数是required 中依赖的指令的作用域
supermanCtrl.addStrength();
}
}
})
获取model的controller上的属性和方法
- @ 为单向传递
mymodel.controller('MyCtrl', ['$scope', function($scope){
$scope.ctrlFlavor = '百威';
}])
mymodel.directive('drink', function(){
return {
restrict: 'AE',
template: "{{flaver}}",
link: function(scope, element, attrs){
scope.flavor = attrs.flaver;
}
}
});
//使用@绑定可以传递字符串 等价于下面
mymodel.directive('drink', function(){
return {
restrict: 'AE',
scope:{
flavor: '@'
},
template: "{{flaver}}"
}
})
- = 为双向绑定
此处不举例 - & 用于调用方法
mymodel.controller('MyCtrl', ['$scope', function($scope){
$scope.sayhello = function(name){
alert(name);
}
}])
mymodel.directive('greeting', function(){
return {
restrict: 'AE',
scope: {
greet: '&'
},
template: ''
}
});
内置指令
form
form可以嵌套
自动校验,防止重复提交
input元素类型进行扩展
text,number,submit....
内置样式: ng-valid ng-invalid ng-pristine ng-dirty
replace 替换里面
service
服务获取数据的方法的封装,可以被传到各个controller中调用。有点像是公共方法的提取。
// 定义一个服务时 名称不要以$开头
model.factory('userlist',['$http', function($http){
function($http){
var doRequest = function(userName, path){
return $http([
method: 'GET',
url:'users.json'
])
}
return {
list: function(username){
return doRequest(username, 'userList');
}
}
}
}])
//自定义服务写在最后面
model.controller('servertest',['$scope', '$timeout', '$http', 'userlist',function(){
...
userlist.list($scope.name).success(function(){...})
...
}])
service 特性
- service都是单例的
- service由$inject 负责实例化
- service在整个应用的生命周期中存在 可以用来共享数据
- 在需要使用的地方利用依赖注入机制注入service
- 自定义的service写在内置Service之后
- 自定义Service避免$开头
其他参数
$scope
scope 是一个 plan old javascript object
scope 提供了一些工具方法 $watch $apply
$scope 是表达式的执行环境-作用域
$scope 是一个树形结构 与DOM标签平行
子scope对象会继承父scope对象
$scope 可以传播事件 类似DOM事件 可以向上向下传
scope不仅是MVC的基础 也是后面实现双向数据绑定的基础
$rootProvider 路由服务
hash后面匹配路径,仅有以下两个方法,支持匹配组
$rootProvider
.when('/list',{
template:'listTemplate'//模板文件路径,
controller: 'listController'//控制器名称
})
.otherwise({
redirectTo:'/list'// default routerß
})
angular 模块化
使用Angular.module来定义模块,防止变量污染
var s = angular.module('modela', [])//后面数组中放置依赖 内容为directors等
s.controller('modelascontroller',['$scope', function($scope){
$scope.greeting = ……
}])
Angular项目的gulp工作流搭建
处理Angular编译问题
值得注意的是,一般书写时按照简写的格式:
angular.module("MyMod").controller("MyCtrl", function($scope, $timeout) {
});
但是压缩js会破快AngularJS文件所需的依赖注入,以至于无法工作,因此压缩前你需要将代码手动修改为下面的形式:
angular.module("MyMod").controller("MyCtrl", ["$scope", "$timeout", function($scope, $timeout) {
}]);
在此着重介绍下ng-annotate这个项目,它会自动帮你做这件事$_$,这个项目正好提供了gulp的插件.
为压缩文件添加md5 并且替换html中的链接
gulp.task('concat',['prdPackLess', 'prdPackJs','prdPackIndex'],function() {
return gulp.src(['static/tmp/**/*.css','!static/tmp/css/sdkheader.css','static/tmp/**/*.js', '!static/tmp/js/sdkheader.js']) //- 需要处理的css/js文件,放到一个字符串数组里 !剔除掉你不想打版本的文件
.pipe($.rev())
.pipe(gulp.dest('static/'))
.pipe($.rev.manifest()) //- 生成一个rev-manifest.json
.pipe(gulp.dest('ref/')); //- 将 rev-manifest.json 保存到 rev 目录内
});
gulp.task('rev',['concat','compressHtml', 'copy'],function() {
return gulp.src(['ref/rev-manifest.json', 'static/tmp/html/**/*.html']) //- 读取 rev-manifest.json 文件以及需要进行css名替换的文件
.pipe(revCollector()) //- 执行文件内css名的替换
.pipe(gulp.dest('static/html')); //- 替换后的文件输出的目录
});
参考链接
前端构建的初步尝试
使用gulp压缩合并Angular项目中的Js
gulp学习指南之CSS合并压缩与MD5命名及路径替换
[用AngularJS开发下一代Web应用]
Angular 与 escript6
由于之前有使用es6开发的体验,于是好奇是不是可以使用es6来开发Angular 开来已经有人这么做了Angular1.X和es6的结合
angular es6开发指南