JavaScript之Ajax(二):form表单操作与模板引擎的定义

1、form表单

 表单主要负责数据采集功能,通过FORM把采集的信息提到服务器进行处理。
(1)基本使用
白经网认为,三部分组成:表单标签、表单域(表单域:文本,密码,隐藏,多行,筛选,单选等)、表单按钮。
form表单的属性是用来规定如何把信息提交到服务器:
action:
向何处发送表单数据,其属性是后端提供的一个URL地址;如果没有指定属性值,提交到当前页面;当提交页面表单后,页面会立即跳转到action属性值指定的URL。
target
可选值有5个,属性值_self默认在相同的框架下打开,_blank在新窗口打开,_parent在父窗口中打开,_top在整个窗口中打开。
method
以何种方式把表单数据提到到action url。默认是get,把数据提交到action url,URL中有提交的数据;post,数据不在URL中,相对安全。 name对应表单域的名子,填写之后,会在URL中显示。
get简单的,少量的数据;而大量的,上传的,涉及安全性的用post。
enctype
有3个参数,默认是application/x-www-form-urlencoded,表示在发送前编码所有字符;multipart/form-data不对字符进行编码,在使用包含文件上传控制的表单时必须使用该值;text/plain,空格转化为加号,但是不对字符编码,很少用。
表单的同步提交
通过提交按钮,页面会跳转到action指定的页面。缺点是,跳转一用户指定的页面,用户体验差;页面之前的状态和数据会丢失;如何解决这两个问题:
可以让表单只采集数据,让ajax提交数据。
(2)通过ajax提取表单数据
使用ajax操作元素之前,都要先引入jQuery。
①监听表单提交事件
方法1
    <script>
        $(function() {
            $('#form').submit(function() {
                alert('监听到了submit提交事件')
            })
        })
    </script>
方法2
    <script>
        $(function() {
            $('#form').on('submit',function() {
                alert('监听表单提交了哈哈')
            })
        })
    </script>
②阻止表单提交的默认行为
也就是不让表单提交后跳转到新的页面,不管哪个表单事件,都可以通过事件对象阻止表单的默认行为
    <script>
        $(function() {
            $('#form').on('submit',function(e) {
                alert('监听表单提交了哈哈')
                e.preventDefault();
            })
        })
③获取表单中的所有数据
常规的方法,通过DOM也可以获取表单中的数据,但是可以使用serialize()一次获取表单中的所有数据。使用serialize()函数时,必须要给每个元素添加name属性。
    <script>
        $(function() {
            $('#form').on('submit',function(e) {
                e.preventDefault();
            // var data = $(this).serialize();
            var data = $('#form').serialize();
            console.log(data);
            })
        })
    </script>
(3)案例:评论列表
引入bootstrap和jQuery之后,再组织代码结构、JS内容。
<body style="padding: 20px">
    <!-- 评论面板 -->
    <div class="panel panel-primary">
          <div class="panel-heading">
                <h3 class="panel-title">发表评论</h3>
          </div>
          <form class="panel-body" id="form">
                <div>评论人</div>
                <input type="text" class="form-control" name="username">
                <div>评论内容</div>
                <textarea class="form-control" name="content" id=""></textarea>
                <button class="btn btn-primary">发表评论</button>
          </form>
    </div>
    <!-- 评论列表 -->
    <ul class="list-group" id="cmtlist">
        <li class="list-group-item">
            <span class="badge" style="background-color: orange">评论时间:</span>
            <span class="badge" style="background-color:skyblue">评论人:</span>
            Item 1
        </li>
    </ul>
    <script>
        //1.获取评论列表
        function getComment() {
            $.ajax({
                method: 'get',
                url: 'http://www.liulongbin.top:3006/api/cmtlist',
                data: {},
                success: function(e) {
                    console.log(e);
                    if(e.status !== 200) return alert('获取数据失败')
                    // 1、定义一个空数据用来接收服务器响应的数据
                    var rows = [];
                    //2.循环从服务器上获取数据并存入空数组
                    $.each(e.data,function(i,item) {
                        var str = '<li class="list-group-item"><span class="badge" style="background-color: orange">评论时间:' + item.time + '</span><span class="badge" style="background-color:skyblue">评论人:' + item.username + '</span>' + item.content + '</li>';
                        rows.push(str);
                    })
                    //3.把获取的数组元素拼接后放入UL列表
                    $('#cmtlist').empty().append(rows.join(''));
                }
                
            })
        }
        getComment();
        //2.提交数据
        $(function() {
            // 2-1监听提交按钮
            $('#form').submit(function(e) {
                // 2-2阻止默认行为
                e.preventDefault();
                // 2-3获取评论人、评论内容数据
                var data = $(this).serialize();
                // 2-4提交数据
                $.post('http://www.liulongbin.top:3006/api/addcmt',data,function(e) {
                    if(e.status !== 201) {
                        return alert('提交数据失败');
                    }
                    // 2-5刷新评论列表
                    getComment();
                    //2-6清空输入框
                    $('#form')[0].reset();
                })
            })
        })
    </script>
