AngularJS学习笔记

1. 介绍

AngularJS是一款由Google公司开发维护的前端MVC框架,其克服了HTML在构建应用上的诸多不足,从而降低了开发成本提升了开发效率。

1.1 特点

AngularJS与jQuery有什么区别?

jQuery更准确来说只一个类库(类库指的是一系列函数的集合),是以DOM做为驱动。
而AngularJS则一个框架(诸多类库的集合)是以数据和逻辑做为驱动。

框架对开发的流程和模式做了约束,开发者遵照约束进行开发,更注重的实际的业务逻辑。

AngularJS有着诸多特性,最为核心的是:模块化双向数据绑定语义化标签依赖注入等。

1.2 MVC

MVC是一种开发模式,把网页分成模型(Model)、视图(View)、控制器(Controller)3部分构成。

模型(Model)用来处理数据(读取/设置),一般指操作数据库。
视图(View)用来展示数据,比如通过HTML展示。
控制器(Controller)用做连接模型和视图的桥梁。

MVC更多应用在后端开发程序里,后被引入到前端开发中,由于受到前端技术的限制便有了一些细节的调整,进而出现了很多MVC的衍生版(子集)如MVVM、MVW、MVP、MV*等。

2. AngularJS的模块化

使用AngularJS构建应用(App)时是以模块化(Module)的方式组织的,即将整个应用划分成若干模块,每个模块都有各自的职责,最终组合成一个整体。采用模块化的组织方式,可以最大程度的实现代码的复用。

2.1 使用AngularJS的HTML基本结构

2.1.1 定义应用

通过为任一HTML标签添加ng-app属性,可以指定一个应用,表示此标签所包裹的内容都属于应用(App)的一部分。

<!-- 为html标签添加ng-app表明整个文档都是应用 -->
<!-- ng-app属性可以不赋值,但是要关联到相应模块时则必须赋值 -->
<html lang="en" ng-app='App'>

2.1.2 定义模块

AngularJS提供了一个全局对象angular,在此全局对象下存在若干的方法,其中angular.module()方法用来定义一个模块。

// 通过module方法定义模块
// 有两个参数
// 第一个:表示模块的名称
// 第二个:表示此模块依赖的其他模块
var app = angular.module( 'app', [] );

2.1.3 定义控制器

控制器(Controller)作为连接模型(Model)和视图(View)的桥梁存在,所以当我们定义好了控制器以后也就定义好了模型和视图。

// app是一个模块实例对象
// 通过这个实例对象定义控制器
// 传入两个参数
// 第1个:定义控制器的名称
// 第2个:一个数组,数组的最后一项是函数,其余是字符串,写明此控制器的依赖关系
app.controller('StarController', ['$scope', function($scope){
    $scope.students = [
        {name: '周杰伦', gender: '男', age: 39},
        {name: '刘德华', gender: '男', age: 60},
        {name: '孙燕姿', gender: '女', age: 36},
        {name: '王力宏', gender: '男', age: 38},
        {name: '陈小春', gender: '男', age: 42},
    ]
}]);

2.1.4 在视图中关联控制器

模型(Model)数据是要展示到视图(View)上的,所以需要将控制器(Controller)关联到视图(View)上,通过为HTML标签添加ng-controller属性并赋值相应的控制器(Controller)的名称,就确立了关联关系。

<!-- 添加ng-controller属性,并赋值为相应的控制器名称 -->
<table ng-controller='StarController'>
    <tr>
        <th>姓名</th>
        <th>性别</th>
        <th>年龄</th>
    </tr>
    <tr ng-repeat='student in students'>
        <td>{{student.name}}</td>
        <td>{{student.gender}}</td>
        <td>{{student.age}}</td>
    </tr>
</table>

2.1.5 整个HTML文档的结构

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>AngularJS的基本结构</title>
</head>
<body ng-app='app'>
    <table ng-controller='StarController'>
        <tr>
            <th>姓名</th>
            <th>性别</th>
            <th>年龄</th>
        </tr>
        <tr ng-repeat='student in students'>
            <td>{{student.name}}</td>
            <td>{{student.gender}}</td>
            <td>{{student.age}}</td>
        </tr>
    </table>
    <script src="./libs/angular.min.js"></script>
    <script>
        var app = angular.module('app', []);
        app.controller('StarController', ['$scope', function($scope){
            $scope.students = [
                {name: '周杰伦', gender: '男', age: 39},
                {name: '刘德华', gender: '男', age: 60},
                {name: '孙燕姿', gender: '女', age: 36},
                {name: '王力宏', gender: '男', age: 38},
                {name: '陈小春', gender: '男', age: 42},
            ]
        }]);        
    </script>
</body>
</html>
效果

3.指令

3.1 常用内置指令

ng-app 指定应用根元素,至少有一个元素指定了此属性。不能互相嵌套。

ng-controller 指定控制器

ng-show 控制元素是否显示,true显示、false不显示

ng-hide 控制元素是否隐藏,true隐藏、false不隐藏

ng-if 控制元素是否“存在”,true存在、false不存在(DOM元素存在与否)。

ng-src 增强图片路径。如果图片路径是src = {{path}}这种形式,并且模型写在文档下面,浏览器从上至下依次解析代码到这里时,还没有解析到模型,当快速刷新网页时,就会显示图片没有请求到。如果是ng-src={{path}},执行到这里会强制先解析模型中的path,就不会出现未请求到的问题,并且解决了重复请求的问题。

ng-href 增强地址

ng-class 控制类名。它的值可以有两种形式。ng-class = {{active}}ng-class = {{active : true}},boolen值控制该类名是否存在。

ng-include 引入模板。可以加载一个外部文件。应用时,可以把一些公共部分抽离成一个独立文件,然后用ng-include = 'src'引入。在没用AngularJS之前,用的是<?php include(demo.html) ?>。但是,要注意的是,ng-include的地址要是同域的,这是因为,js本身读取不了本地文件,需要借助请求的方式,并且这种请求不能是跨域的。

