AngularJS 1.0 学习笔记

软件工程

软件设计原则

  • 避免重复原则(DRY-不要重复代码)

    • 编程的最基本原则是避免重复。
    • 在程序代码中总会有很多结构体,如循环、函数、类等等。
    • 一旦你重复某个语句或概念,就会很容易形成一个抽象体
    • 造价公式:单位工资 X 3倍 X 开发人数 X 天数
  • 抽象原则

    • 程序代码中每一个重要的功能,只能出现在源代码的一个位置
  • 简单原则(KISS-代码越简单越好)

    • 简单的代码占用时间少,漏洞少,并且易于修改
  • 避免创建不要的代码(YAGNI-不要写不需要的代码)

    • 除非你需要它,否则别创建新功能
  • 尽可能做可运行的最简单的事

    • 在编程中,一定要保持简单原则
    • 作为一名程序员不断的反思“如何在工作中做到简化呢?”
    • 这将有助于在设计中保持简单的路径。
  • 开闭原则(OCP-对拓展持“开放”态度,对修改持“封闭”态度)

    • 你所编写的软件实体(类、模块、函数等)最好是开放的,这样别人可以拓展开发
    • 对于你的代码,得限定别人不得修改
    • 别人可以基于你的代码进行拓展编写,但却不能修改你的代码
  • 最小惊讶原则

    • 最小惊讶原则通常是在用户界面方面引用,但同样适用于编写的代码
    • 代码应该尽可能减少让读者惊喜
    • 你编写的代码只需按照项目的要求来编写。
    • 其他华丽的功能就不必了,以免弄巧成拙
  • 单一责任原则(SRP)

    • 某个代码的功能,应该保证只有单一的明确的执行任务
  • 高内聚低耦合原则(HCLC)

    • 代码的任何一个部分应该减少对其他区域代码的依赖关系
    • 尽量不要使用共享参数
    • 相似的功能代码应尽量放在一个部分
    • 低耦合往往是完美结构系统和优秀设计的标志
  • 最少知识法则/迪米特法则

    • 让一个对象知道的越少越好
    • 该代码只和与其有直接关系的部分连接

软件设计模式

  • 概念

    • 是前人的优秀的项目经验的总结,在某种特定的场景下的特定的代码设计方法
    • C/C++/Java/PHP/JS的设计模式是通用的
    • 总共有23+种设计模式
  • MVC模式

    • Model
      • Model 模型 Modal 模态框 Module 模块
      • 模型,指业务数据,Web项目中由JS中由变量(数字、字符串、对象、数组等)来担当
    • View
      • 视图,指业务数据在用户面前的呈现,Web项目中由HTML(增强型)来担当
    • Controller
      • 控制器,负责获取、删除、更新模型数据,Web项目中由JS中的function来担当

AngularJS概述

Angular概述

  • 概述
    • AngularJS是一个纯JS框架,基于jQuery对DOM操作做了进一步的封装
    • 使用MVC操作代替所有的DOM操作
    • 用于以数据操作为主的SPA(单页应用)应用

Angular四大特性

  • AngularJS的四大特性:
    • 采用MVC模式,页面中再也无需出现DOM操作
      • Model: 模型,即业务数据,ng中由保存在特定范围内的变量来担当
      • View: 视图,负责数据的呈现,ng中由HTML(增强型)来担当
      • Controller: 控制器,负责操作(CDUD)数据,ng中由模块中的function来担当

双向数据绑定

  • 方向一:把Model数据绑定到View上,此后不论何时只要Model发生了改变,则View中的呈现会立即随之改变!

    • 实现方法:都实现了方向一的绑定
      • {{}}
      • ngBind
      • ngRepeat
      • ngIf
      • ngSrc
  • 方向二:把View(表单控件)中修改绑定到Model上,此后不论任何时候,只要View中的数据一修改,Model中的数据会自动随之修改

    • 实现方法:只有ngModel指令
    • 可以使用$scope.$watch('模型变量名', fn)监视一个模型变量值的改变
    • 单行文本输入域、多行文本输入域、下拉框、单选按钮控件默认会把自己的value属性值绑定到一个Model变量
    • 复选框会把一个true/false值绑定到一个Model变量
    • input
    • textarea
    • select 与value相关
    • input[radio] 与value相关
    • input[checkbox]
// 方向一
angular.module('myModule11', ['ng']).
  controller('c11', function($scope, $interval){
    $scope.age = 10;
    $interval(function(){
      $scope.age++;
    }, 1000);
    $scope.sum = 0;
    $scope.sumer = function(){
      $scope.sum++;
    }
  })
// 轮播
angular.module('M12', ['ng']).
  controller('C12', function($scope, $interval){
    $scope.num = 1;
    $scope.pro = function(){
      $interval.cancel(t);
      $scope.num > 1 ? $scope.num-- : $scope.num = 5;                
    }
    $scope.next = function(){
      $interval.cancel(t);
      $scope.num < 5 ? $scope.num++ : $scope.num = 1;
    }
    let t = $interval(function(){
      $scope.num < 5 ? $scope.num++ : $scope.num = 1;
    }, 1000)
  })
// 进度条
angular.module('M13', ['ng']).
  controller('C13', function($scope, $interval){
    let percentage = 0;
    $scope.myStyle = { '0%'};
    let t = $interval(function(){
      percentage += 10;
      $scope.myStyle.width = percentage + "%"
      percentage < 100 ? percentage : $interval.cancel(t);
      // if(percentage>=100){
      //     $interval.cancel(t);
      //     console.log('已完成');
      // }
    }, 50)
  })
<!-- 方向二 -->
<!DOCTYPE html>
<html ng-app="M14">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<section class="container" ng-controller="C14">
    <input ng-model="userName">
    <p ng-bind="userName"></p>