</body>

2、模板引擎

 通过字符串拼接的形式渲染UI结构,如果UI结构比较复杂,需要特别注意UI之间标签的嵌套,修改也非常麻烦。模板引擎可以解决这样的问题。
(1)基本概念
根据程序员的模板结构和数据,自动生成一个完整的HTML结构。把数据和模板结构提交给模板引擎,会自动生成HTML结构。
减少了字符串的拼接过程;代码结构更加清楚;代码更易于阅读与后期的维护。
(2)art-tempplate模板引擎
简约、超快的模板引擎,查看官网首页。
①传统方式渲染UL结构与数据
<body>
    <div class="info"></div>
    <div>姓名:<span class="name"></span></div>
    <div>年龄:<span class="age"></span></div>
    <div>isVIP:<span class="isvip"></span></div>
    <div>注册时间:<span class="regTime"></span></div>
    <div>业余爱好:
        <ul id="hobby">
            <li>爱好1</li>
            <li>爱好1</li>
            <li>爱好1</li>
        </ul>
    </div>
    <script>
    var data = {
      title: '<h3>用户信息</h3>',
      name: 'zs',
      age: 20,
      isVIP: true,
      regTime: new Date(),
      hobby: ['吃饭', '睡觉', '打豆豆']
    } 
    $(function() {
        $('.info').html(data.title);
        $('.name').html(data.name);
        $('.age').html(data.age);
        $('.isvip').html(data.isVIP);
        $('.regTime').html(data.regTime);
        var rows = [];
        $.each(data.hobby,function(i,item) {
            var str = '<li>' + item + '</li>';
            rows.push(str);
        })
        $('#hobby').html(rows.join(''));
 
    })
    </script>
</body>
②模板引擎的使用步骤
导入art-tempplate
定义数据:定义一个数据对象
定义模板:模板的HTML结构必须定义到script标签中,type="text/html"
调用template函数:两个参数分别为模板的ID,渲染的数据对象
渲染HTML结构
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./bootstrap-3.4.1-dist/css/bootstrap.min.css">
    <script src="./jquery-3.6.0/jquery-3.6.0.min.js"></script>
    <!-- 1.导入模板库 -->
    <script src="./jquery-3.6.0/template-web.js"></script>
</head>
<body>
    <div id="container"></div>
    <!-- 3.定义模板,必须指定type和id -->
    <script type="text/html" id="tpl-user">
        <h1>{{ name }}--{{ age }}岁了</h1>//两个花括号内放数据对象的属性名,即可调用数据
    </script>
    <script>
    //2.定义数据
    var data = {
        name: '白龙网',
        age: 18
    }
    //4.调用模板函数,需要指定模板ID与数据对象
    var str = template('tpl-user',data);
    //5.使用DOM把数据渲染到HTML
    $('#container').html(str)
    </script>
