20、angular1之pass-word密码组件、input-select输入下拉组件 、single-select此消彼长组件和both-select此静彼长组件、on-off开关组件、simple-dialog多层弹窗组件,含插槽ng-transclude、input-datalist组件、layui中的datetime示例、京东购物车、两种作用域绑定

一、pass-word组件(自定义密码输入框,可预览)
使用场景:修改密码时,对密码进行多方位验证,并显示验证结果。
<!DOCTYPE html>
<html  ng-app="myModel">
<head>
  <meta charset="UTF-8">
  <title>passWord</title>
  <script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.6.2/angular.js"></script>
</head>
<body>
  <div ng-controller="thisCtrl">
    <pass-word 
      label-width="150" 
      is-must-fill="true" 
      label-text="原密码" 
      this-type="password" 
      placeholder="原密码" 
      is-disabled="false"
      self-show='pastShow'
      is-past-input='true' 
      self-data="oldPassword" 
      verify="verify" 
    ></pass-word>
    <pass-word 
      label-width="150" 
      is-must-fill="true" 
      label-text="输入新密码" 
      this-type="password"  
      placeholder="输入新密码" 
      is-disabled="false" 
      is-first-input='true' 
      key-of-reg-exp="simplePassword" 
      self-data="params.newPassword" 
      sibling-data="newPasswordSecond" 
      one-tip="oneTip" 
      many-tips="manyTips"
      self-show='selfShow'
      sibling-show='siblingShow'
    ></pass-word>
    <pass-word 
      label-width="150" 
      is-must-fill="true" 
      label-text="再次输入新密码" 
      this-type="password"  
      placeholder="再次输入新密码" 
      is-disabled="false" 
      key-of-reg-exp="simplePassword" 
      self-data="newPasswordSecond" 
      sibling-data="params.newPassword" 
      one-tip="oneTip" 
      many-tips="manyTips" 
      self-show='siblingShow'
      sibling-show='selfShow'
    ></pass-word>   
  </div>
