《用AngularJS开发下一代Web应用》读书笔记②:AngularJS应用骨架

调用Angular

  • 使用Angular首先要做两件事:
    1. 加载angular.js库
    2. 使用ng-app指令告诉Angular应该管理DOM中哪一部分
  • 鉴于天朝在网络世界中修筑了万里长城,推荐使用国内的CDN缓存代码库(cdn.css.net网站中提供各种CDN的搜索)
<script 
    src="https://cdn.css.net/files/angular.all/1.2.18/angular-all.min.js">
</script>
  • ng-app指令告诉Angular应该盖页面的哪一块,即Angular的模块。
<!-- 如果是构建纯Angular应用,可以写在`<html>`元素中 -->
<html ng-app>
    ···
</html>

或者

<html>
    <!-- 如果应用已经使用了类似JSP、Rails的其他技术,也可以写在要管理的DOM元素中 -->
    <div  ng-app>
        ···
    </div>
</html>

Model View Controller

  • 第一章提到Angular框架是通过MVC风格来设计web应用的,使之具有很强的灵活性。MVC包括:
    • 模型(Model)用来容纳数据,代表应用当前的状态;
    • 视图(View)用来展示数据;
    • 控制器(Controller)来管理模型和视图之间的关系;
  • 我们可以通过$scope对象的属性来创建数据模型,可以只用基本数据类型来存储数据,也可以用一个JavaScript类来存储数据。
  • 定义Angular的正确方式应该是,先定义Angular模块,然后把控制器定义为模块的一个方法。 通过模块机制可以帮助我们把代码从全局命名空间中隔离开来。
<!doctype html>
<!-- 通过ng-app设置Angular管理的范围  -->
<html ng-app='myApp'>
    <head>
        <!-- 引入Angular的文件 -->
        <script src="https://cdn.css.net/files/angular.all/1.2.18/angular-all.min.js"></script>
    </head>
    <!-- 通过ng-controller声明控制器-->
    <body ng-controller='TextController'>
        <!-- 通过双花括号定义双向绑定的数据 -->
        <p>{{hello}} {{TextObj.message}}!</p>
        <script type="text/javascript">
            //通过angular.module()方法声明模块,第一个参数是模块名,第二个参数是需要依赖的模块(这里是空数组)
            var myAppModule = angular.module('myApp',[]);
            //通过module.controller()方法声明控制器,参数是控制器方法的具体实现
            myAppModule.controller('TextController',function($scope){
                //变量
                var hello = 'hello';
                //对象
                var TextObj = {
                    message : "World"   
                };  
                //赋值给$scope
                $scope.TextObj = TextObj;
                $scope.hello = hello;
            });
        </script>
    </body>
</html>

模板和数据绑定

<div ng-repeat='item in items'>
    <span>{{item.title}}</span>
</div>
  • 如上代码所示,Angular的模板是HTML片段,而items数组是数据模型,通过ng-repeat指令Angular框架会遍历items数组的每一项,并给<div>元素生成一份拷贝,延展出HTML内容。
  • 在实际应用中,items数组往往是通过请求服务端的数据结果。
  • 一个Angular应用的基本运作流程如下:
    ① 用户请求Angualr应用起始页;
    ② 浏览器向服务器发起HTTP连接,加载起始页.html页面(页面中包含模板);
    ③ 开始解析页面,加载Angualr框架到页面,DOM构建完毕后开始查找ng-app指令,根据这个指令来定义Angular的模板边界;
    ④ Angular框架的初始化工作:注册监听器、执行一些DOM操作、从服务器获取初始化数据。初始化工作完毕后,模板被转换成DOM视图;
    ⑤ 连接到服务器,加载需要展示给用户看的其他数据;
显示文本
  • 将JS变量的文本值显示到UI中,除了使用双花括号之外,还可以使用'ng-bind'指令。两种形式的输出内容是相同的。
  • 使用使用双花括号的方式,可以让模板代码阅读起来更自然,但有一个问题,就是在Angular使用数据替换这些花括号之前,未被渲染好的模板可能会被用户看到。造成这种现象的原因是,浏览器需要先加载并渲染HTML页面,Angular才开始初始化工作。
