I have a promise that returns some data. I use that data in a data grid (ng-grid, specifically). The grid only displays a small portion of the data unless I open the console at which point it displays all of the data. Why would this happen?
Here is some code:
//This returns a promise with the results of my db query
Admin.getShipments().then(function (data) {
$log.log('data from promise: ', data); <-- this lists 20 elements
$scope.shipments = data;
});
$scope.gridOptions = {
data: 'shipments',
// some column definitions here... removed as not necessary
};
Using the above my grid only displays 6 elements while the console log shows that there are actually 20. If I inspect element (open console) then the grid will suddenly display all 20 elements.
Why? Is the value for $scope.shipments simply not fully populated when the grid goes to retrieve it? If so, how do you work around this?
Here is an attempt at a solution that does not work...
I tried placing the data grid setup inside the promise "then", thinking that this would ensure that I had all of the needed data but the entire grid fails (saying the grid values are undefined) when I do this. Here is an example of that:
//This fails entirely and the grid complains about undefined values
Admin.getShipments().then(function (data) {
$log.log('data from promise: ', data); <-- this lists 20 elements
$scope.shipments = data;
$scope.gridOptions = {
data: 'shipments',
// some column definitions here... removed as not necessary
};
});
How can I reliably get all of my retrieved data to display in this grid without having to open the console to make it appear (and why would simply opening console have such an effect?)
after testing your code, I successfully reproduced this bug, when grid height is missing, by default the grid renders only 20 rows, after adding height, the grid rendered properly.
Example:
js:
$http.get('largeLoad.json').success(function (data) {
$scope.shipments=data;
});
$scope.gridOptions = {
data: 'shipments'
};
});
css:
.gridStyle{
height:500px;
}
Live example: http://plnkr.co/edit/bg6NMW162qKbm6QUlINo?p=preview
This is definitely some sort of bug in ng-grid. I found this value in their code:
// the # of rows we want to add to the top and bottom of the rendered grid rows
var EXCESS_ROWS = 6;
Changing that number changes the number of elements that are displayed. So right now it's 6 and when I load the page I only see 6 records until I open console at which point they all appear. If I set it to 10 I see 10 until I open console and so on. If the number is larger than the available number of records then all records are displayed so a workaround is to simply make the number larger than the number of records you have. Obviously that's a hack but that's all I have time for right now. I'll update if I get around to actually solving the bug.
Actually this sounds like a buffer flush issue. You have a call to print to the console .. I would guess that there isn't a flush to that final call unless the console is open and thus it's blocking the flush to the document.
I would take the call to the console out and see if it works.. if so then research how to force a flush of that buffer.
Related
I am trying to load initial data (from a local JSON string) using knockouts.js in order to displayed it in the UI right after loading the page.
I have tried a LOT of options but all failed or produce errors.
My latest try is http://jsfiddle.net/z22m1798/17/ (base on the 'Cart' example) which produce the error:
TypeError: cartLine.filterValue is not a function.
The relevant code is:
//Load initial data from server
var JSONdataFromServer = $("#JSONdataFromServer").val();
console.log(JSONdataFromServer);
var dataFromServer = ko.utils.parseJson(JSONdataFromServer);
self.lines.push(new CartLine(dataFromServer));
Someone maybe know what am I doing wrong here?
It's look like a difficult issue.
Thanks
I found that the way to solve it (don't sure it is the most accurate way) is by receiving from the server the entire JSON so the first cells will contain the filters I wish to display. In addition, the server will send a parameter "filtersToDisplay" that indicate how many filters need to display.
By default, filtersToDisplay=1 to display only the first line.
http://jsfiddle.net/z22m1798/25/
for (i = 0; i < $("#filtersToDisplay").val(); i++) {
self.lines.push(new CartLine(self.lines))// Put one line in by default
}
Thanks
When using the Scroller extention for datatables, you don't have pagination but all rows in one scrollbar. I would like to know, what event is fired after scrolling down the tables, to e.g. see row 50-60 of 100.. I also need to know how to get that 10 rows out from the datatable. I'am using the lastest versions. Thanks alot.
This is how you get the rows from the current page in Datatable.net without the Scroller extention.
drawCallback: function (settings) {
var api = new $.fn.dataTable.Api(settings);
// Output the data for the visible rows to the browser's console
console.log(api.rows({ page: 'current' }).data().length);
}
Update 1: My Init of Scroller table:
initScrollerTable = function ($table, url, inclFilter, dataTableOptionsSpecific) {
$.ajax({
method: "POST",
url: url,
data: dataParameterHelper.getCommonData(),
dataType: "json"
})
.done(function (rows) {
var dataTableOptions = $.extend(
{},
{ data: rows },
dataTableOptionsSpecific
);
initTable($table, inclFilter, dataTableOptions);
});
};
Update 2 Deeper elaborating on the origional reason for the question to clarify.
Okay long story short. My table contained 26000 rows, and it took 7mins for me to load it. It contained of ALOT of DB calls and the JSON was a size of 21MB! I wanted to optimize it.
First attempt: I tripped my json to absolute minimum bringing it down to 1.5MB but it still took almost 7mins. On the second test it contained all the html, with hardcoded numbers/strings and i did 0 DB calls. It only took 3.5secs!. Yesterday I didn't knew I only had to focus on optimizing my calls to the DB.
Yesterday, when I posted the origional questing, my idea was to populate the table without any data pulled form the DB, and instead load them in a kind of lazy loading way. Let's say I showed the 10 first rows. with 3 column each where I need to call the DB for each cell, that's 30 times total. So the idea was to make 30 request for the (10) current rows, and replace the placeholder with the actually value. If you understand. I would still be better than 26.000 * 3 DB calls :)
And for that I needed to hook up on an event to get the 10 current rows-id's, I could then loop through making the 30 AJAX request. So maybe it's the scroller event I need for that, if there is someone like that.
But I don't know if it's a good idea. Usually a "good" idea is only "good" before/until you learn the best pratice :)
I think I will start focusing on reducing the DB calls with some inner joins and what have you, retrieving a big resultset I can loop through and populate all my 26.000(and later 50.000) rows, in under 15 secs!
SOLUTION
Use xhr event to handle Ajax requests and page.info() to retrieve information about the table as shown below.
Please note that event handler needs to be attached before you initialize your table to handle initial Ajax request.
$('#example').on('xhr.dt', function ( e, settings, json, xhr ) {
var api = new $.fn.dataTable.Api(settings);
var info = api.page.info();
console.log('Data has been loaded', info);
});
DEMO
See this jsFiddle for code and demonstration.
I have this function in a Backbone view:
updateToServer: function(e) {
e.preventDefault();
var id = e.target.getAttribute('data-id');
var file = this.collection.get(id);
var data = {};
$(e.target).serializeArray().map(function(x) {data[x.name] = x.value;});
file.save(data);
this.$el.modal('hide');
}
If I allow this to run naturally, I get undefined is not a function on file.save(data). However, if I set a breakpoint in Chrome DevTools at file.save(data) and evaluate that function manually in the console before resuming, both save functions work.
Why is this happening and how can I fix it?
Here's the entire view in case you need additional context: https://gist.github.com/raddevon/d3ddf1bba101b6b67c4b#file-supportfilesview-js-L155-L163
Update: New discovery: On the second run, this works. I have an even listener on form submit. When I click the submit button the first time, I get the error. If I click again, the model saves.
Can you try this
updateToServer: function(e) {
e.preventDefault();
var id = e.target.getAttribute('data-id');
var file = this.collection.get(id);
var data = {};
$(e.target).serializeArray().map(function(x) {data[x.name] = x.value;});
this.$el.modal('hide');
setTimeout(function(){
file.save(data);
}, 200); //try with different values for timer
}
I have added a 200 millisecond timer.
This might not be your actual solution but at least you will come to know if there is some asynchronous stuff going on before 'file' is actually formed.
Try different values for the timer. I mean keep increasing the timer and see if you are still not able to get rid of the error.
Once you are sure that 'file' is formed asynchronously then you can look into why that's happening.
And try console.logs instead of debuggers for debugging so that you can test without pausing the execution.
Hope that helps.
This was not at all what I suspected, and I hadn't given enough information in the question without realizing it. The line in my code that triggered the exception was file.save(), but the actual exception was happening inside Backgrid.
I provide a form to allow users to update models from the collection displayed in a grid. A particular column is defined as an integer column, but I hadn't converted the value coming from the form to an integer. As a result, Backgrid was trying to run toFixed on a string. I modified my form serialization code to convert strings containing only integers into integers. Now, everything works as expected.
Here's that serialization code:
$(e.target).serializeArray().map(function(x) {
data[x.name] = x.value === 'on' ? true : x.value;
if (!isNaN(parseInt(data[x.name])) && isFinite(data[x.name])) {
data[x.name] = parseInt(data[x.name]);
}
});
If I had to guess, I'd say that's probably a bit naive, but it seems to be working well in my application.
Thanks to everyone for the help!
I am using Ember data and The RESTAdapter with an extension for Django.
Here is a JSBin
Here is how our routes are set up:
App.Router.map(function() {
this.resource('locations');
this.resource('location', {path:'locations/:location_id'}, function() {
this.resource('items', function() {
this.route('create');
});
this.resource('item', { path:'item/:item_id' }, function() {
this.route('edit');
});
});
});
App.LocationsRoute = Ember.Route.extend({
model: function () {
return this.get('store').find('location');
}
});
App.ItemsRoute = Ember.Route.extend({
model: function(){
//Get the model for the selected location and grab its item list.
//This will do a call to /locations/1/items
return this.modelFor('location').get('items');
}
});
Now this all works fine when we navigate to locations/1/items. The user is presented with a list of items relevant to the location with id 1.
When the user clicks one of these items it brings the url to #/locations/1/item/1 and displays the details of the item with id 1.
Now what doesnt work is this:
When I hit the back button the #/locations/1/items route loads but it does not have its data any more and no REST call to api/locations/1/items occurs. Even though the data displayed just fine when we first navigated to #/locations/1/items.
It is like Ember said "Well we already loaded that data, so we dont need to call the api again" but the data is somehow not being displayed in our template.
If I change the ItemsRoute model to this:
return this.get('store').find('item');
The scenario above works perfectly fine but the data is not based on our location.
Is there something I am missing with using this.modelFor? Or is my route set up incorrect?
Let me know if theres any extra info you need.
Update 1:
Having changed the model function to this I have discovered some new insights:
model: function(){
//Get the model for the selected location and grab its item list.
//This will do a call to /locations/1/items
var location = this.modelFor('location');
var items = location.get('items');
return items;
}
Upon first load of #/locations/1/items the location variable holds the location model and the items variable holds something which has a 'content' property, 'isFulfilled: true' and some other things.
This correctly works and displays the list of items. When i click on a particular item and got to #/locations/1/items/1 then hit the back button. The breakpoint triggers and location is still correctly populating with the location model.
However the items variable seems to just be a PromiseArray, do I need to somehow wait for the promise to be fulfilled before this model hook returns? I thought Ember already did this automatically? I suppose this means that the route is loading before the promise is fulfilled and thats why there is not data displayed?
Therefore my list is not populated correctly.
I'm on a phone, so I'll update this a bit later with more, but the problem is your location resource isn't a child of locations. Becaude of that, ember says why waste time fetching that route if it isn't a part of that resource path. It only hits the location route, which I'm betting you don't have a model hook for fetching the single location (at least based on the code above).
Ok, here is the deal. I have fixed the issue but have no idea why it fixed the issue.
Without further ado:
Here is a jsbin example of the exact setup I have. The only difference between the real one and the jsbin is the fact that I am using the RestAdapter instead of the FixtureAdapter. This is not technically true because I am using the ember-django-rest-adapter which extends the REST one.
The issue described in the question does not present itself in the jsbin but the exact setup with the ember-django-rest-adapter does present the issue.
The fix was to break the cyclic relationship between User -> Site -> Items -> User
For example if I comment out the 'locations' relationship in the User model, the back button works.
Or if I comment out the 'owner' relationship to User in the Item model the back button works.
I might ask a separate question to see what the reasoning behind the problem is, although if someone can shed any light in to why this is happening I'll happily accept the answer here.
I have a very simple interface containing a grid, a search field, a search button and a data store from a JSON data source.
The default state of the grid is showing all the data for the given user and entering a term in the search field and clicking the search button applies a filter which then re-executes a call to fetch a new JSON result set and put that data in the grid. I'm doing so the following way:
quick_search: function(search_term, store) {
store.clearFilter(true);
store.filter([{property: 'filter', value: search_term}]);
}
The following function is executed just fine when the search button is clicked, but I'm seeing this scenario:
User starts with 100 records in the grid
User searched for "test" (has 25 records)
The appropriate 25 records show in the grid
User then searches for "stack" (45 results)
The grid quickly shows the original 100 records momentarily and then shows the correct 45 records
And when that last step occurs, it's a very quick blip.
Is this the correct pattern for re-freshing grid data on the fly in a search-esque way? Am I perhaps incorrectly clearing the filter?
Found the solution... Previous solution was trying to run load and was resulting in multiple requests back to my webserver fetching multiple result sets (first the incorrect, then the correct).
quick_search: function(search_term, store) {
store.filters.clear();
store.filter([{property: 'filter', value: search_term}]);
}