Django:如何使用Ajax/Javascript(非jQuery)阻止页面在我单击按钮时重新加载?

huangapple go评论69阅读模式
英文:

Django: How to prevent a page from reloading when I click button with Ajax/Javascript? (not jQuery)

问题

home.html页面(index.html > login.html > home.html)中,我有以下内容:

  • combobox(独立的,与数据库无关,而是与列表相关);
  • textarea
  • button

当我点击按钮时,它会在文本区域中打印一些文本(使用views.py中找到的条件)。非常简单。

问题: 问题在于当我点击按钮时,页面会重新加载,然后才会在文本区域中打印内容。在没有使用jQuery的情况下,如何使用Ajax/Javascript阻止页面在点击按钮时重新加载?谢谢大家!

重要提示: 请记住,我将不得不添加许多组合框,因此我希望尽可能好地管理它们。是否可能在py文件中尽可能多地管理代码,而不是将其分割成js文件或js标签?

P.S: 我在stackoverflow上搜索了很多问题或各种教程,但都无法解决。显然,尽管这些问题相似,但每个解决方案都是不同的情况,存在不同的问题和解决方案。

我希望有人能帮助我。谢谢大家!

代码

home.html(index > login > home)

index.html是127.0.0.1:8000/。只有成功登录后,Home.html才会打开。当我查看表单时的地址是127.0.0.1:8000/home,当我点击提交时,我仍然得到127.0.0.1:8000/home(这是正确的)。显然,我以一种简化和缩短的方式编写了HTML:

{% extends 'app1/base.html' %}

{% load static %}

{% block content %}

<div class="container p-5">
  <div class="row mx-auto">
    <div class="col-6">
      <form action="" method="POST" novalidate class="form-group">
        {% csrf_token %}
        <select name="color" class="form-select">
          {% for i in color_choices %}
          <option value="{{i}}">{{i}}</option>
          {% endfor %}
        </select>
        <textarea class="form-control" name="msg" cols="30" rows="10">{{message}}</textarea>
        <button class="btn btn-primary mt-3" type="submit">Submit</button>
      </form>
    </div>
  </div>
</div>
{% endblock content %}

views.py

@login_required
def home(request):
  color_choices = ("Red","Blue","Black","Orange")
  message = ''
  if request.method == "POST":
    picked = request.POST.get('color')
    if picked == 'Red':
      message = "<<< You chose red"
      print(message)
    elif picked == 'Blue':
        message = "<<< You chose blue"
    elif picked == 'Black':
        message = "<<< You chose black"
    else:
        message = "<<< Oh no, you chose orange"
  context = {'message':message,'color_choices':color_choices}
  return render(request, 'app1/home.html', context)

forms.py

from django import forms

class SimpleCombobox(forms.Form):
    Col1 = 'Red'
    Col2 = 'Blue'
    Col3 = 'Black'
    Col4 = 'Orange'
      
    COLOR_CHOICES = (
        (Col1, u"Red"),
        (Col2, u"Blue"),
        (Col3, u"Black"),
        (Col4, u"Orange"),
    )
    cities = forms.ChoiceField(choices=COLOR_CHOICES)


class SimpleTextbox(forms.Form):
    coverletter = forms.CharField(required=False,
              widget=forms.Textarea(
                # rows and colums of the textarea
                attrs={'rows': 4, 'cols': 40}))
    

# 登录
class LoginForm(forms.Form):
    username = forms.CharField(max_length=65)
    password = forms.CharField(max_length=65, widget=forms.PasswordInput)
英文:

Premise: A question that can be useful to many. Fetch instead of Ajax. Today Ajax is often replaced by the newer Fetch in Javascript, which provides a better alternative. If you're looking for a way to avoid page reloading, I recommend using Fetch in pure Javascript. Ajax is still used and still great, but Fetch offers a more modern solution.

In the home.html page (index.html > login.html > home.html) i have a:

  • combobox (independent, not connected to the database but to a list);
  • textarea;
  • button;

When i click the button, it prints some text in the textarea (using a condition found in views.py). Very simple.

Problem: The problem is that when i click the button, the page reloads and then prints in the textarea. How can I prevent the page from reloading, with Ajax/Javascript, when i click the button (without using jQuery)? Thank you all!

Important: bearing in mind that I will have to add many comboboxes, so I would like to manage them as best as possible. Is it possible to manage the code as much as possible in the py files and not split it too much in the js file or js tag?

P.S: I searched many questions on stackoverflow or various tutorials, but I couldn't solve. Apparently, even though these are similar questions, each solution is a different case with different problems and solutions.

I hope someone can help me. Thank you all!