</section>
<script src="js/angular.js"></script>
<script>
angular.module('M14', ['ng']).
    controller('C14', function($scope, $interval){
        // 监视一个Model数据的改变
        $scope.$watch('userName', function(){
            console.log($scope.userName);
        })
    })
</script>
</body>
</html>
<!-- 简易版购物车计算器 -->
<!DOCTYPE html>
<html lang="en" ng-app="M15">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<section class="container" ng-controller="C15">
    <p>简易版购物车计算器</p>
    <p>单价:<input type='text' ng-model="price"> 数量:<input type='number' ng-model="count"> 小计:<span ng-bind='total'>0</span></p>
</section>
<script src="js/angular.js"></script>
<script>
angular.module('M15', ['ng']).
    controller('C15', function($scope, $interval){
        $scope.$watch('price', function(){
            $scope.total = parseInt($scope.price) * parseInt($scope.count);
        })
        $scope.$watch('count', function(){
            $scope.total = parseInt($scope.price) * parseInt($scope.count);
        })
    })
</script>
</body>
</html>
<!--同意条款 ngIf方式 操作DOM-->
<section class="container" ng-controller="C16">
   <input type="checkbox" ng-model='agree'><span>我同意本站的使用条款</span><br>
   <input class='btn btn-success' type="button" value="提交注册信息" ng-if="agree">
</section>
<script>
angular.module('M16', ['ng']).
    controller('C16', function($scope){
        $scope.agree = true;
    })
</script>
<!--同意条款 ngShow方式 操作display-->
<section class="container" ng-controller="C16">
   <input type="checkbox" ng-model='agree'><span>我同意本站的使用条款</span><br>
   <input class='btn btn-success' type="button" value="提交注册信息" ng-show="agree">
</section>
<script>
angular.module('M16', ['ng']).
    controller('C16', function($scope){
        $scope.agree = true;
    })
</script>
<!--同意条款 disabled方式 -->
<section class="container" ng-controller="C16">
   <input type="checkbox" ng-model='agree'><span>我同意本站的使用条款</span><br>
   <input class='btn btn-success' type="button" value="提交注册信息" ng-disabled="!agree">
</section>
<script>
angular.module('M16', ['ng']).
    controller('C16', function($scope){
        $scope.agree = true;
    })
</script>
<!-- 头像选择 -->
<section class="container" ng-controller="C17">
  <select ng-model="portrait">
    <option value="1.jpg">小萝莉</option>
    <option value="2.jpg">小鲜肉</option>
    <option value="3.jpg">萌大叔</option>
    <option value="4.jpg">胖大婶</option>
  </select>
  <img ng-src="img/{{portrait}}" style=" 120px;">
  <p ng-bind="portrait"></p>
</section>
<script>
angular.module('M17', ['ng']).
  controller('C17', function($scope, $interval){
    $scope.portrait = '1.jpg';
  })
</script>
<!--全选/取消全选-->
<section class="container" ng-controller="C18">
  <table class='table table-bordered'>
    <thead>
      <tr>
        <th>选择</th>
        <th>姓名</th>
        <th>工资</th>
        <th>操作</th>
      </tr>
    </thead>
      <tbody>
        <tr ng-repeat="item in employee">
          <td><input type='checkbox' ng-checked="selectAll"></td>
          <td ng-bind="item.name">Sunny</td>
          <td ng-bind="item.salary">8500</td>
          <td><button class='btn btn-danger'>删除</button></td>
        </tr>
      </tbody>
  </table>
  <input type="checkbox" ng-model="selectAll">
  <span ng-hide='selectAll'>全选</span>
  <span ng-show='selectAll'>取消全选</span>
</section>
<script src="js/jquery-1.11.3.js"></script>
<script src="js/angular.js"></script>
<script>
angular.module('M18', ['ng']).
  controller('C18', function($scope){
    $scope.employee = [{
      name: 'Sunny',
      salary: 7200
    },{
      name: 'Tom',
      salary: 6400
    },{
      name: 'Jerry',
      salary: 7800
    }]
    $scope.selectAll = false;
  })
</script>

依赖注入

  • 依赖注入概念
    • 依赖(Dependency):Driver对象的创建和运行必须一个car对象,称为Drive对象,“依赖于”Car对象
function(car){
  car.start();
  car.run();
  car.stop();
}
  • 依赖对象的解决方法:
    • 主动创建
    • 被动注入(inject)
      • 一般由特定框架来创建Driver对象,发现其依赖于一个Car对象,框架自动创建被依赖的Car对象 —— 称为“依赖注入”
// 主动创建
var c1 = new Car();  // 创建被依赖的对象
var d1 = new Driver(c1); // 使用被依赖的对象
// 依赖注入
module.controller('控制器名', function(){$scope, $http})
  • 控制器的创建

    • 控制器对象不能受手动创建
    • 必须由框架来创建
      • <div ng-controller="C30"></div>
    • 注意:
      • 控制器对象的构造函数是由AngularJS来调用的,不能手动调用
      • Angular会根据控制器对象的构造函数的形参名来创建依赖的参数对象(形参名不能随意指定!)
      • 若控制器对象未声明形参,则Angular不会传递任何实参进来
      • 控制器对象的形参名必须是Angular可识别的,但是数量和顺序没有限制
      • AngularJS会根据每一个形参的名称来查找创建被依赖的对象,并自动注入进来
  • JS压缩功能的解决方案

    • 若使用了JS的压缩功能,会自动将依赖对象的形参进行精简混淆,则Angular就无法再根据形参名实现依赖注入了
    • 在数组内,形参的数量与顺序一一对应