</body>
</html>
③标准语法
又花括号可以进行变量输出,循环输出。可以是变量{{value}}、对象属性{{obj.key}}、{{obj['key']}}、三元表达式{{a ? b : c}}、逻辑运算{{a || b}}、算术运算{{a + b}}等。
过滤器,需要处理的值,以参数的形式提交给过滤器函数,通过返回值,输出新值,过滤器函数本质上是一个函数。{{ value | filterName }}。注册过滤器函数的语法:
template.defaults.inports.filterName = function(value) {return处理结果}。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./bootstrap-3.4.1-dist/css/bootstrap.min.css">
    <script src="./jquery-3.6.0/jquery-3.6.0.min.js"></script>
    <script src="./jquery-3.6.0/template-web.js"></script>
</head>
<body>
    <div id="container"></div>
    <script type="text/html" id="tpl-user">
        <!-- 1、原文输出 -->
        {{@ Text }}
        <!-- 2、条件输出 -->
        {{if flag === 0}}
        输出flag的值是0
        {{else if flag === 1}}
        输出flag的值是1
        {{/if}}
        <!-- 3、循环输出 -->
        以each开始并添加数组名,以each结束,$index表示索引号,$value表示索引项
        <ul>
            {{each hobby}}
              <li>{{ $index }},{{ $value }} </li>
            {{/each}}
        </ul>
        <!-- 4.过滤器 -->
        <h1>{{ regTime | dateFormat }}</h1>
    </script>
    <script>
        template.defaults.imports.dateFormat = function(date) {
            var y = date.getFullYear();
            var m = date.getMonth() + 1;
            var d = date.getDate();
            return y + '-' + m + '-' + d;
        }
    var data = {
        name: '白龙网',
        age: 18,
        Text: '<h3>原文输出添加@</h3>',
        flag: 1,
        hobby: ['学习','爬山','健身'],
        regTime: new Date()
    }
    var str = template('tpl-user',data);
    $('#container').html(str)
    </script>
</body>
</html>
案例1:新闻列表获取数据
该案例涉及模板引擎的的使用,循环列表的定义,获取新闻列表,时间过滤函数,补0函数等功能。
<html lang="en"><head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <link rel="stylesheet" href="./assets/news.css">
  <script src="./lib/jquery.js"></script>
  <script src="./lib/template-web.js"></script>
  <script src="./js/news.js"></script>
</head>
<body>
  <div id="news-list"></div>
<!-- 定义模板 -->
<script type="text/html" id="tpl-news">
  {{each data}}
  <div class="news-item">
    <img class="thumb" src="{{'http://www.liulongbin.top:3006' + $value.img}}" alt="">
    <div class="right-box">
      <h1 class="title">{{$value.title}}</h1>
      <div class="tags">
        {{each $value.tags}}
        <span>{{$value}}</span>
        {{/each}}
      </div>
      <div class="footer">
        <div>
          <span>{{$value.source}}</span>&nbsp;&nbsp;
          <span>{{$value.time | dateFormat}}</span>
        </div>
        <span>评论数:{{$value.cmtcount}}</span>
      </div>
    </div>
  </div>
  {{/each}}
</script>
<script>
  // 定义过滤器
template.defaults.imports.dateFormat = function(datestr) {
  var dt = new Date(datestr);
  var y = dt.getFullYear();
  var m = padZero(dt.getMonth() + 1);
  var d = padZero(dt.getDate());
  var hh = padZero(dt.getHours());
  var mm = padZero(dt.getMinutes());
  var ss = padZero(dt.getSeconds());
  return y + '-' + m + '-' + d + ' ' + hh + ':' + mm + ':' + ss;
}
// 补0函数
function padZero(n) {
  if (n < 10) {
    return '0' + n;
  } else {
    return n;
  }
}
// 获取新闻列表
$(function() {
  function getNewslist() {
    $.get('http://www.liulongbin.top:3006/api/news',function(e) {
    if (e.status !==200) {
      return alert('获取新闻列表失败');
    }
    
    for (var i = 0 ; i < e.data.length; i++) {
      e.data[i].tags = e.data[i].tags.split(',');
    }
    console.log(e);
    var htmlstr = template('tpl-news',e);
    $('#news-list').html(htmlstr);
 
    })
  };
  getNewslist();
})
</script>
</body>
</html>
(3)模板引擎的基本原理
exec()函数
检测字符串中的正则表达式是否匹配,如果字符串中有匹配的值,则返回匹配值,否则就返回null.
  <script>
    var str = 'hello';
    var pattern = /o/;
    var result = pattern.exec(str);
    console.log(result);
  </script>