表单输入
  • 对所有标准的表单元素,Angualr框架都支持,并且操作起来非常方便,只需要将ng-model属性声明到要操作的元素即可。
  • 案例:Angular操作复选框
<from ng-controller='SomeController'>
    <!-- 通过ng-model将控件值绑定到youCheckedIt变量中 -->
    <input type="checkbox" ng-model='youCheckedIt' />
</from>
  • 我们还可以通过ng-change指令监听控件的改变
  • 案例:自动获取十倍的数字计算控件
<from ng-controller='StartUpController'>
    Starting: <input type="text" ng-model='funding.startingEstimate' />
    Remomendation:{{funding.needed}}
</from>
<script type="text/javascript">
    var myAppModule = angular.module('myApp',[]);
    myAppModule.controller('StartUpController',function($scope){
        $scope.funding = {startingEstimate : 0};
        $scope.computeNeeded = function(){
            $scope.funding.needed = $scope.funding.startingEstimate * 10;
        };
    });
</script>
  • ng-change指令只能监听页面控件的变化,如果是接收到服务器返回的信息,需要刷新数据模型,该怎么办呢?
  • $watch()函数用来监听一个表达式,当表达式发送改变时会调用一个回调函数
myAppModule.controller('StartUpController',function($scope){
    $scope.funding = {startingEstimate : 0};
    $scope.computeNeeded = function(){
        $scope.funding.needed = $scope.funding.startingEstimate * 10;
    };
    //添加$watch
    $scope.watch('funding.startingEstimate',computeNeeded);
});
  • Angular支持通过ng-submit指令监听表单提交动作。
<from ng-controller='SomeController' ng-submit='requestFnding()'>
  ···
</from>
  • Angular提供一系列类似浏览器原生事件属性的指令,比如ng-clickng-dbclick
浅谈非入侵式JavaScript
  • 非入侵式JavaScript代码主要可以从以下两点来解读:
    • JavaScript的兼容性问题,需要根据在不同浏览器编写不同的代码;
    • 不同类库集成进来引发的全局命名冲突问题;
    • 不同的事件监听器绑定数据结构和行为,让代码变得难以理解和维护,扩展性差;
  • 一开始我们会把事件处理器定义在元素上,然后为这些元素赋ID值,获取元素的引用。或者,也可以通过一个统一的处理器来管理这些事件,但大多数应用,最后还是会把代码弄得乱七八糟。
  • 而Angular是通过ng-eventhandler="expression"来处理,这里的eventhandler可以被替换成clickmousedown等原生的JS事件。
  • 虽然也还是将事件绑定通过元素属性进行声明,但是具有以下特点:
    • Angular帮助开发者屏蔽浏览器的差异性;
    • 通过Angular控制器的作用域范围避免扰乱全局命名空间的秩序;
    • 没有在任何地方引用DOM或者DOM事件,所有定位元素和处理事件监听都在Angular内部完成;
    • 传统的JavaScript程序的页面逻辑和DOM是强耦合的,给单元测试增加了复杂性,而Angular则不会有这些问题;
列表、表格以及其他迭代类型
  • ng-repeat指令可以遍历数据模型,并动态构建DOM节点,可算是Angular最常用的指令之一了。
  • 案例:学生花名册系统
<!DOCTYPE html>
<!-- 1.通过ng-app设置Angular管理的范围 -->
<html ng-app='myApp'>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <!-- 2.引入Angular的文件 -->
    <script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js"></script>
    <body>
        <ul ng-controller='StudentListController'>
            <li ng-repeat='student in students'>
                <a href="javascript:void(0);">{{$index+1}}.{{student.name}}</a>
            </li>
        </ul>
        <button ng-click='addStudent()'>Add</button>
        <script type="text/javascript">
            //伪数据
            var students = [
                {name : 'Mary Contrary',id:1},
                {name : 'Jack Spart',id:2},
                {name : 'Jill Hill',id:3},
                {name : 'William Liang',id:4},
                {name : 'Yoki Chen',id:5}
            ];
            var myApp = angular.module('myApp',[]);
            myApp.controller('StudentListController',function($scope){
                $scope.students = students;
                $scope.addStudent = function(){
                    $scope.students.splice(1,0,{name:'test',id:6});
                }
            });
        </script>
    </body>