CODE

home.html (index > login > home)

index.html is 127.0.0.1:8000/. Home.html will only open after successful login. The address of when I view the forms is 127.0.0.1:8000/home and when I click Submit I still get 127.0.0.1:8000/home (rightfully so). Obviously I wrote the html in a reduced and shorter way:

{% extends &#39;app1/base.html&#39; %}

{% load static %}

{% block content %}

&lt;div class=&quot;container p-5&quot;&gt;
  &lt;div class=&quot;row mx-auto&quot;&gt;
    &lt;div class=&quot;col-6&quot;&gt;
      &lt;form action=&quot;&quot; method=&quot;POST&quot; novalidate class=&quot;form-group&quot;&gt;
        {% csrf_token %}
        &lt;select name=&quot;color&quot; class=&quot;form-select&quot; &gt;
          {% for i in color_choices %}
          &lt;option value=&quot;{{i}}&quot;&gt;{{i}}&lt;/option&gt;
          {% endfor %}
        &lt;/select&gt;
        &lt;textarea class=&quot;form-control&quot; name=&quot;msg&quot; cols=&quot;30&quot; rows=&quot;10&quot;&gt;{{message}}&lt;/textarea&gt;
        &lt;button class=&quot;btn btn-primary mt-3&quot; type=&quot;submit&quot;&gt;Submit&lt;/button&gt;
      &lt;/form&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
{% endblock content%}

views.py

@login_required
def home(request):
  color_choices = (&quot;Red&quot;,&quot;Blue&quot;,&quot;Black&quot;,&quot;Orange&quot;)
  message =&#39;&#39;
  if request.method ==&quot;POST&quot;:
    picked = request.POST.get(&#39;color&#39;)
    if picked == &#39;Red&#39;:
      message = &quot;&lt;&lt;&lt; You chose red&quot;
      print(message)
    elif picked == &#39;Blue&#39;:
        message = &quot;&lt;&lt;&lt; You chose blue&quot;
    elif picked == &#39;Black&#39;:
        message = &quot;&lt;&lt;&lt; You chose black&quot;
    else:
        message = &quot;&lt;&lt;&lt; Oh no, you chose orange&quot;
  context = {&#39;message&#39;:message,&#39;color_choices&#39;:color_choices}
  return render(request, &#39;app1/home.html&#39;, context)

forms.py

from django import forms

class SimpleCombobox(forms.Form):
    Col1 = &#39;Red&#39;
    Col2 = &#39;Blue&#39;
    Col3 = &#39;Black&#39;
    Col4 = &#39;Orange&#39;
      
    COLOR_CHOICES = (
        (Col1, u&quot;Red&quot;),
        (Col2, u&quot;Blue&quot;),
        (Col3, u&quot;Black&quot;),
        (Col4, u&quot;Orange&quot;),
    )
    cities = forms.ChoiceField(choices=COLOR_CHOICES)


class SimpleTextbox(forms.Form):
    coverletter = forms.CharField(required=False,
              widget=forms.Textarea(
                # rows and colums of the textarea
                attrs={&#39;rows&#39;: 4, &#39;cols&#39;: 40}))
    

#LOGIN
class LoginForm(forms.Form):
    username = forms.CharField(max_length=65)
    password = forms.CharField(max_length=65, widget=forms.PasswordInput)

答案1

得分: 1

基本上,通过使用.preventDefault来阻止默认的点击处理。然后,您可以使用Fetch API发送post请求,并且需要从cookies中检索csrftoken并将其设置在请求标头上。以下是一个示例:

{% block content %}
<div class="container p-5">
  <div class="row mx-auto">
    <div class="col-6">
      <form action="" method="POST" novalidate class="form-group">
        {% csrf_token %}
        <select id="select-color" name="color" class="form-select" >
          {% for i in color_choices %}
          <option value="{{i}}">{{i}}</option>
          {% endfor %}
        </select>
        <textarea id="text-message" class="form-control" name="msg" cols="30" rows="10">{{message}}</textarea>
        <button class="btn btn-primary mt-3" type="submit" id="button-submit">Submit</button>
      </form>
    </div>
  </div>
</div>

<script>
  function getCookie(name) {
    // ...
  }

  async function postData(url = "", data = {}) {
      const csrftoken = getCookie('csrftoken');
      const response = await fetch(url, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "X-CSRFToken": csrftoken,
        },
        body: JSON.stringify(data),
      });
      return response.json();
  }

  document.getElementById("button-submit").addEventListener("click", (event) => {
    event.preventDefault();
    const selected_color = document.getElementById("select-color").value;
    postData(url="{% url 'fetch-message' %}", {color: selected_color})
    .then((response) => { 
      document.getElementById("text-message").innerHTML = response.message
     });
    });
</script>
{% endblock content%}

您可以在一个视图中完成,但最好将它们分开:

@login_required
def home(request):
  color_choices = ("Red","Blue","Black","Orange")

  context = {'color_choices':color_choices}
  return render(request, 'app1/home.html', context)

def fetch_message(request):
  data = json.loads(request.body)
  picked = data['color']
  message = ''

  if picked == 'Red':
    message = '<<< You chose red'
  elif picked == 'Blue':
      message = '<<< You chose blue'
  elif picked == 'Black':
      message = '<<< You chose black'
  else:
      message = '<<< Oh no, you chose orange'

  return JsonResponse({'message': message})

urls.py

urlpatterns = [
    path('', home, name='home'),
    path('fetch/message/', fetch_message, name='fetch-message'),
]

重要的是name='fetch-message',这样我们可以在模板中使用{% url 'fetch-message' %}来反向解析URL。

英文:

Basically by blocking the default click handling by using .preventDefault. Then you can send a post request using the Fetch API and for that you will need to retrieve csrftoken from cookies and set it on the request headers. Here is an example:

{% block content %}
&lt;div class=&quot;container p-5&quot;&gt;
&lt;div class=&quot;row mx-auto&quot;&gt;
&lt;div class=&quot;col-6&quot;&gt;
&lt;form action=&quot;&quot; method=&quot;POST&quot; novalidate class=&quot;form-group&quot;&gt;
{% csrf_token %}
&lt;select id=&quot;select-color&quot; name=&quot;color&quot; class=&quot;form-select&quot; &gt;
{% for i in color_choices %}
&lt;option value=&quot;{{i}}&quot;&gt;{{i}}&lt;/option&gt;
{% endfor %}
&lt;/select&gt;
&lt;textarea id=&quot;text-message&quot; class=&quot;form-control&quot; name=&quot;msg&quot; cols=&quot;30&quot; rows=&quot;10&quot;&gt;{{message}}&lt;/textarea&gt;
&lt;button class=&quot;btn btn-primary mt-3&quot; type=&quot;submit&quot; id=&quot;button-submit&quot;&gt;Submit&lt;/button&gt;
&lt;/form&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;script&gt;
function getCookie(name) {
...
}
async function postData(url = &quot;&quot;, data = {}) {
const csrftoken = getCookie(&#39;csrftoken&#39;);
const response = await fetch(url, {
method: &quot;POST&quot;,
headers: {
&quot;Content-Type&quot;: &quot;application/json&quot;,
&quot;X-CSRFToken&quot;: csrftoken,
},
body: JSON.stringify(data),
});
return response.json();
}
document.getElementById(&quot;button-submit&quot;).addEventListener(&quot;click&quot;, (event) =&gt; {
event.preventDefault();
const selected_color = document.getElementById(&quot;select-color&quot;).value;
postData(url=&quot;{% url &#39;fetch-message&#39; %}&quot;, {color: selected_color})
.then((response) =&gt; { 
document.getElementById(&quot;text-message&quot;).innerHTML = response.message
});
});
&lt;/script&gt;
{% endblock content%}

You could do it in just one view, but it is good practice to keep them apart:

@login_required
def home(request):
color_choices = (&quot;Red&quot;,&quot;Blue&quot;,&quot;Black&quot;,&quot;Orange&quot;)
context = {&#39;color_choices&#39;:color_choices}
return render(request, &#39;app1/home.html&#39;, context)
def fetch_message(request):
data = json.loads(request.body)
picked = data[&#39;color&#39;]
message = &#39;&#39;
if picked == &#39;Red&#39;:
message = &quot;&lt;&lt;&lt; You chose red&quot;
elif picked == &#39;Blue&#39;:
message = &quot;&lt;&lt;&lt; You chose blue&quot;
elif picked == &#39;Black&#39;:
message = &quot;&lt;&lt;&lt; You chose black&quot;
else:
message = &quot;&lt;&lt;&lt; Oh no, you chose orange&quot;
return JsonResponse({&#39;message&#39;: message})

urls.py

urlpatterns = [
path(&#39;&#39;, home, name=&#39;home&#39;),
path(&#39;fetch/message/&#39;, fetch_message, name=&#39;fetch-message&#39;),
]

What is important is name=&#39;fetch-message&#39;, so that we can reverse resolve the URL on the template with {% url &#39;fetch-message&#39; %}.

huangapple
  • 本文由 发表于 2023年6月26日 03:03:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/76552009.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定