ng-disabled 表单禁用。值为boolen值。ng-disabled = true表单禁用。

ng-readonly 表单只读

ng-checked 单/复选框表单选中,值为boolen值。checked是一个无值属性,ng-checked = true时,就会存在checked这个属性。

ng-selected 下拉框表单选中。selected也是一个无值属性,ng-selected = true时,选中。

ng-repeat 遍历

3.2 自定义指令

AngularJS允许根据实际业务需要自定义指令,通过angular全局对象下的directive方法实现。

<body ng-app="App">
    <div haoxiang></div>
    <haoxiang></haoxiang>
    <div class="haoxiang"></div>
    <!-- Angular 只认识带着directive的注释 -->
    <!-- directive:haoxiang -->
    <script src="./libs/angular.min.js"></script>
    <script>
        var App = angular.module('App', []);

        // 自定义指令
        // App.directive()方法来实现自定义指令
        // 第一个参数,指令名称
        // 第二个参数,是一个回调函数,在回调函数中实现指令逻辑
        App.directive('haoxiang', function () {
            // 在此实现指令的逻辑
            // 返回一个对象
            // 对象里的内容是固定的
            return {
                // A 是单词 attribute 首字母,将指令当属性来用,带有haoxiang这个属性的元素就加载下面的模板
                // E 是单词 element   首字母,将指令当标签来用,haoxiang标签就加载下面的模板
                // C 是单词 class     首字母,将指令当类来用,拥有haoxiang这个类名的元素就加载下面的模板
                // M 是单词 mark      首字母,将指令将注释来用
                restrict: 'ECMA',
                replace: true,
                // 定义模板
                template: '<div><h1>Hello AngularJS!</h1>',
                // 要求所有html模板中的元素,在同一个根元素下
                template: '<div><h1>Hello AngularJS!</h1><a href="#">百度</a></div>',
                // 下面这种写法就是错的,因为两个元素不在一个根元素下
                template: '<h1>Hello AngularJS!</h1><a href="#">百度</a>',
                // 当模板内容太多,可以把模板放在独立的html文档中
                // 注意:使用这种方式,要放在服务器环境下,保证同域名
                // 同样,这个html文档中的各个元素也要在同一个根目录下
                templateUrl: './Hello.html',
                // 也可以添加一些方法,比较复杂,这里不做介绍
                link: function () {
                    // 
                },
                compile: function () {
                    // 
                }
            }
        });
    </script>
</body>

4.数据绑定

AngularJS是以数据做为驱动的MVC框架,所有模型(Model)里的数据经由控制器(Controller)展示到视图(View)中。

所谓数据绑定指的就是将模型(Model)中的数据与相应的视图(View)进行关联,分为单向绑定和双向绑定两种方式。

4.1 单向绑定

单向数据绑定是指将模型(Model)数据,按着写好的视图(View)模板生成HTML标签,然后追加到DOM中显示。

4.2 双向绑定

双向绑定则可以实现模型(Model)数据和视图(View)模板的双向传递。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>数据绑定</title>
</head>
<body ng-app='App'>
    <div class="box" ng-controller="DemoCtrl">
        <form action="">
            <input type="text" ng-model="msg">
            <input type="button" value="显示" ng-click="show()">
        </form>
        <p>您输入的是:{{msg}}</p>
    </div>
    <script src="./libs/angular.min.js"></script>
    <script>
        var App = angular.module('App', []);
        App.controller('DemoCtrl', ['$scope', function ($scope) {
            $scope.show = function () {
                alert($scope.msg);
            }
        }]);
    </script>
</body>
</html>

4.3 相关指令

{{}} 和 ng-bind
在AngularJS中通过{{}}或者ng-bind指令来实现模型(Model)数据向视图模板(View)的绑定,模型数据通过一个内置服务$scope来提供,这个$scope是一个空对象,通过为这个对象添加属性或者方法便可以在相应的视图(View)模板里被访问。

{{}}ng-bind的简写形式,但是它们还有区别。
第1个区别ng-bind不能绑定多个数据,如ng-bind = 'name age'这种写法是错误的。可以写成{{name}}{{age}}。通过ng-bind-template可以绑定多个数据。
第2个区别。通过{{}}绑定数据时会有“闪烁”现象,ng-bind不会有闪烁现象。{{}}使用方便,为标签添加ng-cloak并将script标签移到上面可以解决“闪烁”现象。原理:页面载入时,载入到引入angular的script标签时,会自动在页面中添加style,为添加了ng-cloak的标签设置display=none,隐藏了添加了ng-cloak的标签,解析完写了模型的script后再将其显示。

ng-model
通过为表单元素添加ng-model指令实现视图(View)模板向模型(Model)数据的绑定。

ng-init
通过ng-init可以初始化模型(Model)也就是$scope。

<body ng-app="App">
    <div ng-controller="DemoCtrl" ng-init="title='学习内容'; info='Angular'">
        <h1>{{title}}{{info}}</h1>
    </div>
    <script src="./libs/angular.min.js"></script>
    <script>
        var App = angular.module('App', []);
        App.controller('DemoCtrl', ['$scope', function ($scope) {

        }])
    </script>
</body>

事件处理
AngularJS对事件也进行了扩展,无需显式的获取DOM元素便可以添加事件,易用性变的更强。通过在原有事件名称基础上添加ng-做为前缀,然后以属性的形式添加到相应的HTML标签上即可。如ng-click、ng-dblclick、ng-blur等。

<body ng-app='App'>
    <div class="box" ng-controller="DemoCtrl">
        <li ng-click="single()">单击事件</li>
        <li ng-dblclick="double()">双击事件</li>
        <li ng-mouseover="xuanting()">悬停事件</li>
        <li ng-mouseout="likai()">离开事件</li>
    </div>

    <script src="./libs/angular.min.js"></script>
    <script>
        var App = angular.module('App', []);

        App.controller('DemoCtrl', ['$scope', function ($scope) {
            $scope.single = function () {
                alert('单击事件');
            }

            $scope.double = function () {
                alert('双击事件');
            }

            $scope.xuanting = function () {
                console.log('鼠标来了');
            }

            $scope.likai = function () {
                console.log('鼠标走了');
            }
        }]);
    </script>
