AngularJS学习笔记

软件工程

软件设计原则

  • 避免重复原则(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 = {width: '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="width: 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>

Angular国际化项目

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

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:不可以

Angular模块指令

  • 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>
  • Angular中ngAnimate模块提供的指令

  • 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>

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的父作用域对象
  • 作用域对象的关系

    • 嵌套关系的作用域

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>

如何压缩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);

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

  • 多页应用

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

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

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

如何仿照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树上
上一篇:IDEA插件


下一篇:读经典,学古文,从《大学》开始