</html>
  • ng-repeat指令中还可以通过$index返回当前引用元素的序号,通过$first$middle以及$last返回布尔值,判断当前元素是否为首个元素、是否为中间元素以及末尾元素。
显示和隐藏
  • Angualr通过ng-showng-hide指令显示和隐藏元素,当ng-show在表达式为true时显示元素,false时隐藏元素,ng-hide则恰好相反。
<!DOCTYPE html>
<!-- 1.通过ng-app设置Angular管理的范围 -->
<html ng-app='myApp'>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <!-- 2.引入Angular的文件 -->
    <script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js"></script>
    <body>
        <div ng-controller='SwichMenuController'>
            <button ng-click='toggleMenu()'>Add</button>    
            <ul ng-show='menuState.show'>
            </ul>   
        </div>
        <script type="text/javascript">
            var myApp = angular.module('myApp',[]);
            myApp.controller('SwichMenuController',function($scope){
                $scope.menuState.show = false;
                $scope.toggleMenu = function(){
                    $scope.menuState.show = !$scope.menuState.show;
                }
            });
        </script>
    </body>
</html>
CSS类和样式
  • Angular可以动态设置CSS类和样式,通过双花括号进行数据绑定。
  • 案例:动态修改颜色
<!DOCTYPE html>
<html ng-app='myApp'>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">
            .menu-disabled-true{ color:gray }
        </style>
    </head>
    <script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js"></script>
    <body>
        <div ng-controller='StudentListController'>
            <ul>
                <li class="menu-disabled-{{isDisabled}}" ng-click='stun()'></li>
            </ul>   
        </div>
        <script type="text/javascript">
            var myApp = angular.module('myApp',[]);
            myApp.controller('SwichMenuController',function($scope){
                $scope.isDisabled = false;
                $scope.stun = function(){
                    $scope.isDisabled = true;
                }
            });
        </script>
    </body>
</html>
  • Angular框架同样适用于内联样式,比如style="{{someexpression}}",这种方式具有很大的灵活性。
  • 除此之外,Angular框架还提供ng-classng-style指令,接受一个表达式。
  • 案例:通过ng-class显示错误和警告信息
<!DOCTYPE html>
<html ng-app='myApp'>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">
            .error{background-color: red;}
            .warrning{background-color: yellow;}
        </style>
    </head>
    <script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js"></script>
    <body>
        <div ng-controller='HeaderController'>
            <div ng-class='{error:isError,warning:isWarning}'>
                {{messageText}}
            </div>
        </div>
        <button ng-click='showError()'>Simulate Error</button>
        <button ng-click='showWarning()'>Simulate Warning</button>
        <script type="text/javascript">
            var myApp = angular.module('myApp',[]);
            myApp.controller('HeaderController',function($scope){
                $scope.isError = false;
                $scope.isWarning = false;
                
                $scope.showError = function(){
                    $scope.messageText = 'This is an Error';
                    $scope.isError = true;
                    $scope.isWarning = false;
                };
                
                $scope.showWarning = function(){
                    $scope.messageText = 'This is an Warning';
                    $scope.isWarning = true;
                    $scope.isError = false;
                };
            });
        </script>
    </body>
</html>
反思src和href属性
  • Angular通过ng-srcng-href指令动态设置超链接和图片属性
<img ng-src='/images/cats/{{favoriteCat}}'>
<a ng-href='/shop/category={{numberOfBalloons}}'>some text</a>
表达式
  • Angular在模板中使用表达式是为了在模板、业务逻辑和数据之间建立联系,避免让业务逻辑渗透到模板中。
  • angular的表达式可以进行简单的数据运算(+、-、*、/、%),进行比较运算(==、!=、>、<、>=、<=),执行布尔逻辑(&&、||、!),以及位运算(^、&、|)。
  • 但是我们应该清晰地区分视图和控制器之间的职责,尽量不要类似运算符的业务逻辑操作放到模板中。
  • 表达式是通过Angular内置的解析器执行的,但是不支持循环结构(for、while)、流程控制操作(if-else、throw)以及修改数据的操作符(++、--),请在控制器中执行或者通过指令执行。
