I have the following AJAX script running in my Django template:
function create_table() {
$.ajax({
method: "GET",
url: "/api/data/",
success: function(data){
console.log('button clicked')
console.log(data)
//$('#table').html('<div class="test">' + data['Name'] +'</div>');
//$('#table').load('table_to_load.html');
},
error: function(error_data){
console.log("errorrr")
console.log(error_data)
}
})
}
document.getElementById("create_table").onclick = function() {
create_table();
return false;
}
The purpose of this script is to create a HTML table upon button click populated by dictionary data fetched by the AJAX call.
The AJAX call collects the data correctly, however, I don't know how to go about inserting the table.
Should I write the table HTML in pure Javascript/jQuery inside the AJAX call? Or maybe load a pre-prepared HTML (how do I reference its directory inside the call?)?
My preferred method though would be to write the template for the table in Django's template tag language and somehow reference in it the data fetched by AJAX. Something like:
<table>
<tr>
<th>dictionary key</th>
<th>dictionary value</th>
</tr>
{% for key, value in dictionary.items %}
<tr>
<td>{{ key }}</td>
<td>
{{ value }}
</td>
</tr>
{% endfor %}
</table>
But I am not sure if it's possible.
You can do it either way, but you seem like you are trying to do in in 1/2 of both ways.
You can use ajax to make a call and get back some json data, and then use that json object to build the html string and use jquery to insert that html at the correct point in your page - nothing wrong with that way imo, though I personally am not a fan of hand stitching together html statements (mostly because for complicated pages it gets hard to design them and debug them this way, but for small pieces its OK).
An alternative is to have jquery call django view (via a url you have defined), and have that django view make the database call and use a template that is already in your project as a html doc, and then render that template and data just like you would any other django page. That view would return an httpresponse containing html, and then jquery would just insert that html into the page as before.
Related
I have a view in HTML whereby it displays a list of table with attendees;
<table>
{% for people in Attendees %}
<tr>
<td>{{ people.id }}</td>
<td>{{ people.name }}</td>
<td>
<a id='deleteAttendeeButton' class="btn btn-primary btn-xs">Delete</a>
</td>
</tr>
{% endfor %}
</table>
The table has a delete button for all entries and when clicked, I am using AJAX for a GET request as per below;
$("#deleteAttendeeButton").on("click", function () {
$.ajax({
url: '/modifyAttendee/?id={{ people.id }}',
type: 'GET',
})
});
I want to use AJAX to send the people.ID variable to view so that the view can determine what object to delete in the database. However, the issue is I cannot seem to pass in a jinja variable in AJAX.
Am I missing something in my AJAX statement? If not, what would be an ideal solution?
Note: I do not want to use 'href' in the button because I do not want to reload the page.
Well, it doesn't work like that. The template tags are evaluated at render time, but you need the ID of the element that was clicked. You can use the data element to hold the ID and get it back in the JS.
There are a few other things wrong here too; in particular, HTML element ids need to be unique, but you are using the same one for every link in the loop. You should use a class instead.
<a class='deleteAttendeeButton btn btn-primary btn-xs' data-people_id="{{ people.id }}">Delete</a>
...
$(".deleteAttendeeButton").on("click", function () {
var people_id = $(this).data('people_id');
$.ajax({
url: '/modifyAttendee/?id=' + people_id,
type: 'GET',
})
(Note, unless you have specifically configured it, Django does not use Jinja; this is almost certainly Django template language which is similar but not the same. It doesn't affect your problem, though.)
I have a drag and drop UI. Its like a bucket list. What I have done is I have seperated the forms and the UI html for easier data manipulation. I have an empty form like this: (Take note that I am using spring form tags "form:")
<form:form method="POST" id="sampleForm" commandName="sampleForm" modelAttribute="sampleForm" class="navbar-form navbar-center">
<table id="formToBeSaved">
</table>
</form:form>
And then I have a script that is triggered whenever my other sortable receives an item. Sample below:
$("#sortable2").sortable({
connectWith : "#sortable2 ul",
scroll: false,
receive: function(event,ui) {
var saveElement = '<tr id="saveRow['+statusCounter+']"><td><form:input type="hidden" '+
'path="sampleFormModel['+statusCounter+']." class="form-control" '+
'id="name['+statusCounter+'].sampleForm" value="'+ someValue +'"></form:input></td></tr>'
console.log(saveElement);
$("#formToBeSaved").append(saveElement);
statusCounter = statusCounter + 1;
console.log(statusCounter);
};
},
Now the problem is, whenever I submit the form thru ajax an error occurs. It seems that the system does not recognize my < form:input > tag (since I am using spring) because I've just assigned it as a string into a variable. Is there a way to fix this? Thanks stackoverflow community!
You are using Spring XML templates, but the web browser can only work with HTML. This works when you are declaring your code in JSP files because Sprint will parse them and transform them to HTML.
So instead of manually adding JSP to the document, you should edit your JSP files in a way they generate the JSP you expect. In this case, you could use <c:forEach> to generate each <tr>. This answer could help you to do that.
If you only have access to the datas required by these <tr> dynamically, I do not know Sprint enough to help you. Maybe you could find some resources on this question.
Deleted the previous question to clarify it a bit.
I have A Django powered website and a template with dynamically populated table.
Template:
<table id = 'my_table'>
<thead>
......
</thead>
<tbody>
</tbody>
</table>
Javascript
$(function ()
{
refresh_vendors_list_table();
});
function refresh_vednors_list_table()
{
$.get("/ajax_refresh_vendors_list_table/", function(response)
{
$('#my_table').find('tbody').html(response);
});
}
View:
def ajax_refresh_vendors_list_table(request):
template = 'vendors/vendors_list_paginator.html'
vendors_qs = vendors_qs.filter(...)
context = {'vendors_qs':vendors_qs}
return render_to_response(template,
context,
context_instance=RequestContext(request))
Template for the table (vendors_list_paginator.html)
{% load el_pagination_tags %}
{% paginate vendors_qs %}
{% for vendor in vendors_qs %}
<tr>
......
</tr>
{% endfor %}
{% show_more_table %}
When pressing each row in the table, I am redirected to the corresponding vendor's profile. After editing the profile, I press the back button and coming back again to the table page. At this stage, I start to debug the $.get("/ajax_refresh_vendors_list_table/", data, function(response)
I also put a breakpoint at template = 'vendors/vendors_list_paginator.html' (let's call it breakpoint A)
A very strange behaviour is observed in $.get function: the corresponding Django view is not called (I don't see my app stop at breakpoing A) and yet $('#my_table').find('tbody').html(response); is executed directly as if the $.get function should execute successfully !!!!
In other words, $.get function is executed without any participation of server-side (I emphasize the word any. At first I thought that maybe I messed up the URLConf file and some other view was called. To check this, I cleared up the Django IDE console prior to pressing the browser back button and after pressing it, saw nothing in the Django IDE console meaning that $.get request successfully executed without ANY VIEW CALLED !!!)
Does it have something to do with pagination ? I am completely lost. Help please !
UPDATE
As per Sardorbek's answer, I've included the following decorators before the definition of ajax_refresh_vendors_list_table
#require_http_methods(["GET"])
#never_cache
def ajax_refresh_vendors_list_table(request):
...
Hm... Looks like your browser is caching this request. Trying wrapping this url with never_cache
never_cache(url(...))
He is currently working on code that has to filter the data in the table. Ajax will call the link and gets the response (json) results with answer. However, I came across a problem. I have to somehow render tables and I do not want to do this by append etc.
Can I somehow again generate views or blade file?
The default view is DefController#index but ajax use url which controller is DefController#gettabledata.
public function gettabledata($id){
return response()->json(Def::find($id)->getallmy->all());
}
You can put the part in your template corresponding to the table in a separate .blade.php file, and #include that in your main layout.
main.blade.php :
<html>
...
<body>
<div class="table-container">
#include('table')
</div>
</body>
...
And
table.blade.php:
<table>
#foreach($rows as $row)
<tr>
<td> $row->title ... </td>
</tr>
#endforeach
</table>
In this way you can use a simple jQuery $('div.table-container').load(url) and on your server just render and respond that part as an html string. return view('table', $data)
Javascript:
function refreshTable() {
$('div.table-container').fadeOut();
$('div.table-container').load(url, function() {
$('div.table-container').fadeIn();
});
}
The answer is yes, you can. Webinan certainly pointed you in the right direction. This approach is slightly different.
First things first, you need a seperate view for the table. A very basic example for the HTML markup:
<div class="table-container">
#include('partials.table') // this view will be async loaded
</div>
We start by making a call to the server with jQuery (can be done with Javascript too) using the shorthand ajax function: var $request = $.get('www.app.com/endpoint');. You can also pass along any data to your controller on the backend.
Now on the serverside, within your controller, render and return the table view:
class EndpointController extends Controller
{
/**
* Returns a rendered table view in JSON format.
*
* #param Request $request
* #return \Illuminate\Http\JsonResponse
*/
public function ajax(Request $request)
{
$html = view('partials.table', compact('view'))->render();
return response()->json(compact('html'));
}
}
If everything worked out, the done callback will be triggered. Simply grab the html variable and set it as the content of the table's container.
function renderTable() {
var $request = $.get('www.app.com/endpoint'); // make request
var $container = $('.table-container');
$container.addClass('loading'); // add loading class (optional)
$request.done(function(data) { // success
$container.html(data.html);
});
$request.always(function() {
$container.removeClass('loading');
});
}
Hope this helps!
To update and change page content without reloading the page in Laravel 5.4 i do the following:
First create the blade view in the "views" folder called "container.blade.php" it will contain the following code (in this case a select box that is rendering a list of abilities from the package Bouncer (but you can use the #foreach on any Laravel collection you like):
<select>
{{ $abilityList = Bouncer::role()::where('name','admin')->first()->getAbilities()->pluck('name') }}
#foreach ( $abilityList as $ab )
<option value="{{ $ab }}">{{ $ab }}</option>
#endforeach
</select>
Add this to you main blade file (e.g. home.blade.php) making sure to use a div with an id you can reference:
<div id="abilityListContainer">
#include('container')
</div>
Now on your main blade file (e.g. home.blade.php) add a button that will trigger the function that will communicate with the Laravel controller:
<input type="button" value="reload abilities" onClick="reloadAbilities()"></input>
Then add the javascript for this function, this loads the generated html into your div container (note the "/updateAbility" route next to ".get" - this is a Laravel route which we will set up in the next step):
var reloadAbilities = function()
{
var $request = $.get('/updateAbility', {value: "optional_variable"}, function(result)
{
//callback function once server has complete request
$('#abilityListContainer').html(result.html);
});
}
Now we set up the Laravel route for this action, this references our controller and calls the function "updateAbilityContainer". So edit your /routes/web/php file to have the following route:
Route::get('updateAbility', array('as'=> 'updateAbility', 'uses'=>'AbilityController#updateAbilityContainer'));
Finally in app/Http/Controllers make the file "abilityController.php" (you can also use "php artisan make:controller abilityController"). Now add this function to process the changes, generate the html and return it to the javascript function (note you may also have to use the namespaces as well):
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Input;
class AbilityController extends Controller
{
public function updateAbilityContainer()
{
// use this if you need to retrieve your variable
$request = Input::get('value');
//render and return the 'container' blade view
$html = view('container', compact('view'))->render();
return response()->json(compact('html'));
}
}
Thats it, your blade "container" will now reload when you click the button and any changes to the collection you are rendering should update without reloading the page.
Hopefully this fills in some blanks left in the other answers. I hope it works for you.
In my Django project, I have a page that displays all the instances of a particular model. I want to auto-refresh the page every few seconds, but only update the relevant part of the page.
I'll describe each part. First - I have a Django view. In its simplest form, it looks like this
class MyListView(ListView):
lookup_field = 'uuid'
model = Foo
template_name = 'mylist.html'
def get_queryset(self):
return list(Foo.objects.all()) # finds all my objects from DB
def get_context_data(self, **kwargs):
objects = self.get_queryset()
context = super(MyListView, self).get_context_data(**kwargs)
context['foos'] = objects # this gets passed to html page
return context
So it finds all Foo objects from the database and returns it to the html.
The html page displays a table, where each entry is a row. Furthermore, there is a checkbox at the beginning of each row. Like this
<table id="content">
<thead>
<tr>
<th><input type="checkbox" id="chckHead"/></th>
<th>UUID</th>
<th>Attribute1</th>
<th>Attribute2</th>
</tr>
</thead>
<tbody>
{% for item in foos %}
<tr id="{{ item.uuid }}">
<td>
<input type="checkbox" name="checkboxlist" class="chcktbl" />
</td>
<td><code>{{ item.uuid }}</code></td>
<td>{{ item.attribute1 }}</td>
<td>{{ item.attribute2 }}</td>
</tr>
{% endfor %}
</tbody>
</table>
Now, some of the attributes of the Foo model may get updated in the background, so I want to somehow refresh the page periodically with new data.
I did that in javascript, like this
function setup() {
setTimeout("refreshPage();", 5000);
}
function refreshPage() {
window.location = location.href;
}
$(document).ready(setup);
So every 5 seconds, JS would call refreshPage(), and my page gets refreshed. If the database was changed behind the scenes, my page would reflect it.
The problem with the above is that it refreshes the whole page, which includes the checkbox. So if I had already selected a checkbox, the refresh would reset it to its original state ('not checked').
So, what is the correct way to solve this problem?
Just really high level:
The window.location that you're adding is going to reset the whole page. By this time you've already figured that out. What you're looking for is something could be handled by using a page partial or by using a REST endpoint and pushing it back through the template.
Here are the steps you'll want refreshPage to preform instead.
Save off any values that you think are important
Send an ajax request to get either the partial or json
Render that new information to the page
Put back the values from step one over the values rendered in step 3
More likely you're going to want to wait to do the refreshPage if they're currently editing the area (saves you step 1 and 4). That, may be a choice you've already voted against.
I hope that helps, it's a very non-technical answer but I'm not sure I could give you a very specific answer without knowing A LOT more about your project.