angular.module('M30', ['ng']).
    controller('C30', ['$scope', '$http', '$animate' ,function(a, b, c){
        console.log('c3控制器对象的实例开始创建...');
        console.log(arguments);
        console.log('c3控制器对象的实例创建完成!');
    }])

模块化(Module)设计

  • 设计原则

    • 模块化设计体现着“高内聚低耦合”设计原则
    • 项目中,可以根据功能的不同,将不同的组件放置在不同的模块中
      • 用户管理相关内容全部放在userModule
      • 商品相关的内容全部放在productModule
  • AngularJS中有两种模块

    • AngularJS官方提供的模块
      • ng ngRoute ngAnimate ngTouch ...
    • 用户自定义的模块
      • userModule productModule orderModule ...
    • 一个AngularJS的模块中可以包含哪些组件?
      • controller组件:用于维护Model模型数据(自定义模块)
      • directive组件:用于View中输出/绑定Model数据
      • service组件:用于在不同的控制器中提供某种函数服务
      • filter组件:用于对View中输出的数据进行格式化
      • provider组件:
      • function组件:
      • object组件:
      • type组件:
  • Angular模块中的常用组件之filter

    • filter:过滤器,用于Model数据在View中呈现时进行某种格式的筛选/过滤/格式化
    • 在View中使用过滤器时,需要借助于管道 |
    • ng模块中提供的过滤器
      • lowercase 把数据格式化为小写
        • 语法:{{表达式 | lowercase}}
      • uppercase 把数据格式化为大写
        • 语法:{{表达式 | uppercase}}
      • number 把数字型数据格式化为三位一个逗号的字符串,同时指定小数点位数
        • 语法:{{表达式 | number:小数位数}}
      • currency 把数字型数据格式化为货币格式的字符串,同时指定货币符号
        • 语法:{{表达式 | currency:'货币符号'}}
      • date 把数据/Date型数据格式化为特定日期时间格式的字符串
        • 真实项目中,往往不使用Date类型表示日期时间
        • 而使用长整型的数字来代替
        • 所有的编程语言/数据库系统都支持长整型数字,都可以把数字和日期时间随意的转换
        • 语法: {{表达式 | date:'日期时间格式'}}
<section class="container" ng-controller="C31">
    <p ng-bind="ename"></p>
    <p ng-bind="ename.toUpperCase()"></p>
    <!--过滤器在使用时要借助管道-->
    <p ng-bind="ename | uppercase"></p>
    <p ng-bind="ename | lowercase | uppercase"></p>
</section>
<script>
angular.module('M31', ['ng']).
    controller('C31', function($scope){
        $scope.ename = 'Tom';
    })
</script>
<!--服务器中运行-->
<!DOCTYPE html>
<html lang="en" ng-app="M32">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="css/bootstrap.css">
</head>
<body>
<!--
    点击一个按钮“加载员工数据”,
    向服务器发起异步的AJAX请求,
    获取服务器的一段JSON数据;
    加载完数据后,按钮即禁用/消失
-->
<section class="container" ng-controller="C32">
  <button class='btn btn-success' ng-click='loadData()' ng-disabled='agree'>加载员工数据...</button>
  <hr>
  <table class="table">
    <thead>
      <tr>
        <th>序号</th>
        <th>姓名</th>
        <th>工资</th>
        <th>入职日期</th>
      </tr>
    </thead>
    <tbody>
      <tr ng-repeat='(key, item) in employee'>
        <td ng-bind='key'></td>
        <td ng-bind='item.ename | uppercase'></td>
        <td ng-bind='item.salary | currency:"¥ "'></td>
        <td ng-bind='item.hiredate | date:"yyyy年mm月dd日"'></td>
      </tr>
    </tbody>
  </table>  
</section>
<script src="js/jquery-1.11.3.js"></script>
<script src="js/angular.js"></script>
<script>
angular.module('M32', ['ng']).
  controller('C32', function($scope, $http){
    $scope.loadData = function(){
      $scope.agree = true;
      $http.get('data/5.json').success(function(data){
        $scope.employee = data;
      })
    }
  })