区分UI和控制器和职责
  • 控制器有三种职责:
    • 为数据模型设置初始状态;
    • 通过$scope对象把数据模型和函数暴露给函数;
    • 监视模型其余部分的变化,并采取相应的动作;
  • 为了让控制器保持小巧和可管理的状态,我们建议为视图中的每一块功能区域创建一个控制器。
  • Angular中把控制器绑定到DOM节点上主要有两种方式:
    1. 在模板中通过ng-controller属性声明;
    2. 通过路由把控制器绑定到DOM模板片段;
  • 同时,开发者还可以创建嵌套的控制器,使它们可以通过继承树的结构来共享数据模型和函数,让应用的代码保持简介,容易维护。
<!-- ChildController的$scope对象可以访问ParentController的$scope对象上所有的属性 -->
<div ng-controller='ParentController'>
    <div ng-controller='ChildController'></div>
</div>
利用$scope暴露模型数据
  • Angular利用向控制器传递$scope对象的机制,可以把模型数据暴露给视图。可以把$scope当成一个上下文环境,而在这个环境中,数据模型的变化变得可以观察,甚至还可以通过模板自身创建数据模型。
<!-- ① 可以直接在模板表达式中定义变量 -->
<button ng-click='count=3'>Set Count to Three</button>
<!-- ② 也可以通过构造器函数定义变量 -->
<div ng-controller='CountController'>
   <button ng-click='setCount()'>Set Count to Three</button>
</div>
<script type="text/javascript">
function CountController($scope){
   $scope.setCount = function(){
        $scope.count = 3;
   };
}
</script>
使用$watch监控数据模型的变化
  • $scope$watch函数可以实现,当数据模型发生变化时,及时得到通知。
  • 函数签名:$watch(watchFn,watchAction,deepWatch)
  • watchFn是一个字符串,可以是Angular表达式或者对象名,它代表数据模型的当前值。值得注意的是,这个字符串所代表的对象会被轮训检查多次,所以务必要保证它不会产生其他的副作用。
  • watchAction是当数据模型发生变化时调用的函数或者表达式,其函数签名是 function(newValue,oldValue.scope)
  • deepWatch是个可选的boolean值,带包是否检查数据模型的每个属性。如果想要监控数组中的元素,或者的对象中所有的属性变化,可以使用这个参数。
  • 同时,$watch()执行后还会返回一个函数,当你不再需要接收变更通知时,可以执行这个返回的函数,以销毁整个监听器。
//监听someProperty属性值的变化
var dereg = $scope.$watch('someModel.someProperty',callbackOnChange());
dereg();    //销毁
watch()中的性能注意事项
  • 实际上,Angular在实现watch()方法时,会把所有被监控的属性都拷贝一份,然后拿来和当前的值进行比较,检查是否有变化。
  • 这样的运行机制会造成性能损耗,推荐通过一个Chrome调试插件(Batatrang)来检测,看看那些数据绑定的操作比较昂贵。
  • 有两种方式可以监控多个属性或者对象:
    1. 通过“+”号连接这些属性;

$scope.$watch('things.a+things.b',callMe(...));

2. 将要监控的属性放到一个数组或者对象中,并将`deepWatch`参数设置成true;

$scope.$watch('things',callMe(...),true);



### 使用Module(模块)组织依赖关系
- 在开发中经常会遇到的难题是 __如何把代码组织在合理的功能范围内?__ 
- MVC的处理方式,是将控制器看做一个解耦的方法,把正确的数据模型暴露给视图模板。但其他用来支撑我们应用的代码该怎么办?要把它们写在控制器函数里面吗?这么做会使控制器变成一个垃圾场,使得控制器的代码变得难以理解,无法维护。
- `模块机制`解决了上诉问题,它把那些负责提供特殊服务的代码称为`依赖服务`,它专门来组织应用中各个功能区块的依赖关系,甚至可以自动进行依赖注入。
- 案例:当控制器需要从服务器获取商品列表时,通常情况我们会把相关的处理通通写到一个方法中

function ItemsViewController($scope){
var items = [];
//1.向服务器发起请求
//2.解析响应并放到Items对象
//3.将Items数组设置到$scope上,以便视图能够显示
$scope.items = items;
}