</body>

ng-repeat
通过ng-repeat可以将数组或对象数据迭代到视图模板中,ng-switch on配合ng-switch-when可以对数据进行筛选。

<body ng-app="App">
    <div ng-controller="DemoCtrl">
        <ul>
            <!-- 两种写法 -->
            <li ng-repeat="item in list" ng-bind="item"></li>
            <li ng-repeat="item in list">{{item}}</li>
        </ul>
        <ul>
            <li ng-repeat="(key, val) in list">
                {{key}}:{{val}}
            </li>
        </ul>       
        <table>
            <tr ng-repeat="(key, val) in obj">
                <td>{{key}}</td>
                <td>{{val}}</td>
            </tr>
        </table>

        <!-- 例如只有当值为css才显示 -->
        <ul>
            <!-- 写不写on都可以 -->
            <li ng-repeat='item in list' ng-switch on='item'>
                <span ng-switch-when='css'>{{item}}</span>
            </li>

            <li ng-repeat="item in list" ng-switch="item">
                <a href="javascript:;" ng-switch-when="js">{{item}}</a>
            </li>
        </ul>
    </div>
    <script src="./libs/angular.min.js"></script>
    <script>
        var App = angular.module('App', []);
        App.controller('DemoCtrl', ['$scope', function ($scope) {
            $scope.list = ['html', 'css', 'js'];
            $scope.obj = {
                name: '小明',
                age: 18,
                sex: '男',
                hobby: '吹牛'
            }
        }]);
    </script>
</body>

一个Tab栏切换小demo

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Tab 标签</title>
    <style>

        body {
            margin: 0;
            padding: 0;
            background-color: #F7F7F7;
        }
    
        .tabs {
            width: 400px;
            margin: 30px auto;
            background-color: #FFF;
            border: 1px solid #C0DCC0;
            box-sizing: border-box;
        }

        .tabs nav {
            height: 40px;
            text-align: center;
            line-height: 40px;
            overflow: hidden;
            background-color: #C0DCC0;
            display: flex;
        }

        nav a {
            display: block;
            width: 100px;
            border-right: 1px solid #FFF;
            color: #000;
            text-decoration: none;
        }

        nav a:last-child {
            border-right: 0 none;
        }

        nav a.active {
            background-color: #9BAF9B;
        }

        .cont {
            overflow: hidden;
        }

        .cont ol {
            line-height: 30px;
        }

    </style>
</head>
<body ng-app="Tabs">

    <div class="tabs" ng-controller="TabCtrl">
        <nav>
            <a href="javascript:;" ng-class="{active: active == 'local'}" ng-click="change('local')">国内新闻</a>
            <a href="javascript:;" ng-class="{active: active == 'global'}" ng-click="change('global')">国际新闻</a>
            <a href="javascript:;" ng-class="{active: active == 'sports'}" ng-click="change('sports')">体育新闻</a>
            <a href="javascript:;" ng-class="{active: active == 'funny'}" ng-click="change('funny')">娱乐新闻</a>
        </nav>
        <div ng-switch on="active">
            <section class="cont" ng-switch-when="local">
                <ol>
                    <li>国内新闻</li>
                </ol>
            </section>
            <section class="cont" ng-switch-when="global">
                <ol>
                    <li>国际新闻</li>
                </ol>
            </section>
            <section class="cont" ng-switch-when="sports">
                <ol>
                    <li>体育新闻</li>
                </ol>
            </section>
            <section class="cont" ng-switch-when="funny">
                <ol>
                    <li>娱乐新闻</li>
                </ol>
            </section>
        </div>
    </div>
    <script src="../libs/angular.min.js"></script>
    <script>
        var Tabs = angular.module('Tabs', []);
        Tabs.controller('TabCtrl', ['$scope', function ($scope) {
            // 定义初始状态
            $scope.active = 'local';
            $scope.change = function (key) {
                $scope.active = key;
            }
        }]);
    </script>
</body>
</html>

5.作用域

通常AngularJS中应用(App)是由若干个视图(View)组合成而成的,而视图(View)又都是HTML元素,并且HTML元素是可以互相嵌套的,另一方面视图都隶属于某个控制器(Controller),进而控制器之间也必然会产生嵌套关系。

每个控制器(Controller)又都对应一个模型(Model)也就是$scope对象,不同层级控制器(Controller)下的$scope便产生了作用域。

可以参考JavaScript中函数的作用域来理解。

5.1 全局作用域(根作用域)

一个AngularJS的应用(App)在启动时会自动创建一个根作用域$rootScope,这个根作用域在整个应用范围(ng-app所在标签以内)都是可以被访问到的。

5.2 子作用域

通过ng-controller指令可以创建一个子作用域,每当有一个控制器就有一个子作用域,新建的作用域可以访问其父作用域的数据。

例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>AngularJS 作用域</title>
</head>
<!-- ng-app 所属标签所在的作用域就是根作用域 -->
<body ng-app="App" ng-init="name='努尔哈赤'">
    <div class="parent" ng-controller="ParentCtrl">
        <dl>
            <dt>{{name}}</dt>
            <dd>1200</dd>
        </dl>
        <div class="child" ng-controller="ChildCtrl">
            <dl>    
                <dt>{{name}}</dt>
                <dd>1100</dd>
            </dl>
            <div class="son" ng-controller="SonCtrl">
                <dl>
                    <dt>{{name}}</dt>
                    <dd>1000</dd>
                </dl>
            </div>
        </div>
    </div>
    <script src="./libs/angular.min.js"></script>
    <script>
        var App = angular.module('App', []);
        App.controller('DemoCtrl', ['$scope', function ($scope) {

        }]);
        // 什么是AngularJS的作用域:当div的嵌套导致控制器的嵌套,被嵌套的控制器可以访问到父级控制器中的数据
        // 就像函数作用域一样

        // 父
        App.controller('ParentCtrl', ['$scope', function ($scope) {

            $scope.name = '顺治';
        }]);

        // 子
        App.controller('ChildCtrl', ['$scope', function ($scope) {

            $scope.name = '康熙';
        }]);

        // 孙
        App.controller('SonCtrl', ['$scope', function ($scope) {

            $scope.name = '雍正';
        }]);

        // 根作用域:ng-app 所属标签所在的作用域就是根作用域
        // 子作用域:每当有一个控制器,就有一个子作用域
    </script>
