PDA

View Full Version : [CLOSED] Problem with paging...



RCN
May 22, 2012, 7:30 PM
Hi,

In a GridPanel of mine, I have a Store and a PagingToolbar. The Store uses a AjaxProxy. In the method that is called in the proxy I return a StoreResult.

I don't want to do a count on my tables, so everytime I set the StoreResult.Total to int.MaxValue, except when i know its the last page. this way the button 'next page' stays able to click when i know it's not the last page and disable when i do, and i hide the 'last page' button.

But when i put a number of page that don't exists (for example: 2222) and he tries to select data for that page (because it's within the total) I set the StoreResult.Success to false. This is enough to make the grid stay with the same rows but its like the store actually went to that page, because the Store.currentPage on js is the same page number that does not exists (in the example it would be 2222) and the PagingToolbar shows this page on the page 'selector'.

There's someway to really cancel the page from 'turning' in my cenario?

Example:


//To build the Grid...
{
GridPanel grid = new GridPanel();

Store store = new Store { ID = "test_store" };

store.RemoteSort = true;
store.PageSize = 33;

AjaxProxy proxy = new AjaxProxy();
proxy.StartParam = "startIndex";
proxy.LimitParam = "pageSize";
proxy.PageParam = "pageNumber";
proxy.SortParam = "ordenacao";
proxy.ActionMethods.Read = HttpMethod.POST;

proxy.Reader.Add(new JsonReader { Root = "data", TotalProperty = "total", MessageProperty = "message" });

proxy.Url = "/Entidade/CarregarRegistros/";

store.Proxy.Add(proxy);

grid.Store.Add(abastecedor);

PagingToolbar pagingToolbar = new PagingToolbar { ID = "test_bar", HideRefresh = true };

pagingToolbar.Listeners.BeforeRender.Handler = "this.getComponent('last').hidden = true;";

pagingToolbar.AfterPageText = string.Empty;

pagingToolbar.BeforePageText = string.Empty;

pagingToolbar.DisplayMsg = "Records from {0} to {1}.";

grid.BottomBar.Add(pagingToolbar);
}

//now for the proxy method...
public StoreResult CarregarRegistros(int startIndex, int pageSize, int pageNumber, string ordenacao, string parametrosDeFiltro, string identificadorDaAplicacao)
{
StoreResult result = new StoreResult();

IList<IEntidade> entityList = //Get the records from the DB, based on the page...

if (entityList.Any())
{
int total;

//IF the number of entities is smaller then the size of the page
if (entityList.Count < pageSize)
{
//get the total based on the current page, because you are on the last one
total = (pageNumber- 1) * pageSize+ entityList.Count;
}
//ELSE (still not the last page)
else
{
//set total to max, because i don't know how many are (and i don't want to know)
total = int.MaxValue;
}

result.Data = entityList;
result.Total = total;
}
//ELSE (if there's no results, the page that he is searching does not exists)
else
{
result.Success = false; // this does not cancel the page from turning, even the DisplayMsg of the toolbar has 'big numbers' on the {0} and {1}.
result.Message = "There are no records on the page that you want to go.";
}

return result;
}


Thanks in advance,

Daniil
May 23, 2012, 9:42 AM
Hi,

A bit strange scenario, I have never met such scenarios before. Please clarify is retrieving total records count so high-cost operation?


There's someway to really cancel the page from 'turning' in my cenario?

Well, I would prevent it on client returning false from Store BeforeLoad listener.

RCN
May 23, 2012, 11:40 AM
Well...
One of our tables has 26 millions rows... to apply a filtered search on a text column in this table is... expensive... and if we bring more than just the page, this operation could cost us more time than we are able to use...

You see, i thought about the Store.BeforeLoad event too but, that means that I would have to select the filtered rows again to see if the page exists or not. And i would prefer not to do that...

Edit: to be more precise, i did a count on the table i think its the biggest... SELECT count(*) FROM bigTable... 41.459.684 rows... and it took 00:00:42

Thanks in advance,