</script>
</body>
</html>
// 5.json
[{
    "ename": "Tom",
    "salary": 12000,
    "hiredate": 1601968856625
},{
    "ename": "Jerry",
    "salary": 15000,
    "hiredate": 1604588394625
},{
    "ename": "John",
    "salary": 9000,
    "hiredate": 1601969624625
},{
    "ename": "Sunny",
    "salary": 13000,
    "hiredate": 1601884394625
},{
    "ename": "Mary",
    "salary": 11000,
    "hiredate": 1601968394815
}

AngularJS数据绑定的原理&最大的缺陷

  • 绑定原理

    • 每一次方向1的绑定都会在$digest队列中生成一个执行DOM操作的函数
    • 若一个ngApp中有N次数据绑定就会生成N个这样的函数
    • 只要某一个Model数据发生了值的改变,立即会自动执行$digest队列的每一个函数,进行View的更新
    • 队列的数据轮询
  • setInterval()和$interval()的不同

    • window.setInterval()只会执行指定的任务,即使修改了Model数据也不会自动轮询$digest队列
    • $interval()的执行体中会在最后自动执行:$scope.$digest()/$scope.$apply()
      • 轮询$digest队列,执行其中的每一个DOM操作函数
      • 简言之,$interval = setInterval()+$scope().$digest()
<!DOCTYPE html>
<html lang="en" ng-app="M20">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="css/bootstrap.css">
</head>
<body>
<section class="container" ng-controller="C20">
    <p>完整版购物车计算器</p>
    <button class='btn btn-success' ng-click='addProduct()'>添加商品</button>
    <hr/>
    <div>
        <p ng-repeat="(index, item) in cart" class='alert alert-success'>
            单价:<span ng-bind='item.price'></span>
            数量:<input type='number' ng-model="item.count">
            小计:<span ng-bind='item.price*item.count'>0</span>
        </p>
    </div>
    <div>总计:<span ng-bind='getTotal()'></span></div>
</section>
<script src="js/jquery-1.11.3.js"></script>
<script src="js/angular.js"></script>
<script>
    angular.module('M20', ['ng']).
        controller('C20', function($scope, $interval){   
            $scope.cart = []
            $scope.cart.push({price: 10.5, count: 2});
            $scope.cart.push({price: 5.5, count: 5});
            $scope.addProduct = function(){
                let p = {price: Math.round(Math.random()*500)/10, count: Math.ceil(Math.random()*10)}
                $scope.cart.push(p);
                console.log($scope.cart.count);
            }
            $scope.getTotal = function(){
                let total = 0;
                angular.forEach($scope.cart, function(v, k){
                    total += v.count * v.price; 
                    console.log(v);
                })
                return total
            }
        })
    </script>
</body>
</html>

Web项目中“单页应用”和“多页应用”的比较

  • 多页应用

    • 一个项目中有多个完整的.html页面
    • 多个页面间的跳转可以使用超链接、表单提交、JS(location.href="xx.html")
    • 页面切换是同步请求:客户端先删除第一个页面的DOM结构,发起HTTP请求,等待服务器给第二个页面的响应数据...一片惨白
    • 每个页面都是一个完整的DOM树
    • 页面切换时控制权在浏览器手中,不可能添加任何的过场动画效果
  • 单页应用(SPA)

    • 只有一个.html是完整的(缺少body主体),其他.html都是不完整的(可能只是一个div而已)
    • 多个“伪页面”间的跳转可以使用超链接、JS(...)
    • 伪页面切换是异步请求:客户端首先请求一个完整的页面,然后再发起异步AJAX请求,获取不同的模板页面,插入在当前的DOM树
    • 整个项目只有一个完整的DOM树
    • 伪页面切换的本质是一棵DOM树上的两个DIV在切换,可以很容易的添加各种过场动画
  • 总结

    • 单页应用完全可以实现传统的多页面的效果
    • 同时还可以降低服务器和客户端数据传输量、加快页面显示速度、添加丰富的过场动画效果!

Angular国际化项目

  • 国际化项目
    • internationalization
    • 简写 i18n
    • 一个项目可以根据客户端的不同,呈现出不同的语言

Angular模块指令

Angular表达式

  • Angular表达式
    • 语法:{{表达式}}
    • 作用:在当前位置“输出”该表达式的值
    • 表达式运算
      • 算术运算 自加自减不可以
      • 比较运算 都可以
      • 逻辑运算 都可以
      • 三目运算 都可以
      • 赋值运算 += -= *= 等运算赋值不可以
      • 特殊运算符 不可以使用typeof()
      • 调用string的方法和属性
        • {{"apple".split('')}} 字符串变数组
        • {{"apple".slice(1, 4)}} 提取字符串
        • {{"apple".substr(0, 4)}} 从字符串某处提取N个字符
        • length属性 <span ng-init="ename='Tom'"></span> <p>{{ ename.length }}</p>
        • toUpperCase()方法 <span ng-init="ename='Tom'"></span> <p>{{ ename.toUpperCase() }}</p>
      • 创建新对象
        • 直接量语法: <p>{{ {ename:'Tom',age:30}.age }}</p>
        • new 构造方法:不可以
      • 创建数组
        • 数组直接量:<p>{{ [1,2,3] }}</p>
        • 数组直接量:<p>{{ [1,2,3][1] }}</p>
        • new Array:不可以

ng模块提供的指令(Directive)

  • Angular中ng模块提供的指令(Directive)
    • ngAPP: 自动载入/启动一个Angular应用
      • 语法:
        • ng-app
        • <ANY ng-app="xxx"></ANY>
        • <ANY ng-app></ANY>
        • <ANY data-ng-app="xxx"></ANY>
      • 注意:一个HTML页面中只允许使用一次ngApp指令!用于确定AngularJS应用的范围
    • ngInit: 用于声明Model变量
      • ng-init
      • 语法:<ANY ng-init="变量名=值;变量名=值;..."></ANY>
      • 注意:Model变量声明时不能使用Var
    • ngController: 创建一个控制器对象的实例(即调用Controller函数)
      • ng-controller
      • 语法:<ANY ng-controller="控制器名"></ANY>
    • ngBind: 在当前元素的innerHTML上输出指定的表达式的值
      • ng-bind
      • 语法:<ANY ng-bind="表达式"></ANY>
      • 说明:此指令的作用与{{}}一样,只是可以防止闪动问题
    • ngRepeat: 为HTML增加循环功能,循环输出当前元素
      • 语法:<ANY ng-repeat="变量名 in Model数组/对象"></ANY>
      • 语法:<ANY ng-repeat="(下标, 值) in Model数组/对象"></ANY>
    • ngIf: 为HTML增加选择功能,
      • 只有在表达式值为true时,当前元素才添加到DOM树
      • 否则就从DOM树上删除
      • 语法:<ANY ng-if="表达式"></ANY>
    • ngSrc: 解决img等标签的src属性中包含{{}}产生的404问题
      • 语法:<img ng-src="路径/{{表达式}}">
    • ngClick: 为元素绑定监听函数(不是全局函数,而是Model函数)
      • 语法:<ANY ng-click="模型函数()"></ANY>
      • 注意:使用$scope.模型函数名 = function(){}格式来声明模型函数
    • ngStyle: 允许你在HTML元素上条件化设置CSS样式
      • 语法:<ANY ng-style="属性对象"></ANY>
      • 赋值为一个Model对象,用于为当前元素指定样式
    • ngModel: 使用NgModelController绑定一个 input,select, textarea (或自定义表单控件) 到域上的一个属性
      • 语法:
    • ngShow: 根据ngShow属性上表达式来显示或隐藏给定的HTML元素,通过display:none/block来控制当前元素是否显示
      • 语法:<ANY ng-show="表达式"></ANY>
    • ngHide: 根据ngHide属性上表达式来显示或隐藏给定的HTML元素,通过display:none/block来控制当前元素是否显示
      • 语法:<ANY ng-hide="表达式"></ANY>
    • ngDisabled: 赋值为true/false,可以控制当前元素是否禁用
      • 语法:<ANY ng-disabled="表达式"></ANY>
    • ngChecked: 赋值为true/false,可以控制当前元素是否选中
      • 语法:<ANY ng-checked="表达式"></ANY>
<!-- ngInit指令 -->
<div class="container" >
    <h2>Angular中创建Model变量的两种方法</h2>
    <h3>使用ngInit指令</h3>
    <!-- ngInit指令作为HTML元素属性来使用 -->
    <span ng-init="price=10.5"></span>
    <p>{{ price }}</p>
    <!-- ngInit指令作为HTML元素样式来使用 -->
    <span class="ng-init: price=22.5"></span>
    <p>{{ price }}</p>
</div>

ngAnimate模块

  • Angular中的ngAnimate模块

    • ngAnimate可以支持JS、Transition、Keyframes动画
    • 但它本身未提供任何的动画效果
    • 而是为上面三种技术提供了相应的“动画钩子(Hooks)”
  • ng模块中提供的指令:ngRepeat, ngInclude, ngIf, ngSwitch, ngShow, ngHide, ngView and ngClass 默认都提供了动画钩子

  • 使用ngAnimate模块调用其CSS Transition动画钩子的步骤:

    • 在index.html引入angular.js和angular-animate.js
    • 自定义模块中声明依赖于ngAnimate模块
      • 特定的指令就会产生动画钩子
      • angular.module('app', ['ng', 'ngRoute', 'ngAnimate'])
    • 为ngView声明class,样式中指定transition动画,现在就可以查看到动画钩子
      • <div class="page" ng-view></div>
    • 可以看到ngAnimate为即将要离开的ngView添加了ng-leave和ng-leave-active两个class,为即将要进入的ngView添加了ng-enter和ng-enter-active两个class
      • 为这四个class编写特定的class样式即可
<!-- index.html -->
<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="css/bootstrap.css">
    <link rel="stylesheet" href="css/app.css">
</head>
<body>
<div class="page" ng-view></div>
<script src="js/jquery-1.11.3.js"></script>
<script src="js/angular.js"></script>
<script src='js/angular-route.js'></script>
<script src='js/angular-animate.js'></script>
<script src='js/app.js'></script>
</body>
</html>
// app.js
angular.module('app', ['ng', 'ngRoute', 'ngAnimate']).
    controller('startCtrl', function($scope, $location){
      $scope.message = 'Hello start!';
      $scope.jump = function(routeUrl){
          $location.path(routeUrl); //修改路由地址
      }
    }).
    controller('mainCtrl', function($scope){
      $scope.message = 'Hello main!'
    }).
    controller('detailCtrl', function($scope){
      $scope.message = 'Hello detail!'
    }).
    config(function($routeProvider){
        $routeProvider.
            when('/start', {
                templateUrl: 'tpl/start.html',
                controller: 'startCtrl',
            }).
            when('/main', {
                templateUrl: 'tpl/main.html',
                controller: 'mainCtrl'
            }).
            when('/detail', {
                templateUrl: 'tpl/detail.html',
                controller: 'detailCtrl'
            }).
            otherwise({
                redirectTo: '/start'
            })
    })
/* app.css */
body {
    position: relative;
    overflow: hidden;
}
.page {
    transition: all .15s linear;
    position: absolute;
     100%;
}
/* 即将要离开的ngView动画开始时的样式 */
.page.ng-leave {
    /* left: 0; */
    opacity: 1;
}
/* 要离开的ngView动画结束时的样式 */
.page.ng-leave.ng-leave-active {
    /* left: -100%; */
    opacity: 0;
}
/* 即将要进入的ngView动画开始时的样式 */
.page.ng-enter {
    /* left: 100%; */
    opacity: 0;
}
/* 要进入的ngView动画结束时的样式 */
.page.ng-enter.ng-enter-active {
    /* left: 0; */
    opacity: 1;
}

ngRoute模块

  • Angular中ngRoute模块
    • 概念:
      • ngRoute模块可以让用户自定义“路由字典”
      • 自动解析请求URL中的理由地址,查找路由字典
      • 自动发起异步AJAX请求,把获取的结果放在当前页面中
    • 使用ngRoute模块的步骤:
      • 创建唯一完整的页面:index.html,引入angular.js和angular-route.js
      • 在index.html的body中使用ngView指令声明一个容器元素用于盛放模板页面
      • 创建自定义模块,声明依赖于ng和ngRoute两个模块
      • 在当前模块中使用ngRoute提供的对象配置路由字典
        • 调用config(function(){})
        • 注入方法 $routeProvider
      • 再创建几个模板页面,只需要有div元素即可
      • 测试路由字典的配置是否正确
<!--index.html-->
<!DOCTYPE html>
<html ng-app="M33">
<head lang="en">
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="css/bootstrap.css">
</head>
<body>   
<div ng-view></div>
<script src="js/jquery-1.11.3.js"></script>
<script src="js/angular.js"></script>
<script src="js/angular-route.js"></script>
<script>
angular.module('M33', ['ng', 'ngRoute']).
  config(function($routeProvider){
    $routeProvider.
      when('/start', {
        templateUrl: 'tpl/start.html'
      }).
      when('/main', {
        templateUrl: 'tpl/main.html'
      }).
      when('/detail', {
        templateUrl: 'tpl/detail.html'
      }).
      otherwise({
        // 若URL中未提供路由地址或提供了不存在的路由地址
        // 重定向
        redirectTo: '/start'
      })
  })
</script>
</body>
</html>
<!--start.html-->
<div class="panel panel-danger">
    <div class="panel-heading">
        <h3 class="panel-title">
            Start
        </h3>
    </div>
    <div class="panel-body">
        起始页
        <p><a href="#/detail" class='btn btn-success'>跳转到详情页</a></p>
        <p><a href="#/main" class='btn btn-success'>跳转到菜单页</a></p>
    </div>
</div>
<!--detail.html-->
<div class="panel panel-success">
    <div class="panel-heading">
        <h3 class="panel-title">
            Start
        </h3>
    </div>
    <div class="panel-body">
        详情页
        <p><a href="#/start" class='btn btn-success'>跳转到起始页</a></p>
        <p><a href="#/main" class='btn btn-success'>跳转到菜单页</a></p>
    </div>
</div>
<!--main.html-->
<div class="panel panel-info">
    <div class="panel-heading">
        <h3 class="panel-title">
            Start
        </h3>
    </div>
    <div class="panel-body">
        菜单页
        <p><a href="#/detail" class='btn btn-success'>跳转到详情页</a></p>
        <p><a href="#/start" class='btn btn-success'>跳转到起始页</a></p>
    </div>
</div>
  • 使用ngRoute模块需要注意的问题:
    • 由于模板页面被客户端请求后挂载在index.htmlDOM树上
      • 所以其中所有的图片等外部资源文件的路径必须相对于index.html
      • 而不是模板页面
    • 使用ngRoute模块时,无需为模板页面中的某个元素单独设置ngController
      • 只需要再声明路由字典时设置整个模板页面所需的控制器即可
    • 在不同的模板页面间跳转可以采用两种方式:
      • 超链接方式
        • <a href="#/路由地址"></a>
      • JS编程方式
        • <button ng-click='jump()'></button>
        • $scope.jump = function(){$location.path('/路由地址');}
// app.js
angular.module('app', ['ng', 'ngRoute']).
  controller('startCtrl', function($scope, $location){
    $scope.message = 'Hello start!';
    $scope.jump = function(routeUrl){
      $location.path(routeUrl); //修改路由地址
    }
  }).
  controller('mainCtrl', function($scope){
    $scope.message = 'Hello main!'
  }).
  controller('detailCtrl', function($scope){
    $scope.message = 'Hello detail!'
  }).
  config(function($routeProvider){
    $routeProvider.
      when('/start', {
        templateUrl: 'tpl/start.html',
        controller: 'startCtrl',
      }).
      when('/main', {
        templateUrl: 'tpl/main.html',
        controller: 'mainCtrl'
      }).
      when('/detail', {
        templateUrl: 'tpl/detail.html',
        controller: 'detailCtrl'
      }).
      otherwise({
        redirectTo: '/start'
      })
  })
<!--start.html-->
<div class="panel panel-danger">
    <div class="panel-heading">
        <h3 class="panel-title">
            Start _ 起始页
        </h3>
    </div>
    <div class="panel-body">
        <p><a href="#/detail" class='btn btn-success'>跳转到详情页</a></p>
        <p><a class='btn btn-success' ng-click="jump('/main')">跳转到菜单页</a></p>
        <hr>
        <hr>
        <p class='text-justify text-danger'>jQuery全屏滚动插件fullPage.js</p>
        <p ng-bind='message'></p>
        <img class='img-responsive' src="img/10.jpg" alt="">
    </div>
</div>

ng模块中提供的服务(server)

  • $rootScope

    • 用于在不同的控制器间共享数据
  • $interval

    • 提供周期性定时器服务
angular.module('myModule11', ['ng']).
  controller('c11', function($scope, $interval){
    $scope.age = 10;
    let t = $interval(function(){
      $scope.age > 100 ? $interval.cancel(t) : $scope.age++;
    }, 1000)
  })
  • $timeout

    • 提供一次性定时器服务
  • $http

    • 发起异步的AJAX请求服务
<?php
// 向客户端输出员工的信息,以JSON格式
header('Content-Type: application/json');
$empList = [];
$empList[] = [
    'eno' => 101,
    'ename' => 'Tom',
    'salary' => rand(1000, 10000)
];
$empList[] = [
    'eno' => 102,
    'ename' => 'Jerry',
    'salary' => rand(4500, 10000)
];
$empList[] = [
    'eno' => 103,
    'ename' => 'Sunny',
    'salary' => rand(1000, 10000)
];
$str = json_encode($empList);
echo $str;
?>
<!DOCTYPE html>
<html lang="en" ng-app="M19">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="css/bootstrap.css">
</head>
<body>
<section class="container" ng-controller="C19">
  <button class='btn btn-success' ng-click='loadEmpInfo()'>加载员工数据...</button>
  <table class='table table-bordered'>
    <thead>
      <tr>
        <th>选择</th>
        <th>姓名</th>
        <th>工资</th>
        <th>操作</th>
      </tr>
    </thead>
    <tbody>
      <tr ng-repeat="item in employee">
        <td><input type='checkbox' ng-checked="selectAll"></td>
        <td ng-bind="item.eno">Sunny</td>
        <td ng-bind="item.ename">Sunny</td>
        <td ng-bind="item.salary">8500</td>
        <td><button class='btn btn-danger'>删除</button></td>
      </tr>
    </tbody>
  </table>
  <input type="checkbox" ng-model="selectAll">
  <span ng-hide='selectAll'>全选</span>
  <span ng-show='selectAll'>取消全选</span>
</section>
<script src="js/jquery-1.11.3.js"></script>
<script src="js/angular.js"></script>
<script>
angular.module('M19', ['ng']).
  controller('C19', function($scope, $http){
    $scope.employee = [];
    $scope.selectAll = false;
    $scope.loadEmpInfo = function(){
      // 点击按钮,则向服务器发起AJAX请求
      $http.get('data/19.php').
        success(function(data){
            for(let i=0; i<data.length;i++){
              $scope.employee.push(data[i]);
            }
        })
    }
  })
</script>
</body>
</html>

Angular中声明变量与作用域

Angular业务数据

  • Angular中声明变量——Model数据
    • 有两种方式可以声明Model变量
      • 使用ngInit指令声明
        • ngInit指令可以声明为HTML元素的属性或样式
        • ngInit指令声明的Model变量可以先使用再声明
        • ngInit指令可以一次声明多个Model变量,用分号隔开即可
        • ngInit指令可以声明哪些类型的Model变量
          • number 可以
          • string 可以
          • boolean 可以
          • 对象 直接量可以
          • 数组 直接量可以
          • 对象的数组 直接量可以
          • 注意:使用ng-init定义Model变量时,不能使用new关键字
          • 此方法把View和Model混杂在一起,不推荐使用
      • 使用Controller创建Model变量 推荐使用
        • 创建模块 <= 创建Controller <= 创建Model变量
        • 注意:新版本的Angular要求控制器必须声明在一个模块中!
        • 具体步骤
          • 声明一个自定义的模块(module)
            • angular.module('模块名',[])
          • 在当前AngularJS应用中注册自定义模块
            • ng-app="模块名"
          • 在自定义模块中创建Controll函数,其中创建Model数据
            • $scope是AngularJS中的域模型,也称为作用域实例,其实就是个可劲儿造的空对象
            • $scope.模型变量名 = 值;
          • 在View中创建Controller对象的实例,指定其作用范围
            • <ANY ng-controller="控制器名">...控制器的有效范围...</ANY>
          • 在控制器的作用范围内输出控制器中声明的Model变量
            • 可以使用{{}}输出Model变量的值
<!-- MVC基础操作 -->
<!DOCTYPE html>
<!-- 2.在Angular应用中注册自定义模块 -->
<html ng-app="myModule1">
<head lang="en">
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="css/bootstrap.css">
</head>
<body>
  <div class="container">
    <div ng-controller="myController1">
      <!-- 5.在控制器的作用范围内输出控制器中声明的Model变量 -->
      <p>ename:{{ename}}</p>
      <p>age:{{age}}</p>
      <p>birthday:{{birthday.getFullYear()+'-'+(birthday.getMonth()+1)}}</p>
    </div>
    <div ng-controller="myController2">
      <p>ename:{{ename}}</p>
      <p>age:{{age}}</p>
      <p>birthday:{{birthday.getFullYear()+'-'+(birthday.getMonth()+1)}}</p>
    </div>
  </div>
<script src="js/angular.js"></script>   
<script>
  // 1.声明自定义模块
  angular.module('myModule1', ['ng']).
    controller('myController1', function($scope){
      // 3.创建一个Controller函数
      console.log('1对象的实例开始创建...');
      // 4.使用Controller函数创建/修改/删除Model数据
      $scope.ename = 'Tom';
      $scope.age = 18;
      $scope.birthday = new Date();
      console.log('1对象的实例创建完成!');
    }).
    controller('myController2', function($scope){
      // 3.创建一个Controller函数
      console.log('2对象的实例开始创建...');
      // 4.使用Controller函数创建/修改/删除Model数据
      $scope.ename = 'Jerry';
      $scope.age = 19;
      $scope.birthday = new Date();
      console.log('2对象的实例创建完成!');
    })
</script>
</body>
</html>
<!-- 对象操作 -->
<!DOCTYPE html>
<html ng-app="myMd5">
<head lang="en">
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="css/bootstrap.css">
</head>
<body>
<div class="container">
  <div ng-controller="myCtrl5">
    <p>name:{{stu.sname}}</p>
    <p>age:{{stu.age}}</p>
    <p ng-bind="'生日:'+stu.birthday.getFullYear()"></p>
    <p ng-bind="'性别:'+stu.sex"></p>
    <p>成绩:{{stu['score']}}</p>
  </div>
</div>
<script src="js/angular.js"></script>   
<script>
  angular.module('myMd5', ["ng"]).
    controller("myCtrl5", function($scope){
      $scope.stu = new Object;
      $scope.stu.sname = 'Sunny';
      $scope.stu.age = 23;
      $scope.stu.sex = "女";
      $scope.stu.score = 99;
      $scope.stu.birthday = new Date();
    })
</script>
</body>
</html>
<!-- for循环 -->
<!DOCTYPE html>
<html ng-app="myMd6">
<head lang="en">
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="css/bootstrap.css">
</head>
<body>
<div class="container">
  <div ng-controller="myCtrl6">
    <p>{{scoreList}}</p> 
    <ul>
      <li ng-repeat="item in scoreList">
        {{item}}
      </li>
      <li ng-repeat="(index, num) in scoreList">
        {{index}} - {{num}}
      </li>
    </ul>
  </div>
</div>
<script src="js/angular.js"></script> 
<script>
  angular.module("myMd6", ["ng"]).
    controller("myCtrl6", function($scope){
      $scope.scoreList = [95, 93, 89];
      $scope.scoreList.push(99);
      $scope.scoreList[$scope.scoreList.length] = 94;
    })
</script>
</body>
</html>

Angular域模型 (实例作用域)

  • $scope $rootScope

    • 每个控制器的实例都对应一个作用范围对象,即$scope
    • 在控制器中声明的Model数据,必须保存在控制的作用范围内
    • 一个HTML中可以声明多个控制器实例,每个控制器都有自己的作用范围,这些范围内的数据彼此隔离,不会相互影响,可以由不同的开发人员来编写
    • 为了在多个控制器间共享数据,可以将Model数据保存在一个"全局作用范围内"
    • $rootScope 真个AngularAPP中有且只有一个$rootScope对象
    • 且此对象是所有$scope的父作用域对象
  • 作用域对象的关系

    • 嵌套关系的作用域

如何压缩css/js文件,得到xx.min.js/css

  • YUI框架:Yahoo User Interface,是由雅虎的前端工程师将自己的工作经验整理出来的工具集合,其中一个小工具:

    • yui-compressor,可用于压缩CSS和JS文件
    • yuicompressor-2.4.2.jar 置放于C盘根目录下
    • 使用步骤:
      • 下载并安装Java语言的运行环境——JDK
      • 在Java命令下运行yui-compressor工具
        • java.exe -jar c:/yui-comressor-2.4.2.jar e:/my.js >e:/my.min.js
        • 也可以把此工具配置为WebStorm中的FileWatcher(files/setting/tools),只要用户编辑一个.js/.css文件,即自动调用此工具得到压缩后的文件
    • 压缩效果:
      • 删除所有的注释
      • 删除没有语义的空白字符
      • 尽可能简化局部的变量名,函数名,形参名 —— 称为混淆
      • 注意:所有的数据值(如数字、字符串等)、关键字不会做任何改变
      • ctrl+alt+L 压缩恢复
  • JS框架

// 自调函数的写法
/*
*这是我的一个JS框架
* author: 文华
* email:  liwenhua@tedu.cn
* */
+function(win, doc){
  var age = 20;
  var ename = 'Tom Cruise';

  //下面有一个执行相加操作的函数
  function add(num1, num2){
    var sum  = 0;
    sum = num1 + num2;
    return sum;
  }

  add(age, ename);   //调用add函数

}(window, document);

如何仿照AngularJS实现SPA的页面切换效果

  • 页面URL形如:
    • http://127.0.0.1/index.html#/路由地址
    • 浏览器首先请求基础页面(index.html),再解析URL中的路由地址
    • 查找路由字典,形如
      • /start -> tpl/start.html

      • /main -> tpl/main.html

      • ...
      • 获取当前URL中路由地址所对应的真实模板页面的地址
      • location.protocol
      • location.hostname
      • location.port
      • location.path
      • location.hash
    • 客户端发起异步AJAX请求,获取目标模板页面,将服务器返回的HTML片段(只含有几个DIV),插入到当前的DOM树上

页面包含

  • 一个项目中的多个页面中可能包含完全相同的内容

    • 如京东商城中的很多页面都有完全相同的header和footer部分
    • 若每个HTML都拷贝一遍相同的内容
    • 就违反了DRY设计原则("不要重复你自己的代码"),造成代码的维护困难
  • 解决方法有如下几种:[最好]

    • 使用Web服务器的SSI(ServerSideInclude)
      • 修改Web服务器的配置文件
    • 使用服务器端动态编程语言进行页面的包含,如:PHP:
      • include('header.php');
      • ...
      • include('footer.php');
      • 三个PHP页面会组成一个大的响应消息,一次性返回给客户端
  • 纯前端解决方案:

    • 使用frameset或者iframe[尽量少使用]
  • 纯前端解决方案:使用AJAX异步请求:

    • 产品详情页.html
    • 注意:ngInclude指令必须赋值为一个字符串
      • <div ng-include=" 'tpl/header.html' "></div>
      • <div ng-include=" 'tpl/footer.html' "></div>
    • perspective: 1024px; 3D视图属性可影响 ng-include
<div id='header'></div>
<div>主体内容</div>
<div id='footer'></div>
$(document).read(function(){
  $('#header').load('header.html');   // XHR
  $('#footer').load('footer.html');   // XHR
})
  • AngularJS中提供了一个类似于方案4的AJAX解决方案:ngInclude指令
<div ng-include=" 'tpl/header.html' "></div>
<div ng-include=" 'tpl/footer.html' "></div>

多人协作开发模式

  • 配置目录

    • view1 开发者1使用的目录
      • 开发者1使用的母版
      • 开发者1使用的母版脚本
    • view2 开发者2使用的目录
      • 开发者2使用的母版
      • 开发者2使用的母版脚本
    • js 公共版本js库
      • angular.js
      • angular-animate.js
      • angular-route.js
      • app.js 项目母版脚本
    • index.html 项目母版
  • index.html 项目母版

<!DOCTYPE html>
 <html lang="en" ng-app="myApp">
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>My AngularJS App</title>
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="app.css">
</head>
<body>
  <div ng-view></div>
  <script src="js/angular.js"></script>
  <script src="js/angular-route.js"></script>
  <script src="js/app.js"></script>
  <script src="view1/view1.js"></script>
  <script src="view2/view2.js"></script>
</body>
</html>
  • app.js 项目母版脚本
'use strict';
angular.module('myApp', [
  'ngRoute',
  'myApp.view1',
  'myApp.view2'
]).
config(['$routeProvider', function($routeProvider) {
  $routeProvider.otherwise({redirectTo: '/view1'});
}]);
  • 开发者2使用的母版
<p>This is the partial for view 2.</p>
  • 开发者2使用的母版脚本
'use strict';
angular.module('myApp.view2', ['ngRoute']).
  config(['$routeProvider', function($routeProvider) {
    $routeProvider.when('/view2', {
      templateUrl: 'view2/view2.html',
      controller: 'View2Ctrl'
    });
  }]).
  controller('View2Ctrl', [function() {

  }]);
原文地址:https://www.cnblogs.com/SharkJiao/p/13780314.html