</body>
</html>

6.过滤器

在AngularJS中使用过滤器格式化展示数据,在“{{}}”中使用“|”来调用过滤器,使用“:”传递参数。

6.1 内置过滤器

  1. currency将数值格式化为货币格式
  2. date日期格式化,年(y)、月(M)、日(d)、星期(EEEE/EEE)、时(H/h)、分(m)、秒(s)、毫秒(.sss),也可以组合到一起使用。
  3. filter在给定数组中选择满足条件的一个子集,并返回一个新数组,其条件可以是一个字符串、对象、函数
  4. json将Javascrip对象转成JSON字符串。
  5. limitTo取出字符串或数组的前(正数)几位或后(负数)几位
  6. lowercase将文本转换成小写格式
  7. uppercase将文本转换成大写格式
  8. number数字格式化,可控制小位位数
  9. orderBy对数组进行排序,第2个参数布尔值可控制方向,true倒序

例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>AngularJS 过滤器</title>
    <style>
        dl {
            line-height: 40px;
        }
    
        dt {
            font-size: 18px;
            font-weight: bold;
        }

        dd {
            margin: 0;
        }
    </style>
</head>
<body ng-app="App">
    <dl ng-controller="DemoCtrl">
        <dt>使用过滤器将数据处理成一定的格式再进行展示</dt>
        <dd>当前时间为: {{now|date:'yyyy-MM-d h:m:s EEEE'}}</dd>
        <dd>价格: {{price|currency:'¥':3}}</dd>
        <dd>介绍: {{info|uppercase}}</dd>
        <dd>介绍2: {{brief|lowercase}}</dd>
        <!-- 过滤器可以被连续使用 -->
        <dd>介绍3: {{brief|lowercase|uppercase}}</dd>
        <dd>limitTo: {{info|limitTo:4}}</dd>
        <dd>limitTo: {{info|limitTo:-4}}</dd>
        <dd>limitTo: {{list|limitTo:2}}</dd>
        <dd>limitTo: {{list|limitTo:-2}}</dd>
        <dd>number: {{num|number:2}}</dd>
        <dd>number: {{num1|number}}</dd>
        <dd>json: {{list|json}}</dd>
        <dd>json: {{obj|json}}</dd>
        <!-- 正序 -->
        <dd>orderBy: {{list|orderBy}}</dd>
        <!-- 默认第二个参数为false,正序 -->
        <dd>orderBy: {{list|orderBy:'':false}}</dd>
        <!-- 倒序 -->
        <dd>orderBy: {{list|orderBy:'':true}}</dd>
        <!-- 第一个参数指:以谁为排序方式 -->
        <dd>orderBy: {{students|orderBy:'score':true}}</dd>
        <!-- 把包含s的拿出来 -->
        <dd>filter: {{list|filter:'s'}}</dd>
        <!-- 把男的拿出来 -->
        <!-- 注意:{} 后面一定要跟着一个空格,否则会报错 -->
        <dd>filter: {{students|filter:{sex:'男'} }}</dd>
    </dl>
    <script src="./libs/angular.min.js"></script>
    <script>

        var App = angular.module('App', []);

        App.controller('DemoCtrl', ['$scope', function ($scope) {
            // 当前时间
            $scope.now = new Date();

            $scope.price = 12.345;

            $scope.info = 'my name is brother';

            $scope.brief = 'My age is 18';

            $scope.list = ['html', 'js', 'php', 'css', 'photoshop'];

            // number 过滤器功能是保留几个小数位  并不能转换数值 例如'123abc' 不能 转成 123
            // 但是 字符串'123' 可以转成 数字123
            $scope.num = '12.345';

            $scope.num1 = '123abc';

            $scope.obj = {
                name: 'jack',
                age: 10
            }

            $scope.students = [
                {name: '小明', sex: '男', age: 18, score: 94},
                {name: '小米', sex: '女', age: 19, score: 92},
                {name: '小雷', sex: '男', age: 16, score: 98},
                {name: '小花', sex: '女', age: 19, score: 91}
            ];
        }]);
    </script>
</body>
</html>

6.2 自定义过滤器

通过模块对象实例提供的filter方法自定义过滤器。

<body ng-app="App">

    <div class="box" ng-controller="DemoCtrl">
        {{str|uppercase}}
        <!-- AngularJS 在使用"|"调用过滤器时会将前面的数据str当成参数传递给下面的自定义函数 -->
        {{str|yell:'我是一个参数':123456}}
        {{str|slice:3}}
        <h1>{{str|capitalize}}</h1>
    </div>

    <script src="./libs/angular.min.js"></script>
    <script>
        var App = angular.module('App', []);

        App.controller('DemoCtrl', ['$scope', function ($scope) {
            $scope.str = 'hello angular!';
        }]);


        // App.filter()可以自定义过滤器
        // 第一个参数 过滤器名称
        // 第二个参数 回调函数
        App.filter('yell', function () {
            return function (arg, arg2, arg3) {
                // arg 就是 str
                // arg2 就是 '我是一个参数'
                // arg3 就是 123456
                console.log(arg); //hello angular!
                console.log(arg2); //'我是一个参数'
                console.log(arg3); //123456
            }
        });

        // 模拟字符串截取功能
        App.filter('slice', function () {
            return function (input, length) {
                // input 就是需要格式化处理的数据
                // length 表示截取几个长度
                return input.slice(0, length);
            }
        });

        // 首字母大写功能
        App.filter('capitalize', function () {
            return function (input) {
                // input 就是需要格式化处理的数据
                console.log(input[0])
                return input[0].toUpperCase() + input.slice(1);
            }
        });

        
    </script>
