用Angular(1.X)开发上一代web应用

以前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
<span>{{11234567789 | data:"MM/DD/YYYY @ h:mma"}}</span>
model.filter('filtera',function(){
    return function(item){
        return item +'hahahhah';
    }
})

Template

没什么好说的 就是View层用来展示的模板

Directors

指令 有点像是组件,特点是可以做一定的DOM操作,如事件绑定,返回指定模板到容器等。
angular 没有组件的概念,视图的复用使用directory来实现的。
指令的四种类型

restrict 匹配模式

restrict 元素 例子
A 标签(默认) <div my-menu=Products></div>
E 元素 <my-menu title=Products></my-menu>
C 样式类 <div class=my-menu:Products></div>
M 注释方式

注意
当需要创建带有自己的模板的指令时,使元素名称的方式创建指令
当需要为已有的HTML标签增加功能的时候,使用属性的方式创建指令

template 模板
templateUrl 模板路径 支持异步请求
templateCatch 模板缓存

module.run(function($templateCatch){
//  run 方法在所有依赖注入加载完成后执行,且仅执行一次
    $templateCatch.put('hello.html',"<div>....</div>")
});
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')
        })
    }
}
})

通过属性来获取不同的处理方法,达到指令复用的目的。

<div>
    <loader howtoload="loadData">hahaha</loader>
</div>
model.director('loader', function(){
    return {
        restrict:'AE',
        link: function(scope, element, attrs){
            element.bind('mouseenter', function(){
                scope.$apply(attrs.howtoload)//html 不区分大小写 统一使用小写
            })
        }
    }
})

指令间通信

通过暴露controller,其他指令依赖该指令,调用controller上暴露出来的方法

    <superman strength>动感超人---力量</superman>
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上的属性和方法

  • @ 为单向传递
    <drink flavor="{{ctrlFlavor}}"></drink>
mymodel.controller('MyCtrl', ['$scope', function($scope){
    $scope.ctrlFlavor = '百威';
}])
mymodel.directive('drink', function(){
    return {
        restrict: 'AE',
        template: "<div>{{flaver}}</div>",
        link: function(scope, element, attrs){
            scope.flavor = attrs.flaver;
        }
    }
});
//使用@绑定可以传递字符串 等价于下面
mymodel.directive('drink', function(){
    return {
        restrict: 'AE',
        scope:{
        flavor: '@'
        },
        template: "<div>{{flaver}}</div>"
    }
})
  • = 为双向绑定
    此处不举例
  • & 用于调用方法
    <greeting greet="sayhello(name)"></greeting>
mymodel.controller('MyCtrl', ['$scope', function($scope){
    $scope.sayhello = function(name){
        alert(name);
    }
}])
mymodel.directive('greeting', function(){
    return {
        restrict: 'AE',
        scope: {
            greet: '&'
        },
        template: '<input type="text" ng-model="userName" /><button ng-click="greeting({name:userName})"></button>'
    }
});

内置指令

form
form可以嵌套
自动校验,防止重复提交
input元素类型进行扩展
text,number,submit....
内置样式: ng-valid ng-invalid ng-pristine ng-dirty

    <form name="myform" ng-submit="save()" ng-controller="TestForModule">
    //如果这里写了ng-submit 不要在input中再写一遍 否则会重复递交
        <input name="userName" type="text" ng-model="user.userName" required/>
        <input type="submit" ng-disabled="myForm.$invalid" />
        // $invalid 内置校验方法
    </form>
    
        

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开发指南

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容