- 这种做法虽然可行,但却存在很多问题:
    * 功能代码无法重用:同样的代码片段,如果其他控制器也需要从服务端获取商品列表时,却只能重写一遍,这会给代码维护工作造成很大的负担;
    * 混淆控制器与与获取数据功能之间的代码边界:获取数据功能如果需要加上一些,例如服务器认证、解析数据等因素时,代码的复杂性将变得不可控制;
    * 难以进行单元测试:进行测试的工作变得复杂,甚至可能需要真正启动一个服务器来返回模拟数据进行测试,使得测试工作难以把控;
  通过模块化和Angular内置的依赖注入功能,可以这样写:

//创建模块
var myApp = angular.module('shoppingModule',[]);
//设置服务工厂
shoppingModule.factory('Items',function(){
var items = {};
items.query = function(){
//这里返回虚拟数据,真实的场景是从服务器拉取数据
return [
{name : 'Mary Contrary',id:1},
{name : 'Jack Spart',id:2},
{name : 'Jill Hill',id:3},
{name : 'William Liang',id:4},
{name : 'Yoki Chen',id:5}
];
};
return items;
});
//调用
function ShoppingController($scope,Items){
$scope.items = items.query();
}

  我们把`Items`对象定义成一个服务,把`$scope`对象和`Items`服务作为参数传递进去。
  > 提示:传递Angular在创建控制器时,是通过参数名匹配来注入所需要的服务的,比如`ShoppingController($scope,Items)`中查找`Items`服务。由于是以字符串的形式查找依赖关系的,所以控制器的参数是没有顺序的。

- 服务在Angular当中都是单例的对象,它们用来执行不要的任务,支撑应用的功能。开发者通过创建自定义的服务,共享在控制器之间,实现代码的复用。
- Angular内置了很多服务,例如`location`服务,用来和浏览器的地址栏做交互;还有`router`服务,用来根据URL地址的变化切换视图;以及`http`服务,用来和服务器进行交互等等;
- 根据Angular的API,有三种创建服务的函数:
  * __`provider(name,Object OR constructor())`__ :`name`是服务的名字,后面接一个对象或者构造函数。如果是Object的话,对象当中必须返回一个名为`$get()`的函数,否则Angular会认为传递的是一个构造函数,通过调用改构造函数返回服务的实例;
  * __`factory(name,$getFunction())`__ :和`provider()`函数相似,服务名后面接一个构造函数,当调用这个函数时,会返回服务的实例;
  * __`service(name,constructor())`__ :和`constructor`参数类似,Angular调用它来创建服务实例;
> 需要注意的是:Angular内置服务通常以符号开头,为了避免命名冲突,定义服务名时应当避免以$符号打头。

- 服务自身可以相互依赖,也可以通过`Module`接口定义模块之间的依赖关系。比如:A模块要引用SnazzyUIWidgets和SuperDataSync模块

var appMod = angular.module('app',['SnazzyUIWidgets','SuperDataSync']);




### 使用过滤器格式数据
- 合理化地在应用界面中显示数字、日期以及金额对于用户体验来说是很重要的,但这些字符串的格式化处理对逻辑来说是没有意义的。
- Angular通过过滤器来格式化数据,其语法是:`{{expression | filterName : parameter1 : ...parameterN }}`
- `expression`可以是任意的Angular表达式,`filter`是需要使用的过滤器名称,过滤器通过管道符号`|`来绑定,过滤器的参数之间使用冒号`:`分隔。
- 案例:Angular内置了众多过滤器帮助我们格式化数据,同时也可以创建自定义的过滤器

