Notes|HTML|Ajax

d.l.spm
17 min readMay 3, 2020

--

  • Django ajax 傳值+頁面刷新

Ajax 透過 script 來訪問 url 獲取 data,所以不能重定向(redirect)

左圖:一般瀏覽器和伺服器流程圖 | 右圖:加入 ajax 後的流程圖

Django ajax 傳值

1. 觸發事件
2. ajax 傳送(url)
3. view 處理
4. ajax success 處理後端回傳

ajax 流程圖 4 步驟

問題:如何知道點選哪個 comment 的回覆?
解決:使用 js ,觸發點選回覆的物件

一、觸發事件

1. id=”id_comment{{ forloop.counter0 }}”
− forloop.counter 一個整數表示迭代次數(id_comment0, id_comment1…)

用途:id_comment的值從0開始,到後端可以使用陣列的方式取直

  • cc = Post.objects.get(id = 4).post_comment.all() -> 取第 0 筆 cc[0]
#html:
<input type="button" value="回覆" style="border-style:none;color:red;" name="reply" id="id_comment{{ forloop.counter }}"/>
#script
<script src="/static/jquery-3.2.1.min.js"></script>
var token = $('input[name=csrfmiddlewaretoken]').val();
<script>
$('input[id^="id_comment"]').click(function() {
var strid =this.id
var regexp2 = /[0-9]+/g
var comment_id = strid.match(regexp2)
alert("id = " + comment_id + "\n");
});
</script>

2. name=”reply”
−這邊也可以換成 class 因為這兩個都不是唯一值

用途:把 id 放進 username或是 id 裡,透過點選 name 物件傳到後端(很簡單但很明顯在傳 id)

  • 到後端 comment_id 可以直接用
#html
<input type="button" value="回覆" style="border-style:none;color:red;" name="reply" username="{{ com.id }}"/>
#script
<script src="/static/jquery-3.2.1.min.js"></script>
var token = $('input[name=csrfmiddlewaretoken]').val();
<script>
$("input[name=reply]").click(function () {
var comment_id = $(this).attr("username");
});
</script>

二、ajax 傳送(url)
只需要把這段 code 放到其他觸發事件的 function 裡就可以用了

id_name、id_text:是 forms 自動產生的 id

$.ajax({
url:"/blog_v2/post_ajax/{{ post_id }}", //記得加雙引號最後也要加斜線
type:"POST",
data:{
"id":$(this).attr("username"),
"id_email":$("#id_email").val(),
"id_name":$("#id_name").val(),
"id_text":$("#id_text").val(),
"post_id":{{ post_id }},
csrfmiddlewaretoken: token,
},
//success:function 處理後端回傳資料,後續會在這邊處理不重新整理即可顯示新值
success:function (data) {
if (data.rel == 'success'){
window.location = '/blog_v2/post_beta/' + data.post_id + '/'
}
},
error:function() {
alert('失败')
},
})

三、修改 urls.py(guestbook/urls.py)

path('ajax/', views.get_ajax),

修改 views.py(guestbook/views.py)
safe=False 代表禁止頁面重新整理

@csrf_exempt
def get_ajax(request):
if request.is_ajax():
print('ajax_is'+request.POST.get("id"))
id_comment = request.POST.get("id")
id_name = request.POST.get("id_name")
id_text = request.POST.get("id_text")

if id_text != "" and id_name != "":
if request.POST.get("id_email") == None:
id_email = ""
else:
id_email = request.POST.get("id_email")

com = Comment.objects.get(id=id_comment)

try:
Comment_reply.objects.create(name=id_name, email=id_email, text=id_text, comment=com)
except:
rel = "fail"
else:
rel = "success"
else:
rel = "fail"
return JsonResponse({'rel':rel, 'post_id':post_id}, safe=False)
#return redirect('/guestbook1/beta')

四、ajax success
code 都在上面了,補充下一節會介紹 success 動態生成評論來達到頁面刷新

success:function (data) {
if (data.rel == 'success'){
window.location = '/blog_v2/post_beta/' + data.post_id + '/'
}
},

到這邊已經可以儲存到資料庫裡剩下顯示了,這邊會發現在後端 redirect 沒有作用(無法重新定向),是因為 ajax 只負責局部提交,只會請求返回資料其餘不會做,因此我們在前端做 window.location='/questbook1/beta/'

參考:

Django ajax 頁面刷新

  • 初探 jQuery
  • 提交評論
  • 回覆評論

初探 jQuery

使用 jQuery 來取 html 元素,jQuery 就像包裝過後的 Javascript ,更於理解使用。

console.log 就像我們的 print 一樣,打印資料讓我們了解程式有沒有運行到那裡。

<form action="" method="post" name="form1" id="guestbook_form">

jquery 動態產生物件兩種方法:

  • prepend
  • append
$(name).append(x); # 將 x 視為該元素一部份,插入到該元素後面
$(name).appendTo(x); # 將該元素視為 x 的一部份,放到 x 的後面
$(name).prepend(x); # 將 x 視為該元素一部份,插入到最前面
$(name).prepend(x); # 將該元素視為 x 的一部份,放到 x 的最前面
# name 可以是 class 或 id

