PDA

View Full Version : [CLOSED] Saving state of application elements



ingbabic
Oct 30, 2015, 9:52 AM
Hello
In following sample when you select one of the tree nodes, grid is shown in central panel. When you resize columns, change their position, or visibility, close application and then open it again, changes will be saved and columns will be displayed as user arranged them. So far so good. Problem is when you click between nodes. Nothing is remembered then and grid is shown with no regard to user changes.
I tried similar technique for remembering last selected accordion panel and last selected node but it does not work at all :(
How that would be possible?

index.cshtml


Html.X().Panel().Layout(LayoutType.Border).Border( false).Height(600)
.Items
(
Html.X().Panel().Title("Components").Region(Region.West).Layout(LayoutType.Accordion) .Width(200)
.Items
(
Html.X().TreePanel().Title("Codebooks")
.Root
(
Html.X().Node().Text("Codebooks").Expanded(true)
.Children
(
Html.X().Node().Text("Countries").Leaf(true),
Html.X().Node().Text("Cities").Leaf(true)
)
)
.DirectEvents(de =>
{
de.SelectionChange.Url = "ShowCodebook";
de.SelectionChange.ExtraParams.Add(new Parameter("codebook", "selected.length>0 ? selected[0].data.text: ''", ParameterMode.Raw));
}),
Html.X().TreePanel().Title("Astrology")
.Root
(
Html.X().Node().Text("Signs").Expanded(true)
.Children
(
Html.X().Node().Text("Aries").Leaf(true),
Html.X().Node().Text("Taurus").Leaf(true),
Html.X().Node().Text("Gemini").Leaf(true),
Html.X().Node().Text("Cancer").Leaf(true),
Html.X().Node().Text("Leo").Leaf(true),
Html.X().Node().Text("Virgo").Leaf(true),
Html.X().Node().Text("Libra").Leaf(true),
Html.X().Node().Text("Scorpio").Leaf(true),
Html.X().Node().Text("Sagittarius").Leaf(true),
Html.X().Node().Text("Capricorn").Leaf(true),
Html.X().Node().Text("Aquarius").Leaf(true),
Html.X().Node().Text("Pisces").Leaf(true)
)
)
)
,
Html.X().Panel().ID("Center1").Region(Region.Center).Title("Main").Layout(LayoutType.Fit)
)


Codebook.cshtml


@model string

@(
Html.X().GridPanel().Title(Model).Stateful(true).S tateID("grid_" + Model).StateEvents(new string[] { "columnresize", "columnmove", "columnvisible", "columnsort" })
.ColumnModel
(
c =>
{
if (Model == "Countries")
{
c.Columns.Add(Html.X().Column().Text("Country name"));
c.Columns.Add(Html.X().Column().Text("Area"));
c.Columns.Add(Html.X().Column().Text("Inhabitans"));
c.Columns.Add(Html.X().Column().Text("BDP"));
c.Columns.Add(Html.X().DateColumn().Text("President"));
}
else
{
c.Columns.Add(Html.X().Column().Text("City name"));
c.Columns.Add(Html.X().Column().Text("Area"));
c.Columns.Add(Html.X().Column().Text("Inhabitans"));
c.Columns.Add(Html.X().Column().Text("Country"));
c.Columns.Add(Html.X().DateColumn().Text("Major"));
}
}
)
)


Controller


public ActionResult Index()
{
return View();
}

public ActionResult ShowCodebook(string codebook)
{
return new Ext.Net.MVC.PartialViewResult
{
ViewName = "Codebook",
Model = codebook,
ContainerId = "Center1",
ClearContainer = true,
RenderMode = RenderMode.AddTo
};

Daniil
Oct 30, 2015, 7:10 PM
Hi @ingbabic,

I'll review what might be done.

Please clarify do you save the state in cookies?

ingbabic
Nov 02, 2015, 8:20 AM
Yes, sorry I forgot to mention that:


@Html.X().ResourceManager().StateProvider(StatePro vider.Cookie)

Daniil
Nov 02, 2015, 4:01 PM
Please specify explicit IDs for all the Columns. Otherwise the state cannot be always restored properly.

ingbabic
Nov 03, 2015, 9:01 AM
Well... Now it works quite unpredictable. If I for example stretch column and immediately exit application, when next time I start application, the change will not be saved (as opposite to previous situation). If I do change selection between nodes after I do some of column arranging it seems that columns state is remembered and restored, but I had situation where it was not like this (especially if I start app again after some, not long, delay). Unfortunately I did not catch exact scenario, how this does happen. Any way one thing is certain it does NOT work quite :(

Daniil
Nov 03, 2015, 10:07 AM
If I for example stretch column and immediately exit application, when next time I start application, the change will not be saved (as opposite to previous situation)

Please clarify are you sure it didn't happen before? I am pretty much sure setting Columns' IDs should not affect it.

There is the save delay which is 100 by default. Maybe, the state is not saved if you quickly exit the application.
http://docs.sencha.com/extjs/5.1/5.1.1-apidocs/#!/api/Ext.state.Stateful-cfg-saveDelay

Please try to set it to 1 or 0, maybe.

ingbabic
Nov 03, 2015, 12:29 PM
Well I what I did, I cleared all cookies and things started to work. Is there a possibility to have more control over it, as deleting of all cookies is, let's say, hard option? Also you pointed out that i need to set columns' IDs, is it sufficient to do with StateIDs?
At the end how I can have other elements remembered too (last selected node in tree and accordion panel)?

Daniil
Nov 03, 2015, 1:25 PM
Is there a possibility to have more control over it, as deleting of all cookies is, let's say, hard option?

That is the API of Ext.state.CookieProvider. Hopefully, you'll find something helpful.
http://docs.sencha.com/extjs/5.1/5.1.1-apidocs/#!/api/Ext.state.CookieProvider


Also you pointed out that i need to set columns' IDs, is it sufficient to do with StateIDs?

Yes, it is.


At the end how I can have other elements remembered too (last selected node in tree and accordion panel)?

As far as I can remember there is no such the functionality by default. My recommendation would be reviewing the Stateful API. There are ways to achieve the requirement.
http://docs.sencha.com/extjs/5.1/5.1.1-apidocs/#!/api/Ext.state.Stateful

ingbabic
Nov 03, 2015, 2:25 PM
There are ways to achieve the requirement.

I believe there are. For example I found this (http://damiansromek.pl/2010/12/20/how-to-extjs-stateful-components/). I tried to apply this to my sample, but it does not work. Here is my new tree view from sample that we started.


Html.X().TreePanel().Title("Codebooks").Stateful(true).StateID("treePanel").StateEvents(new string[] { "expand", "collapse" })
.Root
(
Html.X().Node().Text("Codebooks").Expanded(true)
.Children
(
Html.X().Node().Text("Countries").Leaf(true),
Html.X().Node().Text("Cities").Leaf(true)
)
)
.DirectEvents(de =>
{
de.SelectionChange.Url = "ShowCodebook";
de.SelectionChange.ExtraParams.Add(new Parameter("codebook", "selected.length>0 ? selected[0].data.text: ''", ParameterMode.Raw));
})
.GetState(g =>
{
g.Handler = "return this.collapsed";
})

Execution comes to handler of GetState, but it does nothing, meaning tree view is not shown as I left it when I close application. Please help!

ingbabic
Nov 03, 2015, 3:57 PM
Hi, I almost come to solution. Please review this


@(
Html.X().Panel().Layout(LayoutType.Border).Border( false).Height(600)
.Items
(
Html.X().Panel().Title("Components").Region(Region.West).Layout(LayoutType.Accordion) .Width(200)
.Items
(
Html.X().TreePanel().Title("Codebooks").Stateful(true).StateID("treePanel").StateEvents(new string[] { "expand", "collapse", "selectionchange" })
.Root
(
Html.X().Node().Text("Codebooks").Expanded(true)
.Children
(
Html.X().Node().Text("Countries").Leaf(true),
Html.X().Node().Text("Cities").Leaf(true)
)
)
.Listeners(l =>
{
l.StateRestore.Handler = " this.collapsed = state.collapsed; var record = this.getStore().getNodeById(state.selected); this.getSelectionModel().select(record)";
})
.DirectEvents(de =>
{
de.SelectionChange.Url = "ShowCodebook";
de.SelectionChange.ExtraParams.Add(new Parameter("codebook", "selected.length>0 ? selected[0].data.text: ''", ParameterMode.Raw));
})
.GetState(g =>
{
g.Handler = "return {collapsed : this.collapsed, selected: this.getSelectionModel.getSelection()[0].internalId};";
})
,
Html.X().TreePanel().Title("Astrology").Stateful(true).StateID("treePanelAstr").StateEvents(new string[] { "expand", "collapse", "selectionchange" })
.Root
(
Html.X().Node().Text("Signs").Expanded(true)
.Children
(
Html.X().Node().Text("Aries").NodeID("aries").Leaf(true),
Html.X().Node().Text("Taurus").NodeID("taurus").Leaf(true),
Html.X().Node().Text("Gemini").NodeID("gemini").Leaf(true),
Html.X().Node().Text("Cancer").NodeID("cancer").Leaf(true),
Html.X().Node().Text("Leo").NodeID("leo").Leaf(true),
Html.X().Node().Text("Virgo").NodeID("virgo").Leaf(true),
Html.X().Node().Text("Libra").NodeID("libra").Leaf(true),
Html.X().Node().Text("Scorpio").NodeID("scorpio").Leaf(true),
Html.X().Node().Text("Sagittarius").NodeID("sagittarius").Leaf(true),
Html.X().Node().Text("Capricorn").NodeID("capricorn").Leaf(true),
Html.X().Node().Text("Aquarius").NodeID("aquarius").Leaf(true),
Html.X().Node().Text("Pisces").NodeID("pisces").Leaf(true)
)
)
.Listeners(l =>
{
l.StateRestore.Handler = "this.collapsed = state.collapsed; var record = this.getStore().getNodeById(state.selected); this.getSelectionModel().select(record)";
})
.GetState(g =>
{
g.Handler = "return {collapsed : this.collapsed, selected: this.getSelectionModel().getSelection()[0].internalId};";
})

)
,
Html.X().Panel().ID("Center1").Region(Region.Center).Title("Main").Layout(LayoutType.Fit)
)
)

Only what still does not work is selecting last selected node. Please help. Thanks.

Daniil
Nov 03, 2015, 9:34 PM
Only what still does not work is selecting last selected node.

Please clarify what exactly doesn't work?

Does GetState save a correct internalId?

Does the StateRestore try to select the node? What happens in the StateRestore handler?

Are you sure to rely on a Node's internalId if no Node's ID is set up? I guess an internalId is auto-generated and it might change and the StateRestore won't find a correct Node.

ingbabic
Nov 04, 2015, 7:06 AM
Please clarify what exactly doesn't work?


Huh, I thought that you insist on samples that you can copy/paste it in Visual Studio and immediately reproduce problem. Nevertheless I'll explain problem more clearly:

Start sample
Select one of the panels (for example, Astrology)
Select one node in tree (for example Sagittarius)
Exit sample
Start sample again
Now, situation is that sample correctly show last selected panel when I left application, but it doesn't select a node




Does GetState save a correct internalId?

yes


Does the StateRestore try to select the node? What happens in the StateRestore handler?

No State restore does not select the node. I don't quite understand your second question. Let's go line by line:

this.collapsed = state.collapsed;
Here I set collapsed state of panel. It works

var record = this.getStore().getNodeById(state.selected)
Here I get correct node, based on selected property saved in get state.

this.getSelectionModel().select(record)
Here I'm trying to select that node, but it does not happen (although I've got correct node).


Are you sure to rely on a Node's internalId if no Node's ID is set up?
You're right, NodeID is set in second panel, I forgot to set it in first, but for previously mentioned test case it does not have impact.

Generally I would like is, as you said that there a number of solutions for this problem, your opinion am I doing it on the right way? Maybe there are some more elegant solutions. For example you saw that I set same pieces of code for both accordion panels, which is not so nice especially from perspective of supporting that in future. Could it be done better, with less code and how? Also if this is the only way, why selecting node is not working?

Daniil
Nov 05, 2015, 8:48 AM
Huh, I thought that you insist on samples that you can copy/paste it in Visual Studio and immediately reproduce problem.

Still a verbal explanation helps a lot:) Also we do not always provide a ready solution. Specifically, for experienced Ext.NET developers as you are. Our support also means "pushing into right directions". As well it is not always required for us to run samples. Sometimes it is possible to determine something just looking at the test case and reading the verbal description. In this specific case I was not sure what exactly doesn't work and asked to clarify something.


Here I'm trying to select that node, but it does not happen (although I've got correct node).

Okay, now I am almost sure what is going wrong. A node is selected too early.

As stated in ExtJS docs a ViewReady event should be used to select a default row.
http://docs.sencha.com/extjs/4.2.1/#!/api/Ext.panel.Table-event-viewready

So, I would expect this working in a StateRestore handler.


this.on('viewready', function() {
this.getSelectionModel().select(record);
});


Generally I would like is, as you said that there a number of solutions for this problem, your opinion am I doing it on the right way? Maybe there are some more elegant solutions.

I think you are on the right way.


For example you saw that I set same pieces of code for both accordion panels, which is not so nice especially from perspective of supporting that in future.

I would recommend this approach:

l.StateRestore.Fn = "onStateRestore"

g.Fn = "myGetState"

ingbabic
Nov 05, 2015, 2:29 PM
Well, you said that I am experienced in Ext.NET and I think I am just repetitious, asking silly questions, but I hope I'm not too annoying. Might be that sometimes I'll learn something. This thing with viewready would never come across my mind, simply I because don't know it, because this is too large, I don't know what ever is inside. But at least it works :)