</body>

7. 依赖注入

AngularJS采用模块化的方式组织代码,将一些通用逻辑封装成一个对象或函数,实现最大程度的复用,这导致了使用者和被使用者之间存在依赖关系。

所谓依赖注入是指在运行时自动查找依赖关系,然后将查找到依赖传递给使用者的一种机制。

常见的AngularJS内置服务有$http、$location、$timeout、$rootScope等

依赖注入有两种方式:推断式注入和行内注入。

7.1 推断式注入

没有明确声明依赖,AngularJS会将函数参数名称当成是依赖的名称。

这种方式会带来一个问题,当代码经过压缩后函数的参数被压缩,这样便会造成依赖无法找到。

// 这个控制器依赖$scope, $http服务
// 未明确声明依赖,这时会自动将函数传入的参数名当成依赖对待
App.controller('DemoCtrl', function ($scope, $http) {
    
});

7.2 行内注入

以数组形式明确声明依赖,数组元素都是包含依赖名称的字符串,数组最后一个元素是依赖注入的目标函数。

推荐使用这种方式声明依赖

App.controller('DemoCtrl', ['$scope', '$http', function ($scope, $http) {

}]);

8.服务

服务是一个对象或函数,对外提供特定的功能。

8.1 内建服务

8.1.1 $location

$location是对原生Javascript中location对象属性和方法的封装。

<script>
    var App = angular.module('App', []);

    // $location服务用来获取地址信息,与原生BOM对象 location 类似

    App.controller('DemoCtrl', ['$location', '$scope', function ($location, $scope) {

        console.log($location);

        $scope.absUrl = $location.absUrl();

        $scope.host = $location.host();

        $scope.port = $location.port();

        $scope.protocol = $location.protocol();

        // AngularJS在原有基础上重新定义了地址的组成部分
        // AngularJS认为第1个#后面的?才是请求参数
        $scope.search = $location.search();

        // AngularJS 认为第1个#号不表示锚点
        // 如果再次出现#才认为锚点
        $scope.hash = $location.hash();

        // path:与原生的pathname相对应
        // AngularJS 认为第1个#号开始之后的/之后的部分属于path,不包含请求参数
        $scope.path = $location.path();

    }]);

    // location.search:地址上 ? 后的数据,表示参数
    // location.hash:地址上 # 后的数据,表示锚点,也叫hash
    // location.pathname:地址从根(/)开始的部分,不包含请求参数

    // AngularJS 实质上是将原生BOM对象的location.hash
    // 进行了封装,重新定义的了 path hash search
    // 重新定义之后的path hash search,都是从第一个#之后才开始算

</script>

8.1.2 $timeout&$interval

$timeout&$interval对原生Javascript中的setTimeout和setInterval进行了封装。

var App = angular.module('App', []);

App.controller('DemoCtrl', ['$scope', '$timeout', '$interval', function ($scope, $timeout, $interval) {

    // 延时执行
    $timeout(function () {
        console.log('延时了3秒')
    }, 3000);

    // 取消延时
    $timeout.cancel();

    // 间歇函数
    $interval(function () {
        console.log(1);
    }, 500);

    // 一秒刷新一下时间
    var timer = $interval(function () {
        $scope.now = new Date();
    }, 1000);

    $scope.stop = function () {
        $interval.cancel(timer);
    }

}]);;

8.1.3 $filter

$filter在控制器中格式化数据。

var App = angular.module('App', []);

// $filter是过滤器,过滤器可以在视图中使用{{|}},也可在模型中使用

App.controller('DemoCtrl', ['$scope', '$filter', function ($scope, $filter) {

    // AngularJS 自带的过滤器有9种

    // $filter是一个函数,传递不同的参数会返回不同具体功能的过滤器

    // $filter('date') 其返回值就是一个格式化时间的过滤器
    // $filter('currency') 其返回值就是一个格式化货币的过滤器
    // $filter('uppercase')、$filter('limitTo') ....

    // 原始数据
    var now = new Date;
    // 时间过滤器
    var date = $filter('date');
    // 格式化数据
    now = date(now, 'yyyy-MM-dd H:m:s');
    $scope.now = now;

    var list = ['html', 'css', 'js', 'php', 'node'];
    var limitTo = $filter('limitTo');
    list = limitTo(list, 3);
    $scope.list = list;

    // 原始信息
    var content = 'my name is xiaoming';
    // 创建过滤器
    var uppercase = $filter('uppercase');
    // 格式化数据
    $scope.content = uppercase(content);

}]);

8.1.4 $log

$log打印调试信息。

App.controller('DemoCtrl', ['$scope', '$log', function ($scope, $log) {

    // $log 是一个对象

    $log.log('调试信息');

    $log.info('调试信息');

    $log.error('调试信息,错了吗?');

    $log.warn('调试信息,警告');

    $log.debug('调试信息');

}]);

8.1.5 $http

$http用于向服务端发起异步请求。

App.controller('DemoCtrl', ['$scope', '$http', function ($scope, $http){
    // 发起异步请求
    $http({
        method: 'post',
        url: 'demo.php',
        data: {name: 'xule', age: 10},
        headers: {'Content-Type': 'application/x-www-form-urlencoded'}
    }),success(function (info){
        // 可以把服务端返回的数据绑定到模型上
        $scope.result = info;
    }),error(function (info)){
    };
}]);

要注意的是,请求方式不同,传递的数据格式也不同。

(1)当请求方式为post时,有两种写法。

数据为key=val&key1=val1格式时,需要通过Content-Type告知其类型为application/x-www/form-urlencoded。

数据为{key: val, key1: val1}格式时,需要通过Content-Type告知其类型为application/json。但是AngularJS默认使用这种格式,这里的headers可以写也可以不写。

