12、angular1之分页组件(包含勾选、过滤、请求服务)、laydate、购物车、两种根绑定、函数两种传递、首页index.html、全局方法、五种服务、过滤、重要指令(ng-)、单选框|复选框|下拉框三者联合案例、子组件向父组件传值、directive自定义标签、获取不到新value、多种forEach、ui.router、jqLite的API参考、前端路由(2300行)

一、angular1之分页组件(包含勾选、过滤、请求服务)
<!DOCTYPE html>
<html lang="en" ng-app="myModel">
<head>
  <meta charset="UTF-8">
  <title>分页组件之angular1.6.2版</title>
  <script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.6.2/angular.js"></script>
  <style>
    .simpleDialog {
      position: fixed;
      width: 100%;
      height: 100%;
      top: 0;
      left: 0;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    .simpleDialog .mask {
      position: fixed;
      width: 100%;
      height: 100%;
      top: 0;
      left: 0;
      background: black;
      opacity: 0.5;
    }
    .simpleDialog .content {
      position: fixed;
      background: white;
      opacity: 1;
      display: flex;
      flex-direction: column;
    }
    .simpleDialog .content .title {
      display: flex;
      background: blue;
      color: white;
      padding: 10px;
      cursor: pointer;
    }
    .simpleDialog .content .title .titleText {
      flex: 1;
    }
    .simpleDialog .content .title .titleCross {
      width: 10px;
      padding: 0 5px;
    }
    .simpleDialog .content .tip {
      flex: 1;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .simpleDialog .content .conform {
      display: flex;
      justify-content: center;
      padding: 10px;
      background: blue;
    }
    .complexDialog {
      position: fixed;
      width: 100%;
      height: 100%;
      top: 0;
      left: 0;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    .complexDialog .mask {
      position: fixed;
      width: 100%;
      height: 100%;
      top: 0;
      left: 0;
      background: black;
      opacity: 0.5;
    }
    .complexDialog .content {
      position: fixed;
      background: white;
      opacity: 1;
      display: flex;
      flex-direction: column;
    }
    .complexDialog .content .title {
      display: flex;
      background: blue;
      color: white;
      padding: 10px;
      cursor: pointer;
    }
    .complexDialog .content .title .titleText {
      flex: 1;
    }
    .complexDialog .content .title .titleCross {
      width: 10px;
      padding: 0 5px;
    }
    .complexDialog .content .tip {
      flex: 1;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .complexDialog .content .conform {
      display: flex;
      justify-content: center;
      padding: 10px;
      background: blue;
    }
    table {
      border-collapse: collapse;
      border: 1px solid #cbcbcb;
    }
    table td,
    table th {
      padding: 5px;
      border: 1px solid #cbcbcb;
    }
    table thead {
      background-color: #e0e0e0;
      color: #000;
      text-align: left;
    }
    .filter {
      width: 998px;
      border: 1px solid gray;
      padding: 10px 0px;
    }
    .filter .line {
      display: flex;
    }
    .filter .line .group {
      width: 330px;
    }
    .filter .line .group .label {
      display: inline-block;
      width: 120px;
      height: 24px;
      line-height: 24px;
      text-align: right;
    }
    .filter .line .group .input {
      display: inline-block;
      width: 180px;
      height: 24px;
      line-height: 24px;
      border-radius: 3px;
    }
    .filter .line .group .select {
      display: inline-block;
      width: 188px;
      height: 26px;
      line-height: 26x;
      border-radius: 3px;
    }
    .wholeCircle {
      position: fixed;
      width: 100%;
      height: 100%;
      left: 0;
      top: 0;
      background: rgb(145, 138, 138);
      opacity: 0.5;
      z-index: 100;
      user-select: none;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    .wholeCircle .content {
      background: #fff;
      border-radius: 5px;
      overflow: hidden;
      z-index: 101;
      box-shadow: 5px 2px 6px #000;
      width: 100px;
      display: flex;
      flex-direction: column;
      align-items: center;
    }
    @keyframes customCircle {
      0% {
        transform: rotate(0deg);
      }
      100% {
        transform: rotate(-360deg);
      }
    }
    .circle {
      animation: customCircle 1s infinite linear;
    }
  </style>
</head>
<body>
  <div ng-controller="thisCtrl">
    <div>
      <div style="padding: 5px 0; color: red">
        <button
          style="color: red"
          ng-click="divideDatas.getResultOfCheckAndFilter(divideDatas.isShowFilter,divideDatas.isUseFilter,divideDatas.filterOptions)"
        >
          获取勾选和过滤结果
        </button>
        <span ng-bind="divideDatas.toServerDatas"></span>
      </div>
      <div style="padding-bottom: 5px">
        <img
          src="{{checkDatas.stateAllPages&&checkDatas.allExcludedIds.length===0?checkImg.yes:checkImg.no}}"
          ng-click="checkDatas.clickAllPages(divideDatas.tableDatas)"
        />
        <span ng-bind="checkDatas.textAllPages"></span>
      </div>
      <div style="padding-bottom: 5px">
        <button ng-click="divideDatas.toggleShowFilter()">
          {{divideDatas.isShowFilter?'关闭过滤':'使用过滤'}}
        </button>
        <button ng-click="divideDatas.emptyFilterOptions({value5:'已读后台'})">
          清空过滤
        </button>
        <button ng-click="divideDatas.request(1)">刷新</button>
      </div>
      <div
        style="margin-bottom: 5px"
        class="filter"
        ng-show="divideDatas.isShowFilter"
      >
        <div class="line">
          <div class="group">
            <label class="label">标签</label>
            <input
              class="input"
              type="text"
              ng-model="divideDatas.filterOptions.value1"
            />
          </div>
          <div class="group">
            <label class="label">这就是长标签</label>
            <input
              class="input"
              type="text"
              ng-model="divideDatas.filterOptions.value2"
            />
          </div>
          <div class="group">
            <label class="label">标签</label>
            <input
              class="input"
              type="text"
              ng-model="divideDatas.filterOptions.value3"
            />
          </div>
        </div>
        <div class="line" style="padding-top: 10px">
          <div class="group">
            <label class="label">这就是长标签</label>
            <input
              class="input"
              type="text"
              ng-model="divideDatas.filterOptions.value4"
            />
          </div>
          <div class="group">
            <label class="label">下拉框</label>
            <select
              class="select"
              ng-model="divideDatas.filterOptions.value5"
              ng-options="item.back as item.front for item in resultDo"
            ></select>
          </div>
          <div class="group">
            <label class="label"></label>
            <button
              style="width: 188px; height: 28px"
              ng-click="divideDatas.useFilter()"
            >
              过滤
            </button>
          </div>
        </div>
      </div>
      <table style="width: 1000px">
        <thead>
          <tr>
            <th>
              <img
                src="{{checkDatas.stateThisPage?checkImg.yes:checkImg.no}}"
                ng-click="checkDatas.clickThisPage(divideDatas.tableDatas,divideDatas.allItemsNum)"
              />
            </th>
            <th>序号</th>
            <th>数据1</th>
            <th>数据2</th>
            <th>数据3</th>
            <th>数据4</th>
            <th>数据5</th>
            <th>数据6</th>
          </tr>
        </thead>
        <tbody>
          <tr ng-repeat="data in divideDatas.tableDatas track by $index">
            <td>
              <img
                src="{{data.state?checkImg.yes:checkImg.no}}"
                ng-click="checkDatas.clickSingleItem(data,divideDatas.tableDatas,divideDatas.allItemsNum)"
              />
            </td>
            <td ng-bind="(divideDatas.nowPageNum-1)*10 + ($index+1)"></td>
            <td ng-bind="data.key1"></td>
            <td ng-bind="data.key2"></td>
            <td ng-bind="data.key3"></td>
            <td ng-bind="data.key4"></td>
            <td ng-bind="data.key5"></td>
            <td ng-bind="data.key6"></td>
          </tr>
        </tbody>
      </table>
      <divide-page
        divide-datas="divideDatas"
        check-datas="checkDatas"
      ></divide-page>
    </div>
  </div>
</body>
<script>
  var app = angular.module('myModel', []);
  app.controller('thisCtrl', function ($scope,checkImg) {
    $scope.modelData = {
      value: 5
    };
    $scope.change = function (value) {
      console.log(value);
    };
    $scope.submit = function () {
      console.log('此处向后台发送请求');
    };
    $scope.requiredDataSimple = {
      simpleDialogTitleId: 'wholeTitleId',
      simpleDialogContentId: 'wholeContentId'
    };
    $scope.clickButtonSimple = function () {
      $scope.requiredDataSimple.isShow = true;
      $scope.requiredDataSimple.width = '600px';
      $scope.requiredDataSimple.title = '详情';
      $scope.requiredDataSimple.tip = '提交失败';
      $scope.requiredDataSimple.zIndex = 10;
    };
    $scope.requiredDataComplex = {
      complexDialogTitleId: 'wholeTitleId1',
      complexDialogContentId: 'wholeContentId1'
    };
    $scope.clickButtonComplex = function () {
      $scope.requiredDataComplex.isShow = true;
      $scope.requiredDataComplex.width = '1060px';
      $scope.requiredDataComplex.height = '700px';
      $scope.requiredDataComplex.title = '详情';
      $scope.requiredDataComplex.zIndex = 10;
      $scope.requiredDataComplex.cancel = function () {
        console.log('cancel');
      };
    };
    $scope.checkImg = checkImg;
    //<part-circle is-show='thisCircle'></part-circle>
    $scope.thisCircle = { isShow : false };
    $scope.array=[true,true,true,true,true];
    $scope.divideDatas = {
      //1、请求配置1(路由和方式)
      url: '',
      method: 'post',
      //2、请求配置2(请求参数的key,前端通过这个配置,发送数据给后台)
      nowPageNumToServer:'nowPageNumToServer', //发送给后台的当前页码key
      allPagesNumToServer:'allPagesNumToServer', //发送给后台的所有页页数key
      allItemsNumToServer:'allItemsNumToServer', //发送给后台的所有页数据数key
      eachPageItemsNumToServer:'eachPageItemsNumToServer', //发送给后台的每页最多数据数key
      //3、响应配置(返回数据的key,前端通过这个配置,获取后台的数据)
      nowPageNumFromServer: 'nowPageNumFromServer', //来自服务器的当前页码key
      allPagesNumFromServer:'allPagesNumFromServer', //来自服务器的所有页页数key
      allItemsNumFromServer:'allItemsNumFromServer', //来自服务器的所有页数据数key
      eachPageItemsNumFromServer:'eachPageItemsNumFromServer', //来自服务器的每页最多数据数key
      tableDatasKey: 'tableDatasArray', //来自服务器的表格数据key
      //4、分页初始化配置(分页在本页面首次渲染时,使用该数据)
      nowPageNum: 0, //当前页数
      allPagesNum: 0, //所有页数
      allItemsNum: 0, //所有页所有条目数
      eachPageItemsNum: 10, //每页展示条目数
      //5、初始化以下数据,供页面使用(前端根据需要决定,不受后台影响)
      dataOrParams: 'data', //参数放在哪个配置下,只有data和Params两个选择
      extraParams: {}, //页码、页条目数、过滤条件外的参数,调用divideDatas.request函数,重新渲染页面。
      filterOptions: {}, //过滤条件
      isShowFilter: false, //是否显示过滤条件。有时默认false时,显示一部分过滤条件,点击“展开”后,显示剩余过滤条件
      isUseFilter: false, //是否使用过滤条件。点击过滤后,才使用过滤条件。
      isAbandonInit: false, //是否放弃初始化组件
      partCircle: false, //把当前页控制局部转圈隐现的变量告诉cyRequest
      isUsePartCircle: false, //是否使用局部转圈
      isUseOneCircle: false, //是否使用单个全局转圈
      isUseManyCircle: false, //是否使用多个全局转圈
      frontMoreText:  '', //('文字 ')或者("文字 "+result.numOne+" 文字 "),
      totalText: '共', //,
      totalUnit: '条', //总数据的单位
      backMoreText: '', //(' 文字')或者("文字 "+result.numThree+" 文字"),
      //6、以下是现成方法,调用即可
      //过滤条件打开或关闭时执行的函数
    };
    $scope.resultDo = [
      { front: '全部前端', back: '全部后台' },
      { front: '已读前端', back: '已读后台' },
      { front: '未读前端', back: '未读后台' }
    ];
    $scope.divideDatas.filterOptions.value5 = '已读后台';
  });
  app.factory('checkImg', function () {
    return {
      yes:
        'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADqADAAQAAAABAAAADgAAAAC98Dn6AAAA+UlEQVQoFZWSMU4DMRBF/584G7QSRcIxuAZKEykNEiUVHVTQRaKh4AIcgAvQpkukVDlBOAYNSGSlXXuwpViyYYFdS9aMZ/6bsezh5HZ3T2KhqkfosEhWqnjkyd1u3xWKdQMsfaEAB0Zilf8swfdU0w0klmpGpz1BvpbHcklbPf8Okts0CfJtWBTz/Yc++Jc8S3PZVQfKGwiuvMD6XYsMzm1dT/1jXKdQ8E0asHRrAzOzbC6UGINWHPQp1UQ/6wjF2LpmJSKfhti4Bi8+lhWP4I+gAqV1uqSi8j9WRuF3m3eMWVUJBeKxzUoYn7bEX7HDyPmB7QEHbRjyL+/+VnuXDUFOAAAAAElFTkSuQmCC',
      no:
        'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADqADAAQAAAABAAAADgAAAAC98Dn6AAAAbklEQVQoFWM8c+ZMLQMDQxUQcwAxMeAHUFEbC5CoYmNj02ZmZn5FjK6/f/+K/fr16ypIIwdIk7a29hdiNF69ehWkjIOJGMXY1IxqxBYqULEhFDiglPMDlIygKQKPryBSILUgPSCNbaC0B6RJSuQAbowizhJuOsAAAAAASUVORK5CYII='
    };
  });
  app.factory('cyRequest', function ($http, wholeCircle) {
    function thisHttp(clientDatas, deferred) {
      var toServerDatas = {
        method: clientDatas.method,
        url: clientDatas.url
      };
      var dataOrParams = clientDatas.dataOrParams || 'data';
      toServerDatas[dataOrParams] = clientDatas.AllParams;
      if (clientDatas.responseType) {
        toServerDatas.responseType = clientDatas.responseType;
      }
      if (clientDatas.isUsePartCircle) {
        clientDatas.partCircle.isShow = true;
      }
      if (clientDatas.isUseOneCircle) {
        wholeCircle.isShow = true;
      }
      if (clientDatas.isUseManyCircle) {
        wholeCircle.isShow = true;
      }
      $http(toServerDatas)
        .then(function (result) {
          //result.status(不是result.clientDatas.status)===200时进入这里,否则进入catch
          if (clientDatas.isDirectUseDatas) {
            deferred.resolve(result);
            //如果出错就一直转圈,那么可以把finally里的代码放到这里
          } else {
            if (result.clientDatas.status === 1000) {
              //全局弹窗告知结果
            } else if (result.clientDatas.status === 0) {
              //全局弹窗告知结果
            } else {
              deferred.resolve(result.clientDatas);
              //如果出错就一直转圈,那么可以把finally里的代码放到这里
            }
          }
        })
        .catch(function (err) {
          deferred.reject(err);
        })
        .finally(function () {
          if (clientDatas.isUsePartCircle) {
            clientDatas.partCircle.isShow = false;
          }
          if (clientDatas.isUseOneCircle) {
            wholeCircle.isShow = false;
          }
          if (clientDatas.isUseManyCircle) {
            // $scope.array=[true,true,true,true,true];
            // wholeCircle: {
            //   array: $scope.array,
            //   index: 0
            // }, 
            var flag = false;
            clientDatas.wholeCircle.array[clientDatas.wholeCircle.index]=false;
            for(var i=0; i<clientDatas.wholeCircle.array.length; i++){
              if(clientDatas.wholeCircle.array[i]) flag = true
            }
            if(!flag) wholeCircle.isShow = false;
          }
        });
    }
    return function (clientDatas) {
      var deferred = $q.defer();
      if (clientDatas.warn) {
        //全局弹窗告知结果
        $scope.requiredData = {
          isShow: true,
          width: '400px',
          title: '详情',
          tip: '确认提交',
          submit: function () {
            thisHttp(clientDatas, deferred);
          }
        };
      } else {
        thisHttp(clientDatas, deferred);
      }
      return deferred.promise;
    };

    // $scope.array=[true,true,true,true,true];
    // cyRequest({
    //   method: 'post',
    //   url: 'a/b/c',
    //   warn: '确定提交吗?',
    //   dataOrParams: 'data',
    //   AllParams: {key:1},
    //   partCircle: $scope.thisCircle,//把当前页控制局部转圈隐现的变量告诉cyRequest
    //   isUsePartCircle: true,//是否使用局部转圈
    //   isUseOneCircle: true,//是否一个请求使用一个全局转圈
    //   isUseManyCircle: true,//是否多个请求共用一个全局转圈
    //   wholeCircle: {//多个请求共用一个全局转圈
    //     array: $scope.array,
    //     index: 0
    //   },
    // })
  });
  app.directive('dividePage', function () {
    var html = `
      <div ng-show="divideDatas.allPagesNum>=1" style="display:flex;width:1000px;margin-top:20px;">
        <div>
          <div>
            <button 
              ng-hide="divideDatas.allPagesNum<=10" 
              ng-click="clickDividePage('front') " 
              ng-disabled="divideDatas.nowPageNum===1"
            >上一页</button>
            <button 
              ng-repeat="num in divideArray track by $index" 
              ng-bind="num" 
              ng-click="clickDividePage(num) " 
              ng-style="num===divideDatas.nowPageNum?{ 'color':'red'}:{'color':'black'}" 
              ng-disabled="num==='...'"
              style="margin:0 3px;"
            >
            </button>
            <button 
              ng-hide="divideDatas.allPagesNum<=10" 
              ng-click="clickDividePage('back') "  
              ng-disabled="divideDatas.nowPageNum===divideDatas.allPagesNum"
            >下一页</button>
          </div>
          
        </div>
        <div style="display:flex; flex:1; justify-content:flex-end;">
          <div style="margin-right:20px;">
            <span>转到第</span>
            <input type="text" ng-model="customString" ng-keydown="clickDividePage('leap',$event)" style="width:30px;">
            <span>页</span>
            <button ng-click="clickDividePage('leap',{which:13})">Go</button>
          </div>
          <div>
            <span>每页显示</span>
            <select ng-model="divideDatas.eachPageItemsNum" ng-options="item.back as item.front for item in numOptions" ng-disabled="true"></select>
            <span>条,</span>
          </div>
          <div>
            <span ng-bind="divideDatas.frontMoreText"></span>
            <span ng-bind="divideDatas.totalText"></span>
            <span ng-bind="divideDatas.allItemsNum"></span>
            <span ng-bind="divideDatas.totalUnit"></span>
            <span ng-bind="divideDatas.backMoreText"></span>
          </div>
        </div>   
      </div> 
    `;
    return {
      restrict: 'E',
      template: html,
      scope: {
        divideDatas: '=divideDatas',
        checkDatas: '=checkDatas'
      },
      controller: function ($scope, $timeout /* , cyRequest */) {
        $scope.checkDatas = $scope.checkDatas||{};
        $scope.divideDatas = $scope.divideDatas||{};
        $scope.checkDatas.idKey = $scope.checkDatas.idKey||'id', //每条数据的唯一标志
        $scope.checkDatas.stateThisPage = $scope.checkDatas.stateThisPage||false, //当前页所有项是否全选
        $scope.checkDatas.allIncludedIds = $scope.checkDatas.allIncludedIds||[], //所有被选中数据的ID构成的数组
        $scope.checkDatas.allExcludedIds = $scope.checkDatas.allExcludedIds||[], //所有没被选中数据的ID构成的数组
        $scope.checkDatas.textAllPages = $scope.checkDatas.textAllPages||'全选未启用,没有选择任何项!', //复选框被点击后的提示文字。
        $scope.checkDatas.stateAllPages = $scope.checkDatas.stateAllPages||false, //复选框被点击后的提示文字。
        $scope.checkDatas.isAbandonChecklist = $scope.checkDatas.isAbandonChecklist||false,
        $scope.request = $scope.divideDatas.request = function (pageNum) {
          var AllParams = $scope.getResultOfCheckAndFilter(
            $scope.divideDatas.isShowFilter,
            $scope.divideDatas.isUseFilter,
            $scope.divideDatas.filterOptions,
            $scope.divideDatas.extraParams,
          );
          AllParams.nowPageNum = pageNum;
          // cyRequest({
          //   url: $scope.divideDatas.url,
          //   method: $scope.divideDatas.method,
          //   dataOrParams: $scope.divideDatas.dataOrParams,
          //   AllParams: AllParams,
          //   partCircle: $scope.divideDatas.partCircle,
          //   isUsePartCircle: $scope.divideDatas.isUsePartCircle, 
          //   isUseOneCircle: $scope.divideDatas.isUseOneCircle,
          //   isUseManyCircle: $scope.divideDatas.isUseManyCircle,
          // }).then(function () {
          var data = [];
          for (var i = 1; i <= 144; i++) {
            var obj = {
              id: 'id' + i,
              key1: '数据' + (i + 0),
              key2: '数据' + (i + 1),
              key3: '数据' + (i + 2),
              key4: '数据' + (i + 3),
              key5: '数据' + (i + 4),
              key6: '数据' + (i + 5),
              key7: '数据' + (i + 6)
            };
            data.push(obj);
          }
          var result = {
            tableDatasArray: [],
            nowPageNumFromServer: 5,
            allPagesNumFromServer: 15,
            allItemsNumFromServer: 144,
            eachPageItemsNumFromServer: 10
          };
          result.tableDatasArray = data.slice(
            (pageNum - 1) * 10,
            pageNum * 10
          );
          $scope.customString =
            pageNum || result[$scope.divideDatas.nowPageNumFromServer];
          $scope.divideDatas.nowPageNum =
            pageNum || result[$scope.divideDatas.nowPageNumFromServer];
          $scope.divideDatas.allPagesNum =
            result[$scope.divideDatas.allPagesNumFromServer];
          $scope.divideDatas.allItemsNum =
            result[$scope.divideDatas.allItemsNumFromServer];
          $scope.divideDatas.eachPageItemsNum =
            result[$scope.divideDatas.eachPageItemsNumFromServer];
          $scope.divideDatas.tableDatas =
            result[$scope.divideDatas.tableDatasKey];
          if(angular.isFunction($scope.divideDatas.executeOnceBefore)){
            $scope.divideDatas.executeOnceBefore($scope.divideDatas.tableDatas);
            $scope.divideDatas.executeOnceBefore = null;
          }
          if(angular.isFunction($scope.divideDatas.executeEveryTrue)){
            $scope.divideDatas.executeEveryTrue($scope.divideDatas.tableDatas);
          }
          if(angular.isFunction($scope.divideDatas.executeOnceAfter)){
            $scope.divideDatas.executeOnceAfter($scope.divideDatas.tableDatas);
            $scope.divideDatas.executeOnceAfter = null;
          }
          if ($scope.checkDatas) {
            if(!$scope.checkDatas.isAbandonChecklist){
              $scope.checkDatas.signCheckbox($scope.divideDatas.tableDatas);
            }else{
              $scope.checkDatas.init();
            }
          }
          $scope.createDividePage();
          // }).catch(function(){
          //   if (angular.isFunction($scope.divideDatas.executeEveryError)) {
          //     $scope.divideDatas.executeEveryError(result);
          //   }
          // })
        };
        $scope.numOptions = [
          { back: 10, front: 10 },
          { back: 20, front: 20 },
          { back: 30, front: 30 },
          { back: 40, front: 40 },
          { back: 50, front: 50 }
        ];
        $scope.getResultOfCheckAndFilter = $scope.divideDatas.getResultOfCheckAndFilter = function (
          isShowFilter,
          isUseFilter,
          filterOptions,
          extraParams
        ) {
          var checkboxDatas;
          var toServerDatas;
          extraParams = extraParams||{};
          if (!$scope.checkDatas.stateAllPages) {
            if ($scope.checkDatas.allIncludedIds.length === 0) {
              //return 弹窗告知:没有勾选项
            }
            checkboxDatas = {
              isSelectAll: false,
              allIncludedIds: $scope.checkDatas.allIncludedIds
            };
          } else {
            checkboxDatas = {
              isSelectAll: true,
              allExcludedIds: $scope.checkDatas.allExcludedIds
            };
          }
          if (isShowFilter||isUseFilter) {
            toServerDatas = angular.merge(
              {},
              { checkboxDatas: checkboxDatas },
              { filterOptions: filterOptions },
              extraParams
            );
          } else {
            toServerDatas = angular.merge({}, { checkboxDatas: checkboxDatas }, extraParams);
          }
          this.toServerDatas = toServerDatas; //这行代码在实际项目中不需要
          return toServerDatas;
        };
        $scope.createDividePage = function () {
          var divideArray = [];
          var allPagesNum = $scope.divideDatas.allPagesNum;
          var nowPageNum = $scope.divideDatas.nowPageNum;
          console.log( allPagesNum    )
          console.log( nowPageNum    )
          if (allPagesNum >= 1 && allPagesNum <= 10) {
            for (var i = 1; i <= allPagesNum; i++) {
              divideArray.push(i);
            }
          } else if (allPagesNum >= 11) {
            if (nowPageNum > 6) {
              divideArray.push(1);
              divideArray.push(2);
              divideArray.push(3);
              divideArray.push('...');
              divideArray.push(nowPageNum - 1);
              divideArray.push(nowPageNum);
            } else {
              for (i = 1; i <= nowPageNum; i++) {
                divideArray.push(i);
              }
            }
            // 以上当前页的左边,以下当前页的右边
            if (allPagesNum - nowPageNum >= 6) {
              divideArray.push(nowPageNum + 1);
              divideArray.push(nowPageNum + 2);
              divideArray.push('...');
              divideArray.push(allPagesNum - 2);
              divideArray.push(allPagesNum - 1);
              divideArray.push(allPagesNum);
            } else {
              for (var i = nowPageNum + 1; i <= allPagesNum; i++) {
                divideArray.push(i);
              }
            }
          }
          $scope.divideArray = divideArray;
        };
        $scope.clickDividePage = function (stringOfNum, event) {
          var allPagesNum = $scope.divideDatas.allPagesNum;
          var nowPageNum = $scope.divideDatas.nowPageNum;
          if (stringOfNum === 'front' && nowPageNum != 1) {
            nowPageNum--;
          } else if (stringOfNum === 'back' && nowPageNum != allPagesNum) {
            nowPageNum++;
          } else if (stringOfNum === 'leap') {
            if (event.which != 13) return; //不拦截情形:(1)聚焦输入框、按“Enter”键时;(2)点击“GO”时
            var customNum = Math.ceil(parseFloat($scope.customString));
            if (customNum < 1 || customNum == 'NaN') {
              nowPageNum = 1; //不给提示
            } else if (customNum > allPagesNum) {
              nowPageNum = allPagesNum; //不给提示
            } else {
              nowPageNum = customNum;
            }
          } else {
            nowPageNum = Math.ceil(parseFloat(stringOfNum));
          }
          $scope.customString = nowPageNum;
          $scope.request(nowPageNum);
        };
        $scope.checkDatas.init = function () {
          //点击“刷新”、“过滤”、“清除过滤”时执行
          this.idKey = idKey ? idKey : 'id';
          this.allIncludedIds = [];
          this.allExcludedIds = [];
          this.textAllPages = '全选未启用,没有选择任何项!';
          this.stateAllPages = false;
          this.stateThisPage = false;
        };
        $scope.checkDatas.clickAllPages = function (itemArray) {
          //所有页所有条目全选复选框被点击时执行的函数
          if (this.stateAllPages) {
            if (this.allExcludedIds.length > 0) {
              this.stateAllPages = true;
              this.stateThisPage = true;
              this.textAllPages = '全选已启用,没有排除任何项!';
              angular.forEach(itemArray, function (item) {
                item.state = true;
              });
            } else if (this.allExcludedIds.length == 0) {
              this.stateAllPages = false;
              this.stateThisPage = false;
              this.textAllPages = '全选未启用,没有选择任何项!';
              angular.forEach(itemArray, function (item) {
                item.state = false;
              });
            }
          } else {
            this.stateAllPages = true;
            this.stateThisPage = true;
            this.textAllPages = '全选已启用,没有排除任何项!';
            angular.forEach(itemArray, function (item) {
              item.state = true;
            });
          }
          this.allExcludedIds = [];
          this.allIncludedIds = [];
        };
        $scope.checkDatas.clickThisPage = function (itemsArray, allItemsNum) {
          //当前页所有条目全选复选框被点击时执行的函数
          var that = this;
          this.stateThisPage = !this.stateThisPage;
          angular.forEach(itemsArray, function (item) {
            item.state = that.stateThisPage;
            if (item.state) {
              that.delID(item[that.idKey], that.allExcludedIds);
              that.addID(item[that.idKey], that.allIncludedIds);
            } else {
              that.delID(item[that.idKey], that.allIncludedIds);
              that.addID(item[that.idKey], that.allExcludedIds);
            }
          });
          if (this.stateAllPages) {
            if (this.stateThisPage && this.allExcludedIds.length === 0) {
              this.textAllPages = '全选已启用,没有排除任何项!';
            } else {
              this.textAllPages =
                '全选已启用,已排除' +
                this.allExcludedIds.length +
                '项!排除项的ID为:' +
                this.allExcludedIds;
            }
          } else {
            if (!this.stateThisPage && this.allIncludedIds.length === 0) {
              this.textAllPages = '全选未启用,没有选择任何项!';
            } else {
              this.textAllPages =
                '全选未启用,已选择' +
                this.allIncludedIds.length +
                '项!选择项的ID为:' +
                this.allIncludedIds;
            }
          }
        };
        $scope.checkDatas.clickSingleItem = function (item, itemsArray, allItemsNum) {
          //当前页单个条目复选框被点击时执行的函数
          var that = this;
          item.state = !item.state;
          if (item.state) {
            this.stateThisPage = true;
            this.addID(item[this.idKey], this.allIncludedIds);
            this.delID(item[this.idKey], this.allExcludedIds);
            angular.forEach(itemsArray, function (item) {
              if (!item.state) {
                that.stateThisPage = false;
              }
            });
          } else {
            this.stateThisPage = false;
            this.addID(item[this.idKey], this.allExcludedIds);
            this.delID(item[this.idKey], this.allIncludedIds);
          }
          if (this.stateAllPages) {
            if (this.stateThisPage && this.allExcludedIds.length === 0) {
              this.textAllPages = '全选已启用,没有排除任何项!';
            } else {
              this.textAllPages =
                '全选已启用,已排除' +
                this.allExcludedIds.length +
                '项!排除项的ID为:' +
                this.allExcludedIds;
            }
          } else {
            if (!this.stateThisPage && this.allIncludedIds.length === 0) {
              this.textAllPages = '全选未启用,没有选择任何项!';
            } else {
              this.textAllPages =
                '全选未启用,已选择' +
                this.allIncludedIds.length +
                '项!选择项的ID为:' +
                this.allIncludedIds;
            }
          }
        };
        $scope.checkDatas.signCheckbox = function (itemsArray) {
          //标注当前页被选中的条目,在翻页成功后执行。
          var that = this;
          if (this.stateAllPages) {
            this.stateThisPage = true;
            angular.forEach(itemsArray, function (item) {
              var thisID = item[that.idKey];
              var index = that.allExcludedIds.indexOf(thisID);
              if (index > -1) {
                item.state = false;
                that.stateThisPage = false;
              } else {
                item.state = true;
              }
            });
          } else {
            this.stateThisPage = true;
            angular.forEach(itemsArray, function (item) {
              var thisID = item[that.idKey];
              var index = that.allIncludedIds.indexOf(thisID);
              if (index === -1) {
                item.state = false;
                that.stateThisPage = false;
              }else{
                item.state = true;
              }
            });
          }
        };
        $scope.checkDatas.addID = function (id, idArray) {
          var index = idArray.indexOf(id);
          if (index === -1) {
            idArray.push(id); //如果当前页的单项既有勾选又有非勾选,这时勾选当前页全选,需要这个判断,以免重复添加
          }
        };
        $scope.checkDatas.delID = function (id, idArray) {
          var index = idArray.indexOf(id);
          if (index > -1) {
            idArray.splice(index, 1);
          }
        };
        
        $scope.divideDatas.toggleShowFilter = function () {
          this.isUseFilter = false;
          this.isShowFilter = !this.isShowFilter;
          if (!this.isShowFilter) {
            this.request(1);
          }
        };
        //点击过滤时执行的函数
        $scope.divideDatas.useFilter = function () {
          this.isUseFilter = true;
          this.request(1);
        };
        //清空过滤条件时执行的函数
        //清空选项时,所有值恢复成默认
        $scope.divideDatas.emptyFilterOptions = function () {
          var that = this;
          angular.forEach(that.filterOptions, function (value, key) {
            if(that.filterOptionsInit[key]){
              that.filterOptions[key]= that.filterOptionsInit[key]
            }else{
              that.filterOptions[key] = undefined;
            }
          });
          this.request(1);
        };
        if (!$scope.divideDatas.isAbandonInit) {
          $scope.request(1);
        }
      }
    };
  }); 
  </script>
</html>


二、angular1里用laydate.js
1、示例
来源:https://www.layui.com/doc/modules/laydate.html#type
<dir-datepicker date-model="pagin_init.filter_option.beginTime"></dir-datepicker>
(function () {
  angular
    .module('common-dir')
    .directive('dirDatepicker', function () {
      return {
        restrict: 'E',
        template: '<input  type="text" class="form-control common-select-time" ng-model="dateModel"  id="{{dateInputId}}"  style="width:214px" ng-disabled="isDisable" />',
        replace: true,
        scope: {
          format: '@',
          dateModel: '=',
          isDisable: '=',
          callback: '&'
        },
        controller: function ($scope) {
          $scope.dateInputId = 'id' + Math.random();
          angular.element(document).ready(function () {
            var config = {
              elem: '#' + $scope.dateInputId,
              theme: '#007bff',
              trigger: 'click',
              done: function (value) {
                $scope.dateModel = value;
                angular.isFunction($scope.callback) ? $scope.callback() : '';
              }
            };
            if ($scope.format) {
              config.format = $scope.format;//自定义格式
            } else {
              config.type = 'datetime';//控件选择类型
            }
            laydate.render(config);
          });
        },
        link: function () {
        }
      };
    });
})();
laydate.render({
  elem: '#test',
  done: function(value, date, endDate){
    //控件选择完毕后的回调,点击日期、清空、现在、确定均会触发。
    //回调返回三个参数,分别代表:生成的值、日期时间对象、结束的日期时间对象
    console.log(value); //得到日期生成的值,如:2017-08-18
    console.log(date); //得到日期时间对象:{year: 2017, month: 8, date: 18, hours: 0, minutes: 0, seconds: 0}
    console.log(endDate); //得结束的日期时间对象,开启范围选择(range: true)才会返回。对象成员同上。
  }
});
2、laydate使用详解
laydate.render({ 
  elem: '#test',
  type:'datetime', // year年选择器,month年月选择器,date年月日选择器,time时间选择器,datetime日期时间选择器
  range: true, //或 range: '~' 来自定义分割字符开启左右面板选择范围
  value: getNowTime(new Date(),true) + "~" + getNowTime(new Date(), false), //'2018-08-18'//必须遵循format参数设定的格式 初始值
  isInitValue: false, //是否允许填充初始值,默认为true
  format: 'yyyy-MM-dd HH:mm:ss', //'yyyy年MM月dd日' //可任意组合  自定义格式
  min: '2017-1-1', //最小范围内的日期时间值
  max: '2017-12-31', //最大范围内的日期时间值
  trigger: 'click', //采用click弹出
  show: true, //直接显示
  closeStop: '#test1', //这里代表的意思是:点击 test1 所在元素阻止关闭事件冒泡。如果不设定,则无法弹出控件
  position: 'static',
  showBottom :false,
  btns: ['clear', 'confirm'],
  lang: 'en', // 'cn'
  theme: 'molv', //主题
  calendar: true, // 是否显示公历
  mark: {
    '0-10-14': '生日',
    '0-12-31': '跨年' //每年12月31日
    '0-0-10': '工资', //每个月10号
    '2017-8-15': '', //具体日期
    '2017-8-20': '预发', //如果为空字符,则默认显示数字+徽章
    '2017-8-21': '发布',
  },
  ready: function(date){
    console.log(date); //控件打开时触发 得到初始的日期时间对象:{year: 2017, month: 8, date: 18, hours: 0, minutes: 0, seconds: 0
  },
  change: function (value, date, endDate) { //日期时间选择变化事件
    var nowdate = new Date();
    var enddate = new Date(endDate["year"], endDate["month"] - 1, endDate["date"], endDate["hours"], endDate["minutes"], endDate["seconds"]);
    if (enddate.getTime() > nowdate.getTime()) {
        timescope.hint("不可超出当前日期");
        $(".laydate-btns-confirm").addClass('layui-btn-disabled');
    } else {
        $(".laydate-btns-confirm").removeClass('layui-btn-disabled');
    }
  }, 
  done: function (value, date, endDate) { //日期选择确认事件
    if (value == "") {
      return;
    }
    // console.log(date, endDate)
    var startdate = new Date(date["year"], date["month"] - 1, date["date"], date["hours"], date["minutes"], date["seconds"]);
    mergestarttime = startdate.getTime();
    var enddate = new Date(endDate["year"], endDate["month"] - 1, endDate["date"], endDate["hours"], endDate["minutes"], endDate["seconds"]);
    mergeendtime = enddate.getTime();
    getflowmerge(name_arrays);
  }, 
})
3、说明
(1)laydate.js可以通过<html><head><script src="./laydate.js">存在于window.laydate中
(2)在任意一个js文件中,均可以通过console.log(laydate)打印,通过laydate.render(obj)调用
(3)laydate.js不可以通过import "./laydate.js" 获取laydate,这种情形适合于导入自执行函数
(4)laydate.js不可以通过import laydate from "./laydate.js"获取laydate

三、agular1购物车
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/4.1.1/css/bootstrap.css">
  <script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.5.8/angular.js"></script>
</head>
<body ng-app="myApp" ng-controller="myCtrl" style="margin:20px;" ng-cloak >
  <div>
    <label>全选</label>
    <input type="checkbox" ng-model="allCheck" ng-click="allChecked()">
    <span>总金额:{{ totalValue | currency:"¥"}}</span>
  </div>
  <table class="table table-bordered" ng-repeat="oneShop in allShops track by $index"  ng-init="outerIndex = $index" style="margin:30px auto">
    <thead>
    <tr>
      <th><input type="checkbox" ng-model="oneShop.checked" ng-click="shopChecked(oneShop,allShops)"></th>
      <th colspan="7">{{oneShop.shopName}}</th>
    </tr>
    </thead>
    <tbody>
      <tr ng-repeat="goods in oneShop.shopGoods track by $index"  ng-init="innerIndex = $index">
        <td><input type="checkbox" ng-model="goods.checked" ng-click="goodsChecked(goods,oneShop.shopGoods,oneShop,allShops)"></td>
        <td>{{goods.goodsName}}</td>
        <td>{{goods.price}}元/件,共</td>
        <td><span ng-click="changeNumber(goods,oneShop.shopGoods,oneShop,allShops,'+')" style="display: inline-block;width:25px;text-align: center; border:1px solid gray;user-select: none;cursor: pointer;">+</span></td>
        <td>{{goods.number}}</td>
        <td><span ng-click="changeNumber(goods,oneShop.shopGoods,oneShop,allShops,'-')" style="display: inline-block;width:25px;text-align: center; border:1px solid gray;user-select: none;cursor: pointer;">-</span></td>
        <td>件,本商品共: {{goods.singleMoney}}元</td>
        <td ng-click="delete(innerIndex,outerIndex,oneShop,allShops)" style="user-select: none;cursor: pointer;">删除</td>
      </tr>
    </tbody>
</table>
</body>
</html>
<script>
var myApp = angular.module("myApp", []);
myApp.controller("myCtrl", function ($scope) {
  $scope.allShops = [
    {
      shopName: "专卖店一:北京鸡",
      shopGoods: [
        {
          goodsName: "北京鸡1",
          picture: "images/allShops_01.jpg",
          price: 150.0,
          singleMoney: 150.0,
          number:1
        },
        {
          goodsName: "北京鸡2",
          picture: "images/allShops_02.jpg",
          price: 119.0,
          singleMoney: 119.0,
          number:1
        },
        {
          goodsName: "北京鸡3",
          picture: "images/allShops_03.jpg",
          price: 101.0,
          singleMoney: 101.0,
          number:1
        },
      ],
    },
    {
      shopName: "专卖店二:北京鸭",
      shopGoods: [
        {
          goodsName: "北京鸭1",
          picture: "images/allShops_04.jpg",
          price: 89.0,
          singleMoney: 89.0,
          number:1
        },
        {
          goodsName: "北京鸭2",
          picture: "images/allShops_05.jpg",
          price: 99.0,
          singleMoney: 99.0,
          number:1
        },
      ],
    },
    {
      shopName: "专卖店三:北京鹅",
      shopGoods: [
        {
          goodsName: "北京鹅1",
          picture: "images/allShops_06.jpg",
          price: 289.0,
          singleMoney: 289.0,
          number:1
        },
      ],
    },
  ];
  $scope.totalValue = 0;
  //增减该件商品
  $scope.changeNumber = function (goods, shopGoods, oneShop, allShops, type) {
    if(type === '-'){
      goods.number--;
      if(goods.number <= 1) goods.number = 1;
    }else if(type === '+'){
      goods.number++;
    }
    goods.singleMoney = goods.price * goods.number;
    goods.checked = true;
    $scope.goodsChecked(goods, shopGoods, oneShop, allShops);
    $scope.getTotalMoney();
  };
  //删除该件商品
  $scope.delete = function (innerIndex, outerIndex, oneShop, allShops) {
    oneShop.shopGoods.splice(innerIndex, 1);
    if (oneShop.shopGoods.length <= 0) {
      allShops.splice(outerIndex, 1);
    }
    $scope.getTotalMoney();
  };
  /*所有商品总金额计算*/
  $scope.getTotalMoney = function () {
    var total = 0;
    angular.forEach($scope.allShops, function (outerItem) {
      angular.forEach(outerItem.shopGoods, function (innerItem) {
        if (innerItem.checked) {
          total += innerItem.price * innerItem.number;
        }
      });
    });
    $scope.totalValue = total;
  };
  /*勾选单件商品*/
  $scope.goodsChecked = function (
    goods,
    shopGoods,
    oneShop,
    allShops
  ) {
    var flag = true;
    if (goods.checked) {
      angular.forEach(shopGoods, function (innerItem) {
        if (!innerItem.checked) {
          flag = false;
        }
      });
    } else {
      $scope.allCheck = false;
      oneShop.checked = false;
      flag = false;
    }
    if (flag) {
      oneShop.checked = true;
      $scope.allCheck = true;
      angular.forEach(allShops, function (outerItem) {
        if (!outerItem.checked) {
          flag = false;
        }
      });
    }
    $scope.getTotalMoney();
  };
  /*勾选单家商铺*/
  $scope.shopChecked = function (oneShop, allShops) {
    if (oneShop.checked) {
      var flag = true;
      angular.forEach(oneShop.shopGoods, function (innerItem) {
        innerItem.checked = true;
      });
      angular.forEach(allShops, function (outerItem) {
        if (!outerItem.checked) {
          flag = false;
        }
      });
      if (flag) {
        $scope.allCheck = true;
      }
    } else {
      $scope.allCheck = false;
      angular.forEach(oneShop.shopGoods, function (innerItem) {
        innerItem.checked = false;
      });
    }
    $scope.getTotalMoney();
  };
  /*勾选全部商铺*/
  $scope.allChecked = function () {
    if ($scope.allCheck) {
      angular.forEach($scope.allShops, function (oneShop) {
        oneShop.checked = true;
        angular.forEach(oneShop.shopGoods, function (innerItem) {
          innerItem.checked = true;
        });
      });
    } else {
      angular.forEach($scope.allShops, function (oneShop) {
        oneShop.checked = false;
        angular.forEach(oneShop.shopGoods, function (innerItem) {
          innerItem.checked = false;
        });
      });
    }
    $scope.getTotalMoney();
  };
});    
</script>
 
四、angular1两种作用域绑定
angular1模块、控制器和作用域的两种绑定方式,模块不必通过ng-app关联到HTML的标签上,也不必通过angular.bootstrap()关联到HTML的标签上!
1、正常绑定 
<!DOCTYPE html>
<html ng-app="app">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
<body>
  <div>
    <div ng-controller="myCtrl">
      {{ hello }}
    </div>
  </div>
  <script type="text/javascript">
    var module = angular.module("app", []);
    module.controller("myCtrl", function ($scope) {
      //app模块的控制器(myCtrl)将作用域的hello 赋值为 "a Angular app"
      $scope.hello = "a Angular app";
    });
  </script>
</body>
</html>
2、异常绑定
<!DOCTYPE html>
<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
<body>
  <div id="app1">
    <div ng-controller="myCtrl">
      {{ hello }}
    </div>
  </div>
  <div id="app2">
    <div ng-controller="myCtrl">
      {{ hello }}
    </div>
  </div>
  <script type="text/javascript">
    var module1 = angular.module("test1", []);
    module1.controller("myCtrl", function ($scope) {
      //test1模块的控制器(myCtrl)将作用域的hello 赋值为 "a Angular app"
      $scope.hello = "a Angular app";
    });
    var module2 = angular.module("test2", []);
    module2.controller("myCtrl", function ($scope) {
      //test2模块的控制器(myCtrl)将作用域的hello 赋值为 "a Angular app"
      $scope.hello = " another Angular app";
    });
    /*此方法用于手动加载angularjs模板*/
    angular.bootstrap(document.getElementById("app1"), ['test1']);
    /*此方法用于手动加载angularjs模板*/
    angular.bootstrap(document.getElementById("app2"), ['test2']);
  </script>
</body>
</html>

五、两种函数传递
1、以(对象)变量传递 
<!doctype html>
<html lang="en" ng-app="appModule">
<head>
  <meta charset="UTF-8">
  <title>函数当作变量传进去</title>
</head>
<body ng-controller="myCtrl">
  <p ng-click="fnClick('我是原有标签')">点击原有标签</p>
  <my-text fn="fnClick"></my-text>
  <br/>
  <script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.5.8/angular.js"></script>
  <script>
    var app = angular.module('appModule', []);
    app.controller('myCtrl', function ($scope) {
      $scope.fnClick = function (tip) {
        alert(tip)
      }
    });
    app.directive('myText', function () {
      return {
        restrict: 'E',
        template: '<h1 ng-click="thisFn()"><span>点击自定义标签</span></h1>',
        scope: {
          fn: '='
        },
        controller: function ($scope) {
          $scope.thisFn=function(){
            $scope.fn('我是自定义标签,函数当作变量传入')
          }
        }
      }
    })
  </script>
</body>
</html>
2、以函数传递
<!doctype html>
<html lang="en" ng-app="appModule">
<head>
  <meta charset="UTF-8">
  <title>函数当作函数传进去</title>
</head>
<body ng-controller="myCtrl">
  <p ng-click="fnClick('我是原有标签','AAA')">点击原有标签</p>
  <my-text fn="fnClick(tipA,tipB)"></my-text>
  <br />
  <script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.5.8/angular.js"></script>
  <script>
    var app = angular.module('appModule', []);
    app.controller('myCtrl', function ($scope) {
      $scope.fnClick = function (tip,pit) {
        console.log(pit)
        alert(tip)
      }
    });
    app.directive('myText', function () {
      return {
        restrict: 'E',
        template: '<h1 ng-click="thisFn()"><span>点击自定义标签</span></h1>',
        scope: {
          fn: '&'
        },
        controller: function ($scope) {
          $scope.thisFn=function(){
            $scope.fn({tipA:'我是自定义标签,函数当作函数传入',tipB:'BBB'})
          }
        }
      }
    })
  </script>
</body>
</html>

六、angular项目下首页文件index.html,
1、index.html注入
<!DOCTYPE html>
<html lang="en" ng-controller="mainCtrl">
<head>
</head>
<body ondragstart="return false;">
  <my-app></my-app>
</body>
<script src="/audit-html/static/common/jquery.js"></script>
/* 打包下列文件 */
<!-- build:js(src) scripts/all.min.js --> 
  <!-- inject:js --> 
  /* 默认js文件注入此处 */
  <!-- endinject --> 
  <!-- inject:partials --> 
  /* 特定js文件注入此处 */
  <!-- endinject --> 
<!-- endbuild --> 
</html>
2、gulp打包angular1静态文件的流程----开发环境
(1)监听到有文件变化时,向index.html文件注入css和js文件,渲染,
(2)从上到下执行css文件,
(3)从上到下渲染body,遇到组件,跳过
(4)从上到下执行js文件,遇到异步,跳过,产生依赖模块
(5)执行异步,并将根模块与html文档绑定,给根模块注入依赖,如果有依赖没定义,那么会出错,不会执行config和run
(6)解析组件,这时根组件及其内部组件所需要的html文件,按照指定路径加载,并用run函数定义全局的数据或逻辑
3、gulp打包angular1静态文件的流程----生产环境
(1)将所有的css和js文件,注入到index.html,
(2)将其它的html文件打包成1个js文件,注入到index.html,
(3)将index.html的“所有的css文件”和“所有js文件”打包,插入到另一个index.html里,渲染,
(4)从上到下执行css文件,
(5)从上到下渲染body,遇到组件,跳过
(6)从上到下执行js文件,遇到异步,跳过,产生依赖模块
(7)执行异步,并将根模块与html文档绑定,给根模块注入依赖,如果有依赖没定义,那么会出错,不会执行config和run
(8)解析组件,这时根组件及其内部组件所需要的html文件,从(2)中的js获取,并用run函数定义全局的数据或逻辑
4、执行main.js,在$stateChangeStart事件监听函数里,遇到异步跳过,直奔状态,此时状态对应的配置不存在,页面出错;正确的处理方式为,在权限异步函数的成功回调里,在前端权限列表生成后,再次执行$state.go(),填充到<ui-view>标签里;
5、根模块的依赖模块加载完毕后,根模块挂载到页面上,有如下两种情形
用法一:
<html ng-app="myApp" ng-controller="myCtrl">
  <body></body>
</html>
<script>
  //此处导入依赖['deploy1','deploy2']
  var app = angular.module('myApp', ['deploy1','deploy2']);
  app.controller('myCtrl', function ($scope) { });
</script>
用法二:
<html ng-controller="myCtrl">
  <body></body>
</html>
<script>
  var app = angular.module('myApp', ['deploy1','deploy2']);
  app.controller('myCtrl', function ($scope) { });
  //此处导入依赖['deploy1','deploy2']
  angular.bootstrap(document, ['myApp']);
</script>
6、angular版本,Google发行
(1)AngularJS,首版,2009年
(2)AngularJS,1.0版,2010年,
(3)Angular,2.0版,2016年09月份
(4)Angular,4.0版,2017年03月份
(5)Angular,5.0版,2017年11月份
(6)Angular,6.0版,2018年05月份
(7)Angular,7.0版,2018年10月份
(8)Angular,8.0版,2019年05月份
(9)Angular,9.0版,2020年02月份
(10)Angular,10.0版,2020年06月份
(11)Angular,11.0版,2020年11月份
(12)Angular,12.0版,2021年05月份
(13)Angular,13.0版,2021年12月份
(14)Angular,14.0版,2022年06月份
(15)Angular,15.0版,2022年11月份
(16)Angular,16.0版,2023年05月份

七、全局方法
附、原理,脏值检测,当$scope的属性值发生变化的时候,促使页面重新渲
1、config 配置模块。在提供者注册和配置阶段执行。只能注入提供者和常量配置块。在config阶段,注入 provider 的时候需要加上 provider 后缀,可以调用非 $get 方法。
2、run 运行模块。执行后创建了发射器和用于启动应用程序。只能注入运行实例和常量。返回的方法在 run 阶段注入的时候,无需加 provider 后缀,只能调用 $get 返回的方法。
3、执行的先后顺序:provider(定义默认)>config(修改默认)>run(最后执行)
(1)定义服务calc
 app.provider('calc',function () {this.currency = '&';console.log(1);});
(2)配置服务calc
 app.config(function (calcProvider) { calcProvider.currency = '钱';console.log(2);});
(3)运行服务calc
 app.run(function (calc) { console.log(3);});
另外,请求总逻辑(1)获取权限,(2)获取公共数据,(3)跳转页面。在别处获取公共数据,会出现页面已渲染、数据没到位的情况。
4、$http
$http({
  url:url,//请求的url路径
  method:method,//GET/DELETE/HEAD/JSONP/POST/PUT,DELETE会将params的配置用问号传参的方式拼接到url里,同时将data配置放在send里;
  params:params,//get请求时使用,转为?param1=xx1¶m2=xx2发送给服务器
  data:data//POST请求时使用,将被当做消息体发送给服务器
})

八、五种服务类型
来源:https://blog.csdn.net/qq_33587050/article/details/52138069
1、constant服务:constant不可以修改,可以注入到config里。
(1)app.constant("name",obj)
(2)name为服务的名字,obj为一个json对象.
(3)constant创建服务返回一个json对象(也就是第二个参数中传入的对象)。
2、value服务:value可以修改,不可以注入到config里。
(1)app.value("name",obj)
(2)name为服务的名字,obj为一个json对象.
(3)value创建服务返回一个json对象(也就是第二个参数中传入的对象)。
3、service服务
(1)app.service("name",constructor)
(2)name为服务的名字,第二个参数是一个构造函数,它的实例是实际被注入的服务。
4、factory服务
(1)app.factory("name",function(){return obj})
(2)name为服务的名字,第二个参数是一个普通函数,函数返回对象obj,obj是实际被注入的服务.
5、provider服务
(1)App.provider("myConfig", function () { this.$get= function () { return lastObj }});
(2)name为服务的名字,第二个参数是一个构造函数,其内要有$get方法,$get方法要返回对象lastObj,lastObj是真正被注入的服务。this.$get中的this不是myConfig,lastObj中的this才是myConfig。
6、装饰服务
(1)app.config(function($provide){ $provide.decorator("name",function($delegate){ return $delegate }) });
(2)同样是通过config,在参数函数中注入$provider服务,$provider服务有个decorator方法,它接受两个参数,第一个参数"name",是要被装饰的服务的名字,第二个参数是一个函数,函数中注入$delegate,$delegate就是被装饰的服务的实例,然后在函数中操作$delegate,就相当于操作了该服务的实例。
(3)示例:
app.config(function ($provide) {//通过config,在参数函数中注入$provider服务
    $provide.decorator('name',function ($delegate) {//$provider服务有个decorator方法,它接受两个参数,
        var fn = $delegate; //先保存以前的$delegate
        $delegate = function () {//在外面套一层函数
            var list = Array.from(arguments);
            list[1] = list[1]+2000;
            fn.apply(null,list);
        };
        return $delegate;//在函数中返回$delegate;
    })
});

九、过滤
1、表达式中添加过滤器
  <p>姓名为 {{ lastName | uppercase }}</p>
  <p>姓名为 {{ lastName | lowercase }}</p>
  <p>总价 = {{ (quantity * price) | currency }}</p>
2、向指令添加过滤器
  <li ng-repeat="x in names | orderBy:'country'">
    {{ x.name + ', ' + x.country }}
  </li>
  <input type="text" ng-model="test">
  <li ng-repeat="x in names | filter:test | orderBy:'country'">
    {{ (x.name | uppercase) + ', ' + x.country }}
  </li>
3、自定义过滤器
  app.filter('reverse', function() { //可以注入依赖
      return function(text) {
          return text.split("").reverse().join("");
      }
  });
4、filter使用规则:
(1)用来处理一个数组,然后可以过滤出含有某个子串的元素,作为一个子数组来返回。
(2)可以是字符串数组,也可以是对象数组。
(3)如果是对象数组,可以匹配属性的值。
(4)如果是字符串数组,可以匹配字符串的组成部分。

十、重要指令(ng-)  
1、ng-if、ng-hide、ng-show的用法
  (1)ng-if 如果条件为 false时; “移除” HTML 元素。
  (2)ng-show 如果条件为 false时; “隐藏” HTML 元素。
  (3)ng-hide 如果条件为 true时; “隐藏” HTML 元素。
  (4)ng-trim="false",自动去掉(input)前后空格。
  (5)ng-bind与{{}}的区别,ng-bind可以解决后者闪烁加载的问题
  另外:<span> {{protoNum}} </span>
    <img src="" ng-hide="protoNum==undefined? false:protoNum+1"/>
    (1)在HTML里,undefined+1的运行结果是字符串“undefined1”,
    (2)在js里,undefined+1的运行结果是数字NaN。
2、ng-repeat:用于遍历数组或对象,
(1)<tr ng-repeat="(key,value) in arrayOrObject"><td>{{key}}{{value}}</td></tr>,
  用这种格式遍历数组,key是每一项的索引,value是每一项的值。
  用这种格式遍历对象,key是每一项的属性名,value是每一项的属性值。
(2)<tr ng-repeat="item in arrayOrObject"><td>{{item}}</td></tr>,
  用这种格式遍历数组,item是数组中的每一项;
  用这种格式遍历对象,item是对象中的每一项的属性值。
3、ng-model
  以<input type="text" ng-model="name">{{name}}为例:
(1)如果input所在的作用域内已经声明name并给name赋值了,那么ng-model会把该值获取到,并在input里输出。同时{{name}}也会把该值取到页面上。
  当input里面的值改变时,ng-model把作用域内的name值进行修改,同时{{name}}也会用修改后的name值覆盖原来页面上的name值。 
(2)如果input所在的作用域内没有声明name,那么ng-model会在作用域内声明一个name。
  当input里面输入值后,ng-model会对作用域内的name进行赋值,同时{{name}}也会在页面上输出。
(3)原理:ng-model根据input标签的type属性值的不同,监听用户的输入事件如onchange,onkeyup,onkeydown。
(4)type="radio",
  ng-model的值就是选中的那个radio所对应的ng-value的值;初始化单选框,yesOrNo=1,则前者选中,yesOrNo=2,则后者选中,此单选框被选中;点击1单选框,则1单选框被选中,赋值yesOrNo=1
  <input type="radio" name="inlineRadioOptions" ng-model="yesOrNo" ng-value="1">。//ng-value:设置输入框的值。
  <input type="radio" name="inlineRadioOptions" ng-model="yesOrNo" ng-value="2">。//ng-value:设置输入框的值。
(5)type="checkbox"
  <input type="checkbox" ng-model="all"> Check all<br><br>
  <input type="checkbox" ng-checked="all">Volvo<br>
  <input type="checkbox" ng-checked="all">Ford<br>
  <input type="checkbox" ng-checked="all">Mercedes
(6)<select ng-model="myLog.resultDo" ng-options="item.value as item.key for item in resultDo"></select>
  $scope.resultDo = [
    { value: '', key: '全部' },
    { value: '已读', key: '已读' },
    { value: '未读', key: '未读' }
  ]
  https://blog.csdn.net/mydtudysy/article/details/78040969
4、ng-bind-html,如果没有$sce或$sanitize的辅助,它是不会生效的。
  (1)内置的$sce.trustAsHtml(),它不经过净化,直接将html绑定给元素,保留所有的属性和事件。
  (2)注入第三方模块ngSanitize就行了,不用注入$sanitize服务,$sanitize会自动对html标签进行净化,并会把标签的属性以及绑定在元素上的事件都移除,仅保留了标签和内容。
  (3)$sce用法示例
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.5.8/angular.js"></script>
    </head>
    <body ng-app="myApp" ng-controller="myCtl">
    <div ng-bind-html="content">
    </div>
    </body>
    </html>
    <script>
        var app = angular.module('myApp', []);
        app.controller('myCtl',function($scope,$sce){
            $scope.temporaryContent="My name is: <h1 style='color:red'>John Doe</h1>";
            $scope.content = $sce.trustAsHtml($scope.temporaryContent);
        });
    </script>
(4)ngSanitize用法示例
  <!DOCTYPE html>
  <html>
  <head>
      <meta charset="UTF-8">
      <title></title>
      <script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.5.8/angular.js"></script>
      <script src="https://cdn.bootcss.com/angular-sanitize/1.5.9/angular-sanitize.js"></script>
  </head>
  <body >
  <div ng-app="myApp" ng-controller="myCtrl">
      <p ng-bind-html="myText"></p>
  </div>
  </body>
  </html>
  <script>
    var app = angular.module("myApp", ['ngSanitize']);
    app.controller("myCtrl", function($scope) {
        $scope.myText = "My name is: <h1 style='color:red'>John Doe</h1>";
    });
  </script>
5、ng-class(三元、数组、对象)、ng-bind(三元、数组)、ng-style(三元)与ng-src(三元双括号)用法示例
  <!DOCTYPE html>
  <html lang="en" ng-app="myModel">
  <head>
    <meta charset="UTF-8">
    <title>样式</title>
    <style>
      .red{color:red}
      .green{color:green}
    </style>
    <script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.6.2/angular.js"></script>
  </head>
  <body>
    <div ng-controller="firstCtrl">
      <div ng-class="isTrue?'red':'green'">1、class三元</div>
      <div ng-class="['red','green'][num1]">2、class数组</div>
      <div ng-class="{'red':isTrue,'green':!isTrue}">3、class对象</div>
      <div ng-style="isTrue?{'color':'green','width':'200px'}:{'color':'red','width':'300px'}">4、style三元</div>
      <div ng-style="{'color': isTrue? 'red':'green','width': isTrue? '200px':'300px', }">5、style对象(三元)</div>
      <div ng-style="{'color': ['red','green'][num1], }">6、style对象(数组)</div>
      <div><span ng-bind="isTrue?'7、':'8、'"></span><span>bind三元</span></div>
      <div><span ng-bind="['7、','8、'][num1]"></span><span>bind数组</span></div>
      <div><span>{{isTrue?'9、':'0、'}}</span><span>三元取值</span></div>
      <div><span>{{['9、','0、'][num1]}}</span><span>数组取值</span></div>
    </div>
  </body>
  </html>
  <script>
    var app = angular.module('myModel', []);
    app.controller('firstCtrl', function ($scope) {
      $scope.isTrue=true;
      $scope.num1=1;
      $scope.width='200px';
      $scope.height='20px';
    });
  </script>
  <img ng-src="{{isTrue?path1:path2}}" alt=""/>
6、ng-transclude
  <!DOCTYPE html>
  <html lang="en" ng-app="myModel">
  <head>
    <meta charset="UTF-8">
    <title>多个插槽之angular1.6.2版</title>
    <script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.6.2/angular.js"></script>
  </head>
  <body>
    <div ng-controller="thisCtrl">
      <panel color="blue">
        <panel-header><h1>{{title}}</h1></panel-header>
        <panel-body><h1>{{content}}</h1></panel-body>
        <panel-footer><h1>{{footer}}</h1></panel-footer>
      </panel>
    </div>
  </body>
  <script>
    var app = angular.module('myModel', []);
    app.controller('thisCtrl',function($scope){
      $scope.title = "头";
      $scope.content = "体";
      $scope.footer = "脚";
    });
    app.directive('panel', function(){
      return {
        template: `
          <div style="color:{{aaa}}">
            <div>下面放置插槽header:</div>
            <div ng-transclude="header"></div>
            <div>下面放置插槽body:</div>
            <div ng-transclude="body"></div>
            <div>下面放置插槽footer:</div>
            <div ng-transclude="footer"></div>
          </div>
        `,
        restrict: 'E',
        replace: true,
        transclude: {
          'header': 'panelHeader',
          'body': 'panelBody',
          'footer': 'panelFooter'
        },
        controller: function ($scope) {
            
        },
        link: function (scope, element, attr,) {
          scope.aaa = attr.color
        }  
      }
    });
  </script>
  </html>
7、ng-app
  根模块的依赖模块加载完毕后,根模块才可以挂载到页面上
  (1)用法一
    <html ng-app="myApp" ng-controller="myCtrl">
      <body></body>
    </html>
    <script>
      //此处导入依赖['deploy1','deploy2']
      var app = angular.module('myApp', ['deploy1','deploy2']);
      app.controller('myCtrl', function ($scope) { });
    </script>
  (2)用法二
    <html ng-controller="myCtrl">
      <body></body>
    </html>
    <script>
      var app = angular.module('myApp', ['deploy1','deploy2']);
      app.controller('myCtrl', function ($scope) { });
      //此处导入依赖['deploy1','deploy2']
      angular.bootstrap(document, ['myApp']);
    </script>

十一、单选框|复选框|下拉框三者联合案例展示
说明:单选框与复选框在点击事件发生时的区别
1、单选框在点击后:呈现选中状态,ng-model取值ng-value(初始化时,两者相等则选中);
2、复选框在点击后:呈现相反状态,ng-model取反ng-model(初始化时,布尔转换ng-checked的结果赋值给ng-model,决定是否选中),ng-checked永远保持原始值;
  <!DOCTYPE html>
  <html lang="en" ng-app="myModel">
  <head>
    <meta charset="UTF-8">
    <title>联合案例展示</title>
    <script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.6.2/angular.js"></script>
  </head>
  <body>
  <div ng-controller="firstCtrl">
    <div style="margin-bottom:30px;">
      <span>右边是type="checkbox"</span>
      <input type="checkbox" style="width:20px;transform:scale(2)" ng-checked="checkboxCheck" ng-model="checkboxModel" ng-click="checkbox()">
    </div>
    <div style="margin-bottom:30px;">
      <span>右边是select</span>
      <select ng-model="myLog.resultDo" ng-options="item.back as item.front for item in resultDo" style="width:120px;height:30px" ng-change="selectOption()"></select>
    </div>
    <div style="margin-bottom:30px;">
      <span>右边是type="radio"</span>
      <input id="terminal" type="radio" name="options" ng-model="deviceMode.mode" ng-value="0" style="width:20px;transform:scale(2)" ng-click="selectRadio()"/>
      <label for="terminal" style="margin-right:40px;" ng-click="selectRadio()">终端</label>
      <input id="platform" type="radio" name="options" ng-model="deviceMode.mode" ng-value="1" style="width:20px;transform:scale(2)" ng-click="selectRadio()"/>
      <label for="platform" ng-click="selectRadio()">平台</label>
    </div>
    </div>
  </body>
  </html>
  <script>
    var app = angular.module('myModel', []);
    app.controller('firstCtrl', function ($scope) {
      $scope.myLog={resultDo:"全部后台"};
      $scope.resultDo = [{ back: '全部后台', front: '全部前端' },{ back: '已读后台', front: '已读前端' },{ back: '未读后台', front: '未读前端' }];
      $scope.deviceMode = {mode: 0};
      $scope.checkboxCheck = false;/*永远不变*/
      $scope.checkboxModel = true;/*交替改变*/
      $scope.checkbox = function () {
        console.log($scope.checkboxCheck);
        console.log($scope.checkboxModel)
      };
      $scope.selectRadio = function () {
        console.log($scope.deviceMode)
      };
      $scope.selectOption = function () {
        console.log($scope.myLog.resultDo)
      };
    });
  </script>
3、下拉框分组
  <!DOCTYPE html>
  <html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title></title>
    <script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.6.2/angular.js"></script>
  </head>
  <body ng-app="myapp">
    <div ng-controller="mainCtrl">
      <div>
        <p>你的选择是:{{selectedOption}}</p>
        <select ng-options="item.id as item.name group by item.sex for item in resultDo"
          ng-model="selectedOption"></select>
      </div>
    </div>
    <script type="text/javascript">
      var myapp = angular.module('myapp', []);
      myapp.controller('mainCtrl', ['$scope', function ($scope) {
        $scope.selectedOption ="hanmeimei";
        //定义包含对象的数组 resultDo
        $scope.resultDo = [
          { "id": "lilei", "name": "李雷", "sex": "man" },
          { "id": "hanmeimei", "name": "韩梅梅", "sex": "woman" },
          { "id": "jack", "name": "杰克", "sex": "man" }
        ];
      }])
    </script>
  </body>
  </html>
4、下拉框可输入
  <div class="form-group">
    <label>资产名称</label>
    <div class="form-group">
      <input 
        list="device_name"
        type="text"
        class="form-control"
        style="width: 300px" ng-model="addDialogItem.device_name"/>
      <datalist id="device_name">
        <option 
          ng-repeat="option in assetName track by $index" 
          value="{{option.device_name}}">{{option.device_name}}</option>
      </datalist>
    </div>
  </div>

十二、子组件向父组件传值
附:兄弟组件传值,在父组件的作用域里定义一个对象,分别传给两个子组件,在子组件里,给该对象的一个属性赋值
1、属性传值
  思路:在父组件定义一个函数,在函数体里,将函数的参数赋值给父组件$scope的一个属性;通过属性传参的方式把这个函数传给子组件。在子组件里,给这个函数传参并执行,实现子组件向父组件传值!
  另外,页面改变组件的值,在页面刚渲染时,就向后台发送请求,获取数据,把数据对象与组件的ng-if绑定,同时作为参数传给组件,在子组件里给参数添加一个函数属性,通过页面的数据对象调用该函数!如果数据对象不与组件的ng-if绑定,那么组件在渲染时,会因没有数据而出现页面错误!
  也可以将ng-if与false绑定,等到数据对象生成后,再与true绑定!
  <!DOCTYPE html>
  <html lang="en" ng-app="appModule" ng-controller="ancestorCtrl">
    <head>
        <meta charset="UTF-8">
        <title>子组件向父组件传值</title>
        <script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.5.8/angular.js"></script>
    </head>
    <body>
      <my-parent></my-parent>
    </body>
  </html>
  <script>
    var app = angular.module('appModule', []);
    app.controller("ancestorCtrl", function ($scope) {});
    app.directive('myParent', function () {
      return {
        template: "<div><my-child son-method='parentMethod'></my-child>" + "<div>{{sonData}}</div></div>",
        restrict: 'E',//指令类型  E:element A:attribute M:comment C: class
        replace: true,//模板放在<my-parent>内部还是取代它,默认是false,放在其内部
        controller: function ($scope) {
          $scope.parentMethod = function (key) {
            $scope.sonData = key;
          };
        },
      }
    });
    app.directive('myChild', function () {
      return {
        template: "<div><button ng-click='thisMethod(\"222\")'>点击获取来自子组件的数据</button></div>",
        restrict: 'E',
        replace: true,
        scope: {
          sonMethod:'=sonMethod'
        },
        controller: function ($scope) {
          $scope.index = 1;
          $scope.thisMethod = function (key) {
            $scope.index += 1;
            $scope.sonData = key + $scope.index;
            $scope.sonMethod($scope.sonData)
          };
        },
      }
    })
  </script>
2、监听传值
  <body>
    <div ng-controller="parent">
      Parent: {{parent}}
      <input type="text" ng-model="parent"/>
      <div ng-controller="child">
        {{change}} Child: {{child}}
      </div>
    </div>
  </body>
  (1)父广播、子监听
    angular.module("app", [])
      .controller("parent", function($scope) {
        $scope.$watch("parent", function(n, o) {
          if (n == o) {
            return;
          }
          $scope.$broadcast("parentChange", n)
        })
      })
      .controller("child", function($scope) {
        $scope.$on("parentChange", function(e, m) {
          $scope.change = "changed";
          $scope.child = m;
        })
      })
    </script>
  (2)父监听、子发射
    angular.module("app", [])
      .controller("parent", function($scope) {
        $scope.$on("childChange", function(e, m) {
          $scope.change = "changed";
          $scope.parent = m
        })
      })
      .controller("child", function($scope) {
        $scope.$watch("child", function(n, o) {
          if (n == o) {
            return;
          }
          $scope.$emit("childChange", n)
        })
      })
    </script>
  另有,孙:发射、祖:先监听后广播

十三、directive自定义标签
1、基本案例
  <!DOCTYPE html>
  <html lang="en" ng-app="appModule" ng-controller="thisCtrl">
  <head>
    <meta charset="UTF-8">
    <title></title>
    <script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.5.8/angular.js"></script>
  </head>
  <body>
    <this-div outer-function='outerFunction'>
      <button ng-click="slotFunction()">这是插槽-可以读取当前页的内容</button>
    </this-div>
  </body>
  </html>
  <script>
    var app = angular.module('appModule', []);
    app.controller("thisCtrl", function ($scope) {
      $scope.slotContent = "这是组件的插槽内容!";
      $scope.slotFunction = function () {
        console.log($scope.slotContent)
      };
      $scope.outerFunction= function () {
        console.log($scope.slotContent)
      };
    });
    app.directive('thisDiv', function () {
      var str = '';
      str += "<div>";
      str += "<div ng-transclude></div>";
      str += "<button ng-click='innerFunction()'>这是组件-可以读取当前页的内容</button>";
      str += "</div>";
      return {
        template: str,
        restrict: 'E',
        replace: true,
        transclude: true,
        scope: {
          outerFunction: '='
        },
        controller: function ($scope) {
          $scope.innerFunction = function () {
            $scope.outerFunction()
          }
        }
      }
    });
  </script>
2、嵌套
  <!DOCTYPE html>
  <html lang="en" ng-app="appModule" ng-controller="ancestorCtrl">
  <head>
      <meta charset="UTF-8">
      <title></title>
      <script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.5.8/angular.js"></script>
  </head>
  <body>
  <my-parent>
      {{ancestorName}}
      <my-child selfAttr="myAttr">{{childName}}</my-child>
  </my-parent>
  </body>
  </html>
  <script>
      var app = angular.module('appModule', []);
      app.controller("ancestorCtrl", function ($scope) {
          $scope.ancestorName = "我所处的位置,表明我只能在ng-transclude里出现。";
          $scope.childName = "变量必须定义在作用域里,myParent和myChild的作用域我进不去。";
      });
      app.directive('myParent', function () {
          return {
              template: "<div><p>第一段</p>" + "<div ng-transclude></div>" + "<p>第二段</p>" + "<div>{{parentName}}</div></div>",
              //template: '<div>我是指令生<div ng-transclude></div>成的内容</div>',//templateUrl导入自定义元素的模板
              restrict: 'E',//指令类型  E:element A:attribute M:comment C: class
              replace: true,//模板放在<my-parent>内部还是取代它,默认是false,放在其内部
              transclude: true,//是否保留自定义指令开闭标签之间的内容,该内容插入模板中使用ng-transclude属性的地方,默认为false,如上。<div ng-view></div>通过路由放入数据
              scope: {
                //获取自定义标签的属性值//false:继承父作用域;true:继承父作用域且创建独立作用域;{}:不继承父作用域且创建独立作用域。
                //scope:{name:'@name',绑定字符串  name:'=name',绑定变量  name:'&name',绑定函数}。传进来的变量,在值改变后,父作用域的该变量的值也随之改变。
              },
              controller: function ($scope) {
                //指令的controller和link可以进行互换。controller的主要作用(1)导入自定义或ng内置的服务;(2)提供可在子指令里复用的行为。
                $scope.focusAAA = function () {};
                this.parentMethod = function (son) {
                  console.log('son:',son);
                }
              },
              compile: function(element, attributes) {
                //compile和link是相斥的。如果同时设置了两项,compile返回的函数会被当作链接函数把后面定义的link函数所取代,也就是说,后面的link函数将起不到作用。controller先运行,compile后运行,link不运行
              },
              link: function (scope, element, attr, ctrl, linker) {}
          }
      });
      app.directive('myChild', function () {
          return {
              template: "<div><span ng-click='toParent()'>这里是span1;</span><span>这里是span2。</span><div ng-transclude></div></div>",
              restrict: 'E',
              replace: true,
              transclude: true,
              scope: {},
              require: '?^myParent',//?不要抛出异常;^允许查找父元素的controller
              controller: function ($scope) {
                console.log( $scope.parentMethod  )
              },
              /*compile: function(element, attributes) {},*/
              //'myParent':在当前指令中查找控制器;'^myParent'在上游指令中查找控制器;'?^myParent':当前指令若没有,就向上游查找
              link: function (scope, element, attr, ctrl, linker) {
                  ctrl.parentMethod();
                  linker(scope, function (clone) {
                      console.log(clone.text())
                  })
              }
          }
      })
  </script>

十四、获取不到新value
  症状:angular对象数据的value被更改后,获取的仍然是旧value
  原因:在第三方插件或原生JS里进行的变化,没法触发脏值检测。
    angular.module('myApp', []).controller('MessageController', function ($scope) {
      $scope.getMessage = function () {
        setTimeout(function () {
          $scope.$apply(function () {
            $scope.message = 'Fetched after 3 seconds';
            console.log('message:' + $scope.message);
          });
        }, 2000);
      }
      $scope.getMessage();
    });
    $scope.getMessage = function () {
      setTimeout(function () {
        $scope.message = 'Fetched after two seconds';
        console.log('message:' + $scope.message);
        $scope.$apply(); //this triggers a $digest
      }, 2000);
    }
    angular.element(document).ready(function () {
      var config = {
        elem: '#' + $scope.dateInputId,
        theme: '#007bff',
        trigger: 'click',
        done: function (value) {
          $scope.dateModel = value;
          $scope.$apply();//在此处执行
          angular.isFunction($scope.updata) ? $scope.updata() : '';
          //在此处执行无效
        }
      };
      laydate.render(config);
    });

十五、[].forEach|$.each|angular.forEach用法示例
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title></title>
    </head>
    <body>
      本代码不能运行
    </body>
  </html>
  <script>
    //[].forEach
    [3, 3, 3].forEach(function (a, b, c, d) {
      //3 0 [ 3, 3, 3, 3, 3 ] undefined
      console.log(a, b, c, d)
    })
    //$.each
    $.each([3, 3, 3, 3], function (a, b, c) {
      console.log(a, b, c)
      //0 3 undefined

    })
    $.each({ a: 'b' }, function (a, b) {
      console.log(a, b)
      //a,b
    });
    //angular.forEach
    angular.forEach([3, 3, 3], function (a, b, c, d) {
      console.log(a, b, c, d)
      //3 0 Array(6) undefined
    });
    angular.forEach({ a: 'b' }, function (a, b) {
      console.log(a, b)
      //b,a
    });
  </script>

十六、ui.router路由模块
1、提供$stateProvider对象,
  (1)用$stateProvider.state(stateNameString, stateConfigObject)配置路由。
  (2)用template去替换<ui-view></ui-view>或<div ui-view></div>。
  (3)stateConfigObject包含以下内容:
  (3-1)template: string/function,html模板字符串,或者一个返回html模板字符串的函数。
  (3-2)templateUrl:string/function,模板路径的字符串,或者返回模板路径字符串的函数。
  (3-3)templateProvider:function,返回html模板字符串或模板路径的服务。
  (3-4)controller:string/function,新注册一个控制器函数或者一个已注册的控制器的名称字符串。
  (3-5)controllerProvider:function,返回控制器或者控制器名称的服务
  (3-6)controllerAs:string,控制器别名。
  (3-7)parent:string/object,手动指定该状态的父级。
  (3-8)resolve:object,将会被注入controller去执行的函数,<string,function>形式。
  (3-9)url:string,当前状态的对应url。
  (3-10)views:object,视图展示的配置。<string,object>形式。
  (3-11)abstract:boolean,一个永远不会被激活的抽象的状态,但可以给其子级提供特性的继承。默认是true。
  (3-12)onEnter:function,当进入一个状态后的回调函数。
  (3-13)onExit:function,当退出一个状态后的回调函数。
  (3-14)reloadOnSearch:boolean,如果为false,那么当一个search/query参数改变时不会触发相同的状态,用于当你修改$location.search()的时候不想重新加载页面。默认为true。
  (3-15)data:object,任意对象数据,用于自定义配置。继承父级状态的data属性。换句话说,通过原型继承可以达到添加一个data数据从而整个树结构都能获取到。
  (3-16)params:url里的参数值,通过它可以实现页面间的参数传递。params:{params1:null,params2:null...}
2、提供$urlRouterProvider对象,
  (1)用$urlRouterProvider.when(url,otherUrl/handler)配置匹配的前端路由,
  (2)用$urlRouterProvider.otherwise('/login')配置不匹配的前端路由。
3、ui.router实际执行步骤
  附1、ngRoute模块提供$routeProvider服务
    1、$routeProvider.when('/bookDetail/:bookId',{}).when()
    2、<a class="btn btn-default" href="#/bookDetail/{{book.id}}">//实参
    3、var bookId = $routeParams.bookId;//获取实参
  附2、ui.router和ngRoute的区别
    1、ui.router模块提供$urlRouterProvider、$stateProvider、$stateParams服务
    2、ngRoute模块提供$routeProvider、$routeParams服务
(1)定义路由状态;
  $stateProvider
  .state('page2', {
    url: '/event-safe/:thisParams',
    params: {
      thisParams: null//前端可以根据thisParams的取值不同,进行不同的处理,与上面的:thisParams二选一使用
    },
    templateUrl: 'event/event-safe.html',
    controller: 'eventSafeCtrl',
  })
(2)激活路由的方式
  (2-1)$state.go():优先级最高的激活方式,$state.go('page',{ thisParams: '123'}),获取路由参数$stateParams.params;
  (2-2)ui-sref:点击包含此指令跳转,例如<a ui-sref="page2({type:1})"></a>
  (2-3)ng-href:<a ng-href="#/page2/1"></a>
(3)ui.router路由嵌套:通过 .语法进行路由层级配置
  代码示例一:
  /* 以下是index.html */
  <div>
    <span>其他内容</span>
    <a ui-sref="about">About</a>
    <a ui-sref="home">Home</a>
    <span>其他内容</span>
    <div ui-view></div>
  </div>
  /* 以下是home.html */
  <div>
    <span>其他内容</span>
    <a ui-sref=".list">List</a>
    <a ui-sref=".graph">Paragraph</a>
    <span>其他内容</span>
    <div ui-view></div>
  </div>
  $stateProvider
    .state("about", {
      url: "/about",
      templateUrl: "about.html",
    })
    .state("home", {
      url: "/home",
      templateUrl: "home.html",
    })
    .state("home.list", {
      url: "/list",
      templateUrl: "home-list.html",
      controller: function ($scope) {
        $scope.dogs = ["Bernese", "Husky", "Goldendoodle"];
      },
    })
    .state("home.graph", {
      url: "/graph",
      template: "I could sure use a drink right now.",
    });
  代码示例二:
  <div>
    <div ui-view="filters"></div>
    <div ui-view="mailbox"></div>
    <div ui-view="priority"></div>
  </div>
  $stateProvider
    .state('inbox', {
      views: {
        'filters': {
          template: '<h4>Filter inbox</h4>',
          controller: function($scope) {}
        },
        'mailbox': {
          templateUrl: 'partials/mailbox.html'
        },
        'priority': {
          template: '<h4>Priority inbox</h4>',
          resolve: {
            facebook: function() {
              return FB.messages();
            }
          }
        }
      }
    }); 
 
十七、jqLite的API参考
1、在jQuery中,$()里面是通过各种选择器获得元素;
2、在jqLite中,通过angular.element(param)获得元素;param只有两种:一种是类似html元素的字符串,一种是Dom元素。如:
  var email=angular.element('<input name="email" id="myId"></input>'); 
  var email=angular.element(document.getElementById(‘myId’));
3、jqLite API参考:https://blog.csdn.net/chi1130/article/details/78556097
  01、addClass()  //为每个匹配的元素添加指定的样式类名
  02、after() 在匹配元素集合中的每个元素后面插入参数所指定的内容,作为其兄弟节点
  03、append()    //在每个匹配元素里面的末尾处插入参数内容
  04、attr()  //获取匹配的元素集合中的第一个元素的属性的值
  05、bind()  //为一个元素绑定一个事件处理程序
  06、children()  //获得匹配元素集合中每个元素的子元素,选择器选择性筛选
  07、clone() //创建一个匹配的元素集合的深度拷贝副本
  08、contents()  //获得匹配元素集合中每个元素的子元素,包括文字和注释节点
  09、css()   //获取匹配元素集合中的第一个元素的样式属性的值
  10、data()  //在匹配元素上存储任意相关数据
  11、detach()    //从DOM中去掉所有匹配的元素
  12、empty() //从DOM中移除集合中匹配元素的所有子节点
  13、eq()    //减少匹配元素的集合为指定的索引的哪一个元素
  14、find()  //通过一个选择器,jQuery对象,或元素过滤,得到当前匹配的元素集合中每个元素的后代
  15、hasClass()  //确定任何一个匹配元素是否有被分配给定的(样式)类
  16、html()  //获取集合中第一个匹配元素的HTML内容
  17、next()  //取得匹配的元素集合中每一个元素紧邻的后面同辈元素的元素集合。如果提供一个选择器,那么只有紧跟着的兄弟元素满足选择器时,才会返回此元素
  18、on()    //在选定的元素上绑定一个或多个事件处理函数
  19、off()   //移除一个事件处理函数
  20、one()   //为元素的事件添加处理函数。处理函数在每个元素上每种事件类型最多执行一次
  21、parent()    //取得匹配元素集合中,每个元素的父元素,可以提供一个可选的选择器
  22、prepend()   //将参数内容插入到每个匹配元素的前面(元素内部)
  23、prop()  //获取匹配的元素集中第一个元素的属性(property)值
  24、ready() //当DOM准备就绪时,指定一个函数来执行
  25、remove()    //将匹配元素集合从DOM中删除。(同时移除元素上的事件及 jQuery 数据。)
  26、removeAttr()    //为匹配的元素集合中的每个元素中移除一个属性(attribute)
  27、removeClass()   //移除集合中每个匹配元素上一个,多个或全部样式
  28、removeData()    //在元素上移除绑定的数据
  29、replaceWith()   //用提供的内容替换集合中所有匹配的元素并且返回被删除元素的集合
  30、text()  //得到匹配元素集合中每个元素的合并文本,包括他们的后代
  31、toggleClass()   //在匹配的元素集合中的每个元素上添加或删除一个或多个样式类,取决于这个样式类是否存在或值切换属性。即:如果存在(不存在)就删除(添加)一个类
  32、triggerHandler()    //为一个事件执行附加到元素的所有处理程序
  33、unbind()    //从元素上删除一个以前附加事件处理程序
  34、val()   //获取匹配的元素集合中第一个元素的当前值
  35、wrap()  //在每个匹配的元素外层包上一个html元素
 
十八、前端路由及触发条件
1、前端路由的作用是 
  (1)记录当前页面的状态; 
  (2)可以使用浏览器的前进后退功能;
2、前端路由的实现是浏览器的hash模式,该模式能做到: 
  (1)url变化时,不向后台发送请求;
  (2)截获url地址,并解析出需要的信息来匹配路由规则。 
  (3)另外,使用浏览器的history模式时,当url变化时,会向后台发送请求;
3、angular1前端路由触发条件
  (1)<a href="#bbb">Home</a>
  (2)$state.go('login')
 
  

  

posted @ 2020-10-27 00:16  WEB前端工程师_钱成  阅读(8486)  评论(0编辑  收藏  举报