Ajax

Posted Rannie

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Ajax相关的知识,希望对你有一定的参考价值。

Ajax知识准备Json

什么是Json

JSON 指的是 javascript 对象表示法(JavaScript Object Notation)

JSON 是轻量级的文本数据交换格式

stringify与parse方法

JavaScript中关于JSON对象和字符串转换的两个方法:

JSON.parse(): 用于将一个 JSON 字符串转换为 JavaScript 对象

JSON.parse(\'{"name":"Howker"}\');
JSON.parse(\'{name:"Stack"}\') ;   // 错误
JSON.parse(\'[18,undefined]\') ;   // 错误

JSON.stringify(): 用于将 JavaScript 值转换为 JSON 字符串

JSON.stringify({"name":"Tonny"})

Ajax简介

Ajax,全称为Asynchronous JavaScript and XML,即异步的JavaScript和XML。

它不是一门编程语言,

而是利用JavaScript在保证页面不被刷新、页面链接不改变 的 情况下与服务器交换数据 并更新部分网页的技术

同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;阻塞

Ajax异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。非阻塞,通过异步回调机制 callback()返回结果

与后端进行交互的方式:

浏览器地址栏输入url: GET

a标签href:GET/POST

form表单:GET/POST

Ajax: GET/POST

初始Ajax

案例:页面有三个input框,一个按钮

用户在前两个框中输入数字,点击按钮保证页面不刷新的情况下将数据发到后端做计算

将计算好的结果再发给前端展示到第三个input框中,基于jQuery封装的Ajax

index.html

<input type="text" id="i1"> + <input type="text" id="i2"> = <input type="text" id="i3">
<button id="d1">按钮</button>

<script>
    $(\'#d1\').click(function () {
        // 获取两个框里的内容,向后端提交异步请求
        // ajax基本语法
        $.ajax({
            // 1  向哪个后盾提交数据
            url: \'\',
            // 2  指定提交当前请求的方式
            type: \'post\',
            // 3  提交的数据
            data: {\'i1\':$(\'#i1\').val(), \'i2\':$(\'#i2\').val()},
            // 4  ajax是异步提交,所以需要给一个回调函数来处理返回结果
            success: function (data) {  // data就是异步提交的返回结果
                // 将异步回调的结果通过DOM操作渲染到第三个input框中
                $(\'#i3\').val(data)
            }
        })
    })
</script>

views.py

def index(request):
    if request.method == \'POST\':
        i1 = request.POST.get(\'i1\')
        i2 = request.POST.get(\'i2\')
        i3 = int(i1) + int(i2)
        return HttpResponse(i3)
    return render(request,\'index.html\')

urls.py

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r\'^index/\', views.index)
]


Ajax常见应用常见

搜索引擎根据用户输入关键字,自动提示检索关键字

还有一个很重要的应用场景就是注册时候的用户名的查重

其实这里就使用了AJAX技术!当文件框发生了输入变化时,使用AJAX技术向服务器发送一个请求,然后服务器会把查询到的结果响应给浏览器,最后再把后端返回的结果展示出来

整个过程中页面没有刷新,只是局部刷新了;

在请求发出后,浏览器不用等待服务器响应结果就可以进行其他操作

Ajax优缺点

  • AJAX使用JavaScript技术向服务器发送异步请求;
  • AJAX请求无须刷新整个页面;
  • 因为服务器响应内容不再是整个页面,而是页面中的部分内容,所以AJAX性能高;
  • 两个关键点:1.局部刷新,2.异步请求

$.ajax()方法参数

url:

要求为String类型的参数,(默认为当前页地址)发送请求的地址。

type:

要求为String类型的参数,请求方式(post或get)默认为get。注意其他http请求方法,例如put和delete也可以使用,但仅部分浏览器支持。

data:

要求为Object或String类型的参数,发送到服务器的数据。如果已经不是字符串,将自动转换为字符串格式。get请求中将附加在url后。防止这种自动转换,可以查看processData选项。

对象必须为key/value格式,例如{foo1:"bar1",foo2:"bar2"}转换为&foo1=bar1&foo2=bar2

如果是数组,JQuery将自动为不同值对应同一个名称。例如{foo:["bar1","bar2"]}转换为&foo=bar1&foo=bar2

success:

要求为Function类型的参数,请求成功后调用的回调函数

由服务器返回,并根据dataType参数进行处理后的数据。

success:function (data){
  //data可能是xmlDoc、jsonObj、html、text等等
}


Content-Type

content-type前后端传输数据的编码格式

urlencoded

formdata

application/json

form表单

默认是urlencoded编码格式传输数据

urlencoded数据格式

  • username=santa&password=123

  • django后端针对该格式的数据,会自动解析并帮你打包到request.POST中

formdata数据格式

  • django后端针对符合urlencoded编码格式数据(普通键值对)还是统一解析到request.POST中
  • 而针对formdata文件数据就会自动解析放到request.FILES中

ajax提交

ajax默认的也是urlencoded编码格式

前后端数据交互 编码格式与数据格式一定要一致

application/json

  • django后端针对json格式数据 并不会做任何的处理,而是直接放在request.body中


Ajax发送数据

基于Ajax发送json数据

1 指定编码格式为application/json

2 将数据序列化为json串,保证与编码格式一致(前端的序列化JSON)

3 后端处理json格式的数据,不会做任何处理,原封不动的封装到request.body中

<button id="d3">ajax发送文件数据</button>
<script>
    $(\'#d3\').click(function () {
        $.ajax({
            url: \'\',
            type: \'post\',
            // 修改content-type 参数
            contentType: \'application/json\',
            data: JSON.stringify({\'username\': \'santa\', \'password\': \'123\'}), // 将数据序列化成json格式字符串
            success: function (data) {
                alert(data)
            }
        })
    })
</script>

views.py

import json
def ab_ct(request):
    if request.method == \'POST\':
        print(request.body)   # b\'{"username":"santa","password":"123"}\'
        json_bytes = request.body
        # json_str = json_bytes.decode(\'utf-8\')
        # json_dict = json.loads(json_str)
        # print(json_dict)   # {\'username\': \'santa\', \'password\': \'123\'}

        # 扩展  json.loads能够自动解码并序列化
        json_dict = json.loads(json_bytes)
        print(json_dict)  # {\'username\': \'santa\', \'password\': \'123\'}
    return render(request, \'ab_ct.html\')

基于Ajax发送文件数据

1 借助于内置对象new FormData()

2 该对象可以携带文件数据,也可以传键值对

3 在Ajax中指定两个参数:

contentType:false 代表不使用任何编码格式,

processData:false 代表不对数据本身做任何处理,FormData()实例化的对象内部自带编码,django后端能够识别

<input type="file" name="myfile" id="i1">
<button id="d3">ajax发送文件数据</button>
<script>
    $(\'#d3\').click(function () {
        // 1  需要先生成一个内置对象
        var myFormData = new FormData();
        // 2 传普通键值对,当普通键值对较多的时候,我们可以利用for循环来添加
        myFormData.append(\'username\', \'santa\');
        myFormData.append(\'password\', 123);
        // 3  传文件
        myFormData.append(\'myfile\', $(\'#i1\')[0].files[0]);
        //  发送Ajax请求
        $.ajax({
            url: \'\',
            type: \'post\',
            data: myFormData,
            //  发送formdata对象需要指定两个关键性的参数
            processData:false,  // 让浏览器不要对你的数据进行任何的操作
            contentType:false,  // 不要使用任何编码格式 对象formdata自带编码格式并且django能够识别该对象
            success: function (data) {
                alert(data)
            }
        })
    })
</script>

views.py

def ab_ct(request):
    if request.method == \'POST\':
                print(request.FILES)   # <MultiValueDict: {\'myfile\': [<InMemoryUploadedFile: DJANGO点击.png (image/png)>]}>
	return render(request, \'ab_ct.html\')

Django内置序列化功能(了解)

序列化目的:

将数据整合成一个大字典形式,再传给前端,方便数据的交互

一般用于前后端分离开发

drf django restframework
from app01 import models
from django.core import serializers

# 以视图函数index为例
def ab_se(request):
    user_queryset = models.Userinfo.objects.all()
    # user_list = []
    # for user_obj in user_queryset:
    #     user_list.append({
    #         \'username\':user_obj.username,
    #         \'password\':user_obj.password,
    #         \'gender\':user_obj.get_gender_display(),
    #     })
    # res = json.dumps(user_list)
    res = serializers.serialize(\'json\',user_queryset)
    # return render(request,\'ab_se.html\',locals())
    return HttpResponse(res)
# res是一个字典套字典{{},{},{},{}...}
# 每一个数据表中的记录作为一个字典
# 前端接收到的只有所有的字典,没有最外层的字典

批量插入数据

bulk_create

def ab_bc(request):
    # 先插入1000条件数据
    # for i in range(1,1001):
    #     models.Book.objects.create(title=\'第%s本书\'%i)
    book_list = []
    for i in range(1,10001):
        book_list.append(models.Book(title=\'新的%s书\'%i))
        models.Book.objects.bulk_create(book_list)  # 批量插入数据的方式

        book_queryset = models.Book.objects.all()
        return render(request,\'ab_bc.html\',locals())

自定义分页器

分页数据是通过总数据切片而来,queryset对象支持正数索引和正向步长,不支持负数

html

{% for book_obj in page_queryset %}
    <p>{{ book_obj.title }}</p>
{% endfor %}
{{ page_obj.page_html|safe }}

views.py

from app01 import models
from app01.utils.mypage import Pagination

def ab_bc(request):
    book_queryset = models.Book.objects.all()
    current_page = request.GET.get(\'page\', 1)
    all_count = book_queryset.count()
    # 1 现生成一个自定义分页器类对象
    page_obj = Pagination(current_page=current_page,all_count=all_count,pager_count=9)
    # 2 针对真实的queryset数据进行切片操作
    page_queryset = book_queryset[page_obj.start:page_obj.end]
    return render(request,\'ab_bc.html\',locals())

mypage.py

class Pagination(object):
    def __init__(self, current_page, all_count, per_page_num=10, pager_count=11):
        """
        封装分页相关数据
        :param current_page: 当前页
        :param all_count:    数据库中的数据总条数
        :param per_page_num: 每页显示的数据条数
        :param pager_count:  最多显示的页码个数

        用法:
        queryset = model.objects.all()
        page_obj = Pagination(current_page,all_count)
        page_data = queryset[page_obj.start:page_obj.end]
        获取数据用page_data而不再使用原始的queryset
        获取前端分页样式用page_obj.page_html
        """
        try:
            current_page = int(current_page)
        except Exception as e:
            current_page = 1

        if current_page < 1:
            current_page = 1

        self.current_page = current_page

        self.all_count = all_count
        self.per_page_num = per_page_num

        # 总页码
        all_pager, tmp = divmod(all_count, per_page_num)
        if tmp:
            all_pager += 1
        self.all_pager = all_pager

        self.pager_count = pager_count
        self.pager_count_half = int((pager_count - 1) / 2)

    @property
    def start(self):
        return (self.current_page - 1) * self.per_page_num

    @property
    def end(self):
        return self.current_page * self.per_page_num

    def page_html(self):
        # 如果总页码 < 11个:
        if self.all_pager <= self.pager_count:
            pager_start = 1
            pager_end = self.all_pager + 1
        # 总页码  > 11
        else:
            # 当前页如果<=页面上最多显示11/2个页码
            if self.current_page <= self.pager_count_half:
                pager_start = 1
                pager_end = self.pager_count + 1

            # 当前页大于5
            else:
                # 页码翻到最后
                if (self.current_page + self.pager_count_half) > self.all_pager:
                    pager_end = self.all_pager + 1
                    pager_start = self.all_pager - self.pager_count + 1
                else:
                    pager_start = self.current_page - self.pager_count_half
                    pager_end = self.current_page + self.pager_count_half + 1

        page_html_list = []
        # 添加前面的nav和ul标签
        page_html_list.append(\'\'\'
                    <nav aria-label=\'Page navigation>\'
                    <ul class=\'pagination\'>
                \'\'\')
        first_page = \'<li><a href="?page=%s">首页</a></li>\' % (1)
        page_html_list.append(first_page)

        if self.current_page <= 1:
            prev_page = \'<li class="disabled"><a href="#">上一页</a></li>\'
        else:
            prev_page = \'<li><a href="?page=%s">上一页</a></li>\' % (self.current_page - 1,)

        page_html_list.append(prev_page)

        for i in range(pager_start, pager_end):
            if i == self.current_page:
                temp = \'<li class="active"><a href="?page=%s">%s</a></li>\' % (i, i,)
            else:
                temp = \'<li><a href="?page=%s">%s</a></li>\' % (i, i,)
            page_html_list.append(temp)

        if self.current_page >= self.all_pager:
            next_page = \'<li class="disabled"><a href="#">下一页</a></li>\'
        else:
            next_page = \'<li><a href="?page=%s">下一页</a></li>\' % (self.current_page + 1,)
        page_html_list.append(next_page)

        last_page = \'<li><a href="?page=%s">尾页</a></li>\' % (self.all_pager,)
        page_html_list.append(last_page)
        # 尾部添加标签
        page_html_list.append(\'\'\'
                                           </nav>
                                           </ul>
                                       \'\'\')
        return \'\'.join(page_html_list)

Ajax结合sweetalert实现删除二次确认

前端book_list.html

{% extends \'home.html\' %}

{% block content %}
    <br>
    <p><a href="{% url \'add_books\' %}" class="btn-danger btn">新增</a></p>
<br>
<br>
<table class="table-striped table-hover table">
    <thead>
        <tr>
            <th>序号</th>
            <th>书名</th>
            <th>价格</th>
            <th>出版时间</th>
            <th>出版社</th>
            <th>作者</th>
            <th>操作</th>
        </tr>
    </thead>
    <tbody>
        {% for book_obj in page_queryset %}
        <tr>
        <td>{{ forloop.counter }}</td>
        <td>{{ book_obj.title }}</td>
        <td>{{ book_obj.price }}</td>
        <td>{{ book_obj.publish_time|date:\'Y-m-d\' }}</td>
        <td>{{ book_obj.publish.name }}</td>
        <td>
            {% for author_obj in book_obj.authors.all %}
                {% if forloop.last %}
                    {{ author_obj.name }}
                {% else %}
                    {{ author_obj.name }},
                {% endif %}
            {% endfor %}

        </td>
        <td>
            <a href="{% url \'edit_bookss\' book_obj.pk %}" class="btn btn-primary">编辑</a>
            <a href="{% url \'del_book\' book_obj.pk%}" class="btn btn-primary" >删除</a>
            <a href="#" class="btn btn-primary can" adel_id="{{ book_obj.pk }}">测试删除</a>
        </td>
        </tr>
        {% endfor %}

    </tbody>

</table>
{{ page_obj.page_html|safe }}

    <script>
    $(\'.can\').click(function () {
        var $aEle  =$(this);
        swal({
          title: "确定删除?",
          text: "You will not be able to recover this imaginary file!",
          type: "warning",
          showCancelButton: true,
          confirmButtonClass: "btn-danger",
          confirmButtonText: "Yes, delete it!",
          cancelButtonText: "No, cancel plx!",
          closeOnConfirm: false,
          closeOnCancel: false
        },
        function(isConfirm) {
            if (isConfirm) {
                $.ajax({
                    url: \'/adel_book/\',
                    type: \'post\',
                    data: {\'adel_id\': $aEle.attr("adel_id")},
                    success: function (data) {
                        if (data.code === 1000){
                            swal("删除成功!", "Your imaginary file has been deleted.", "success");
                            $aEle.parent().parent().remove()
                        } else {
                            swal("删除失败", "Your imaginary file is safe :)", "error");
                        }
                    }
                });

        } else {
                    swal("怂笔", "你成功的刷新我对你的认知", "error");
                }
            });
    })
</script>

{% endblock %}

views.py

def adelete_book(request):
    if request.method == \'POST\':
        back_dic = {"code": 1000, "msg": \'\'}
        delete_id = request.POST.get(\'adel_id\')
        models.Book.objects.filter(pk=delete_id).delete()
        back_dic[\'msg\'] = \'删除成功,跑路\'
        return JsonResponse(back_dic)
    return redirect(reverse(\'all_book\'))

注意点:

1 在标签内自定义一个属性 adel_id="{{ book_obj.pk }} ,绑定事件click

2 通过 $(this) 得到当前标签

3 如果要删除,就会回传当前数据的adel_id给服务器,通过 $(this).attr(\'adel_id\')获取要删除的数据id, 在Ajax的data中定义键值对,

4 通过DOM操作,实现局部的数据交互更新,但整个页面并不刷新

django中如何开启事务

Django文件传输

以上是关于Ajax的主要内容,如果未能解决你的问题,请参考以下文章

Javascript代码片段在drupal中不起作用

前端面试题之手写promise

Ajax 片段元标记 - Googlebot 未读取页面内容

执行AJAX返回HTML片段中的JavaScript脚本

javascript AJAX片段

Spring MVC 3.2 Thymeleaf Ajax 片段