$http({
    url: 'example.php',
    method: 'post',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
    },
    data: {'name=ming&age=11'}
});
$http({
    url: 'example.php',
    method: 'post',
    // 这里的headers可以写也可以不写,默认就是这种形式
    headers: {
        'Content-Type': 'application/json'
    },
    data: {name: 'ming', age: 11}
});

(2)当请求为get方式,使用params专门传递get方式的数据

$http({
    url: 'example.php',
    method: 'get',
    params: {age: 11},
});

如何处理跨域请求呢

通过get方式传递一个事先定义好的函数名,这里有固定的写法:params: {callback: 'JSON_CALLBACK'},JSON_CALLBACK即为"事先定义好的函数名",JSON_CALLBACK是一个占位符,AngularJS 也会像jQuery一样动态创建一个函数,但是动态创建的函数名不固定,先使用一个占位符JSON_CALLBACK占一个位,当动态函数创建成功后再将这个JSON_CALLBACK进行替换。后台接收函数名的时候也用JSON_CALLBACK

$http({
    url: ' ',
    params: {callback: 'JSON_CALLBACK'},
    method: 'jsonp'
}).success(function (info) {
    
});             

使用AngularJS的$http服务的跨域获取天气信息

这里用的是百度车联网API中的天气查询 - 车联网API

注意下面两种传递callback的方法,都是可以的。

<!DOCTYPE html>
<html lang="en" ng-app="App">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <!-- 0A5bc3c4fb543c8f9bc54b77bc155724 -->
    <div ng-controller="WeatherController">
        <table>
            <!-- 视图 -->
            <tr ng-repeat="item in weatherData">
                <td>{{item.date}}</td>
                <td>{{item.temperature}}</td>
                <td>{{item.weather}}</td>
                <td>{{item.wind}}</td>
                <td>![]({{item.dayPictureUrl}})</td>
                <td>![]({{item.nightPictureUrl}})</td>
            </tr>
        </table>
    </div>
    <script src="../libs/angular.min.js"></script>
    <script>
        var App = angular.module('App', []);
        // 定义控制器并声明依赖
        App.controller('WeatherController', ['$scope', '$http', '$log', function($scope, $http, $log) {
            // 请求数据
            $http({
                method: 'jsonp', 
                url: 'http://api.map.baidu.com/telematics/v3/weather?callback=JSON_CALLBACK',
                params: { // 请求的参数
                    location: '北京',
                    output: 'json',
                    ak: '0A5bc3c4fb543c8f9bc54b77bc155724'
                }
            })
            .success(function (data) {
                $log.log(data); // 成功的回调
                // 请求回的数据放到模型上
                $scope.weatherData = data.results[0].weather_data;
            });

        }])
    </script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body ng-app="App">
    <!-- 0A5bc3c4fb543c8f9bc54b77bc155724 -->
    <div ng-controller="WeatherCtrl">
        <table>
            <tr ng-repeat="weather in weathers">
                <td>{{weather.date}}</td>
                <td>{{weather.temperature}}</td>
                <td>{{weather.weather}}</td>
                <td>{{weather.wind}}</td>
                <td>![]({{weather.dayPictureUrl}})</td>
                <td>![]({{weather.nightPictureUrl}})</td>
            </tr>
        </table>
    </div>
    <script src="../libs/angular.min.js"></script>

    <script>
        var App = angular.module( 'App', [] ) ;
        App.controller('WeatherCtrl', ['$scope', '$http', function ($scope, $http) {
            $http({
                url: 'http://api.map.baidu.com/telematics/v3/weather',
                method: 'jsonp',
                params: {
                    callback: 'JSON_CALLBACK',
                    ak: '0A5bc3c4fb543c8f9bc54b77bc155724',
                    location: '北京',
                    output: 'json'
                }
            }).success(function (info) {
                console.log(info);
                $scope.weathers = info.results[0].weather_data;
            });
        }]);
    </script>
</body>
</html>

8.2 自定义服务

通过上面例子得知,所谓服务是将一些通用性的功能逻辑进行封装方便使用,AngularJS允许将自定义服务。

8.2.1 factory方法

<script>
    var App = angular.module('App', []);

    // 模块实例App包含若干方法
    // 使用factory() 来定义服务

    // 第一个参数是服务的名称
    // 第二个参数是一个数组
    // 自定义服务是可以在原有的AngularJS内置服务基础之上来定义
    // 自定义的服务其使用和内置服务一样

    App.factory('sayHello', ['$log', function ($log) {

        // 必须要有一个返回值
        // 其值可以是任意数据类型
        // 但是通常对象或函数更有意义
        
        function sayHello(name) {
            alert('你好' + name);
        }
        return {
            sayHello: sayHello,
            changge: function () {
                console.log('我还会唱歌呢');
            }
        }
    }]);

    // 需求定义一个可以获取当前时间的服务并格式化好
    App.factory('Time', ['$filter', function ($filter) {

        return {
            // 传入格式
            now: function (format) {
                // 如果没传入格式,有个默认格式
                if(!format) {
                    format = 'yyyy-MM-dd';
                }

                var now = new Date;
                // 参考$filter用法
                return $filter('date')(now, format);
            }
        }
    }]);

    App.controller('DemoCtrl', ['sayHello', 'Time', '$scope', function (sayHello, Time, $scope) {

        sayHello('小明');

        sayHello.sayHello('小明');

        sayHello.changge();

        $scope.now = Time.now('yyyy-MM-dd H:m:s');
        
    }]);
</script>

8.2.2 service方法

特点:不需要return

<body>
    <div ng-controller="DemoCtrl">

        <span>{{now}}</span>
    
    </div>
    <script src="./libs/angular.min.js"></script>