📌補充一下:

  • ajax 只對「部分」做更新;這個部分指的就是某個 id, class(所以我們的評論應該要全部包在一個 id, class 裡)
  • 這樣只需對 ‘.guestbook’ 做動態產生即可
<div class="guestbook">
{% for com in comments %}
<div class="media mb-4">
<img class="d-flex mr-3 rounded-circle" src="http://placehold.it/50x50" alt="">
<div class="media-body" style="word-wrap:break-word;overflow: hidden;">

<h5 class="mt-0" style="display:inline;">{{com.name}}</h5>
<div style="clear: both;float: right;">{{com.create_time|date:"Y.m.d"}}</div>

<div style="margin:8px;">{{com.text}}</div>
<!-- <input type="hidden" name="comment_id" id="comment_id" value="{{com.id}}"> -->
<input type="button" value="回覆" name="reply" id="id_comment{{ forloop.counter0 }}" username="{{ com.id }}" class="btn btn-outline-secondary btn-sm"/>


{% for com_ry in com.comment_reply.all %}
<div class="media mt-4">
<img class="d-flex mr-3 rounded-circle" src="http://placehold.it/50x50" alt="">
<div class="media-body">
<h5 class="mt-0">{{com_ry.name}}</h5>
{{com_ry.text}}
</div>
</div>
{% endfor %}
</div>
</div>
{% empty %}
<span id="no_comment">暫無評論</span>
{% endfor %}

提交評論

更改後端回傳 success:function

success:function (data) {
console.log(data);
if (data.rel == 'success'){
console.log('success');

$('#guestbook_form')[0].reset(); //表單清空

if (data.button == 'send'){
console.log('send');
var comment_html =
'<div class="media mb-4">' +
'<img class="d-flex mr-3 rounded-circle" src="http://placehold.it/50x50" alt="">' +
'<div class="media-body" style="word-wrap:break-word;overflow: hidden;">' +

'<h5 class="mt-0" style="display:inline;">' + data.name + '</h5>' +
'<div style="clear: both;float: right;">' + data.create_time + '</div>' +
'<div style="margin:8px;">' + data.text + '</div>' +
'<input type="button" value="回覆" name="reply" username="'+ data.id +'" class="btn btn-outline-secondary btn-sm">' +
{#'<input type="button" value="回覆" name="reply" id="id_comment ' + (data.counter +1) + '" />'#}
'</div>' +
'</div>';

$(".guestbook").prepend(comment_html);
}
$('#no_comment').remove();
}
},

提交回覆

  1. 要把每個評論底下的子評論都用一個 div 包起來
  2. prepend 的 class name 不固定,所以用一個變數來代替

reply{{ com.id }}:reply 後面接每個評論的 id

<div class="reply{{ com.id }}">
{% for com_ry in com.comment_reply.all %}
<div class="media mt-4">
<img class="d-flex mr-3 rounded-circle" src="http://placehold.it/50x50" alt="">
<div class="media-body" style="word-wrap:break-word;overflow: hidden;">
<h5 class="mt-0" style="display:inline;">{{com_ry.name}}</h5>
<div style="clear: both;float: right;">{{com_ry.create_time|date:"Y.m.d"}}</div>

<div style="margin:8px 0px;">{{com_ry.text}}</div>

</div>
</div>
{% endfor %}
</div>

更改後端回傳 success:function

success:function (data) {
if (data.rel == 'success'){

console.log('success');

$('#guestbook_form')[0].reset(); //表單清空
if (data.button == 'reply'){
console.log('send');
var class_name = "." + data.class_name
var comment_html =

'<div class="media mt-4">' +
'<img class="d-flex mr-3 rounded-circle" src="http://placehold.it/50x50" alt="">' +
'<div class="media-body" style="word-wrap:break-word;overflow: hidden;">' +

'<h5 class="mt-0" style="display:inline;">' + data.name + '</h5>' +
'<div style="clear: both;float: right;">' + data.create_time + '</div>' +
'<div style="margin:8px 0px;">' + data.text + '</div>' +
{#'<input type="button" value="回覆" name="reply" id="id_comment ' + (data.counter +1) + '" />'#}
'</div>' +
'</div>';

$(class_name).prepend(comment_html);
}
$('#no_comment').remove();

{#window.location = '/blog_v2/post_beta/' + data.post_id + '/'#}
}
},

沒辦法再新增的評論觸發’回覆’

原因:<div class=”reply{{ com.id }}”> 是在網頁 load 進來時才產生的,因此 ajax new comment 時還沒有相對應的 子評論 <div>
解決:在 ajax 動態產生時把 <div class=”reply{{ com.id }}”> 加進去

ajax 新增的 Dome 元素無法觸發事件

兩種解決方法:live()、on()

on() 解決:全部包在 document 裡面

$( document ).on("click", "input[name=reply]", function () {});$( document ).on("click", "input[name=send]", function () {});

參考:

jQuery 多個元素绑定相同事件

請參考以下有非常多種方式介紹

把全部事件放在括號裡且用 ‘, ’隔開

📌如果沒有用', '隔開的話會失效

$("input[name=reply]).click(function () {});
$("input[name=send]).click(function () {});
# 合併為
$("input[name=reply], input[name=send]").click(function () {

📌但是還沒有 $( document ).on() 的合併方法

--

--