//大小写转换
{{ "lower cap string" | uppercase }} //结果:LOWER CAP STRING
{{ "TANK is GOOD" | lowercase }} //结果:tank is good
//JSON格式化
{{ {foo: "bar", baz: 23} | json }} //结果:{ "foo": "bar", "baz": 23 }
//时间或日期格式化
{{ 1304375948024 | date:"yyyy-MM-dd hh:mm:ss" }} //结果:2011-05-03 06:39:08
//数字格式化
{{ 1.234567 | number:1 }} //结果:1.2
//金额格式化
{{ 250 | currency:"人民币: ¥ " }} //结果:“人民币: ¥ 250.00 ”
//排序格式化
{
{ [{"age": 20,"id": 10,"name": "iphone"},
{"age": 12,"id": 11,"name": "sunm xing"},
{"age": 44,"id": 12,"name": "test abc"}
] | orderBy:'id':true }} //根id降序排
//查找格式化
{
{ [{"age": 20,"id": 10,"name": "iphone"},
{"age": 12,"id": 11,"name": "sunm xing"},
{"age": 44,"id": 12,"name": "test abc"}
] | filter:'s'}} //查找含有有s的行
//字符串截取的格式化
{{ "i love tank" | limitTo:6 }} //结果:i love
{{ "i love tank" | limitTo:-4 }} //结果:tank



### 使用路由和$location切换视图
- 虽然从技术角度来说,Ajax应用是单页面应用,但很多时候处于各种原因,我们需要切换跳转页面,而Angular的`$router`服务负责应对这样的场景。
- 路由服务机制是这样定义的:当浏览器指向特定的URL时,Angular会加载显示一个模板,并实例化一个控制器为此模板提供内容。
- 开发者可以通过`$routerProvider`服务的函数来创建路由,把需要创建的路由当做一个配置项传递给这些函数。

//创建模块
var someModule = angular.module('someModule',[...module dependencies...]);
someModule.config(function($routerProvider){
//通过$routerProvider定义url对应的跳转动作所需要的模板和控制器
$routerProvider.
when('url',{controller:aController,templateUrl:'/path/to/tempate'}).
othenwise('badUrl',{controller:notFoundController,templateUrl:'/path/to/tempate'});
});



### 与服务器交互
- Angular提供的`$http`服务使得与服务器交互更加容易。它支持HTTP、JSONP和CORS方式。它还包含了安全性支持,避免JSON格式的脆弱性和XSRF。

function ShoppingController($scope,$http){
$http.get('/products').success(function(data,status,headers.config){
$scope.items = data;
});
};



### 使用指令修改DOM
- Angular指令扩展了HTML的语法,通过自定义的元素或者属性,把行为和DOM转换关联到一起。通过Angular可以创建可复用的UI组件。
- Angular指令创建语法:(directiveFn是一个工厂函数,用来定义指令的特性)

var app = angular.module('myapp',[]);
app.directive(name,fn);

- 案例:定义ts-hello指令,向DOM中输出“Hello,Angular Directive!”

<!DOCTYPE html>
<html ng-app="DirectiveTestApp">
<head>
<meta charset="UTF-8">
<title>AngularJS Directive</title>
<style></style>
</head>
<script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js"></script>
<body>
<div ng-controller="SomeController">
<ts-hello></ts-hello>
<div ts-hello></div>
<div class="ts-hello"></div>
<div data-ts-hello></div>
</div>
</body>
<script type="text/javascript">
var app = angular.module('DirectiveTestApp', []);
app.directive("tsHello",function(){
return {
restrict : 'EAC',
template : '<h5>Hello,Angular Directive!</h5>'
}
});
</script>
</html>



### 校验用户输入
- 在表单校验上,Angular允许开发者为表单的元素定义一个个发的状态,只有当所有元素都是合法状态时,才允许提交表单。
- 案例:一个用户注册页面,要求必须输入用户名和邮箱,使用H5的required属性

<!DOCTYPE html>
<html ng-app="myApp">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>Sign Up</h1>
<form name="addUserForm" ng-controller="AddUserController">
<div ng-show="message">{{message}}</div>
First name : <input name="firstName" required/>
Last name : <input name="lastName" required/>
Email : <input name="email" required type="email"/>
Age : <input name="age" required type="number"/>
<button ng-click="addUser()" ng-disabled="!addUserForm.$valid">Submit</button>
</form>
<script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js"></script>
<script type="text/javascript">
var myApp = angular.module('myApp',[]);
myApp.controller('AddUserController',function($scope){
$scope.message = '';
$scope.AddUser = function(){
alert("add user success!");
}
});
</script>
</body>
</html>

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

推荐阅读更多精彩内容