<script>
    var App = angular.module('App', []);

    // 还可以使用service() 来定义服务
    // 第一个参数服务的名称
    // 第二个参数还是数组
    // 数组是用来解决依赖
    App.service('Time', ['$filter', function ($filter) {

        // 在此完成当前服务的所有业务逻辑
        
        function Time(format) {
            return $filter('date')(new Date, format);
        }
        
        // 不需要使用return返回
        // this指的就是当前服务
        this.now = Time;

    }]);
    
    App.controller('DemoCtrl', ['$scope', 'Time', function ($scope, Time) {
        $scope.now = Time.now('yyyy-MM-dd H:m:s');
    }])
</script>
</body>

8.2.3 value方法定义常量

<body>
    <div ng-controller="DemoCtrl">
        {{author}}
        {{ver}}
    </div>
    <script src="./libs/angular.min.js"></script>
    <script>
        var App = angular.module('App', []);

        // value定义的本质上一个服务
        // 从表现形式上是一个常量
        // 第一个参数为服务名称 
        // 第二个参数任意[字符串 数值]
        App.value('ver', '1.0');
        App.value('author', 'xiaoming');

        App.controller('DemoCtrl', ['$scope', 'ver', 'author', function ($scope, ver, author) {

            console.log(ver);

            $scope.ver = ver;
            $scope.author = author;

        }])
    </script>
</body>

9.模块加载

AngularJS模块可以在被加载和执行之前对其自身进行配置。我们可以在应用的加载阶段配置不同的逻辑。

先来看一下Angular的执行过程。

AngularJS执行过程.png

只需要明白一点,我们可以通过配置块和运行块可以影响后面的执行。

9.1 配置块

通过config方法实现对模块的配置,AngularJS中的服务大部分都对应一个“provider”,用来执行与对应服务相同的功能或对其进行配置。

比如$log、$http、$location都是内置服务,相对应的“provider”分别是$logProvider、$httpProvider、$locationPorvider。

<body ng-app="App">
    <div ng-controller="DemoCtrl">
        <h1>{{info|capilize}}</h1>
    </div>
    <script src="./libs/angular.min.js"></script>
    <script>
        var App = angular.module('App', []);

        // App.config()方法是用来配置内置模块的
        // 参数是一个数组(可以一次性配置多个)
        // 其具体配置方式为
        // 要想配置某个模块都需要通过“服务名Provider”
        // 例如 想配置$log 就使用 $logProvider进行配置
        // 例如 想配置$filter 就使用$filterProvider进行配置
        // 例如 想配置$http 就使用$httpProvider进行配置

        // 配置$log的功能
        App.config(['$logProvider', '$filterProvider', function ($logProvider, $filterProvider) {
            // 禁用debug方法
            $logProvider.debugEnabled(false);

            // $filter 默认只有9个(过滤器)方法
            // 这里可以通过配置添加一个首字母大写功能
            // 第一个参数写过滤器的名称
            $filterProvider.register('capilize', function () {
                return function (input) {
                    return input[0].toUpperCase() + input.slice(1);
                }
            });

        }]);

        App.controller('DemoCtrl', ['$log', '$scope', function ($log, $scope) {
            $log.log('调试信息');

            $log.debug('debug还能用吗');

            $scope.info = 'my name is xiaoming';
        }]);

        // 总结:在AngularJS执行过程中,可以对某些功能进行配置,使用提供的config方法来现
        // 在进行配置时并不是使用原有的服务名称,而是“服务名称+Provider”
        // 例如 $log 使用 $logProvider
        App.config(['$logProvider', function ($logProvider) {
            // 通过$logProvider就可以配置$log了
            $logProvider.debugEnabled();
        }]);
    </script>
</body>

9.2 运行块

服务是模块形式存在的,对外提供特定功能,一般都是将服务做为依赖注入进去的,然后再进行调用,除了这种方式外我们也可以直接运行相应的服务模块,AngularJS提供了run方法来实现。

不但如此,run方法还是最先执行的,利用这个特点我们可以将一些需要优先执行的功能通过run方法来运行,比如验证用户是否登录,未登录则不允许进行任何其它操作。

App.run(['$http', '$rootScope', function ($http, $rootScope) {
    $http({
        method: 'post',
        url: 'example.php'
    }).success(function (info) {
        // 在根作用域上定义date,相当于在ng-init = '' 中定义
        // 如果要在根作用域上定义多个属性,使用ng-init = '' 会比较麻烦
        // 此时就可以用$rootScope.xxx
        $rootScope.date = info;
    });
}]);

10. 路由

网站在切换各个页面时,网站主域名后的改变的,就是路由。改变一个路由,就会切换一个页面。一个应用是由若个视图组合而成的,根据不同的业务逻辑展示给用户不同的视图,路由则是实现这一功能的关键。通过监听路由的改变,可以实现单页面应用(单页面应用也是有路由的)。

10.1 单页面应用(SPA)

SPA(Single Page Application)
单页面应用就是指将所有的功能通过一个页面展示。

在PC端也有广泛的应用,通常情况下使用Ajax异步请求数据,然后实现内容局部刷新,局部刷新的本质是动态生成DOM,新生成的DOM元素并没有真实存在于文档中,所以当再次刷新页面时新添加的DOM元素会“丢失”,通过单页面应可以很好的解决这个问题。

由于页面没变,内容变了。可以使用XMLHttpRequest向服务器获取数据,并将获取到的数据以DOM操作的方式添加到页面中去。采用这种方式可以增强用户体验,还可以减轻服务器压力。

10.2 路由

在后端开发中通过URL地址可以实现页面(视图)的切换,但是AngularJS是一个纯前端MVC框架,在开发单页面应用时,所有功能都在同一页面完成,所以无需切换URL地址(即不允许产生跳转),但Web应用中又经常通过链接(a标签)来更新页面(视图),当点击链接时还要阻止其向服务器发起请求,通过锚点(页内跳转)可以实现这一点。

实现单页面应用需要具备:
a、只有一页面
b、链接使用锚点