I hope this one is last question for this thread. When I set


g.Fn = "getState"


I do not get anything as parameter in here:


var getState = function (tree) {
return { collapsed: tree.collapsed, selected: tree.getSelectionModel().getSelection()[0].internalId };
}


My tree is undefined...

Daniil
Nov 05, 2015, 3:26 PM
Yes, the getState function doesn't have any parameters.
http://docs.sencha.com/extjs/4.2.1/#!/api/Ext.state.Stateful-method-getState

Please still use the this reference.


Well, you said that I am experienced in Ext.NET and I think I am just repetitious, asking silly questions, but I hope I'm not too annoying.

You are doing very well.


because this is too large

True. I always keep forgetting stuff even if I already knew that. Sometimes I found a forum post with my own sample and think like "wow, I already did this before...really?".

ingbabic
Nov 05, 2015, 3:45 PM
Danill sorry 100x :)
I am trying to make my samples as simple as possible, but sometimes I go too far. Now everything is working in sample, but real application is having nodes filled via AjaxProxy and it seems that even viewready is too early as tree.getStore().getNodeById(state.selected)
does not return anything, although state.selected is correct (simply tree is not filled yet with data).

Do you need sample? :)

Daniil
Nov 05, 2015, 4:04 PM
In this scenario selection should be done in a TreeStore's Load listener.

ingbabic
Nov 06, 2015, 9:45 AM
Ok, still one more problem. StateRestore handler for one tree panel simply doesn't get called. I have checked and rechecked thousand times. Everything is the same for other treepanels in my accordion panel. I am near to start ripping off hair from my head.

ingbabic
Nov 06, 2015, 11:54 AM
And again, as if by some magic, clearing cookies helped to resolve problem. Thanks a lot Danill. Please close this thread.