render a complete twig template extending another one with ajax in Symfony - javascript

I have my main layout named layout.html.twig. In this layout I made the block content for extends him, see the code:
<!DOCTYPE html>
<html>
<!-- my html code, meta header, body ... etc -->
<body>
<div class="col-md-12 panel panel-default" id="content">
{% block content %}
{# extend content to the principal layout #}
{% endblock %}
</div>
</body>
</html>
In fact, each time I woul like to load another page content in my {% block content %}, I extends the layout like this for example in indexEquipement.html.twig:
{% extends "MyBundleBundle::layout.html.twig" %}
{% block content %}
<div class="page-header">
<h4>add an equipement</h4>
</div
<select id="selectEquipement">
<option selected disabled>choose an equipement</option>
<option value="{{ path('addEquipement1') }}">Equipement 1</option>
<option value="{{ path('addEquipement2') }}">Equipement 2</option>
</select>
{% endblock %}
This is the route file for the index:
indexEquipements:
path: /manageEquipements
defaults: { _controller: MySpaceMyBundle:ManageEquipements:indexEquipements }
requirements:
methods: GET
As you can see, in this example, I have a select tag with url for the values option. I try to render the view of my select choice with ajax without reloading the browser page.
Each url for the option values are in fact a view with a different form. I choose an option in my select, and ajax return in the same page the form matching with my choice.
See my controller for the Equipement 1 addAction():
/**
*
* #Route("/manageEquipement/addEquipement1", name="addEquipement1")
* #Method("get")
*/
public function addEquipements1Action() {
$eq = new Equipement1;
$form = $this->createForm(new Equipement1Type(), $eq);
$request = $this->getRequest();
if ($request->isMethod('POST') | ($form->isValid())) {
$form->bind($request);
$em->persist($eq);
$em->flush();
return $this->redirect($this->generateUrl('indexEquipements'));
} else {
return $this->render('MySpaceMyBundle:MyFolder:addEquipement1.html.twig', array('form' => $form->createView() ));
}
}
And now the route file for this action:
addEquipement1:
path: /manageEquipement/addEquipement1
defaults: { _controller: MySpaceMyBundle:ManageEquipements:addEquipement1 }
requirements:
methods: GET
addEquipement1_process:
path: /manageEquipement/addEquipement1/process
defaults: { _controller: MySpaceMyBundle:ManageEquipements:addEquipement1 }
requirements:
methods: POST
How can I render the form for addEquipement1Action() in ajax?
## EDIT ##
This the controller action for addEquipement1Action():
/**
*
* #Route("/manageEquipement/addEquipement1", name="addEquipement1")
* #Method("get")
*/
public function addEquipement1Action() {
$request = $this->getRequest();
$eq = new Equipement1;
$form = $this->createForm(new Equipement1Type(), $eq);
if($request->isXmlHttpRequest()){
$template = $this->forward('MySpaceMyBundle:MyFolder:addEquipement1.html.twig', array('form' => $form->createView() ))->getContent();
$json = json_encode($template);
$response = new Response($json, 200);
$response->headers->set('Content-Type', 'application/json');
if ($request->isMethod('POST') | ($form->isValid())) {
$form->bind($request);
$em->persist($eq);
$em->flush();
return new Symfony\Component\HttpFoundation\RedirectResponse($this->generateUrl('indexEquipements'));
} else {
return $response;
}
}
}
This is the index of equipement:
{% extends "MyBundleBundle::layout.html.twig" %}
{% block content %}
<div class="page-header">
<h4>add an equipement</h4>
</div
<select id="selectEquipement">
<option selected disabled>choose an equipement</option>
<option value="{{ path('addEquipement1') }}">Equipement 1</option>
<option value="{{ path('addEquipement2') }}">Equipement 2</option>
</select>
<div id="formEquipement"></div>
<script type="text/javascript">
$('#selectEquipement').change(function(event) {
$.ajax({
type: "GET",
url: "{{ path('addEquipement1') }}",
dataType: "html",
success: function(data){
$("formEquipement").append(data);
}
});
});
</script>
{% endblock %}
When I apply a choice in my select tag, I can see when I debug in my browser that the ajax call works, but it returns me this error concerning my view I would like to display:
The controller must return a response (null given). Did you forget to
add a return statement somewhere in your controller?
And this is my twig view for addEquiment1.html.twig:
<form action="{{ path('addEquipement1_process') }}" method="POST" {{ form_enctype(form) }}>
<div class="col-md-5">
<br>
<div>
{{ form_errors(form) }}
</div>
<br>
<div>
{{ form_widget(form) }}
</div>
<br>
<div>
{{ form_rest(form) }}
</div>
<br>
<div>
<input type="submit" value="Add" class="btn btn-small btn-success"/>
</div>
</div>
</form>
How can I render this view properly in my actually page without refreshing the browser with Ajax?

How can I render the form for addEquipement1Action() in ajax?
you could put your form in a div container and parse it out via JS in the client
or you separate twig views. include the form yourform.html.twig in the block of indexEquipement.html.twig and add an if statement in your controller that if a parameter onlyForm is set, then render yourform.html.twig else indexEquipement.html.twig. You can of course also use different routes and controler action to drop the if statement, which might would be nicer to read.

Related

How to handle error 404 when total items is lower than" per_page" flask pagination?

I am developing a web application with Flask Python.
In my application, there is a report page that displays a table of results with Flask pagination.
At the bottom of my table, I added a "select options" field called "number_result_per_page" to let the user choose the "per_page" parameters (number of items per page).
I made a javascript that reloads the page with the "per_page" parameters updated according to this user's choice.
PROBLEM :
My problem is if the user is almost on the last page, and he decides to display "100" results per page, and there are less than 100 results to display, I get a 404 error.
I am sure I am not the only one who faced this kind of issue. Is there any solution to avoid this scenario?
Or can I catch this particular error 404 to display an "error" popup and reload the previous page which was correct?
Here is sample of my code:
# Route
#app.route('/task_user_report/<int:task_user_id>/view', methods=['GET', 'POST'])
#login_required
def task_user_report(task_user_id):
args = request.args
per_page_task_report = args.get("per_page_task_report", 1, type=int)
actions = db_mysql.session.query(Action).filter_by(id_task_user=task_user_id) \
.filter(Action.id_contact==Contact.id) \
.paginate(page=page_task_report, per_page=per_page_task_report)
render_template(render_html,title='Task Report',legend='Task Report',actions=actions, \
per_page_task_report=per_page_task_report)
# Template
<div class="row justify-content-center" style="width: 95%;">
<div class="col-7 my-auto text-right">
<div class="text-center mt-4" style="display: inline-block;">
<nav aria-label="Pagination">
<ul class="pagination justify-content-center">
{% for page_num in actions.iter_pages(left_edge=1,right_edge=1,left_current=1, right_current=2) %}
{% if page_num %}
{% if actions.page == page_num %}
<li class="page-item active"><a class="page-link" href="{{ url_for('task_user_report',task_user_id=task_user_id,page_task_report=page_num,per_page_task_report=per_page_task_report) }}">{{ page_num }}</a>
{% else %}
<li class="page-item"><a class="page-link" href="{{ url_for('task_user_report',task_user_id=task_user_id,page_task_report=page_num,per_page_task_report=per_page_task_report) }}">{{ page_num }}</a>
{% endif %}
{% else %}
<li> ... </li>
{% endif %}
{% endfor %}
</ul>
</nav>
</div>
</div>
</div>
<div class="col-5 my-auto text-right">
<select style="width:180px!important;display: inline-block;" id="number_result_per_page" class="form-control form-control-sm w-25 text-right" onchange="change_per_page()">
<option value="10"
{% if per_page_task_report== 10 %}
selected
{% endif %}
>10 results per page</option>
<option value="25"
{% if per_page_task_report== 25 %}
selected
{% endif %}
>25 results per page</option>
<option value="50"
{% if per_page_task_report== 50 %}
selected
{% endif %}
>50 results per page</option>
<option value="100"
{% if per_page_task_report== 100 %}
selected
{% endif %}
>100 results per page</option>
</select></div>
</div>
# Javascript
<script>
function change_per_page() {
var url = window.location.href;
// we get value of per page option
var per_page_option = document.getElementById("number_result_per_page");
var per_page_option_value = per_page_option.value;
// We check if parameter exist in url
var is_per_page = url.includes("per_page_task_report");
if (is_per_page == true){
var queryParams = new URLSearchParams(window.location.search);
// new value of "page_task_report" is set to "per_page_option_value"
queryParams.set('per_page_task_report', String(per_page_option_value));
// change the search property of the main url
history.pushState(null, null, "?"+queryParams.toString());
var new_url = window.location.href;
}
else{
// There are 2 cases :
// [1] url doesn't have any parameters (ex:http://127.0.0.1:5000/task_user_report/2/view)
// [2] url has a parameter (ex:http://127.0.0.1:5000/task_user_report/2/view?page_task_report=2)
var is_page_task_report = url.includes("page_task_report");
if (is_page_task_report == true){
var new_url = url + '&per_page_task_report=' + per_page_option_value;
}
else{
var new_url = url + '?per_page_task_report=' + per_page_option_value;
}
}
window.location.replace(new_url);
}
</script>
In your paginate() call, set error_out=False like this, paginate(..., error_out=False)
The default is True. This behavior is not always desired though. If you are using Flask as an API backend, this can sometimes cause errors. It's up to you how you want to handle it by either providing an empty result set or by defaulting the page back to one when no results are returned.

How to get data from AJAX request with Symfony 4?

I'm trying to get data from an AJAX request with Symfony 4 and what I get is not that I'm expecting.
Here is my routes.yaml
(...)
ajax_test:
path: /ajax/test
defaults: { _controller: 'App\Controller\AjaxTestController::test' }
requirements:
_method: POST
My controller :
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class AjaxTestController extends AbstractController
{
public function test(Request $request) {
if ($request->isXmlHttpRequest()) {
$serializer = $this->get('serializer');
$response = $serializer->serialize('test ok', 'json');
return new JsonResponse(['data' => $response]);
}
return new Response("test ko", 400);
}
}
Here is the template where I make the AJAX request :
{% extends "layout.html.twig" %}
{% set active = 'connexion' %}
{% block page_title 'Login' %}
{% block final_javascripts %}
{{ encore_entry_script_tags('sendCredentials') }}
{% endblock %}
{% block content %}
(...)
<div class="row mt-4">
<div class="col-md-6">
<form id="connexion-form" action="{{ path('security_connexion') }}" method="post">
<div class="form-group">
<label for="email">Email</label>
<input type="text" id="email" name="_email" class="form-control">
</div>
<div class="form-group">
<label for="password">Mot de passe</label>
<input type="password" id="password" name="_password" class="form-control">
</div>
<button type="submit" class="btn btn-primary button">Se connecter</button>
</form>
</div>
</div>
</div>
</div></div>
{% endblock %}
And finally the JavaScript file (sendCredentials.js) where I make AJAX request :
$(document).ready(function() {
$('#connexion-form').submit(function(event) {
sendCredentials($('#email').val(), $('#password').val());
});
});
function sendCredentials(username, password) {
$.ajax({
method: "POST",
url: "/ajax/test",
data: {username: username, password: password},
async: false
}).done(function(msg) {
console.log(msg['data']);
console.log(msg);
});
}
The first log console.log(msg['data']); displays undefined. The second log console.log(msg); displays me the html code of the template itself, that is to say the code of the template generated by twig. I don't understand why. How to get the data wanted : 'test ok' ?
P.S. : I don't use credentials yet (username and password), I'm just making tests, I want first the AJAX request to work.

AJAX update list of object

Can someone help me and say whats wrong I did?
In my django project I have product_detail page which has comment part. I am tring to update comments list after successfully adding new comment with AJAX. Unfortunatly it updates all page. I am little bit comfused and need advice. I need to update only list of comments not all page.
product_detail.html:
<div class="card">
<div class="card-block">
<table id="comment-table">
<thead>
<tr>
<th>Author</th>
<th>Date</th>
<th>Comment Text</th>
</tr>
</thead>
<tbody>
{% include 'project/comment_list.html' %}
</tbody>
</table>
</div>
<div class="card-footer">
<form method="post" class="comment-form" id="comment-form" action="{% url 'project:comment_add' project_code=project.code product_code=product.code %}">
{% csrf_token %}
{% for field in form %}
<div class="form-group{% if field.errors %} has-error{% endif %}">
{% render_field field class="form-control" %}
{% for error in field.errors %}
<p class="help-block">{{ error }}</p>
{% endfor %}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">Send</button>
</form>
</div>
</div>
views.py:
def comment_add(request, project_code, product_code):
data = dict()
project = get_object_or_404(Project, pk=project_code, status='open')
product = get_object_or_404(Product, pk=product_code)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.author = request.user
comment.save()
product.comments.add(comment)
data['form_is_valid'] = True
data['html_comment'] = render_to_string('project/comment_list.html', {'product': product})
form = CommentForm()
else:
data['form_is_valid'] = False
else:
form = CommentForm()
context = {'project': project, 'product': product, 'form': form}
return render(request, 'project/product_detail.html', context)
js:
$(function () {
var saveForm = function () {
var form = $(this);
$.ajax({
url: form.attr("action"),
data: form.serialize(),
type: form.attr("method"),
dataType: 'json',
success: function (data) {
if (data.form_is_valid) {
$("#comment-table tbody").html(data.html_comment);
}
else {
$("#comment-form").html(data.html_comment_form);
}
}
});
return false;
};
$("#comment-form").on("submit", ".comment-form", saveForm);
});
The problem is type="submit" native refresh new page. You have to stop form submitting. Try something like this:
$("#comment-form").submit(function(event) {
/* stop form from submitting normally */
event.preventDefault();
// here your ajax code
});

No response when passing the values through AJAX and Flask

I am new to AJAX and Flask. I asked here about how to pass values and show them on the same page without the need to refresh the page and I was told about Ajax. However, after modifying the example given. I am getting a weird error.
Here's my HTML side:
<script type=text/javascript>
$(function() {
$('a#show').bind('click', function() {
$.getJSON('/show', {
a: $('input[name="node"]').val(),
b: $('input[name="parameter"]').val()
}, function(data) {
$("#result").text(data.result);
});
return false;
});
});
</script>
<form action=""> <!-- method = 'post'-->
<label for="node">Node</label>
<select name="node">
{% for o in data_nodes %}
<option value="{{ o.name }}">{{ o.name }}</option>
{% endfor %}
</select>
<label for="parameter">Parameter</label>
<select name="parameter">
{% for o in data_para %}
<option value="{{ o.name }}">{{ o.name }}</option>
{% endfor %}
</select>
<!-- Write your comments here -->
<button type="submit" class="btn btn-default" href="javascript:void(0);" id="show" >Send</button>
</form>
My Flask side:
#app.route('/dashboard/', methods=['GET', 'POST'])
def dashboard():
return render_template("dashboard.html", data_nodes = [{'name': 'c9 - Office'}, {'name': 'f20 - Home'}, {'name': 'f21 - School'}],
data_para = [{'name': 'Temperature'}, {'name': 'RSSI'}, {'name': 'LQI'}]) # data_nodes, data_para
#app.route('/show')
def show():
a = request.form.get('node')
b = request.form.get('parameter')
print a
print b
result = a+b
return jsonify(result) # I only intend to print the result on the page here for now.
And here's the response I get. The page refreshes too.
127.0.0.1 - - [24/Apr/2016 23:41:10] "GET /dashboard/?node=f20+-+Home&parameter=RSSI&time=t3 HTTP/1.1" 200
127.0.0.1 - - [24/Apr/2016 23:41:05] "GET /static/js/ie10-viewport-bug-workaround.js HTTP/1.1" 404 -
I tried different variations but I am not exactly sure why the result isn't appearing.
Based on your log file, you are making the following request to your webserver:
127.0.0.1 - - [24/Apr/2016 23:41:10] "GET /dashboard/?node=f20+-+Home&parameter=RSSI&time=t3 HTTP/1.1" 200
This means, the parameters you are looking for are coming in as request parameters, not as form elements. You can reference this post for a more thorough discussion, but I would just change form to args.
#app.route('/show')
def show():
a = request.args.get('node')
b = request.args.get('parameter')
Update:
It looks like there are also errors in your HTML file:
<script type=text/javascript>
$(function() {
$('#myForm').submit(function(ev) {
$.getJSON('/show', {
node: $('input[name="node"]').val(),
parameter: $('input[name="parameter"]').val()
}, function(data) {
$("#result").text(data.result);
});
ev.preventDefault();
});
});
</script>
<form id='myForm' action=""> <!-- method = 'post'-->
<label for="node">Node</label>
<select name="node">
{% for o in data_nodes %}
<option value="{{ o.name }}">{{ o.name }}</option>
{% endfor %}
</select>
<label for="parameter">Parameter</label>
<select name="parameter">
{% for o in data_para %}
<option value="{{ o.name }}">{{ o.name }}</option>
{% endfor %}
</select>
<!-- Write your comments here -->
<button type="submit" class="btn btn-default" href="javascript:void(0);" id="show" >Send</button>
</form>

Change status in ajax on selectbox

I have a question, So ,this code work fine only for the first select box and I dont understand why. For first select box update on database work fine but If I tried to change the statut for the second select box the status remain unchanged. Can you help me? Sorry for my english
My view :
{% for gift in aFilterGifts %}
<form action="" id="updateStatus" method="post">
<select id="statusSelect"
name="{{ gift.id }}"
class="form-control"
onChange="updateCadeauStatus({{ gift.id }})">
{% for key,statut in form_logistique.statut.choices %}
<option value="{{ key }}"
{% if gift.etat == key %}selected="selected"{% endif %}>
{{ statut }}
</option>
{% endfor %}
</select>
</form>
{% endfor %}
<script>
function updateCadeauStatus(id) {
var id = id;
var selectedName = $("#statusSelect option:selected").val();
var url_deploy = 'http:localhost/updateStatus'
console.log(id);
console.log(selectedName);
$.ajax({
url: url_deploy,
type: "POST",
async: true,
data: { id_cadeau:id, id_status:selectedName}
});
}
</script>
The controller :
public function updateStatus(){
$iGiftId = $_POST['id_cadeau'];
$iNewStatus = $_POST['id_status'];
$bUpdate = $this->updateStatusByGiftId($iGiftId, $iNewStatus);
}
The model :
public static function updateStatusByGiftId($iGiftId, $iStatusId){
$request = sprintf( ' UPDATE `%s` set etat = %d WHERE id_instant_gagnant = %d ', $table, $iStatusId, $iGiftId);
return Mysqli::query($request, $database);
}
Thx in advance.
All your select boxes do have the same id id="statusSelect".
That's not valid HTML.
One solution would be to append the gift id to the id tag to create unique ids:
{% for gift in aFilterGifts %}
<form action="" id="updateStatus{{ gift.id }}" method="post">
<select id="statusSelect{{ gift.id }}"
name="{{ gift.id }}"
...
and for the script:
<script>
function updateCadeauStatus(id) {
var id = id,
selectedName = $("#statusSelect" + id + " option:selected").val(),
url_deploy = 'http:localhost/updateStatus';
console.log(id);
...

Categories