<body>
    <nav>
        <a href="#login">登录</a>
        <a href="#register">注册</a>
    </nav>
    <div class="content">
        
    </div>
    <!-- 点击登录出现登录界面 -->
    <!-- 点击注册出现注册界面 -->
    <script>
        // 根据锚点的变化来决定到底应该显示哪个界面
        // 如何知道锚点变化了?
        window.onhashchange = function () {
            // 当锚点发生变化后此事件的回调被执行
            var hash = location.hash.slice(1);
            var xhr = new XMLHttpRequest;
            // 将获取到的锚点的变化值以参数的形式传给服务端
            xhr.open('get', 'spa.php?hash=' + hash);
            xhr.send();
            xhr.onreadystatechange = function () {
                if(xhr.readyState == 4 && xhr.status == 200) {
                    var content = document.querySelector('.content');
                    content.innerHTML = xhr.responseText;
                }
            }
        }
    </script>
</body>

通过上面的例子发现在单一页面中可以通过hashchange事件监听到锚点的变化,进而可以实现为不同的锚点准不同的视图,单页面应用就是基于这一原理实现的。

AngularJS对这一实现原理进行了封装,将锚点的变化封装成路由(Route),这是与后端路由的根本区别。

在1.2版前路由功能是包含在AngularJS核心代码当中,之后的版本将路由功能独立成一个模块,下载angular-route.js

8.2.1 如何使用路由

(1)引入angular-route.js
(2)实例化模块(App)时,当成依赖传进去(模块名称叫ngRoute)
(3)配置路由模块
(4)布局模板。通过ng-view指令布局模板,路由匹配的视图会被加载渲染到些区域。

<body ng-app="App">
    <nav>
        <a href="#/login">登录</a>
        <a href="#/register">注册</a>
        <a href="#/list">讲师列表</a>
    </nav>
    <div ng-view></div>
    <!-- AngularJS核心文件 -->
    <script src="./libs/angular.min.js"></script>
    <!-- AngularJS路由插件 -->
    <script src="./libs/angular-route.js"></script>
    <script>
        // 作为依赖传入
        var App = angular.module('App', ['ngRoute']);
        // 路由一定要经过config方法配置才能用
        App.config(['$routeProvider', function ($routeProvider) {
            // 具体使用when方法进行配置
            $routeProvider.when('/login', {
                // 将界面以字符串形式书写,不易管理
                // template: '<h1>我是登录界面</h1>'
                // 将界面内容放到独立文件中,方便管理
                templateUrl: './views/login.html'
            })
            .when('/register', {
                // 将界面以字符串形式书写,不易管理
                // template: '<h1>我是注册界面</h1>'
                // 将界面内容放到独立文件中,方便管理
                templateUrl: './views/register.html'
            })
            .when('/list', {
                templateUrl: './views/list.tpl',
                controller: 'DemoCtrl'
            })
            .otherwise({
                redirectTo: '/login'
            });
        }]);
        App.controller('DemoCtrl', ['$scope', '$routeParams', function ($scope, $routeParams) {
            console.log($routeParams);
            $scope.list = ['html', 'js', 'css', 'php'];

        }]);
    </script>

8.2.2 路由参数

(1)提供两个方法匹配路由,分别是whenotherwisewhen方法需要两个参数,otherwise方法做为when方法的补充只需要一个参数,其中when方法可以被多次调用。
(2)第1个参数是一个字符串,代表当前URL中的hash值。
(3)第2个参数是一个对象,配置当前路由的参数,如视图、控制器等。
1. template 字符串形式的视图模板
2. templateUrl 引入外部视图模板
3. controller 视图模板所属的控制器
4. redirectTo 跳转到其它路由
(4)获取参数,在控制中注入$routeParams可以获取传递的参数

<script src="./libs/angular.min.js"></script>
<script src="./libs/angular-route.js"></script>
<script>
    var App = angular.module('App', ['ngRoute']);
    App.config(['$routeProvider', function ($routeProvider) {
        // ?后面的内容是参数,并不是路由的一部分
        $routeProvider.when('/', {
            template: '<h1>你的参数为:{{params.name}}和{{params.age}}</h1>',
            controller: 'IndexCtrl'
        })
        // 使用:来为路由设置参数
        // :name , :后跟的是一个形参,在地址栏中输入实参。
        // 而没加: 的就是固定写法
        // 比如地址栏可以是/list/haoxiang/18/demo/100形式
        .when('/list/:name/:age/demo/:page', {
            template: '<h1>列表</h1>',
            controller: 'ListCtrl'
        });
    }]);
    App.controller('IndexCtrl', ['$scope', '$http', '$routeParams', function ($scope, $http, $routeParams) {
    
        // $routeParams来获取地址参数,就是?后的那部分
        console.log($routeParams);
        $scope.params = $routeParams;
    }]);
    // 列表控制器
    App.controller('ListCtrl', ['$scope', '$http', '$routeParams', function ($scope, $http, $routeParams) {
        console.log($routeParams);
    }]);
</script>

9.其他

9.1 在AngularJS中使用jQuery

在没有引入jQuery的前提下AngularJS实现了简版的jQuery Lite,通过angular.element不能选择元素,但可以将一个DOM元素转成jQuery对象,如果引提前引入了jQuery则angular.element则完全等于jQuery。

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

推荐阅读更多精彩内容

  • 简介: AngularJS 是一个 JavaScript 框架。它可通过 标签添加到 HTML 页面。 Ang...
    JenniferYe阅读 1,420评论 0 13
  • 1、什么是angularjs AngularJS是一个框架(诸多类库的集合)以数据和逻辑做为驱动(核心)。 Ang...
    margery阅读 309评论 0 0
  • 多重视图和路由 为什么需要路由? 回答这个问题首要要简单介绍一下单页面应用。单页面应用通俗来讲就是在浏览器中运行的...
    罂粟1995阅读 351评论 0 0
  • AngularJS是什么 AngularJS的官方文档这样介绍它: 完全使用JavaScript编写的客户端技术。...
    oWSQo阅读 1,304评论 0 10
  • 真正的教养是原谅那些做不到你自己所做的有教养行为的人,我想我做的不够好,我真的很糟糕吗
    想你itiswritten阅读 101评论 0 0