因为现在公司技术栈是 AngularJs1.x 的,初来乍到的第一个小系统便用 Angular 来搭了,因为之前 有过 SPA 应用的开发经验,所以上手 Angular 比较快,一周多完成的小系统效果也不错。Angular 用于 搭建这样的后台系统非常有优势,主要的优势在于其插件生态的丰富。学而时习之,让我来总结下 Angular 的一些东西。
Angular 一大特点便是数据的双向绑定,数据双向绑定的意思为视图发生变化会触发其对应数据模型 的变化,数据模型变化后也会反映到对应的视图上,这样一来的好处就是,省去了以前自己操纵 DOM 变化 内容的步骤。理解双向绑定后,我们只需要从数据模型的角度去思考问题,而不用考虑视图如何展示。例 如一个图片的列表,我们不用去关心页面如果展示,因为这不重要,知道下面的东西就行了:
双向绑定的几个注意点:
<li ng-repeat="item in arr2 track by $index">{{ item }}</li>
$scope.$watch(
"item",
function(newValue) {
// 默认是 false 哦
},
true
);
在 Angular 官方提供的指令中,例如 ng-click,Angular 包装了 click, 所以会在事件触发后执行相应的数据检查, 如果数据有变动,便会更新视图上的数据。如果我们在指令内容自己用 DOM 绑定事件,并在事件里改变 scope 上的一些 值,其实值是有变的,只是没有更新到视图上,为什么?因为值变化 Angular 不知道,所以需要手动调用 $digest 或者 $apply 方法去更新视图。调用 $digest 只会触发当前数据变动在当前作用域和子作用域的相关监听,而 $apply 会去 遍历查找当前数据变化在应用的作用域树的相关监听。其实在 Angular 中应用表达式 或者 ng-model = "text", 也是利用 $watch 去监听数据,不过 Angular 在背后给你做了。
Module 像是一个代码组织的容器,容器里面又有各式各样的小容器 Controller、Directive、Service、 Filter 等等。做这个系统的时候把一些 Service 和 Constant 单独抽出来了一个模块,现在看来是不必要的, 受公司原来系统的影响这么干的,当时也没仔细想,需要抽出新模块的代码,应该是可以作为插件使用的代码,或者多 个项目可以用到的公共模块,而我做的这个系统抽出的模块上面的两点都没有沾边,所以确实不必要。
因为在系统中没有深入的去实践指令相关的东西,因为时间比较赶,用指令来抽象东西还是麻烦一点,所以大部分都是使用 Controller 来做的,只是很多需要手工复制粘贴,继续来了解下指令:
<div ng-app="app">
<hello></hello>
</div>
<script type="text/javascript">
angular.module("app", []);
angular.module("app").directive("hello", function() {
return {
// 指令声明方式的选项:
// E 元素
// A 属性
// C 样式类
// M 注释
restrict: "E",
// 指令的模板
// 可以用 templateUrl 将模板指定为 html
template: "<div>Hello world!</div>",
// 替换自定义标签选项:
// false(默认),保留自定义标签
// true,自定义标签将会被指令模板替换掉
replace: true,
};
});
</script>
<div ng-app="app">
<hello>
dddddd
</hello>
</div>
<script type="text/javascript">
angular.module("app", []);
angular.module("app").directive("hello", function() {
return {
restrict: "E",
template: "<div>Hello world!<span ng-transclude></span></div>",
// 设置 transclude 为true ,并且在指令模板内部 调用 ng-transclude,
// 自定义标签里面的内容将被放置在 ng-transclude 指令内
transclude: true,
};
});
</script>
<div ng-app="app">
<div ng-controller="helloController as hC">
<hello name="hC.myName" age="hC.myAge" sex="{{ hC.male }}" say="hC.say()">
dddddd</hello
>
</div>
</div>
<script type="text/javascript">
angular.module("app", []);
angular.module("app").controller("helloController", function() {
var vm = this;
vm.myName = "李小龙";
vm.male = "男";
vm.say = function() {
alert("Hello world!");
};
return vm;
});
angular.module("app").directive("hello", function() {
return {
// 如果没有设置 scope 属性或者 scope 的值为 false,祖辈控制器的 scope 将会在
// 当前 directive 的 scope 原型链上,通俗点说就是当前指令可以直接访问到 祖辈
// scope 的属性和方法;
// 如果像下面这样设置了 scope ,directvie 将会有一个独立的作用域;
scope: {
// scope 的 key 可以在使用指令时作为指令的属性传进来,例如下面的 name
// 在 html 中 <hell name="myName"></hello> 使用,如果属性是多个单词的
// 在 scope 中使用驼峰命名如 fullName ,在 html 中使用中划线连接 full-name,
// 也可以用 name: '=full' 声明 fullName 属性,和 fullName 效果相同;
// = 用于从祖辈的 scope 取值,应该可以理解为把祖辈 scope 上的属性以引用的形式传递进来,
// 当祖辈 scope 的属性变化, directive 上的 scope 会变化, 反之
name: "=",
// @ 绑定一个局部 scope 属性到当前 dom 节点的属性值。结果总是一个字符串,因为 dom 属性是字符串。
// 可以用插值表达式,当祖辈 scope 的属性变化, directvie scope 上的属性也会变化, 当 directive 上
// 的属性变化,祖辈 scope 上的对应属性不会变,这和嵌套的 controller 是一个道理
sex: "@",
// & 用于从祖辈的 scope 取函数
say: "&",
},
restrict: "E",
template:
'<div>{{ name }}, {{ sex }}<button ng-click="say()">按钮</button></div>',
replace: true,
// controller 主要用于指令与指令之间的通信
// 配合 require 可以导入相关的指令
controller: function($scope, $attrs) {},
// 在编译之后运行-那么代码就放在link里
link: function(scope, element, attrs, controller) {},
// compile 函数,一般不会使用这个函数,如果有特殊需求可以使用
// compile 调用阶段可以通过 tElem 改变原始的 dom,因为它发生才 ng 创建
// 原始 dom 实例和 scope 实例之前;
compile: function(tElem, tAttrs) {
return {
// pre 函数,当有嵌套指令时,它会在所有的子指令 的 pre-link 函数
// 调用之前调用
pre: function(scope, iElem, iAttrs) {},
// post-link ,就是 link 函数了, link 写在 directive 里的写法就是
// 一种更方便的写法,因为大部分情况下,directive 的逻辑都在 post-link 中
// 处理;
// 如果存在指令嵌套, post-link 函数的执行是最嵌套最深的指令先执行 post-link 函数,
// 一直往上;
post: function(scope, iElem, iAttrs) {},
};
},
};
});
</script>
在项目中暂时没有用到,而且事件系统主要用于解耦,独立性很强,需要用到的时候再详细了解。
新特性只是标题党,只是网上的很多资料都是基于 Angular 刚出来的时候写的,所以更新的一些特性基本没有讲到,由于项目比较赶, 我基本上使用的都是最常用的实践方式,常用但不一定是最佳实践。
// 很多资料在控制器里面都是注入 $scope ,然后在 $scope 上绑定变量或方法 //
其实在 1.2 之后,就不用注入了,可以使用控制器别名方式,为什么用 this
而不继续使用 $scope? // 1.嵌套 contoller 的时候,变量及方法调用更清晰; //
2.Angular 2.0 去掉了 $scope,到时候可以更自然的迁移; // 3.$scope
在实例化时会将原型指向其父作用域, 而 as
方式,是从自定义的构造函数实例化的对象,所以不存在继承的原型 //
所以默认情况下是访问不到父级作用域的变量的。理想状态下,控制器应该是独立的,这很正确;
<div ng-app="app">
<div ng-controller="inner as in">
{{ in.name }}, {{ in.text }}
<div ng-controller="outer as oc">
<!-- text 没有值输出,而在使用 $scope 时会输出,原因看上面 -->
{{ oc.name }}, {{ oc.text }}
</div>
</div>
</div>
<script type="text/javascript">
angular.module("app", []);
angular.module("app").controller("inner", function() {
this.name = "inner";
this.text = "i'm inner";
});
angular.module("app").controller("outer", function() {
this.name = "outer";
});
</script>
component 就是 direnctive 一种更上层的封装,就像 jQuery 中 $.post 是对 $.ajax 的封装, 所谓更高层的封装就是有些东西是要指定的:
<div ng-app="app">
<div ng-controller="helloCtrl as hCtrl">
<hello text="hCtrl.text"></hello>
</div>
</div>
<script type="text/javascript">
angular.module("app", []);
angular.module("app").controller("helloCtrl", function() {
this.text = "hello";
});
angular.module("app").component("hello", {
// 使用 bindings 绑定属性到 controller,而 directive 使用 bindToController 属性
bindings: {
text: "<",
},
// 可以使用默认创建的 $ctrl controller 实例
template: "<div>Hello {{ $ctrl.text }}{{ $ctrl.sex }}!</div>",
controller: function() {
this.sex = "男";
},
});
</script>
存放数据初始化的逻辑的地方,在组件内 Controller 初始化的时候统一加载数据;
angular.module("app").controller("inner", function() {
this.$onInit = function() {
// 在这里处理数据初始化
};
});
这个东西还是很有用的,而且使用起来也简单。
<div ng-app="app">
<div>
<hello>
<hello-head>
head!!!
</hello-head>
<hello-body>
body!!!
</hello-body>
</hello>
</div>
</div>
<script type="text/javascript">
angular.module("app", []);
angular.module("app").directive("hello", function() {
return {
scope: {
name: "=",
sex: "@",
say: "&",
},
// 除了设为 true 还可以指定嵌套的部分哦
// 问号的意思表示这个东西是可选的
transclude: {
head: "?helloHead",
body: "?helloBody",
},
restrict: "E",
template:
'<div><div ng-transclude="head"></div><div ng-transclude="body"></div></div>',
replace: true,
link: function(scope, element, attrs) {},
};
});
</script>
参考资料: