I am trying to load json data from web2py controller to jQuery dataTable via AJAX.
But only blank dataTable is rendered (with desired formatting, search box, etc.)
Can somebody pl. point out into my code as to where I have a mistake.
Data is not displayed (as returned by "get_data" method).
I have made sure that the database tables have been populated.
Controller
def show_data():
return dict()
def get_data():
custdata = db.executesql(qry, as_dict=True)
return custdata
For testing purpose, I returned response.json(custdata) in a separate method & validated the same on "jsonlint.com".
It is valid json.
View (show_data.html)
{{extend 'layout.html'}}
$(document).ready(function() {
var oTable = $('.smarttable').dataTable( {
"sScrollY": "200px",
"sAjaxSource": "{{=URL('MIS','get_data.json')}}",
"sDom": "frtiS",
"bDeferRender": true
} );
} );
Lastly, html table tags are defined for a table with class="smarttable"
Your get_data function should return a dictionary, like this:
def get_data():
custdata = db.executesql(qry, as_dict=True)
return dict(data=custdata)
In web2py, a view is only called if the controller action returns a dictionary -- otherwise, the controller output is simply returned to the client as is (and as is, custdata has not yet been converted to JSON).
When you call the URL /MIS/get_data.json, the .json extension tells web2py to look for a /views/MIS/get_data.json view file to use for rendering the JSON. If it doesn't find that view file, it will trying using /views/generic.json, though it will only use generic.json for local requests, unless you override that by specifying response.generic_patterns=['json'].
As an alternative to using a view to render the JSON, you could also do:
def get_data():
custdata = db.executesql(qry, as_dict=True)
return response.json(custdata)
EDIT: The jQuery DataTables plugin requires the JSON to include some special parameters, so you'll have to add those before returning the JSON. To make things easier, you might consider using PowerTable (a web2py plugin for DataTables), or the jqGrid widget included with web2py's plugin_wiki (the widget can be used on any web page, not just wiki pages).
you have to have the "key" values in the "dictionary" that you give for return .
iTotalRecords,iTotalDisplayRecords,sEcho and aaData. you can find the explanations in http://datatables.net/usage/server-side
Related
There is an issue I am facing in using pagination widget with ajax. What I am doing is, that I select category and then hit an ajax request to fetch the corresponding list by loading the data in the template and returning the html like this:
<f:if condition="{articles}">
<f:then>
<f:widget.paginate objects="{articles}" as="paginatedArticles" configuration="{itemsPerPage: numberOfRecords}">
<f:for each="{paginatedArticles}" as="article">
<h2>
<f:link.action action="show" arguments="{article : article}"> {article.title}</f:link.action>
</h2>
<p>
<f:format.html>{article.description}</f:format.html>
</p>
<hr/>
</f:for>
</f:widget.paginate>
</f:then>
<f:else>
No Records Found
</f:else>
And in my controller in my ajaxMethod I am simply doing
$this->view->assign('articles', $result); so it loads up the template with my result.
But now after rendering the ajax, if I use the pagination, the view breaks. There is no styling or header or anything.
This is how it shows up when I click the next on paginate widget: http://prntscr.com/kr8vg0
Just for completeness, here is the setup.txt which I have written that calls the ajax.
// PAGE object for Ajax call:
tt_content.list.20 = CASE
tt_content.list.20 {
key.field = list_type
}
ajax = PAGE
ajax {
typeNum = 1272
config {
disableAllHeaderCode = 1
disablePrefixComment = 1
additionalHeaders {
1526302502.header = Content-Type: text/html;charset=utf-8
}
}
10 = CONTENT
10 {
table = tt_content
select {
pidInList = this
orderBy = sorting
where = (list_type IN ("articleext_list"))
}
renderObj = < tt_content.list.20
}
}
Any help would be appreciated.
The typeNum that drives (should drive) XHR requests from widgets, is added from within the Fluid extension and does not require you to add a special PAGE object.
Even if you somehow made overrides that call your specific controller action it may not be treated correctly. Usually you would never refer to a content element instance but rather a straight Extbase request bootstrapping. Among other things, because rendering the content object adds wrappers.
So you should remove this and make sure you pass a QueryResult to the pagination widget. Then override the widget template if necessary. The rest should work without having to configure TS.
EDIT:
The pagination widget itself being used in a template that is rendered via XHR means it transfers the arguments you use to load the XHR - including the custom typeNum value. The widget then creates standard links that your click like normal - and they will be a link to the "inner content" of your XHR response because the URL contains a type number.
Here's where it gets bad: you cannot remove this typeNum once it is added. So you will have to instead cause the next/prev etc. links that are clicked, to cause a new XHR request that loads the content (how you do that very much depends on your JS application so can't guide you there).
My comment about ensuring a QueryResult is not relevant unless your pages don't change and you for example always see items 1-10.
But in order to solve this I would actually recommend that you do not use the pagination widget. The main reason being you're already in an XHR context that allows you to receive arguments for your controller action and manipulate the offset and limit parts of the query from within your controller action. That means you can generate your links not to the widget but to the controller action, and for example put a CSS class on the links that should trigger XHR requests vs. those that should reload the entire page (for example to show a detail view). You avoid having to override the pagination template and you control all parameters of all links.
In fact, I would favor a controller argument for offset above using the pagination widget regardless of XHR or not. There's a long list of technical reasons why that I won't list here but suffice it to say, trading a lot of "black box" for having to create a single argument is a very reasonable and predictable-result thing to do.
Is it possible to insert a view using javascript?
I wish to perform an ajax call, get the data, then insert a view in to a page and provide the data from the ajax call to that view?
Is this possible?
Yes you can use the render method on a view. Like so;
$view = View::make('your.view')->render();
And then return the html (which is the output) from your controller method by returning the data stored in $view.
If you need to add any data to the view just add the second parameter to the view make.
alternatively you can do this,
set the route for pages you want to call.
e.g.,
Route::get('callajax','PagesController#showAjax');
and then in PagesController#showAjax you return the view.
e.g.,
public function showAjax()
{
return View::make('ajaxpages');
}
and then in ajax call you code this
$.ajax({
url:'callajax',
});
I have a section of a form that dynamically loads different sets of fields based on the user's selection in a control. I'm using a javascript event handler to detect when the selection changes, and using AJAX (with HTML payload) to pull in the proper set of fields.
I would like to be able to use Laravel's Form::getValueAttribute() method to automatically fill in the form fields' values in both the static and dynamic form parts. However, the partial view that is loaded by my AJAX call does not have the same instance of the Form class as the view with my main Form, so I can't simply call getValueAttribute() in the partial.
My thought is to make the AJAX call a POST, and serialize the necessary data (a subset of Input::old() or the model data depending whether the page is loaded as the result of validation errors, or an UPDATE request) to send along with the POST so that the HTML fragment I get back has the values set properly.
Is this the best way to get what I want? If so, does Laravel have any tools to help with the serialization of form data? If not, what might be a better approach?
I've found an approach I like better. When the view is loaded normally I use AJAX as usual to load the partial. But when the view is loaded for a validation post-back or for editing, I use Laravel's Views' nest method to nest the partial view containing the proper fields directly into the response. The partial then has access to all the Input and error data I need. The user is still able to change the field set as usual but I put up a confirm prompt for them if they have already set some values in a field set they previously selected. If they decide to proceed anyway, the field set is cleared and a new field set is brought in via AJAX as usual.
My code looks something like this:
Controller:
public function newThing() {
if ( Request::session()->has('errors') ) {
// this is a validation post-back
return View::make('thing')
->nest('fields', 'fields_partial');
} else {
// just a normal unfilled form
return View::make('thing');
}
}
public function editThing() {
return View::make('thing')
->nest('fields', 'fields_partial');
}
View: thing.blade.php (just a snip of it)
...
<form>
...
<select id="picker">...</select>
<div class="sub-fields">
{{ isset($fields) ? $fields : '' }}
</div>
...
</form>
...
<script>
$('#picker').change(function() {
// if any .sub-fields inputs have been changed, get confirmation from the user
// if user confirms, do ajax stuff to replace .sub-fields contents with new field set
// otherwise cancel the change
});
</script>
So I have a view with a datatable (javascript library to create pretty tables) that is generated like in this RailsCast
Briefly, the datatable is created in the server side and a new class creates a json response that is rendered in the view
So in my generated view I have a link that I want to trigger an ajax event when clicked and has a data attribute,
link_to( "#{sir.sir_id}" , '#', :data => {'sir-id' => sir.id}, remote: true )
I fetch the value of that data attribute in coffeescript this way:
$("a[data-sir-id]").click ->
data_sir_id = $(this).data("sir-id")
which works fine
I want to make that value (sir-id) available in my controller so I can get the associated model objects and show them in the same view via ajax, it would fill the content of another datatable (this one would not need server-side processing)
How do I create and feed this new datatable with ajax source?
I think I could return some other json object to the view if I manage to use sir_id in my controller but the view has already rendered json when first created.
You can send the data to the controller using a post method.
$("a[data-sir-id]").click ->
data_sir_id = $(this).data("sir-id")
$.ajax
type: 'POST'
url: '/some-path'
data: {sir_id: data_sir_id}
success: console.log('success posting sir-id')
dataType: 'json'
#config/routes.rb
Rails.application.routes.draw do
post "/some-path", to: "some_controller#some_view"
end
#app/controllers/some_controller.rb
class SomeController < ApplicationController
def some_view
SomeModel.find_by_sir_id(params[:sir_id])
end
end
I would like to say that if you can form a link_to with "#{sir.sir_id}" in your view file it seems to me that the sir_id should already be available to you in your controller and you wouldn't need to use ajax. Nevertheless, I hope this ajax solution is what you were looking for :)
I am trying to implement an autocomplete in ASP.NET MVC 3, following this post
but I really can't get it to work. I have looked at a series of other posts and blogs, but no success so far.
I have a DB table which contains regions (in Japanese), like so:
1 ハイチ; 2 ドミニカ共和国; 3 南アフリカ
basically a [key, Name] pair.
In my repository I am doing the following call:
public IQueryable<Region> GetAllRegions()
{
return db.Regions;
}
Getting the raw data, which I pass to the controller, pair down the data according to input and the concatenate to a string like so:
public class RegionsController : Controller
{
Region_Repository rr = new Region_Repository();
public string FindRegions(string q)
{
List<string> regions = rr.GetAllRegions().Select(r => r.Name).Where(s => s.StartsWith(q)).ToList();
return string.Join("\n", regions);
}
}
The controller when accessed via server/Regions/FindRegions/?q=ハ return a page with the single entry "ハイチ" as expected.
On the page I have a textbox with id "#NewRegion" and the script
<script type="text/javascript">
$(document).ready(function () {
$("#NewRegion").autocomplete('#Url.Action("FindRegions", "Regions")');
})
</script>
which I placed underneath the textbox.
I have references to JQuery and JQueryUI from Google API which work since I am executing other JQuery and using the JQueryUI datepicker successfully on this page.
I tried placing the script in a separate file and hardcoding the url as '/Regions/FindRegions', but no change. I had a look in firebug (second day only, so not very proficient at using it yet) and the script doesn't seem to get executed.
The controller is called fine when accessed via URL, but anything I enter into the textbox does not get passed to the controller...
Can anybody see anything wrong with this?
According to the dark depths of the jQuery UI documentation, the parameter used when passing the autocomplete string to the server is called term, not q.
Update: And also, initialize with autocomplete({source: "#(...url...)"}).
Update 2: And also, return the type of data the Autocomplete widget expects, a JSON array of strings or a JSON array of objects.
I got it working in the following way. I changed the controller to return Json data like this:
public ActionResult FindRegions(string term)
{
var regionNames = rr.GetAllRegions().Select(r => r.Name).Where(s => s.Contains(term)).ToList();
return Json(regionNames, JsonRequestBehavior.AllowGet);
}
note: the AllowGet is there so I could check the result via url, so for debug only.
And the autocomplete script became:
$(function () {
$('#NewRegion').autocomplete({ source: '/Regions/FindRegions' } );
});
Now it works, but I am still not sure why.