How to keep page length where there is fewer results? - datatables

When searching through a table or navigating to the last page of the table there may be fewer results that the pageLength setting, so table shinks in height. I would like to prevent that by filling missing rows with empty rows. How can I do that?

You can use the bScrollCollapse property along with sScrollY.
As documentation say:
When vertical (y) scrolling is enabled, DataTables will force the
height of the table's viewport to the given height at all times
(useful for layout). However, this can look odd when filtering data
down to a small data set, and the footer is left "floating" further
down. This parameter (when enabled) will cause DataTables to collapse
the table's viewport down when the result set will fit within the
given Y height.
You can use it like this:
var table = $('#example').dataTable({
"sScrollY": "400",
"bScrollCollapse": false
});
An example of this is here http://live.datatables.net/ukiyek/115/edit#javascript,html
UPDATE:
You can also set the table height to 100% so that the whole area to be filled:
var table = $('#example').dataTable({
"sScrollY": "400",
"bScrollCollapse": false,
"fnDrawCallback": function() {
$(this).attr("height","100%");
}
});
Example here
UPDATE 2:
Found exactly what you are looking for in this thread http://www.datatables.net/forums/discussion/4112/possible-to-keep-datatable-height-static-even-when-filtering
Have a look at this example which adds empty rows at the end.

Adding rows seems like an easier and more generic solution than styling fixed scrolling area (as in MavRoSCy solution n.1).
So, here's what works for me.
$(document).ready(function () {
var table = $('#example').dataTable({});
table.api().on('draw', function () {
var info = table.api().page.info(),
rowsOnPage = info.end - info.start,
missingRowsOnPage = info.length - rowsOnPage;
if (missingRowsOnPage > 0) {
for (var i = 0; i < missingRowsOnPage; i++) {
table.find('tbody').append(buildEmptyRow(6));
}
}
});
});
function buildEmptyRow(columnsCount) {
return '<tr class="empty">' + Array(columnsCount + 1).join('<td><div> </div></td>') + '</tr>';
}
Fiddle: http://live.datatables.net/ruviwabu/1/edit

Related

How to add more default empty rows in list view?

I need to add more default empty rows in the list view.
Right now, it only gives 4 empty rows on a new record. However, I need to add, say, 10 more default empty rows.
This is because I increased the height of the table using css.
.o_list_view .o_list_table {
height: 800px;
}
The result is that it still has 4 rows on default but every row has their height as 25% of the table height. Therefore, I need to add some rows into it so that their height will be fit to the new table height again.
Or another solution, if possible, remove the auto height adjustment for the rows in the table so that it won't be scaled according to the table height.
Either solution is acceptable.
As long as the length of the lines is less than 4, Odoo will try to add an empty row.
To add more default empty rows, you can alter ListRenderer _renderBody:
_renderBody: function () {
var self = this;
var $rows = this._renderRows();
while ($rows.length < 14) {
$rows.push(self._renderEmptyRow());
}
return $('<tbody>').append($rows);
},
You can also set a empty_rows attribute in the tree tag to define the number of empty rows:
XML:
<tree string="" editable="bottom" empty_rows="4">
JavaScript:
var ListRenderer = require('web.ListRenderer');
ListRenderer.include({
_renderBody: function () {
var self = this;
var empty_rows = 4;
if (self.arch && self.arch.attrs.empty_rows) {
empty_rows = self.arch.attrs.empty_rows;
}
var $rows = this._renderRows();
while ($rows.length < empty_rows) {
$rows.push(this._renderEmptyRow());
}
return $('<tbody>').append($rows);
},
});
Check Assets Management on how to add the above code to web.assets_backend bundle and Javascript Module System to create a JavaScript module

columnSummary is not added

