Replace content of block on submit with content of json object - json

I am trying to embed a custom mailchimp form in a twig such that when the user clicks the submit button, the response from mailchimp replaces the content in the div.
I am using Gantry5 and I have successfully implemented the form, and when the user clicks the submit button, I get a response from the mailchimp server.
The problem I am having is that rather than replacing the content in the block, it is refreshing the page and then displaying the json object in raw format.
The twig is below:
{% extends '#nucleus/partials/particle.html.twig' %}
{% block particle %}
{% if particle.title %}<h2 class="g-title">{{ particle.title|raw }}</h2>{% endif %}
{% if particle.headtext %}<div class="g-newsletter-headtext">{{ particle.headtext|raw }}</div>{% endif %}
<div id="mc_embed_signup" class="g-newsletter {{ particle.class|e }}">
<form class="g-newsletter-form validate" id="mailchimp-subscribe" action="//{{ particle.baseurl|e }}" method="POST">
<input type="hidden" name="u" value="{{ particle.uuid|e }}">
<input type="hidden" name="id" value="{{ particle.lid|e }}">
<div id="mergeTable" class="mergeTable">
<label for="MERGE0">Email Address <span class="req asterisk">*</span></label>
<input class="g-newsletter-inputbox required email" type="email" autocapitalize="off" autocorrect="off" name="MERGE0" id="MERGE0" size="25" value="" placeholder="{{ particle.inputboxtext|raw }}">
<input type="submit" value="{{ particle.buttontext|raw }}" name="submit" id="mc-embedded-subscribe" class="g-newsletter-button button {{ particle.buttonclass|e }}">
</div>
<input type="hidden" name="ht" value="{{ particle.htvalue|raw }}">
<input type="hidden" name="mc_signupsource" value="hosted">
</form>
</div>
<script src="/javascripts/application.js" type="text/javascript" charset="utf-8" async defer>
$('#mailchimp-subscribe').submit(function(e) {
var $this = $(this);
$.ajax({
type: "GET", // GET & url for json slightly different
url: "//{{ particle.baseurl|e }}-json?c=?",
data: $this.serialize(),
dataType: 'json',
contentType: "application/json; charset=utf-8",
error: function(err) { alert("Could not connect to the registration server."); },
success: function(data) {
if (data.result != "success") {
// Something went wrong, parse data.msg string and display message
} else {
// It worked, so hide form and display thank-you message.
}
}
});
return false;
});
</script>
{% endblock %}
The response I get from the server is below:
{"result":"success","msg":"Almost finished... We need to confirm your email address. To complete the subscription process, please click the link in the email we just sent you."}
What I want to do is either replace the content in the block with a formatted response or open the formatted response in a modal.
I tried adding data-uk-lightbox data-lightbox-type="iframe" to the button (I am using UIKit" and a modal opens up, but I only get a loading indicator that never refreshes, with even the unformatted json response.
Please advise.

have you tried this?
e.preventDefault()

Related

Form with different target urls to send the POST to

I have one view displaying a form. Now, depending on the button the user chooses, I want to have the data processed in a different way. These "different ways" correspond with different views that I want the POST request go to.
Please help me building a form with multiple buttons leading to different urls/views.
<form action="/your-name/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Process form with view ONE">
<input type="submit" value="Process form with view TWO">
<input type="submit" value="Process form with view THREE">
</form>
My problem here is that the action attribute of the form tag defines where this POST request is going to. How can I change that target url via multiple buttons?
I know I could also handle this logic on the server-side. But the question is: Do I have to? If not, please show me the way
you can use ajax with onclick attribute. post_url as an argument like this
<form method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Process form with view ONE" onclick='testFun(url_one)'>
<input type="submit" value="Process form with view TWO" onclick='testFun(url_two)'>
<input type="submit" value="Process form with view THREE" onclick='testFun(url_three)'>
</form>
To handle this on the frontend without even using ajax, you can use JavaScript to change the action URL of the form based on which button is clicked before submitting the form.
Slight change on your HTML code
<form method="post" id="my-form">
{% csrf_token %}
{{ form }}
<input type="submit" id="view1" value="Process form with view ONE">
<input type="submit" id="view2" value="Process form with view TWO">
<input type="submit" id="view3" value="Process form with view THREE">
</form>
JavaScript for handling the redirection of the form based the button clicked
const form = document.getElementById('my-form');
const button1 = document.getElementById('view1');
const button2 = document.getElementById('view2');
const button3 = document.getElementById('view3');
button1.addEventListener('click', () => {
form.action = '/view1/';
});
button2.addEventListener('click', () => {
form.action = '/view2/';
});
button3.addEventListener('click', () => {
form.action = '/view3/';
});
// Submit the form
form.submit();
Note: You can use jQuery for better code optimization

Multiple Tags From Text Field value

I have created a form for people to subscribe to my newsletter and i would like to classify them by getting their gender and size (i'm in the garment industry). So i need to create tags.
I achieved to create the tag "newsletter", which will be the same for everyone subscribing.
Now I need to get both the size and gender on text field input and know it must be:
" (newletter, something(size), something(gender)) "
Here is the code for the contact[tags]:
{%- form "customer", class: "subscribe-form-flex" -%}<input type="hidden" name="contact[tags]" value="newsletter, ">
For the size:
<input type="hidden" class="text-field-6 desktop w-input" maxlength="256" name="size_" data-name="Size 2" placeholder="" id="Size" value="Size" >
For the Gender:
<input type="hidden" class="text-field-7 desktop w-input" maxlength="256" name="gender_" data-name="Gender 2" placeholder="" id="Gender" required="" value="Gender">
My text field IDs are then:
Size
Gender
Any idea, someone ?
Thank you in advance !
There are probably other solutions, but what I have done to solve a similar problem is
<div id="my_form_container">
{%- form "customer", class: "subscribe-form-flex" -%}
<input id="form_tags" type="hidden" name="contact[tags]"
value="newsletter">
<div id="select_container">
<select required="required" name="sizes"
onchange="var opt=this.options[0];if(opt.getAttribute('role')==='placeholder'&&!opt.selected)opt.parentNode.removeChild(opt);/*This is just to remove the placeholder*/"
id="form_sizes">
<option role="placeholder" value="">Size...</option>
<option>S</option>
<option>M</option>
<option>L</option>
<option>XL</option>
</select>
</div>
{% unless form.posted_successfully? %}
<script type="text/javascript">
$('#my_form_container form').submit(function (e) {
var $formTags = $("#form_tags");
var preferences = $("#form_sizes").val();
$formTags.val($formTags.val() + "," + preferences);
return true;
});
</script>
{% endunless %}
{%- endform -%}
</div>
So in practice you have your form with your inputs (I have added just the size) and then, before you submit the form, you modify the tag input to have all the tags you need.
I don't know your level of Javascript, let me know if you need more details.
For a Shopify store that doesn't use JQuery, you can use the below code.
Firstly create a hidden input with the name="contact[tags]" to point it at the Shopify tags field.
<input id="form_tags" type="hidden" name="contact[tags]">
Get all of your inputs and concatenate them using comma separation which will send these as separate tags.
<script>
document.querySelector("form#contact_form").addEventListener("submit", function(e){
const maleSize = document.getElementById("male-shoe-size").value;
const femaleSize = document.getElementById("female-shoe-size").value;
document.getElementById('form_tags').value = maleSize + "," + femaleSize;
});
</script>

Django, How to pass HTML form values to URL upon form submission?

The project have url as follows,
path('post/<str:state>/',SearchView.as_view(),name='search-data')
I have a HTML form, upon filling and submitting it supposed to pass filled form data to URL.
<form action={% url 'search-data'%} method="get" >
{% csrf_token %}
<input type="text" name="fname">
But it does not work as supposed to be.
When form submitted it gives below URL
http://127.0.0.1:8000/%7Burl?csrfmiddlewaretoken=2RZfZ4cxLB...
You don't have to pass <str:state> argument in your urlpatterns just pass path('post/search',SearchView.as_view(),name='search-data') or whatever you want but problem is when you pass an argument like this post/<str:state>/ than you have to specify that in your form action also
like this {% url 'search-data' state %} initialy you don't have any state so that's why you have to get the state name from your form so finnaly your code look like this
<form action={% url 'search-data'%} method="get" >
{% csrf_token %}
<input type="text" name="fname">
<input type="submit" value="search">
</form>
and than in your views you've to get it from request.GET method like this
def search(request):
state = request.GET.get('fname', None)
.... do whatever you want
return response_or_data

Keyerror when doing a json post request on sqlalchemy

I am working on a project for class where I have to edit starter Code. I keep getting a KeyError code and I am not sure what the issue is.
line 250, in create_venue_submission
name = request.get_json()['name']
KeyError: 'name'
Code Below from new_venue.html
I added id="name", city, etc.. for all the divs . I'm not sure if that is the correct way to do it but thats the only way i figured to pull all the data from the form.
{% extends 'layouts/main.html' %}
{% block title %}New Venue{% endblock %}
{% block content %}
<div class="form-wrapper">
<form id="venueInfo" method="post" class="form">
<h3 class="form-heading">List a new venue <i class="fa fa-home pull-right"></i></h3>
<div id='name' class="form-group">
<label for="name">Name</label>
{{ form.name(class_ = 'form-control', autofocus = true) }}
</div>
<div class="form-group">
<label>City & State</label>
<div class="form-inline">
<div id='city' class="form-group">
{{ form.city(class_ = 'form-control', placeholder='City', autofocus = true) }}
</div>
<div id='state' class="form-group">
{{ form.state(class_ = 'form-control', placeholder='State', autofocus = true) }}
</div>
</div>
</div>
<div id='address' class="form-group">
<label for="address">Address</label>
{{ form.address(class_ = 'form-control', autofocus = true) }}
</div>
<div id='phone_num' class="form-group">
<label for="phone">Phone</label>
{{ form.phone(class_ = 'form-control', placeholder='xxx-xxx-xxxx', autofocus = true) }}
</div>
<div id="genres" class="form-group">
<label for="genres">Genres</label>
<small>Ctrl+Click to select multiple</small>
{{ form.genres(class_ = 'form-control', autofocus = true) }}
</div>
<div id="fb_link" class="form-group">
<label for="genres">Facebook Link</label>
{{ form.facebook_link(class_ = 'form-control', placeholder='http://', autofocus = true) }}
</div>
<input type="submit" value="Create Venue" class="btn btn-primary btn-lg btn-block">
</form>
<script type="text/javascript">
document.getElementById("venueInfo").onsubmit=function(e){
e.preventDefault();
fetch('/venues/create',{
method:'POST',
body:JSON.stringify({
'name': document.getElementById('name').value,
'city': document.getElementById('city').value,
'state': document.getElementById('state').value,
'address': document.getElementById('address').value,
'phone_num': document.getElementById('phone_num').value,
'genres': document.getElementById('genres').value,
'fb_link': document.getElementById('fb_link').value,
}),
headers: {'Content-type': 'application/json'}
})
.then(function(){
})
}
</script>
</div>
{% endblock %}
Code below is from app.py
#app.route('/venues/create', methods=['GET'])
def create_venue_form():
form = VenueForm()
return render_template('forms/new_venue.html', form=form)
#app.route('/venues/create', methods=['POST'])
def create_venue_submission():
name = request.get_json()['name']
print(name)
flash('Venue ' + request.form['name'] + ' was successfully listed!')
return render_template('pages/home.html')
Looks like you're using Flask-WTF to make those form fields, then using a JS function to grab the values from these with getElementById.
The problem is you don't set an id on the form fields. To get a better visualization of this, have a look at the rendered HTML, rather than the template code itself.
So instead of:
{{ form.name(class_ = 'form-control', autofocus = true) }}
You're looking for someting like:
{{ form.name(id='name', class_ = 'form-control', autofocus = true) }}
Then verify that renders to something like:
<input autofocus class="form-control" id="name" name="name" type="text" value="">
Note this now has an id attribute, which should allow your JS function to grab the value.
You'll need to appy this same concept to the other form fields.
I added id="name", city, etc.. for all the divs . I'm not sure if that is the correct way to do it but thats the only way i figured to pull all the data from the form.
Doing this for the divs is no use... document.getElementById('name').value takes the value of an input field, so that's what you'd have to add the id attribute to, as above.
EDIT regarding comment
In your create_venue_submission route, the request.get_json method receives the values submitted by the javascript Fetch request. On the other hand, request.form contains the values if the form is submitted without Javascript.
With the Javascript method: e.preventDefault() prevents that traditional form submission when the button is clicked, and instead submits the Fetch request, with the header {'Content-type': 'application/json'}.
Either by removing that script tag, or running in a browser with javascript disabled it will fall back to the traditional submission method.
So you should probably do some logic in your flask route to test this condition. You can also use request.is_json boolean for this. Remember to do from flask import jsonify. Something like:
if request.is_json:
# Request came in via javascript based on the header provided.
name = request.get_json()['name']
# Add data to database or something
return jsonify({'message':f'{name} was sucessfully listed.'})
else:
# handle traditional form submission
name = request.form['name']
# Add data to database or something
flash('Venue ' + request.form['name'] + ' was successfully listed!')
return render_template('pages/home.html')
Of course you'll then need to handle the returned JSON in your frontend. So insead of:
.then(function(){
})
Something like:
.then(response => response.json())
.then(data => {
console.log(data);
});
Now with the browser's dev tools Console open you should see something like this when submitted:
Object { message: "Burning Man Festival was sucessfully listed." }
Of course you could then swap that console.log line out with anything to manipulate the dom, and access for example data['message'] to get the success string itself.

Fetch preventing rendering template in the controller

I'm working on a project for a class and I have starter code that I am editing. they way its supposed to work is that when the user creates a venue it goes to the home page with a message saying venue x has been listed. Once I add the post request in the new_venue.html it does nothing after I click the submit button nothing happens, but I know its doing something because the app.py prints the name that signed up.
Below is Code from the new_venue.html.
I added the script section and the post request
{% extends 'layouts/main.html' %}
{% block title %}New Venue{% endblock %}
{% block content %}
<div class="form-wrapper">
<form id="venueInfo" method="post" class="form">
<h3 class="form-heading">List a new venue <i class="fa fa-home pull-right"></i></h3>
<div class="form-group">
<label for="name">Name</label>
{{ form.name(id='name', class_ = 'form-control', autofocus = true) }}
</div>
<div class="form-group">
<label>City & State</label>
<div class="form-inline">
<div id='city' class="form-group">
{{ form.city(class_ = 'form-control', placeholder='City', autofocus = true) }}
</div>
<div id='state' class="form-group">
{{ form.state(class_ = 'form-control', placeholder='State', autofocus = true) }}
</div>
</div>
</div>
<div id='address' class="form-group">
<label for="address">Address</label>
{{ form.address(class_ = 'form-control', autofocus = true) }}
</div>
<div id='phone_num' class="form-group">
<label for="phone">Phone</label>
{{ form.phone(class_ = 'form-control', placeholder='xxx-xxx-xxxx', autofocus = true) }}
</div>
<div id="genres" class="form-group">
<label for="genres">Genres</label>
<small>Ctrl+Click to select multiple</small>
{{ form.genres(class_ = 'form-control', autofocus = true) }}
</div>
<div id="fb_link" class="form-group">
<label for="genres">Facebook Link</label>
{{ form.facebook_link(class_ = 'form-control', placeholder='http://', autofocus = true) }}
</div>
<input type="submit" value="Create Venue" class="btn btn-primary btn-lg btn-block">
</form>
<script type="text/javascript">
document.getElementById("venueInfo").onsubmit=function(e){
e.preventDefault();
fetch('/venues/create',{
method:'POST',
body:JSON.stringify({
'name': document.getElementById('name').value,
'city': document.getElementById('city').value,
'state': document.getElementById('state').value,
'address': document.getElementById('address').value,
'phone_num': document.getElementById('phone_num').value,
'genres': document.getElementById('genres').value,
'fb_link': document.getElementById('fb_link').value,
}),
headers: {'Content-type': 'application/json'}
})
.then(function(){
})
}
</script>
</div>
{% endblock %}
below is the code from app.py
#app.route('/venues/create', methods=['GET'])
def create_venue_form():
form = VenueForm()
return render_template('forms/new_venue.html', form=form)
#app.route('/venues/create', methods=['POST'])
def create_venue_submission():
name = request.get_json()['name']
print(name)
flash('Venue ' + request.form['name'] + ' was successfully listed!')
return render_template('pages/home.html')
flash messages work with redirection, refer to https://flask.palletsprojects.com/en/1.1.x/patterns/flashing/#simple-flashing
so instead rendering the template, return redirection object to the home page:
#app.route('/venues/create', methods=['POST'])
def create_venue_submission():
# name = request.get_json()['name']
name = request.values.get('name')
print(name)
flash('Venue ' + request.form['name'] + ' was successfully listed!')
# return render_template('pages/home.html')
return redirect(url_for('home')) # -- HERE --
Update
i think you are doing things the wrong way, you don't need the javascript to submit the form data via ajax post since ajax is used to update the page without reloading it (btw you didn't put any logic in .then(function(){}) callback to show up the message), but after submitting the form you want to redirect the user to the home page with a flash message so the ajax approach you are using is the wrong approach, just remove or comment the javascript code block and add the action to the form
<form id="venueInfo" method="post" action="{{ url_for('create_venue_submission') }}" class="form">
...
and in your function create_venue_submission() you should change
name = request.get_json()['name']
to
name = request.values.get('name')
# other fields
name = request.values.get('name')
city = request.values.get('city')
state = request.values.get('state')
address = request.values.get('address')
phone_num = request.values.get('phone_num')
genres = request.values.get('genres')
fb_link = request.values.get('fb_link')
see this wiki https://stackoverflow.com/a/16664376/12368419
If you are submitting your form via ajax, you will need to redirect in the success portion of the ajax call. Also, keep in mind that flash will not work via ajax. You will need to use the standard form post.
$.ajax({
'url': 'post_url',
success: function(msg){
//read the msg here and determine if success or not then redirect
}
})
This will not work when doing an ajax form post:
return redirect(url_for('index'))
If you use the standard (pseudo code) without posting via ajax, it will redirect:
<form method=post action=route>
<input type=submit>
</form>