提取分组()
  <script>
    var str = '<div>我是{{nameMy}}</div>';
    var pattern = /{{([a-zA-z]+)}}/;
    var result = pattern.exec(str);
    console.log(result);
  </script>
替换内容replace()
用一部分内容替换另外一部分内容。先定义字符串,再写正则,打印匹配结果,再用第一个元素替换第二个元素。
  <script>
    var str = '<div>我是{{nameMy}}</div>';
    var pattern = /{{([a-zA-Z]+)}}/;
    var patternResult = pattern.exec(str);
    str = str.replace(patternResult[0],patternResult[1]);
  </script>
  多次匹配替换
  这里需要注意的是\s*表示匹配0至多个空格。
  <script>
    var str = '<div>{{ name }}今年{{ age }}岁了</div>';
    var pattern = /{{\s*([a-zA-Z]+)\s*}}/;
    var patternResult = pattern.exec(str);
    // 第一次匹配到name
    str = str.replace(patternResult[0],patternResult[1]);
    console.log(str);
    // 第二次匹配age
    var patternResult2 = pattern.exec(str);
    console.log(patternResult2);
    str = str.replace(patternResult2[0],patternResult2[1]);
    console.log(str);
    // 第三次匹配null
    var patternResult3 = pattern.exec(str);
    console.log(patternResult3);
  </script>
  循环多次替换操作
  根据匹配结果,判断是否循环替换内容,简化了程序。
  <script>
    var str = '<div>{{ name }}今年{{ age }}岁了,他读{{ grade }}年级了</div>';
    var pattern = /{{\s*([a-zA-Z]+)\s*}}/;
    var patternResult = null;
    while(patternResult = pattern.exec(str)) {
      str = str.replace(patternResult[0],patternResult[1]);
    }
    console.log(str);
  </script>
  从对象中获取数据替换字符串中的内容,需要注意的是data[patternResult[1]]中方括号内不能加引号
  <script>
    var data = {
      name: '白龙网',
      age: 20,
      grade:30
    }
    console.log(data.name);
    console.log(data['age']);
    var str = '<div>{{ name }}今年{{ age }}岁了,他读{{ grade }}年级了</div>';
    var pattern = /{{\s*([a-zA-Z]+)\s*}}/;
    var patternResult = null;
    while(patternResult = pattern.exec(str)) {
      str = str.replace(patternResult[0],data[patternResult[1]]);
    }
    console.log(str);
  </script>
  实现简易的模板引擎
  <!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="./jquery-3.6.0/template-web.js"></script>
</head>
<body>
  <div id="user-box"></div>
  <!-- 1、定义模板结构 -->
  <script type="text/html" id="tpl-user">
    <div>姓名:{{name}}</div>
    <div>年龄:{{ age }}</div>
    <div>性别:{{  gender}}</div>
    <div>地址:{{address  }}</div>
  </script>
  <!-- 2、定义数据调用模板引擎渲染数据 -->
  <script>
    //2-1定义数据
    var data = {
      name: '白龙网',
      age: 20,
      gender: '男',
      address: '北京海淀中关村'
    }
    //2-2调用模板引擎
    var htmlstr = template('tpl-user',data);
    //2-3定当HTML结构
    document.getElementById('user-box').innerHTML = htmlstr;
    //2-3封闭函数
  </script>
  <!-- 3、封装函数 -->
  <script>
        function template(id,data) {
      var str = document.getElementById('id').innerHTML;
      var pattern = /{{\s*([a-zA-Z]+)\s*}}/;
      var patternResult = null;
      while(patternResult = pattern.exec(str)) {
        str = str.replace(patternResult[0],data[patternResult[1]]);
      }
      return str;
      }
  </script>
</body>
</html>