I am trying to add columnSummary to my table using Handsontable. But it seems that the function does not fire. The stretchH value gets set and is set properly. But it does not react to the columnSummary option:
this.$refs.hot.hotInstance.updateSettings({stretchH: 'all',columnSummary: [
{
destinationRow: 0,
destinationColumn: 2,
reversedRowCoords: true,
type: 'custom',
customFunction: function(endpoint) {
console.log("TEST");
}
}]
}, false);
I have also tried with type:'sum' without any luck.
Thanks for all help and guidance!
columnSummary cannot be changed with updateSettings: GH #3597
You can set columnSummary settings at the initialization of Handsontable.
One workaround would be to somehow manage your own column summary, since Handsontable one could give you some headeache. So you may try to add one additional row to put your arithmetic in, but it is messy (it needs fixed rows number and does not work with filtering and sorting operations. Still, it could work well under some circumstances.
In my humble opinion though, a summary column has to be fully functionnal. We then need to set our summary row out of the table data. What comes to mind is to take the above mentioned additional row and take it away from the table data "area" but it would force us to make that out of the table row always looks like it still was in the table.
So I thought that instead of having a new line we could just have to add our column summary within column header:
Here is a working JSFiddle example.
Once the Handsontable table is rendered, we need to iterate through the columns and set our column summary right in the table cell HTML content:
for(var i=0;i<tableConfig.columns.length;i++) {
var columnHeader = document.querySelectorAll('.ht_clone_top th')[i];
if(columnHeader) { // Just to be sure column header exists
var summaryColumnHeader = document.createElement('div');
summaryColumnHeader.className = 'custom-column-summary';
columnHeader.appendChild( summaryColumnHeader );
}
}
Now that our placeholders are set, we have to update them with some arithmetic results:
var printedData = hotInstance.getData();
for(var i=0;i<tableConfig.columns.length;i++) {
var summaryColumnHeader = document.querySelectorAll('.ht_clone_top th')[i].querySelector('.custom-column-summary'); // Get back our column summary for each column
if(summaryColumnHeader) {
var res = 0;
printedData.forEach(function(row) { res += row[i] }); // Count all data that are stored under that column
summaryColumnHeader.innerText = '= '+ res;
}
}
This piece of code function may be called anytime it should be:
var hotInstance = new Handsontable(/* ... */);
setMySummaryHeaderCalc(); // When Handsontable table is printed
Handsontable.hooks.add('afterFilter', function(conditionsStack) { // When Handsontable table is filtered
setMySummaryHeaderCalc();
}, hotInstance);
Feel free to comment, I could improve my answer.

Vue.js: error setting a computed property

in the following code (jsbin available here) I have two input elements, a range and a text, bound together via a computed property.
var vm = new Vue({
el: '#main-container',
data: {
sliderValue: 100,
},
computed: {
actualValue: {
get: function() {
if (this.sliderValue <= 100) {
return this.sliderValue;
} else {
return Math.round(this.sliderValue * 12.5 - 1150);
}
},
/* set won't work for val > 100*/
set: function(val) {
if (val <= 100) {
this.sliderValue = val;
} else {
this.sliderValue = Math.round((val + 1150)/12.5);
}
}
}
},
methods: {
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.js"></script>
<div id="main-container">
<input type="range" v-model="sliderValue" min=1 max=132>
<input type="text" v-model="actualValue">
<p>Slider: {{sliderValue}}</p>
<p>Actual: {{actualValue}}</p>
</div>
The range goes from 1 to 132, and its range is mapped [1..500] in the text input, with a simple transformation (basically it's a linear mapping with two different slopes for [1..100] and [101..132]) using the actualValue computed property.
Getting actualValue works as expected: dragging the slider correctly updates the input text with appropriate values in the range [1..500].
I'm not able to find a way to set actualValue, though. I'd like to be able to type a value in the text input, and make the slider's thumb update accordingly to the inverse transformation (val + 1150)/12.5.
It works as long as the typed number is in the range [1..100], but it "explodes" for numbers >100, e.g. 101 makes the sliderValue jump at 80892 and actualValue is then re-calculated as 1010000. As far as I understand, it's a looping-feedback scenario.
I've tried also alternative approaches (watch; v-on:change in the text input; using a third variable) to no avail.
Thanks in advance for any suggestion!
It's an amazing puzzle, and challenged me for a long time!
Look at the screenshot below. Your sliderValue and actualValue are strings, not integers. When you set actualValue as 101, you are actually setting it as a string value of "101"
Now, your sliderValue = ((actualValue + 1150)/12.5)
"101" + 1150 = "1011150" (another string!, try it in the developer console)
That messes up your entire calculation. Now you know how to fix it :-)
And you need to get that Vue devtools from here: https://github.com/vuejs/vue-devtools
EDIT: Response to comment #3
Here is the modified jsBin: http://jsbin.com/mahejozeye/1/edit?html,js,output
The only difference is introduction of two console.log statements in your map2 function. This helps you identify if your non-linear mapping function is working correctly or not. If you keep your developer console open, you will see what is happening in this function.
Case 1: When you set the value radius = 25 using the text box, your sliderRadius gets set to 111.55518394648828 and your radius again gets re-calculated as 25. So it comes around in a full circle and everything is stable here.
Case 2: When you set the value radius = 55, your sliderRadius gets set to 173.03607214428857 through your non-linear map2() function, which resets radius to 51.29869180420927
Clearly there is a circular dependency issue. Your sliderRadius and radius are very much dependent on each other and therefore radius is unable to take the value between 51 and 58.
You need to evaluate why it happens, as it has a lot to do with the non-linear mapping function that you have defined. The moment radius can take stable values at 55 (through the map2 function), then your current problem will be resolved.
The simplest fix is to set your input type to number:
<input type="number" v-model="actualValue">
or you can convert your value to an integer with something like:
set: function(val) {
var intVal = parseInt(val, 10);
if (!isNaN(intVal)) {
if (intVal <= 100) {
this.sliderValue = Math.max(1, intVal);
} else {
this.sliderValue = Math.min(132, Math.round((intVal + 1150) / 12.5));
}
}
}

making a linegraph that shows population decay with dc.js and crossfilter

I am creating a dashboard in DC.js. One of the visualizations is a survival curve showing the percentage of survival on the y-axis and the time in weeks on the x-axis
Each record in the dataset contains a deathAfter column called recidiefNa. This shows the number of weeks after death occurred, and shows -99 for survival.
See sketches for example dataset and desired chart form:
I created this code to create the dimensions and groups and draw the desired chart.
var recDim = cf1.dimension(dc.pluck('recidiefNa'));//sets dimension
var recGroup = recDim.group().reduceCount();
var resDim = cf1.dimension(dc.pluck('residuNa'));
var resGroup = resDim.group().reduceCount();
var scChart = dc.compositeChart("#scStepChart");
scChart
.width(600)
.height(400)
.x(d3.scale.linear().domain([0,52]))
.y(d3.scale.linear().domain([0,100]))
.clipPadding(10)
.brushOn(false)
.xAxisLabel("tijd in weken")
.yAxisLabel("percentage vrij van residu/recidief")
.compose([
dc.lineChart(scChart)
.dimension(recDim)
.group(recGroup)
.interpolate("step-after")
.renderDataPoints(true)
.renderTitle(true)
.keyAccessor(function(d){return d.key;})
.valueAccessor(function(d){return (d.value/cf1.groupAll().reduceCount().value()*100);}),
dc.lineChart(scChart)
.dimension(resDim)
.group(resGroup)
.interpolate("step-after")
.renderDataPoints(true)
.colors(['orange'])
.renderTitle(true)
.keyAccessor(function(d){return d.key;})
.valueAccessor(function(d){return (d.value/cf1.groupAll().reduceCount().value()*100 );})
])
.xAxis().ticks(4);
scChart.render();
This gives the following result:
As you can see my first problem is that I need the line to extend until the y-axis showing x=0weeks and y=100% as the first datapoint.
So that's question number one: is there a way to get that line to look more like my sketch(starting on the y-axis at 100%?
My second and bigger problem is that it is showing the inverse of the percentage I need (eg. 38 instead of 62). This is because of the way the data is structured (which is somehting i rather not change)
First I tried changing the valueaccessor to 100-*calculated number. Which is obviously the normal way to solve this issue. However my result was this:
As you can see now the survival curve is a positive incline which is never possible in a survival curve. This is my second question. Any ideas how to fix this?
Ah, it wasn't clear from the particular example that each data point should be based on the last, but your comment makes that clear. It sounds like what you are looking for is a kind of cumulative sum - in your case, a cumulative subtraction.
There is an entry in the FAQ for this.
Adapting that code to your use case:
function accumulate_subtract_from_100_group(source_group) {
return {
all:function () {
var cumulate = 100;
return source_group.all().map(function(d) {
cumulate -= d.value;
return {key:d.key, value:cumulate};
});
}
};
}
Use it like this:
var decayRecGroup = accumulate_subtract_from_100_group(recGroup)
// ...
dc.lineChart(scChart)
// ...
.group(decayRecGroup)
and similarly for the resGroup
While we're at it, we can concatenate the data to the initial point, to answer your first question:
function accumulate_subtract_from_100_and_prepend_start_point_group(source_group) {
return {
all:function () {
var cumulate = 100;
return [{key: 0, value: cumulate}]
.concat(source_group.all().map(function(d) {
cumulate -= d.value;
return {key:d.key, value:cumulate};
}));
}
};
}
(ridiculous function name for exposition only!)
EDIT: here is #Erik's final adapted answer with the percentage conversion built in, and a couple of performance improvements:
function fakeGrouper(source_group) {
var groupAll = cf1.groupAll().reduceCount();
return {
all:function () {
var cumulate = 100;
var total = groupAll.value();
return [{key: 0, value: cumulate}]
.concat(source_group.all().map(function(d) {
if(d.key > 0) {
cumulate -= (d.value/total*100).toFixed(0);
}
return {key:d.key, value:cumulate};
}));
}
};
}

dojo how to get portlet position object that have value(column,row,width,height) in GridContainer?

hi my server tries to store information about portlet. I would like to get information(column,row,width,height) about the location of the portlet. but I think I have. more difficult to me..
for example there are two single portlet in GridContainer. I want to get information about the portlet on the column,row and the value of the width, height.if i imported value one of two portlet in GridContainer, the value of the first portlet row: 0, column: 0, width: 50px, height: 50px, the value of the second portlet row: 0, column: 1, width: 50px , height: 50px.
ask for advice.
my code
var selectedForm=registry.byId('tabContainer').get('selectedChildWidget');
var grid=selectedForm.getChildren();->have portlets in GridContainer
var oldcolumn=null;
var row=1;
for(var i =0; i<grid.length;i++)
{
for(var j=0;j<grid[i].length;j++){
if (oldcolumn==null)
oldcolumn=grid[i].getChildren()[j].domNode.parentNode.cellIndex;
console.log(' column:'+grid[i].getChildren()[j].domNode.parentNode.cellIndex);
if(oldcolumn == nodelist[i].domNode.parentNode.cellIndex)
console.log('column:'+grid[i].getChildren()[j].domNode.parentNode.cellIndex+'row:'+(++row));
else
{
row=1;
oldcolumn=grid[i].getChildren()[j].domNode.parentNode.cellIndex;
console.log(' column:'+grid[i].getChildren()[j].domNode.parentNode.cellIndex'row:'+(++row));
}
}
}
Column
If I read the documentation about the dojox/layout/GridContainer, it states the following:
By requiring the GridContainer, two new attributes are mixed into the
dijit/_WidgetBase class:
column: Holds the current column a widget is in. (If any). Defaults to “1”
dragRestriction: Is the widget draggable? You can use this to prohibit the dragging of a specific Widget
This means you could use the following to get the column:
myPortlet.get("column");
Row
There is no such thing as a "row", so I suppose you want to retrieve the zone. There's no way to retrieve the zone of the portlet though, but you can calculate it by yourself. If you use getChildren() on the GridContainer, the portlets are always returned in the same order based on their column and then their zone. However, this is undocumented and may change in the future.
So this means you could easily write a counter that increments each time a portlet is in the same column (which means he's in the next zone). For example:
var children = registry.byId("myGrid").getChildren();
var perColumn = [ ];
arrUtils.forEach(children, function(portlet) {
var id = portlet.get("id");
var col = portlet.get("column");
if (perColumn[col] !== undefined) {
perColumn[col]++;
} else {
perColumn[col] = 0;
}
var zn = perColumn[col];
console.log(id + ": " + col + " " + zn);
});
Get width + height of portlet
The width and the height of the portlet are determined by its content, so no width or height properties are available. However, you could get the DOM node of the content and retrieve the width and height from it using dojo.position (in recent versions it's the dojo/dom-geom module), for example:
require([ "dojo/dom-geom" ], function(domGeom) {
var box = domGeom.position(myPortlet.get("domNode"));
console.log(box.w + " " + box.h);
});
I also made an example to retrieve all properties (column + zone + width + height): http://jsfiddle.net/dF94F/