Daniil
May 23, 2012, 1:28 PM
Well...
One of our tables has 26 millions rows... to apply a filtered search on a text column in this table is... expensive... and if we bring more than just the page, this operation could cost us more time than we are able to use...

Edit: to be more precise, i did a count on the table i think its the biggest... SELECT count(*) FROM bigTable... 41.459.684 rows... and it took 00:00:42

Agreed, 42 seconds is too long. Did you try to optimize it? It's a common problem and there are many discussions on the internet, for example, this one:
http://stackoverflow.com/questions/1238108/what-is-the-fastest-way-of-getting-table-record-count-with-condition-on-sql-serv

As well, what about to organize counting on the database level? It would allow to just read the total amount from the database. For example, a trigger which would periodically count rows.



You see, i thought about the Store.BeforeLoad event too but, that means that I would have to select the filtered rows again to see if the page exists or not. And i would prefer not to do that...


I'm afraid I have not got why you don't want to use BeforeLoad and why you have to re-select rows. Could you provide more details? Maybe, a simple sample which would demonstrate the problem?

RCN
May 23, 2012, 2:13 PM
It's not possible to add a trigger to count because the total of results that i will need to show its been filtered, so the total must be the total of records that match the search.
but yes, we are trying to optimize everytime..

As for the BeforeLoad... well, if i need the list of records that mach the search criteria to decide if that page exists or not (just like i'm doing in the proxy method), then, i'll have to do my search, and if the page exists, i'll return true and the proxy method will select then again.

if you really need a example, i'll do it for you.

like i said, with the success = false on the proxy the store page does not change the rows shown, than why it's like he changed pages? For me it's like a bug.. coz if i said that the search was not a success then why 'change' pages?

Thanks in advance,

Daniil
May 23, 2012, 7:12 PM
It's not possible to add a trigger to count because the total of results that i will need to show its been filtered, so the total must be the total of records that match the search.
but yes, we are trying to optimize everytime..



As for the BeforeLoad... well, if i need the list of records that mach the search criteria to decide if that page exists or not (just like i'm doing in the proxy method), then, i'll have to do my search, and if the page exists, i'll return true and the proxy method will select then again.


Clear, thanks for the clarification.



like i said, with the success = false on the proxy the store page does not change the rows shown, than why it's like he changed pages? For me it's like a bug.. coz if i said that the search was not a success then why 'change' pages?


Well, I would not consider it a bug, because PagingToolbar is not designed to deal with "wrong" total. Generally, your scenario is a bit specific.

But I would agree, it can annoy that PagingToolbar is updated on unsuccessful load. And I won't be surprised it this behavior would change in some next ExtJS release.

I can suggest the following solution to avoid it for now.

Example

Ext.toolbar.Paging.prototype.onLoad = Ext.Function.createInterceptor(Ext.toolbar.Paging. prototype.onLoad, function () {
var success = arguments[2];

return success;
});

Here is the full example.

Example View

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>

<%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Ext.Net.MVC v2 Example</title>

<script type="text/javascript">
Ext.toolbar.Paging.prototype.onLoad = Ext.Function.createInterceptor(Ext.toolbar.Paging. prototype.onLoad, function () {
var success = arguments[2];

return success;
});
</script>
</head>
<body>
<ext:ResourceManager runat="server" />

<ext:GridPanel ID="GridPanel1" runat="server" AutoHeight="true">
<Store>
<ext:Store runat="server" PageSize="3" ShowWarningOnFailure="false">
<Model>
<ext:Model runat="server">
<Fields>
<ext:ModelField Name="test" />
</Fields>
</ext:Model>
</Model>
<Proxy>
<ext:AjaxProxy Url="/Test/GetDataForRemotePaging">
<Reader>
<ext:JsonReader
Root="data"
TotalProperty="total"
MessageProperty="message"
ReadRecordsOnFailure="false"
/>
</Reader>
</ext:AjaxProxy>
</Proxy>
</ext:Store>
</Store>
<ColumnModel runat="server">
<Columns>
<ext:Column runat="server" Text="Test" DataIndex="test" />
</Columns>
</ColumnModel>
<BottomBar>
<ext:PagingToolbar runat="server" />
</BottomBar>
</ext:GridPanel>
</body>
</html>


Example Controller Action

public ActionResult GetDataForRemotePaging(int start, int limit)
{
List<object> data = this.MyData;
if ((start + limit) > data.Count)
{
limit = data.Count - start;
}
List<object> rangeData = (start < 0 || limit < 0) ? data : data.GetRange(start, limit);

StoreResult r = new StoreResult();
r.Data = rangeData;
r.Total = int.MaxValue;

if (start >= 9)
{
r.Message = "No more records";
r.Success = false;
}

return r;
}

RCN
May 24, 2012, 12:37 PM
Wow... never saw anything like that...

Ok...

I don't know why this happens but,
the number of records in the DisplayMsg is now right ("Records from 1 to 30." when i change from page 1 to 222 in a page with size 30). ok.
but the page number still 222. it's like that on the store.currentPage too (debugged the js).

Thank you very much for your replys,

Daniil
May 24, 2012, 2:31 PM
Please clarify how can I reproduce the problem using my example? What are the steps?

RCN
May 24, 2012, 8:21 PM
got to do a working sample. sry for the delay..


<html>
<head runat="server">
<title>Test</title>
<script type="text/javascript">
Ext.toolbar.Paging.prototype.onLoad = Ext.Function.createInterceptor(Ext.toolbar.Paging. prototype.onLoad, function () {
var success = arguments[2];

return success;
});


var FalhaCargaDeRegistros = function (proxy, response, operation) {

Ext.Msg.alert("Erro", operation.error);

return true;
}

</script>
</head>
<body>
<div>
<ext:ResourceManager ID="ResourceManager1" runat="server" />
<ext:GridPanel ID="GridPanel1" runat="server" AutoHeight="true">
<Store>
<ext:Store ID="Store1" runat="server" PageSize="3" ShowWarningOnFailure="false">
<Listeners>
<Exception Handler="FalhaCargaDeRegistros(proxy, response, operation);">
</Exception>
</Listeners>
<Model>
<ext:Model ID="Model1" runat="server">
<Fields>
<ext:ModelField Name="test" />
</Fields>
</ext:Model>
</Model>
<Proxy>
<ext:AjaxProxy Url="/Example/GetDataForRemotePaging/">
<Reader>
<ext:JsonReader Root="data" TotalProperty="total" MessageProperty="message" ReadRecordsOnFailure="false" />
</Reader>
</ext:AjaxProxy>
</Proxy>
</ext:Store>
</Store>
<ColumnModel ID="ColumnModel1" runat="server">
<Columns>
<ext:Column ID="Column1" runat="server" Text="Test" DataIndex="test" />
</Columns>
</ColumnModel>
<BottomBar>
<ext:PagingToolbar ID="PagingToolbar1" runat="server" />
</BottomBar>
</ext:GridPanel>
</div>
</body>
</html>




public ActionResult GetDataForRemotePaging(int start, int limit)
{

StoreResult resultado = new StoreResult();


int quantidadeDeRegistros = limit;

int numeroDaPagina = (limit + start) / quantidadeDeRegistros;

List<object> listaDeEntidades = new List<object> { new { test = "Page 1" }, new { test = "Page 1" }, new { test = "Page 1" } ,
new { test = "Page 2" }, new { test = "Page 2" }, new { test = "Page 2" } ,
new { test = "Page 3" }, new { test = "Page 3" }, new { test = "Page 3" } };

if (listaDeEntidades.Count > start)
{
int quantidadeNaUltimaPagina = (listaDeEntidades.Count - start);

if (quantidadeNaUltimaPagina > limit)
{
quantidadeNaUltimaPagina = limit;
}

listaDeEntidades = listaDeEntidades.GetRange(start, quantidadeNaUltimaPagina);
}
else
{
listaDeEntidades = new List<object>();
}

//Se retornou algum resultado
if (listaDeEntidades.Any())
{
//Cria variável que irá armazenar o total de entidades na lista até o momento
int total;

//Se a quantidade de elementos na lista de entidades é menor que tamanho da página
if (listaDeEntidades.Count < quantidadeDeRegistros)
{
//Total é igual ao número de entidades nas páginas anteriores mais o número de entidades nessa página
total = (numeroDaPagina - 1) * quantidadeDeRegistros + listaDeEntidades.Count;
}
//Se a quantidade de elementos na lista de entidades é igual ao tamanho da página
else
{
//Total é igual ao máximo valor possível de elementos pois não sabemos nesse momento quantas páginas faltam
total = int.MaxValue;
}

//Atribui como data a lista de entidade
resultado.Data = listaDeEntidades;
//Atribui o total como sendo o total calculado acima
resultado.Total = total;
}
//Se não houver resultados
else
{
resultado.Success = false;
resultado.Message = "Theres no records on the page you selected to go.";
}

return resultado;
}


like i said:
the number of records in the DisplayMsg is now right ("Records from 1 to 3." when i change from page 1 to 222 in a page with size 3). ok.
but the page number still 222. it's like that on the store.currentPage too (debugged the js in the exception handler).

Thanks for everything,

Daniil
May 25, 2012, 10:02 AM
I can suggest the following solution.

Markup

<ext:PagingToolbar runat="server">
<Listeners>
<BeforeChange Handler="this.pageToRestore = this.getStore().currentPage;" />
</Listeners>
</ext:PagingToolbar>

JavaScript

Ext.toolbar.Paging.prototype.onLoad = Ext.Function.createInterceptor(Ext.toolbar.Paging. prototype.onLoad, function () {
var success = arguments[2];

if (success === false) {
this.child("#inputItem").setRawValue(this.pageToRestore);
this.getStore().currentPage = this.pageToRestore;
}

return success;
});

By the way, it also happens if change pages by the Next button - store.currentPage was increased. It has been also fixed with the solution above.

Though I am not sure there won't be any backstage effects. Maybe, it would be better to explicitly load a previous valid page.

Example

Ext.toolbar.Paging.prototype.onLoad = Ext.Function.createInterceptor(Ext.toolbar.Paging. prototype.onLoad, function () {
var success = arguments[2];

if (success === false) {
this.getStore().loadPage(this.pageToRestore);
}

return success;
});

RCN
May 25, 2012, 12:01 PM
If i go with the


this.getStore().loadPage(this.pageToRestore);

it's going to select the records in the DB again. so I'll go with the


this.child("#inputItem").setRawValue(this.pageToRestore);
this.getStore().currentPage = this.pageToRestore;


One question... the following line is really necessary?


this.child("#inputItem").setRawValue(this.pageToRestore);


without her the sample seems to run just fine.

Thanks for all your replies till now and the ones to come, hehe

RCN
May 25, 2012, 12:10 PM
Oh i see, only works when i click on the alert with the mouse... if i press the space bar, the number of the page does not chages...

LOL

ok, that's weird... anyway, i'm gonna do it your way...

Thank you a lot,

Daniil
May 25, 2012, 12:12 PM
So, please clarify can we close the thread?

RCN
May 25, 2012, 1:14 PM
Sure. Thank you a lot.

RCN
May 28, 2012, 12:13 PM
Daniil,

Now I'm trying to apply the same scenario that i put here, in the grid contained by the combobox. Is that possible?

Just asking because I don't have the same direct access to the paging component. Without it i cant hide the last button...


pagingToolbar.Listeners.BeforeRender.Handler = "this.getComponent('last').hidden = true;";


or even apply your solution (described in this thread)...

or theres a way? and I don't know of?

Daniil
May 28, 2012, 4:49 PM
This is how you can access ComboBox PagingToolbar.

Example

<ext:ComboBox runat="server" PageSize="1">
<Listeners>
<AfterRender Handler="var pt = this.getPicker().pagingToolbar;
console.log(pt);" />
</Listeners>
</ext:ComboBox>

RCN
May 29, 2012, 12:22 PM
Thanks for your reply here, but lets continue on the other one... http://forums.ext.net/showthread.php?19226-Problem-trying-to-customise-the-PagingToolbar-of-the-ComboBox

ty