</body>
</html>
<script>
  var app = angular.module('myModel', []);
  app.controller('thisCtrl', function ($scope) {
    $scope.params = {};//发给后台
    $scope.pastShow = {
      isShowOneTip:false,
      isGreenOneTip:false,
      isShowManyTips:false,
    };
    $scope.selfShow = {
      isShowOneTip:false,
      isGreenOneTip:false,
      isShowManyTips:false,
    };
    $scope.siblingShow = {
      isShowOneTip:false,
      isGreenOneTip:false,
      isShowManyTips:false,
    };
    $scope.oneTip = {//由当前页传至本组件
      success: '自定义_输入符合要求!',
      empty: '自定义_该项不能为空!',
      different: '自定义_两次密码输入不一致!',
      formatError: '自定义_输入不符合要求,请输入以字母开头的4-24个字符!'
    };
    $scope.manyTips= [
      '密码要求:',
      '1.必须包含:数字',
      '2.必须包含:字母',
      '3.必须包含:小写字母',
      '4.必须包含:大写字母',
      '5.必须包含:下划线',
    ];
    $scope.oldPassword= '';
    $scope.params.newPassword='';
    $scope.newPasswordSecond= '';
    $scope.verify = function() {
      //把原密码告诉后台,根据后台判断结果,决定是否继续验证
      return true
    }
  });
  app.directive('passWord', function () {
    var html = `
      <div >
        <div ng-|130)+'px'}" >
          <span ng-show="isMustFill" >*</span>
          <label ng-bind="labelText||'密码'"></label>
        </div>
        <div >
          <input 
            type="{{ thisType ? thisType : 'text' }}" 
            ng-model="selfData" placeholder="{{placeholder||''}}" 
            ng-disabled="isDisabled" ng-focus="focus()" 
            ng-change="changeOrBlur()" 
            ng-blur="changeOrBlur()" 
            ng-trim="false" 
            
          >
          <img 
            src="{{servicePicture.eyeClose}}" 
            ng-click="isShowEyeOpen=true;thisType='text'" 
            ng-show="!isShowEyeOpen" 
            
          >   
          <img 
            src="{{servicePicture.eyeOpen}}" 
            ng-click="isShowEyeOpen=false;thisType='password'" 
            ng-show="isShowEyeOpen" 
            
          >
        </div>
        <div >
          <div ng-show="selfShow.isShowOneTip">
            <img 
              src="{{selfShow.isGreenOneTip ? servicePicture.correct : servicePicture.error}}" 
              >
            <span 
              ng-bind="singleTipText" 
              ng-color\':\'green\'}:{\'color\':\'red\'}" 
              >
            ></span>
          </div>  
          <div ng-show="selfShow.isShowManyTips && manyTips.length>0" >
            <img 
              src="{{servicePicture.triangle}}" 
              
            >
            <div >
              <div ng-repeat="li in manyTips" ng-bind="li"></div>
            </div>
          </div>
        </div>
      </div>
      `;
    return {
      restrict: 'E',
      template: html,
      scope: {
        //以下是16种配置项。这是输入框组件,适用于密码输入、普通输入(isPastInput/isFirstInput:true)、旁边提示和弹窗提示。
        labelWidth: '@labelWidth',//标签宽度
        labelText: '@labelText',//标签文字
        placeholder: '@placeholder',//输入框内提示文字
        thisType: '@thisType',//输入框类型(文字或密码)
        keyOfRegExp: '@keyOfRegExp',//会用到的正则
        isMustFill: '=isMustFill',//该项是否必填
        isDisabled: '=isDisabled',//该项是否禁用
        isPastInput: '=isPastInput',//该项是不是原密码xxxxxxxxxxx
        isFirstInput: '=isFirstInput',//该项是不是第一次输入新密码xxxxxxxxxxxx
        selfData: '=selfData',//自身数据============
        siblingData: '=siblingData',//兄弟数据===============
        oneTip: '=oneTip',//单条提示
        manyTips: '=manyTips',//多条提示
        selfShow: '=selfShow',//自身的提示是否显示出来============
        siblingShow: '=siblingShow',//兄弟的提示是否显示出来=============
        verify: '=verify',//verify是函数,但按照变量传进来更好xxxxxxxxxxx
      },
      replace: true,
      controller: function ($scope, serviceRegExp, servicePicture) {
        $scope.servicePicture = servicePicture;
        $scope.inputChange = function (params, message) {
          var params = params;
          var config = {
            init: [false, false, false],
            focus: [false, false, true],
            empty: [true, false, false],
            formatError: [true, false, false],
            different: [true, false, false],
            wrong: [true, false, false],
            success: [true, true, false],
          };
          $scope.selfShow.isShowOneTip = config[params][0];
          $scope.selfShow.isGreenOneTip = config[params][1];
          $scope.selfShow.isShowManyTips = config[params][2];
          $scope.singleTipText = ($scope.oneTip && $scope.oneTip[params]) || message;
        };
        $scope.focus = function () {
          if($scope.isPastInput){
            $scope.inputChange('init');
            return
          }
          if($scope.isFirstInput){
            $scope.siblingData='';
            $scope.siblingShow = {
              isShowOneTip:false,
              isGreenOneTip:false,
              isShowManyTips:false,
            };
          }
          $scope.inputChange('focus');
        };
        $scope.changeOrBlur = function () {
          if ($scope.isMustFill && $scope.selfData=="") {//判断该项是否可以为空
            $scope.inputChange('empty', '该项不能为空!');
            return
          }
          if ($scope.keyOfRegExp && !(serviceRegExp[$scope.keyOfRegExp].test($scope.selfData))) {
            $scope.inputChange('formatError', '格式错误!');
            return
          }
          if (!$scope.isPastInput && !$scope.isFirstInput && $scope.selfData != $scope.siblingData) {
            $scope.inputChange('different', '两次密码输入不一致!');
            return
          }
          if(angular.isFunction($scope.verify) && !$scope.verify()){//验证原密码是否正确
            $scope.inputChange('wrong', '原密码输入错误!');
            return
          };
          $scope.inputChange('success', '输入符合要求!');
        };
      },
      link: function (scope, ele, attrs) {
       
      }
    };
  });
  app.factory('serviceRegExp', function () {
    return {
      simplePassword: /^[a-zA-Z][a-zA-Z0-9]{4,24}$/,//简单密码验证,以大小写字母开头,由大小写字母和数字构成的,共5-25位的密码
      complexPassword: /^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&*?_|~=+\-<>[{}/\'\":;,.\]*]+$)(?![a-zA-z\d]+$)(?![a-zA-z!@#$%^&*?_|~=+\-<>[{}/\'\":;,.\]*]+$)(?![\d!@#$%^&*?_|~=+\-<>[{}/\'\":;,.\]*]+$)[a-zA-Z\d!@#$%^&*?_|~=+\-<>[{}/\'\":;,.\]*]{6,25}$/,//复杂密码验证,同时包含字母、数字、特殊符号如!@#$%^&*?_|~=+\-<>[{}/\'\":;,.\]
      ipFrom0To255: /^(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)$/,//ip地址验证,从0.0.0.0~255.255.255.255
      portFrom0To65535:/^([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/,
      mac:/^[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}$/,
      mobilePhone: /^1[3456789]\d{9}$/,//手机号码验证,中国大陆
      numberNotLimit: /^(0|[1-9][0-9]*)$/,//0或非0开头的数字
      numberWithLimit: /^(0|[1-9][0-9]{3,4})$/,//0或非0开头的数字4-5位
      emailNotLimit: /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/,//邮箱验证
      emailWithLimit: /^[A-Za-z\d]+([-_.][A-Za-z\d]+)*@([A-Za-z\d]+[-.])+[A-Za-z\d]{2,4}$/,//邮箱验证
      chineseCharacters: /^[\u4e00-\u9fa5]{0,}$/,//汉字验证
      blankSpace: /^\s*$/ //零到多个空格
    };
  });
  app.factory('servicePicture', function () {
    return {
      eyeClose: 'data:image/png;base64,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',
      eyeOpen: 'data:image/png;base64,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',
      correct: 'data:image/png;base64,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',
      error: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAACz1JREFUeAHdG2l0VNX5u28msyQhGyQhy2SBkAARqAkqHFrPKdWjVMoiBCmyCFIQCrRIQUCPpMoxgKhQK2URQWQphEKlQqG1lpa2lCPkoByQSSIkk4SQfYFkMpOZ9/rdF2b63rzZ3psZSnp/zHv32+73fe8u3/3uHQIhLrfyk8M7GltGsMDmccDlEY4Y8BlLgMTce3KoQisA1wJAWoGACeslhDCX9El9vzacrzaHUkUSCuE3MnXpNpYr4DiuAOXno4UqJe2gcnZ0yJdAyBFGzRQPKjdXK5HjjSdoDuDy88NKG6/OAWBf5DgY5a1RJThUlOMInGcIs2tQxqj95OxZmxI5rjwBO6AsK0trt1XNAw5Wo4pprg2Eok4IuQkcKcqOz91LLl3qDqSNgBxgzNA/Byz7Do7llECUUMxLiIkA9/OcSutxpTIUOaAsKzLe3t29Db/4VKUNB5OPEDgUHqlbYrja3ixXrmwHGDM0k7D77cQJLl5uYyGlJ+Q2AzA/u9JyUk47yON/KU3XrgQWjj1wxlMTOK4/6nWiNE27zH+LAPxyAFdYyBjTte+zHLcJlzTZvUaOQoHQom4MC9xW1HUzOsMvPX06gCsoUBn3FB1BgUsCUe5+8qKuK4wZ2v30w/lq16eXsNtvwy+/yJegBxGPy+V7OZWWl73p5tVD19M0q3qr8dRo7AnLcU5Y6s0BHntAabrmWYzojj7IY96bYQ4cGshiT5jgaXVw64AbmRGJVnv3NRQS5xDUm5/ogPoIvWZo6vU7Ta52uB0C3axt+/+L8dRgHAoJHWbLr12Np3WJA0rT9TOQYZI74t4Mw+E8nQ5rVxtEDqganarHffs7rkS+6kx0LIQ//iQw0TG+SAPC677zCKQe+CMkfXAA1EmpsmVhaLCFy83VCBnVwoq5tmEBjaiEMF/vmkFDwPDbP4G6XwLYGuqgevZ4sFz72hebbLwufxQY9p8CJjyC59UNexgqnhoJnLnTb1m4aTOUdZTPRYYdDiZnD6DbWpaFVQ6Ev8/EN7fyxlN6dXwi7wz6pYJZtMPyIHXvCafxVLYmfSDoRoyU3QzLwRqau3AwOh3Adle9iNNFsgPh75PpEy0iVeFwMGA31T8yRgRXWtHk5OKXPwmqKHE79pYmsHxzRb5Yjksvb7w6y8HodAB2D3SA/FK/fhWwXV0iRiayD6R+8hmEf3esCC63ohmYA4aDp0EVI16N2Y67UD1vMrBtmEZUUOyYtXKw8Q4oH6jNwoxOngMo52k+/zeomTsR2M4OERujD4eU3cchYuw4EdzfSlhaJhgOnXYOLwcf22WG6rmToKvkggMk+0k4GF0+UG+gjLwDWBs3XbYUAUPnv85C9axnwH6nXQBF4TodpOwshshxk0VwXxV1sgGNPwPqRPGIZC0WqJk/FcwXzvkS4RVPo1u7jZ1GiXgHIKDAK4cfSPPF81A942mwu3RLEhYGybhs9Zn4nB9SMH2c0J//8mGp6SJ6rrsbbi2eAZ3nPhfBFVc4jncAKcuKi2Ktd1uDFfNrhwzj12p133iRbhwuMXWrF0Hb4b0iuLCiiusHhiOfgxaXVmHh7HaoXToL7pz8nRAc6LstIiUhigFbJx5YBC/JQWfmqmlPgK2uVqQgYRhI3LgdYua431nTIIoGORLjMYS7/YufBNt4qpu6q7ZpBINhr/zFVGSatGItvw6mgh9Ad41JhMRNCSS+sQViF74sgvesGidBN3S4CE4rdWt/Cu3HDkjgwQCwHDuSweUvPxjCXGV0V37LO8GKT9eSsLYI+i5by4MJXS32fgp6N0FNXeEKaDu425U9aHW0PY8Y07R/x5fvBU2qiyBVQhI/qWmzBrtgAJp2vgu63IchYsz3JbiGDa9B82/elsCDCcDzyT8TzPrQcOqhYAp2laXCCZEGNNrB/jXTuGU9NL33pquYUNQvMuiF2FBIFsq0NzWACSfGrislQrDb9+Yd794v43HmJ7F0Dgi5A6ilNGyt+vFTQOMFT6Vl33ZoeGuNJ3TQ4dR27AHBWwJ9achipGg13fBIxscOatEO3SNtsBDYA4iyHYUCDRJw6xz97PMeOfs8MwVSdhwGohHlLDzSB4rAIdCCPYDezAh9iX91A8TOfslnQ5FPjMdN1DEgWp1P2kAJcAi04l4g9D2g34p1ELdguUTfu389De2fScPbCEyvpe77A5B72R8JY5AAfA/AJEhNkOS5FRO3eJUz6BESdPzjC7i1cBrG+DOh7egnQhT/Hj7qcT6xwrgkQiSEAQG4GpwDwPfapLCR2HlLIf4V6Xre+eU/cVs7BTjc3uIFC7i9Yj60HtglaUWf9xjGD2eAcUmISAgVArAHlDBAmIsK+b2yRc+YDwnrNktozF9dhJoXJkqSmXVrl0Dz7l9J6GnyMw13iCpMuga74H2ji4yW6EpwKcSOELwSNWUmJL4lPYfowmwxTZywd++4bazhjZXQ9MFGCU6LecG04r9IEiQSQhkAtNke3i/2MpNZ0Yp39OCaDF6vpH3GT4X+b+/Em23YhKBYSq9B9fPjMCCizXkujZteh4bNhRICzYBsMBz9AtQuiRIJoZ8A/OKXky/d6uQzQshT7CefV7LIJ38ESVs/BqJSieisFeVQhcbbmxtFcE+V5veLoH79KxK0BvOEaeiEsIwsCU4BgLe5xwEMHFYgQMRCT4aStx0E4hLJdVdXQtX0p8Bef1tE76vSsmsL1L22jJ7riUjD8ESIDgd6IBNQIcwRyu/sp8Z0zWVsa4QSoXpcslI/xoMLnV7Ebqu7BaapY6HbdFMEl1OJKpgN/TftwLna0Vl7uG3Ym2jmyVr2jRxxPC2Ozgt4tW4UrTil4pjdI1sSMtAzutSPjkuNb6zHzc/TARlP9Wkv3ge1P5sDnE18MVSN+cP41euVqEznJ6etTgdE9u27CxH1ciXqHx0DTESkiM3e2gxVmCG2fmsUwZVW7pw4wmeEOatVJMK1LkJ6qODaX0PUhr0OtNMBdEbE8SA7BWP+9zmwt7c55PHvVTN/CFbjVScsGC93z3wKNQsK8BSq5/I4HV4NRT1pNVnyCWwYVF6OEVhPcc4BtHp7eGJEW1vrTZx4xDnte8SeHtrcEZDwOgY9KjXU/3IFWPxIfHiS5QuuTkkD3fB8oIcxso/GCKnVkqQBmRUVzrM8kQNo42Xpunl2jg1dJtKXhSHEM4xqRnaF+ZCwCYkDKBIvGp7CXjBOSNjb33F++z1emZvsaodzDhAimDBmAS4V/x3YQmTvfG8KY9RukxFuHUD/mUFAtbh32irWGrs4h1mfhQNudtSJMT01tw6gqOxK80FkLuwh672/GEeuya60SrMu90xyOwcIzTWmafagkBeEsN7zTrYPNlkWedPXYw9wMGXHD8P5gJxy1HvLE3U+nvPYhCW+9PXpAPqfnOzM0RNx1+AMH30J/d/jyfbsRycUkOJiuy9dfA4BoQBjhq4Qz/nXCWEP0jud8HB382pOhbXIX71kOYAK5W+Sgn0b7hyj/W3kPtE1YzsvDTZZZeU2ZDuAGmPM1qeAhd314ARL5ISWUS/MrOiQl3RAWxQ5gDqBFho24/8JNuIBQ78eyP39xYmuDv/AtTLH1CXNq/upis9J0JucQZVdH0XHxGQwhKzklfFGHEwcbmqAIcvDk+MzAzGeqhRQDxDaRC9ad9Q2LASWv3D5kBAXrHcMz79ClT/UkKQPhTu6QOQHzQFCJfBa+hCcJKdh6mUazhNDhTgF71fwMxWHqcjhgTcspQr4vbKExAHCFqtyo+LMnZZ81o73cQDy8M9sBszK4F/nAf9Cz99NQF+RFsThVT2OPk30xAavtpfoQFuSbnK5eCgUHoT3/wBN3qXN5DteXQAAAABJRU5ErkJggg==',
      triangle: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAoCAYAAAD+MdrbAAAAAXNSR0IArs4c6QAAAvpJREFUSA2tl7uLE1EUxmcmk6cJ2WTzQDBBBGEVbBR8oY3IglraiCCWVlaipa1iLVYW7oKVIuL6YC0s7f0T8jQmhAghD/L0+wZnPJPE3TvJXpi9Z07u+c059373zqyuHVCbTqd6qVR6diA4wMxisbhVKBSm+qrEarUaGQ6Hb8C5TtZKQJSYnEwmH8G5QBjb0sByuXxkPB7vgnHSIv39Y8gbVRtlnkBm3zHeBQsEAt4zRGbnAfuEhUjKBEKhkJZOp70BMWfXAHqLKyJhkUhES6VSlku5ZGR2B6APs7BYLObASFQCQmMPUOYWYKbMLB6Pa4lEQrr2LhkAHbCniHgko3Rdt0DRaFS6Ldv1RPkrs8GcvYTvrvQTxvkKh8PS7dgLdQhQGCVS/TeckTAMw7BWMhgMSrfLngNiP3JSqP6LcqTP59MymYzm9/ule872SQ/Vj/tvuM5Iv2maWjab3RfGGCdDqH9jNBrtYu7yEkb1MzOWq9IsIObsHEBU/7oMctSPhVBtOubsGFbuB2AuDUj1q8I4zgTsySyM6p8VrCrUBOyyHEz181q2caZdsw39Lcuy4gj7KgntdltrNpvS5ck2sJIPEfFTRnU6Ha1er2uYDulWsg0I9he0dhWLU5YR/X7fgnqdAkdglUolB2Gz/A0J5lajsLn1VJoD5GBsvXVk9BmlnpXB3Ho83vfbx4xxAemo1WqHBoPBO0A3eW83bj1myq24V5sDcjBgfmzHbfS3ZDChPAu5Jf/XXBq0B2GBhrlc7jb657aPPReo0Who3W5Xul32QiBHADbN5/P30T+WEcja0in1uqgtLHl2IA6QewC/AMyVwKJtqgTkAzCnN1Hya5iu858vqmTy3ztfGUgoZHUF3zPvYcZ4bzd51HkCEoCT/TQ+377AzNhA9s5hLJ2qNso/jvK5q47KmKU+lmwA5HO41+vxHXTK9rH3XLIMbrVaa5DPDqCXbL9LBrZTtcdr4jfktIlrRzVGaRwy5Ef7K+h19Y92+4mAWv9W/AHAizQaNQfgsAAAAABJRU5ErkJggg=='
    };
  });
</script>

二、input-select(自定义输入框和下拉框,可预览)
使用场景:有个select标签,选择其中的一项,向后台发送请求,然后根据后台返回的数据,渲染input或select标签,如果是select,还可以继续选择。。。
<!DOCTYPE html>
<html  ng-app="myModel">

<head>
  <meta charset="UTF-8">
  <title>input和select</title>
  <script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.6.2/angular.js"></script>
</head>

<body>
  <div ng-controller="thisCtrl">
    <div>
      <p>=====可预览=====</p>
      <p>1、向后台发送请求,获取返回数据,填充$scope.formDatas</p>
      <p>2、向后台发送请求,获取返回数据,填充$scope.formDatas</p>
    </div>
    <input-select ng-if="formDatas.length>0" form-datas="formDatas">
    </input-select>
  </div>
</body>

</html>
<script>
  var app = angular.module('myModel', []);
  app.controller('thisCtrl', function ($scope/*,cyRequest*/) {
    //此处,向后台发送请求,获取返回数据,填充$scope.formDatas
    $scope.formDatas = [
      { 
        type: 'input',
        inputLabel: '输入框',
        inputValue: '',
      },{
        type: 'select',
        selectLabel: '选择框标签',
        selectValue: '后台1',
        selectOptions: [
          { back: '后台0', front: '前端0' },
          { back: '后台1', front: '前端1' },
          { back: '后台2', front: '前端2' },
          { back: '后台3', front: '前端3' },
          { back: '后台4', front: '前端4' },
          { back: '后台5', front: '前端5' },
        ],
        selectChainThis: [],//默认值。
        selectChainNext: null,
        children: [],//子级。
      },{
        type: 'select',
        selectLabel: '选择框标签2222',
        selectValue: '后台1',
        selectOptions: [
          { back: '后台0', front: '前端0' },
          { back: '后台1', front: '前端1' },
          { back: '后台2', front: '前端2' },
          { back: '后台3', front: '前端3' },
          { back: '后台4', front: '前端4' },
          { back: '后台5', front: '前端5' },
        ],
        selectChainThis: [],//默认值。
        selectChainNext: null,
        children: [],//子级。
      }
    ];
  });
  app.directive('inputSelect', function () {
    var html = `
        <div >
          <div ng-repeat="formData in formDatas track by $index">
            <div ng-if="formData.type==='input'" >
              <label ng-bind="formData.inputLabel+':'" ></label> 
              <input ng-model="formData.inputValue" type="text" />   
            </div>
            <div ng-if="formData.type==='select'" >
              <label ng-bind="formData.selectLabel+':'" ></label>
              <select  
              ng-model="formData.selectValue" 
              ng-options="option.back as option.front for option in formData.selectOptions" 
              ng-change="clickOption(formData)"
              
              ></select>
              <input-select ng-if="formData.children.length>0" form-datas="formData.children"></input-select> 
            </div>
          </div>
        </div>
      `;
    return {
      restrict: 'E',
      template: html,
      scope: {
        formDatas: '=formDatas'
      },
      replace: true,
      controller: function ($scope) {//此处注入“公共请求服务”
        $scope.clickOption = function (formData) {
          var objOuter={};
          objOuter[formData.selectLabel] = formData.selectValue;
          formData.selectChainNext = angular.copy(formData.selectChainThis);
          formData.selectChainNext.push(objOuter);
          //此处,把formData.selectChainNext作为“公共请求服务”的参数,向后台发送请求,获取返回数据result并加工,然后填充formData.children
          var result=[
            {
              type:'input',
              label:'输入框'
            },{
              type:'select',
              label:'选择框标签'
            },{
              type:'select',
              label:'选择框标签2'
            }
          ];
          angular.forEach(result,function(value,index){
            var objInner = { };
            if(value.type==='input'){
              objInner.type = value.type;
              objInner.inputLabel = value.label;
              objInner.inputValue = '';
            }else if(value.type==='select'){
              objInner.type = value.type;
              objInner.selectLabel = value.label;
              objInner.selectValue = '后台1';
              objInner.selectOptions = [
                { back: '后台0', front: '前端0' },
                { back: '后台1', front: '前端1' },
                { back: '后台2', front: '前端2' },
                { back: '后台3', front: '前端3' },
                { back: '后台4', front: '前端4' },
                { back: '后台5', front: '前端5' },
              ];
              objInner.selectChainThis = angular.copy(formData.selectChainNext);
              objInner.selectChainNext = null;
              objInner.children = [];
              if(index===1){
                console.log(objInner.selectChainThis)
              }
            }
            formData.children.push(objInner)
          })
        };
      },
      link: function (scope, ele, attrs) {

      }
    };
  })
</script>

三、single-select和both-select
<!DOCTYPE html>
<html  ng-app="myModel">
<head>
  <meta charset="UTF-8">
  <title>onlySelect</title>
  <script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.6.2/angular.js"></script>
</head>
<body>
  <div ng-controller="thisCtrl">
    <div>一、此消彼长式</div>
    <div>使用场景:</div>
    <div>有上下两行数据,上行有很多备选项</div>
    <div>每点击上行的某项,则该项“会”消失并在下行出现</div>
    <div>每点击下行的某项,则该项“会”消失并在上行出现</div>
    <div >
      <span ng-click="getDataUp()" >点击获取选中数据</span>
      <span ng-bind="selectedDatasUp"></span>
    </div>
    <div >
      <single-select form-datas-up="formDatasUp" current-datas-up="currentDatasUp" two-label-up="twoLabelUp"></single-select>
    </div> 
    <div ></div>
    <div>二、此静彼长式</div>
    <div>使用场景:</div>
    <div>有上下两行数据,上行有很多备选项</div>
    <div>每点击上行的某项,则该项“不”消失并在下行出现,重复点击无效</div>
    <div>每点击下行的某项,则该项“会”消失,但对上行无影响</div>
    <div >
      <span ng-click="getDataDown()" >点击获取选中数据</span>
      <span ng-bind="selectedDatasDown"></span>
    </div>
    <div >
      <both-select form-datas-down="formDatasDown" current-datas-down="currentDatasDown" two-label-down="twoLabelDown"></both-select>
    </div> 
  </div>
</body>
</html>
<script>
  var app = angular.module('myModel', []);
  app.controller('thisCtrl', function ($scope) {
    $scope.formDatasUp = [
      {
        value: '0',
        label: '选项一'
      },
      {
        value: '1',
        label: '选项二'
      },
      {
        value: '2',
        label: '选项三'
      },
      {
        value: '3',
        label: '选项四'
      },
      {
        value: '4',
        label: '选项五'
      }
    ];
    $scope.currentDatasUp = [];
    $scope.twoLabelUp = ['上面标签:','下面标签:'];
    $scope.getDataUp = function(){
      $scope.selectedDatasUp=angular.copy($scope.currentDatasUp);
    };
    ////////////////////////////////////////////////////////////////////////////////////////
    $scope.formDatasDown = [
      {
        value: '0',
        label: '选项一'
      },
      {
        value: '1',
        label: '选项二'
      },
      {
        value: '2',
        label: '选项三'
      },
      {
        value: '3',
        label: '选项四'
      },
      {
        value: '4',
        label: '选项五'
      }
    ];
    $scope.currentDatasDown = [];
    $scope.twoLabelDown = ['备选项:','被选项:'];
    $scope.getDataDown = function(){
      $scope.selectedDatasDown=angular.copy($scope.currentDatasDown);
    };
  });
  app.directive('singleSelect', function () {
    var html = `
        <div>
          
          <div >
            <span ng-bind="twoLabelUp[0]"></span>
            <span ng-repeat="upData in upDatas track by $index" ng-bind="upData.label" ng-click="clickUpUp(upData.value)" ></span>
          </div>
          <div >
            <span ng-bind="twoLabelUp[1]"></span>
            <span ng-repeat="downData in downDatas track by $index" ng-bind="downData.label" ng-click="clickUPDown(downData.value)" ></span>
          </div>
        </div>
      `;
    return {
      restrict: 'E',
      template: html,
      scope: {
        formDatasUp: '=formDatasUp',
        currentDatasUp: '=currentDatasUp',
        twoLabelUp: '=twoLabelUp',
      },
      replace: true,
      controller: function ($scope) {
        $scope.upDatas = angular.copy($scope.formDatasUp);
        $scope.downDatas = [];
       
        $scope.clickUpUp = function (valueOuter) {
          angular.forEach($scope.upDatas,function(value,index){
            if(valueOuter===value.value){
              $scope.upDatas.splice(index,1);
              $scope.downDatas.push(value);
              $scope.currentDatasUp.push(value.value);
              console.log($scope.currentDatasUp)
            }
          })
        };
        $scope.clickUPDown = function (valueOuter) {
          angular.forEach($scope.downDatas,function(value,index){
            if(valueOuter===value.value){
              $scope.upDatas.push(value);
              $scope.downDatas.splice(index,1);
              var thisIndex=$scope.currentDatasUp.indexOf(value.value)
              if(thisIndex>-1){
                $scope.currentDatasUp.splice(thisIndex,1);
              }
              console.log($scope.currentDatasUp)
            }
          })
        };
      },
      link: function (scope, ele, attrs) {

      }
    };
  });
  app.directive('bothSelect', function () {
    var html = `
        <div>
          
          <div >
            <span ng-bind="twoLabelDown[0]"></span>
            <span ng-repeat="upData in upDatasDown track by $index" ng-bind="upData.label" ng-click="clickDownUp(upData)" ></span>
          </div>
          <div >
            <span ng-bind="twoLabelDown[1]"></span>
            <span ng-repeat="downData in downDatasDown track by $index" ng-bind="downData.label" ng-click="clickDownDown(downData,$index)" ></span>
          </div>
        </div>
      `;
    return {
      restrict: 'E',
      template: html,
      scope: {
        formDatasDown: '=formDatasDown',
        currentDatasDown: '=currentDatasDown',
        twoLabelDown: '=twoLabelDown',
      },
      replace: true,
      controller: function ($scope) {
        $scope.upDatasDown = angular.copy($scope.formDatasDown);
        $scope.downDatasDown = [];
        $scope.clickDownUp = function (data) {
          var thisIndex=$scope.currentDatasDown.indexOf(data.value)
          if(thisIndex>-1){
            return
          }else{
            $scope.downDatasDown.push(data);
            $scope.currentDatasDown.push(data.value);
          }
        };
        $scope.clickDownDown = function (data,index) {
          $scope.downDatasDown.splice(index,1);
          var thisIndex=$scope.currentDatasDown.indexOf(data.value)
          if(thisIndex>-1){
            $scope.currentDatasDown.splice(thisIndex,1);
          }
        };
      },
      link: function (scope, ele, attrs) {

      }
    };
  })
</script>

四、on-off组件(真实逻辑,点击后,转圈、向后台发送请求、返回数据、给开关赋值、开关展示状态。点击后,立即改成相反状态,是不对的。)
<html  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>
  <style>
    table{
      border-collapse: collapse;
      width:1000px;
    }
    table td,table th {
      padding: 5px;
      border: 1px solid #cbcbcb;
    }
    table thead {
      background-color: #e0e0e0;
      color: #000;
      text-align: left;
    }
  </style>
</head>
<body>
  <div ng-controller="thisCtrl">
    <on-off this-switch="parentSwitch">
      <span>{{parentSwitch.state?"总开关已开启":"总开关已关闭"}}。总开关关闭时,不能操作分开关!</span>
    </on-off>
    <table>
      <thead>
        <tr>
          <th>序号</th>
          <th>数据1</th>
          <th>数据2</th>
          <th>数据3</th>
          <th>开关1</th>
          <th>开关2</th>
        </tr>
      </thead>
      <tbody>
        <tr ng-repeat="item in tableDatas track by $index">
          <td ng-bind="$index+1"></td>
          <td ng-bind="item.value1"></td>
          <td ng-bind="item.value2"></td>
          <td ng-bind="item.value3"></td>
          <td>
            <on-off parent-switch="parentSwitch" this-switch="item.value4"></on-off>
          </td>
          <td>
            <on-off parent-switch="parentSwitch" this-switch="item.value5"></on-off>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</body>
</html>
<script>
var app = angular.module('myModel', []);
app.controller('thisCtrl', function ($scope) {
  //以下通过向后台请求,获取总开关的数据fromServerParent,经加工后为$scope.parentSwitch
  var fromServerParent={
    isOn:true
  };
  $scope.parentSwitch={
    state:fromServerParent.isOn,
    callback:function(){
      this.state=!this.state; 
    }
  };
  //以下通过向后台请求,获取分开关的数据fromServerData,经加工后为$scope.tableDatas
  var fromServerData=[
    {value1:"数据1",value2:"数据2",value3:"数据3",isOn:true,isSwitch:true},
    {value1:"数据1",value2:"数据2",value3:"数据3",isOn:true,isSwitch:true},
    {value1:"数据1",value2:"数据2",value3:"数据3",isOn:true,isSwitch:true},
    {value1:"数据1",value2:"数据2",value3:"数据3",isOn:true,isSwitch:true},
    {value1:"数据1",value2:"数据2",value3:"数据3",isOn:true,isSwitch:true},
    {value1:"数据1",value2:"数据2",value3:"数据3",isOn:true,isSwitch:true},
    {value1:"数据1",value2:"数据2",value3:"数据3",isOn:true,isSwitch:true}
  ];
  var tableDatas=[];
  angular.forEach(fromServerData,function(value,key){
    var item={};
    item.value1=value.value1;
    item.value2=value.value2;
    item.value3=value.value3;
    item.value4={
      state:value.isOn,
      callback:function(){
       this.state=!this.state; 
      }
    };
    item.value5={
      state:value.isSwitch,
      callback:function(){
       this.state=!this.state; 
      }
    };
    tableDatas.push(item)
  })
  $scope.tableDatas=tableDatas;
});
app.directive('onOff', function () {//(含插槽)
  var html = `
  <div >
    <img ng-src="{{thisSwitch.state?onOff.yes:onOff.no}}" width="65px" height="30px" ng-click="clickSwitch()"/>
    <span ng-transclude ></span>
  </div>
  `;
  return {
    restrict: 'E',
    template: html,
    scope: {
      thisSwitch: '=thisSwitch',
      parentSwitch: '=parentSwitch',
    },
    transclude:true,
    replace: false,
    controller: function ($scope,onOff) {
      $scope.onOff = onOff;
      $scope.clickSwitch = function(){
        //以下总开关关闭时,不能操作分开关!
        if($scope.parentSwitch && !$scope.parentSwitch.state){
          return '弹窗:父级开关阻止本级开关改变'
        }
        if($scope.thisSwitch.isForbid ){
          return '弹窗:本级数据的其它条件阻止本级开关改变'
        }
        $scope.thisSwitch.callback()
      };
    },
    link: function (scope, ele, attr) {
      
    }
  };
});
app.factory('onOff', function () {
  return {
    yes: 'data:image/png;base64,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',
    no: 'data:image/png;base64,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',
  };
})
</script>
 
五、simple-dialog
1、不可拖拽(含插槽)
<!DOCTYPE html>
<html  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 .conform {
      display: flex;
      justify-content: center;
      padding: 10px;
      background: blue;
    }
  </style>
</head>
<body>
  <div ng-controller="thisCtrl">
    <button ng-click="clickButton()" >
      点击-出现-弹窗
    </button>
    <simple-dialog required-data="requiredDataOut">
      1111111
      <simple-dialog required-data="requiredDataMid">
        2222222
        <simple-dialog required-data="requiredDataIn">
          33333333
        </simple-dialog>
      </simple-dialog>
    </simple-dialog>
  </div>
</body>
<script>
  var app = angular.module('myModel', []);
  app.controller('thisCtrl', function ($scope) {
    $scope.clickButton = function () {
      $scope.requiredDataOut.isShow = true;
    };
    $scope.requiredDataOut = {
      isShow : false,
      width : '900px',
      height : '600px',
      openFn : function () {
        $scope.requiredDataMid.isShow = true;
      }
    };
    $scope.requiredDataMid = {
      isShow : false,
      width : '600px',
      height : '400px',
      openFn : function () {
        $scope.requiredDataIn.isShow = true;
      },
    };
    $scope.requiredDataIn = {
      isShow : false,
      width : '300px',
      height : '200px',
    };
    // 一层弹窗可以如下使用
    // $scope.requiredDataOut = {
    //   isShow : false,
    // };
    // $scope.clickButton = function () {
    //   $scope.requiredDataOut.isShow = true;
    // };
  });
  app.directive('simpleDialog', function () {
    var html = `
      <div class="simpleDialog" ng-show="requiredData.isShow">
        <div class="mask" ng-show="requiredData.isShow"></div>
        <div class="content" ng-show="requiredData.isShow">
          <div class="title">
            <span>系统消息</span>
          </div>
          <div ng-transclude ng-|'800px',height:requiredData.height||'400px'}"></div>
          <div class="conform">
            <button ng-click="close()">关闭</button>
            <button ng-click="open()">打开</button>
          </div>
        <div>
      </div>
    `;
    return {
      restrict: 'E',
      template: html,
      transclude: true,
      scope: {
        requiredData: '='
      },
      controller: function ($scope) {
        $scope.close = function () {
          $scope.requiredData.isShow = false;
          if($scope.requiredData.closeFn)$scope.requiredData.closeFn();
        };
        $scope.open = function () {
          if($scope.requiredData.openFn)$scope.requiredData.openFn();
        };
      }
    };
  });
  </script>
</html>
2、可拖拽(含插槽)
<!DOCTYPE html>
<html  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 .conform {
      display: flex;
      justify-content: center;
      padding: 10px;
      background: blue;
    }
  </style>
</head>
<body>
  <div ng-controller="thisCtrl">
    <button ng-click="clickButton()" >
      点击-出现-弹窗
    </button>
    <simple-dialog required-data="requiredDataOut">
      1111111
      <simple-dialog required-data="requiredDataMid">
        2222222
        <simple-dialog required-data="requiredDataIn">
        </simple-dialog>
      </simple-dialog>
    </simple-dialog>
  </div>
</body>
<script>
  var app = angular.module('myModel', []);
  app.controller('thisCtrl', function ($scope, drag) {
    $scope.clickButton = function () {
      $scope.requiredDataOut.isShow = true;
      drag($scope.requiredDataOut.titleId,$scope.requiredDataOut.contentId);
    };
    $scope.requiredDataOut = {
      isShow : false,
      width : '900px',
      height : '600px',
      titleId : "titleId1",
      contentId : "contentId1",
      openFn : function () {
        $scope.requiredDataMid.isShow = true;
        drag($scope.requiredDataMid.titleId,$scope.requiredDataMid.contentId);
      }
    };
    $scope.requiredDataMid = {
      isShow : false,
      width : '600px',
      height : '400px',
      titleId : "titleId2",
      contentId : "contentId2",
      openFn : function () {
        $scope.requiredDataIn.isShow = true;
        drag($scope.requiredDataIn.titleId,$scope.requiredDataIn.contentId);
      },
    };
    $scope.requiredDataIn = {
      isShow : false,
      width : '300px',
      height : '200px',
      titleId : "titleId3",
      contentId : "contentId3",
    };
    // 一层弹窗可以如下使用
    // $scope.requiredDataOut = {
    //   isShow : false,
    // };
    // $scope.clickButton = function () {
    //   $scope.requiredDataOut.isShow = true,
    //   drag();
    // };
  });
  app.directive('simpleDialog', function () {
    var html = `
      <div class="simpleDialog" ng-show="requiredData.isShow">
        <div class="mask" ng-show="requiredData.isShow"></div>
        <div class="content" ng-show="requiredData.isShow" |'contentId'}}">
          <div class="title" |'titleId'}}">
            <span>系统消息</span>
          </div>
          <div ng-transclude ng-|'800px',height:requiredData.height||'400px'}">后备内容</div>
          <div class="conform">
            <button ng-click="close()">关闭</button>
            <button ng-click="open()">打开</button>
          </div>
        <div>
      </div>
    `;
    return {
      restrict: 'E',
      template: html,
      transclude: true,
      scope: {
        requiredData: '='
      },
      controller: function ($scope) {
        $scope.close = function () {
          $scope.requiredData.isShow = false;
          angular
            .element(
              document.getElementById($scope.requiredData.contentId||'contentId')
            )
            .css({
              top: '50%',
              left: '50%',
              transform: 'translate(-50%, -50%)'
            });
          if($scope.requiredData.closeFn)$scope.requiredData.closeFn();
        };
        $scope.open = function () {
          if($scope.requiredData.openFn)$scope.requiredData.openFn();
        };
      }
    };
  });
  app.factory('drag', function () {
    return function (wholeTitleId, wholeContentId) {
      var wholeTitleId = wholeTitleId||'titleId';
      var wholeContentId = wholeContentId||'contentId';
      var oDiv = document.getElementById(wholeContentId);
      oDiv.onmousedown = down;
      function processThis(fn, nowThis) {
        return function (event) {
          fn.call(nowThis, event);
        };
      }
      function down(event) {
        event = event || window.event;
        if (event.target.id != wholeTitleId) return;
        this.initOffsetLeft = this.offsetLeft;
        this.initOffsetTop = this.offsetTop;
        this.initClientX = event.clientX;
        this.initClientY = event.clientY;
        this.maxOffsetWidth =
          (document.documentElement.clientWidth || document.body.clientWidth) -
          this.offsetWidth;
        this.maxOffsetHeight =
          (document.documentElement.clientHeight ||
            document.body.clientHeight) - this.offsetHeight;
        if (this.setCapture) {
          this.setCapture();
          this.onmousemove = processThis(move, this);
          this.onmouseup = processThis(up, this);
        } else {
          document.onmousemove = processThis(move, this);
          document.onmouseup = processThis(up, this);
        }
      }
      function move(event) {
        var nowLeft = this.initOffsetLeft + (event.clientX - this.initClientX);
        var nowTop = this.initOffsetTop + (event.clientY - this.initClientY);
        this.style.left = nowLeft + 'px';
        this.style.top = nowTop + 'px';
      }
      function up() {
        if (this.releaseCapture) {
          this.releaseCapture();
          this.onmousemove = null;
          this.onmouseup = null;
        } else {
          document.onmousemove = null;
          document.onmouseup = null;
        }
      }
    };
  });
  </script>
</html>

六、input-datalist组件
<!DOCTYPE html>
<html  ng-app="myModel">
<head>
  <meta charset="UTF-8">
  <title>inputDatalist</title>
  <script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.6.2/angular.js"></script>
</head>
<body>
  <div ng-controller="thisCtrl">
    <button ng-click="getAllSelectedOptions()">点击获取被选中项</button>
    <input-datalist all-options="allOptions" yes-options="yesOptions" get-values="getValues" max-num="10"></input-datalist>
  </div>
</body>
</html>
<script>
  var app = angular.module('myModel', []);
  app.controller('thisCtrl', function ($scope) {
    $scope.allOptions = [
      {
        server: '0',
        client: '选1项一'
      },
      {
        server: '1',
        client: '选项二'
      },
      {
        server: '2',
        client: '选1项'
      },
      {
        server: '3',
        client: '选项四'
      },
      {
        server: '4',
        client: '选项五'
      },
      {
        server: '41',
        client: '选项五1'
      },
      {
        server: '411',
        client: '选11项五11'
      },
      {
        server: '4111',
        client: '选111项五'
      },
      {
        server: '41111',
        client: '选1111项五'
      },
      {
        server: '411111',
        client: '选11111项五'
      },
      {
        server: '4111111',
        client: '选111111项五'
      },
      {
        server: '42',
        client: '选项五2'
      },
      {
        server: '422',
        client: '选项五22'
      }
    ];
    $scope.yesOptions = [
      '选1项一',
      '选项二',
      '选1项',
      '选项四',
      '选项五',
      '选项五1',
      '选11项五11',
      '选111项五',
      '选1111项五',
      '选11111项五',
      '选111111项五',
      '选项五2',
      '选项五22'
    ];
    $scope.getValues = {};
    $scope.getAllSelectedOptions = function(){
      console.log($scope.getValues.giveValues())
    };
  });
  app.directive('inputDatalist', function () {
    var html = `
        <div
          style="
            display: flex;
            flex-direction: column;
            flex-wrap: wrap;
            align-items: top;
            border: 1px solid #ced4da;
            border-radius: 4px;
            cursor: pointer;
            width: 800px;
            user-select: none;
          "
          tabindex="-1"
          ng-blur="getBlurOut()"
        >
          <!-- 以下是select -->
          <div  class="isShowOptions" ng-click="getOptions($event)">
            <div
              style="
                flex: 1;
                min-height: 36px;
                padding: 5px 0 0 5px;
                display: flex;
                justify-content: flex-start;
                flex-wrap: wrap;
              "
            >
              <div
                ng-repeat="client in yesOptions track by $index"
                style="
                  border: 1px solid #dddddd;
                  padding: 5px 10px;
                  background: #fafafa;
                  margin-right: 5px;
                  margin-bottom: 5px;
                  border-radius: 4px;
                "
                ng-show="$index<maxNum"
              >
                <span ng-bind="client" ></span>
                <img
                  src="{{checkImg.fork}}"
                  ng-click="clickFork(yesOptions,client)" class="fork"
                  
                />
              </div>
              <div 
                style="
                  border: 1px solid #dddddd;
                  padding: 5px 10px;
                  background: #fafafa;
                  margin-right: 5px;
                  margin-bottom: 5px;
                  border-radius: 4px;
                "
                ng-show="yesOptions.length>maxNum"
              >
                <span >...(超过{{maxNum}}个不再显示)...</span>
              </div>
              <div
                style="
                  border: 1px solid #dddddd;
                  padding: 5px 10px;
                  background: #fafafa;
                  margin-right: 5px;
                  margin-bottom: 5px;
                  border-radius: 4px;
                "
              >
                <span >查看全部</span>
              </div>
            </div>
            <div
              style="
                height: 36px;
                width: 32px;
                border-radius: 4px;
                background-size: 12px 6px;
              "
            ></div>
          </div>
          <!-- 以下是option -->
          <div  ng-show="isShowOptions">
            <div
              
            >
              <div>
                <input
                  type="text"
                  class="form-control"
                  
                  ng-focus="getFocusInput()"
                  ng-blur="getBlurInput()"
                  ng-change="changeInputValue()"
                  ng-model="selectedOption"/> 
                <span  ng-show="isShowSelectAll">
                  <img
                    src="{{isSelectAll?checkImg.yes:checkImg.no}}"
                    ng-click="selectAll()"
                  />
                  <span ng-bind="textSelectAll"></span>
                </span>
              </div>
              
              <div >
                <div
                  ng-repeat="option in filterOptions track by $index"
                  style="
                    padding: 5px 10px;
                    background: #fafafa;
                    margin-right: 5px;
                    margin-bottom: 5px;
                    border-radius: 4px;
                  "
                  ng-click="clickOptions(option)"
                  ng-background':'#237eff','border-radius':'4px'}:{'background':'#ffffff','border-radius':'0'}"
                >
                  <span ng-bind="option.client" ></span>
                </div>
              </div>
            </div>
          </div>
        </div>
      `;
    return {
      restrict: 'E',
      template: html,
      scope: {
        yesOptions: '=yesOptions',//所有选项
        allOptions: '=allOptions',//所有选项
        getValues: '=getValues',//获取最终结果
        maxNum: '=maxNum',//最多显示项数
      },
      replace: true,
      controller: function ($scope,$timeout,checkImg) {
        $scope.checkImg = checkImg;
        $scope.isSelectAll = false;
        $scope.isShowSelectAll = true;
        $scope.isFocusInput = false;
        $scope.isShowOptions = false;
        $scope.isClickOptions = false;
        $scope.isClickSelectAll = false;
        $scope.filterOptions = angular.copy($scope.allOptions);
        $scope.textSelectAll = "全部选择";
        $scope.clickFork = function (yesOptions,client) {
          $scope.isClickOptions = false;
          $scope.isClickSelectAll = false;
          angular.forEach(yesOptions,function(option,index){
            if(client===option){
              yesOptions.splice(index,1);
              $scope.textSelectAll = "已选择"+$scope.yesOptions.length+"条";
            }
          })
        };
        $scope.selectAll = function () {
          $scope.isClickOptions = false;
          $scope.isClickSelectAll = true;
          $scope.yesOptions = [];
          $scope.isSelectAll = !$scope.isSelectAll;
          if($scope.isSelectAll){
            angular.forEach($scope.allOptions,function(option){
              $scope.yesOptions.push(option.client)
            })
            $scope.textSelectAll = "已选择"+$scope.yesOptions.length+"条";
          }else{
            $scope.textSelectAll = "全部选择";
          }
        };
        $scope.getOptions = function (event) {
          $scope.isClickOptions = false;
          $scope.isClickSelectAll = false;
          if(event.target.className === "fa fa-close")return;
          $scope.isShowOptions = !$scope.isShowOptions;
        };
        $scope.getBlurOut = function () {
          $scope.isClickOptions = false;
          $scope.isClickSelectAll = false;
          $timeout(function(){
            if(!$scope.isFocusInput){
              $scope.isShowOptions = false;
            }
          })
        };
        $scope.getFocusInput = function () {
          $scope.isClickOptions = false;
          $scope.isClickSelectAll = false;
          $scope.isFocusInput = true;
        };
        $scope.getBlurInput = function () {
          $scope.isFocusInput = false;
          $timeout(function(){
            if(!$scope.isClickOptions && !$scope.isClickSelectAll){
              $scope.isShowOptions = false;
            }
          },200)
        };
        $scope.clickOptions = function (option) {
          var flag = true;
          $scope.isClickOptions = true;
          $scope.isClickSelectAll = false;
          angular.forEach($scope.yesOptions,function(client){
            if(option.client===client){
              flag = false;
              $scope.yesOptions.splice($scope.yesOptions.indexOf(client),1);
            }
          });
          if(flag){
            $scope.yesOptions.push(option.client);
          }
          $scope.textSelectAll = "已选择"+$scope.yesOptions.length+"条";
        };
        $scope.changeInputValue = function () {
          if($scope.selectedOption){
            $scope.isShowSelectAll = false;
            $scope.filterOptions = [];
            angular.forEach($scope.allOptions,function(option){
              if(option.client.indexOf($scope.selectedOption) != -1){
                $scope.filterOptions.push(option);
              }
            }); 
          }else{
            $scope.isShowSelectAll = true;
            $scope.filterOptions = angular.copy($scope.allOptions);
          }
        };
        $scope.getValues.giveValues = function () {
          $scope.isClickOptions = false;
          var client = [];
          var server = [];
          var clientNo = [];
          var serverNo = [];
          var obj;
          angular.forEach($scope.allOptions,function(option){
            if($scope.yesOptions.indexOf(option.client) != -1){
              client.push(option.client)
              server.push(option.server)
            }else{
              clientNo.push(option.client)
              serverNo.push(option.server)
            }
          });
          if($scope.isSelectAll){
            obj = {
              is_all:1,
              client:clientNo,
              server:serverNo,
            }
          }else{
            obj = {
              is_all:0,
              client:client,
              server:server,
            }
          }
          return obj
        };
      },
      link: function (scope, ele, attrs) {

      }
    };
  });
  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=',
      fork:
        'data:image/png;base64,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'
    };
  });
</script>

七、angular1里用layui中的datetime示例
来源: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"  >,
        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)才会返回。对象成员同上。
  }
});

八、agular1实现京东购物车
<!DOCTYPE html>
<html >
<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"  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" >
    <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,'+')" >+</span></td>
        <td>{{goods.number}}</td>
        <td><span ng-click="changeNumber(goods,oneShop.shopGoods,oneShop,allShops,'-')" >-</span></td>
        <td>件,本商品共: {{goods.singleMoney}}元</td>
        <td ng-click="delete(innerIndex,outerIndex,oneShop,allShops)" >删除</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 >
    <div ng-controller="myCtrl">
      {{ hello }}
    </div>
  </div>
  <div >
    <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  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  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>