I am using GridX to display data, but I often change the data store that the GridX is using. I do this by using grid.setStore() and pass in a Dojo JsonStore (this is asynchronous data of course). I do not recreate the grid every time I change the data store, just call setStore(), then refresh().
The grid also has the NestedSort module activated. My problem is this:
If the user sorts on a store, and then chooses a set of different data to look at, the code calls:
grid.sort.clear();
grid.setStore( /* new store*/ );
grid.refresh();
Without .clear() being called, the grid will try to sort the new store of data (which is usually invalid, and throws a 500).
With .clear(), the store will make a call to the server to sort the first data store, before then calling for a fresh batch of the next data store.
Ideally I want to tell the grid to clear any sort preference before I grab the next set of data, so that it is not attempting to sort it. I do not want it to make an additional call to sort the first data immediately before it gets replaced.
I finally worked it out. To clear the sort information in the grid you must modify the grid model. The sorting in the grid model can be done using this:
grid.model.sort();
Related
I have Tabulator nearly working as I need for a web application I am designing. This app is calling web services in a backend app written in Java.
I created an InitialFilter set, the filtering, sorting, and pagination is handled by the backend. Next, I am creating an Accordion control for the various filter inputs by the end-user. No issues yet. I created two buttons, one to Apply the filter based on the user preferences, and another to Reset/Clear the filter parameters.
The Tabulator object is already created and has the default data already showing on the page. When the user sets the custom filter and clicks the Apply button, a JQuery function captures the on-click event and executes the following code.
$(function(){
$('#btn-apply').on('click', function(e){
// handle click event of button
// Get values first
var subFrom = $('#txt-submission-from').val();
var subTo = $('#txt-submission-to').val();
// Set filters
NIBRSTable.clearFilter();
NIBRSTable.addFilter("submissionPeriod", ">=", subFrom);
NIBRSTable.addFilter("submissionPeriod", "<=", subTo);
// Call function to load data
NIBRSTable.setData();
});
});
Error Returned
Ajax Response Blocked - An active ajax request was blocked by an
attempt to change table data while the request was being made
tabulator.min.js:5:24222
I have tried commenting out one source line at a time. It appears the setFilter() calls are causing the Ajax Response Blocked error even though there is not anything actively occurring (the tabulator DOM is already loaded)
I have many more items for which the end-user may filter. The two filters shown in the code listing above are just a start.
That isn't an error message, that is just a console warning.
What it means is that multiple ajax requests have been made in quick succession and that one request has been made before the first one returned, therefore the response of the first request will be ignored so the table isn't partially redrawn.
In this case it is being triggered because you are calling the addFilter function twice in quick succession which is triggering the ajax request twice with the second filter being added before the first ajax request has been sent. (there is also no need to call the setData function, adding a filter when ajaxFiltering is enabled will automatically trigger the request).
To avoid this double ajax request you could pass an array of filter objects into the addFilter function and only call it once:
NIBRSTable.addFilter([
{
field:"submissionPeriod",
type:">=",
value:subFrom
},
{
field:"submissionPeriod",
type:"<=",
value:subTo
},
]);
Oli,
Thank you for the detailed response. Since the filters are dynamic and set by the end-user (i.e. cannot be hardcoded), I created an Object and conditionally adding the filter parameters. Using this object, I can call the NIBRSTable.addFilter(userFilter) and it works like a charm! I did make the mistake of trying to JSON Stringify the object and passing it to the addFilter method, but quickly learned JSON Stringify was unnecessary since the object array was already a JSON object.
Oddly, though I am still receiving a single warning "Ajax Response Blocked" even though there were no pending Ajax actions. I only have one .addFilter() and removed the .setData() as you responded. I will ignore for now since the filtering is working!
Ben
I have a search component that gets its data in the form as a prop of PHP json_encoded data:
<search :data="<?= json_encode($jsonHotels) ?>"></search>
This way it receives the first 25 movies straight away when the page renders.
After this I make an ajax request to fetch the rest of the result if there are more than 25 movies.
The problem is that when I override the data with the data from the AJAX call, the component re-renders.
Previously to combat this I performed an ajax request with an offset of 25 to skip the first 25 movies and only give me the extra results. Pushing this onto the array didn't cause a re-render and this worked perfectly.
That was perfect, until I got to thinking:
What about people who start or refresh the page while they are on page number 2-3-4-5-etc.
I now have to not only mess with my offset, but I also have to possibly prepend data to my array and well as possibly append data with a push.
Is there a way to merge data without causing a re-render? I am wondering if anyone else has every run into the problem.
Simply fetching all results with an AJAX request takes too long and obviously also causes a re-render. Not fetching initial backend data would mean people would be starting at a blank page or a spinner for 2-3 seconds.
Edit:
Ended up with this:
mergeData(state) {
// Smart merge ajaxData and hotels.
const offSet = state.currentPage * state.itemsPerPage;
const start = state.ajaxData.slice(0, offSet - state.itemsPerPage);
const end = state.ajaxData.slice(offSet);
state.data.unshift(...start);
state.data.push(...end);
},
Pretty much slice my array in 2 and remove the currentPage section. By using unshift and push I prevent a re-render from happening.
Remember that your viewmodel is a model of your view. Here, you're not modeling your application, you're just trying to plug your data in.
If you want to display a page of results at a time, have some kind of model for a page of results. When you fetch your data, put it into the appropriate model-pages. Since you're only displaying one page at a time, populating other pages will not cause a re-render.
I'm creating an Web-Application (Frontend and Backend, so both are under my control) using Backbone and Pyramid, being connected via a RESTful API.
During development I encountered a problem several times by now, where Backbone PUTs (=updates) a new model, while it actually should POST (=create) it.
Backbone decides whether to POST or UPDATE a model depending of the presence of an ID-field (if no ID present in the current model: -> POST/create | if so: PUT/update).
However I encountered several situations by now, where this behaviour doesn't match my application logic.
Let's say our main model (and its objects being persistently saved in a relational database in the backend) is called Foo, having fields like id, field_1, field_2.
Example #1: Creating a template or preview of Foo: Before creating (=POSTing) an object of Foo, I can create and show a preview to the user and/or save it as a template.
While doing so, the backend (in case of the preview: temporarily) adds the object to the database and returns the full model - including an ID in its HTTP response - back to Backbone.
Template- and Preview-objects of Foo are (temporarily) saved into the same table, as final objects (column type indicates its type (0 = final/live, 1 = preview, 2 = template)).
When now - after previewing / saving as template - trying to actually CREATE an object of Foo, the Backbone model already has the ID field set and actually PUTs and updates the template or not-anymore-existing preview, instead of POSTing and therewith creating a new Foo inside the database (as intended).
=> solution #1: calling POST /json/preview does not return the ID field, so Backbone doesn't get confused.
=> solution #2: overriding parse() of Foo in Backbone-model to kick out ID field from response
.=> kinda works
Example #2: Having a Periodic model, which refers to a Foo-template. Intention of a Periodic is to offer the user the possibility of semi-automatically creating a new Foo object based on a Foo-template every X months.
Now there is a call GET /json/periodics, which returns all Periodic-objects with its nested Foo-objects (Foo-templates), including their IDs, e.g. [{'interval': 12, template_id: 42, template: { 'id': 42, field_1: 'foo', field_2: 'bar', .. } , { .. } , .. ].
On the frontend the user now can periodically confirm (or skip) creating a new Foo-object, by issuing: periodics[X].template.save() which however again PUTs and therewith updates the Foo-model, instead of POSTing and creating a new one (as intended).
Here again (as in example 1), I could strip out the ID field of Foo - either in the backend or frontend.
However there are situations, where I need the id-field of templates, e.g. when actually editing them, so here I'd need two calls (GET /json/templates_WITHOUT_FOO-IDs and GET /json/templates_WITH_FOO-IDs). which also sounds far from right.
Question is: What's the right (and consistent) way of avoiding Backbone falsely assuming a model should be PUT instead of POSTed in certain situations / views?
Backbone's save and fetch methods just make calls to the Backbone.sync
method, which in turn is just a wrapper for an ajax call. you can pass
in ajax parameters using the save function without having to actually
extend it. basically ends up being something like this:
model.save({attributes you want to save}, {type:'POST', url: 'apiurl/model/:id/played'});
You would have to do this every time though so it is probably better practice to extend Backbone.sync for your model.
The Backbone website has a bit of information about what I'm talking about as far as the Backbone sync and save taking ajax options. There are also a few examples I've seen on extending sync but I can't seem to track them down at the moment.
I have an auto refresh method (something like below) in my controller. In which I will update data in ViewBag, which I use in label of view. But I'm unable to refresh my label automatically. Please help me.
var waitHandle = new AutoResetEvent(false);
ThreadPool.RegisterWaitForSingleObject(waitHandle,(state, timeout) =>
{
// my viewbag
viewbag.time = DateTime.Now.TimeOfDay;
viewbag=date
}, null, TimeSpan.FromSeconds(5), false);
MVC doesn't work like WebForms (where reassigning a value populates to the UI). You'll need to write a separate action (or WebAPI method) and use AJAX/JavaScript to pull updates. (Alternatively you could use SignalR, but that may be overkill).
Also, if this is a recurring event, you may want to look into a library like Quartz.NET to perform the action. Then, post updates to a shared resource (looks like you're storing timestamp of last execution). From there, use setInterval/AJAX on the client to retrieve and display that value.
We're using a lot of YUI data tables to display data, and rather than using the built-in pagination we've done our own so we can paginate on the server side using AJAX without downloading the whole data set (which is often huge).
Whenever we use the data table's sorting funcionality, though, it will only sort the one page because from YUI's point of view that's the entire data set.
I need to be able to call an arbitrary function to reload the page data whenever the user tries to sort the data. I've looked into DataTable's sortFunction parameter and it's not ideal because it gets called multiple times (once for each row combination it needs) and I need to do it just once.
There are probably plenty of hacky ways I could do this, but what's the "nicest" way of going about this?
Ideally, you would sort on the server side.
when create the datatable, one of the config options is generateRequest (see this example: http://developer.yahoo.com/yui/examples/datatable/dt_bhm.html )
generateRequest is a meant to be a function which generates a URL which returns the correct data set with which to fill the table. You probably have this.
For me, whenever I click on the column header (to sort), it makes a new request to the server, getting the correct page of sorted data.
Why not write a custom function and close over a variable that tracks whether it's been called?
var hasBeenCalled = false;
function mySortFunction(){
if(!hasBeenCalled){
// do something
hasBeenCalled = true;
}
}
Then replace the sortFunction with that.