PDA

View Full Version : The GridPanel/Store saving description



Vladimir
Sep 07, 2008, 1:16 PM
Hi All,

I want to clarify the GridPanel/Store saving model:

The content of GridPanel can be saved using to approaches:

1. Using UpdateProxy
2. Using ajax request to the page which contains Store control.

If UpdateProxy is missing in Store then will be used second approach. For triggering saving need to call save function of GridPanel, for example



<ext:Button runat="server" ID="btnSave" Text="Save" AutoPostBack="false" Icon="Disk">
<Listeners>
<Click Handler="#{GridPanel1}.save();" />
</Listeners>
</ext:Button>


I splitted description on several post for easy navigation

------------------
1. Saving using UpdateProxy.

If Store contain UpdateProxy then saving function will be pass saved data to update proxy. Examples of UpdateProxy

Example1


<UpdateProxy>
<ext:HttpWriteProxy Method="POST" Url="../Shared/Save.ashx" />
</UpdateProxy>

In this example the changed records will be submit to HttpHandler Save.ashx (instead handler you can use any aspx page)

Example2


<UpdateProxy>
<ext:HttpWriteProxy Method="POST" Url="../Shared/CustomerService.asmx/SaveCustomers" HandleSaveResponseAsXml="true" />
</UpdateProxy>

In this example we use WebService. Please pay attention that must be set two properties: Method=POST and HandleSaveResponseAsXml="true" (this indicate that save response from server will be has xml representation)

How to handle submitted data on server

The changed data submitted on server in "data" form field. You can retrieve with next code


Request.Form["data]

If you use webservice then signature of saving method should be (of course the name can be free)


[WebMethod]
public AjaxResponse SaveCustomers(string data)
{
.....
}


For easy handling of submitted data on server you can use StoreDataHandler class. The constructor of this class accept the HttpContext or submitted data (as string).
This class allowing the developer represent the submitted data as Json string, as Xml or as Object collection (if you have bussines object on which can be mapped submitted data)


StoreDataHandler dataHandler = new StoreDataHandler(context); // where context is HttpContext

string json = dataHandler.JsonData;
ChangeRecords<Customer> data = dataHandler.ObjectData<Customer>(); //as object collection
XmlDocument xml = dataHandler.XmlData;


The Object data return ChangeRecords<T> class. This class contains three collection
List<T> Inserted
List<T> Deleted
List<T> Updated

The developer can specified additional parameters which will be passed to server during save ajax request. The parameters can be specified in WriteBaseParams. In HtppHandler (or any page) these parameters can be retrieved from Request.Form collection, if using web service then each parameter must be in signature of web method

Let's see on example of saving data in HttpHandler


public void ProcessRequest(HttpContext context)
{
//Create instance and passing to the constructor HttpContext for automatically retrieving submitted data
StoreDataHandler gd = new StoreDataHandler(context);

//Class for building save response for confirmation on client
AjaxResponse sr = new AjaxResponse(true);

try
{
//Convert data to business objects collection
ChangeRecords<Customer> data = gd.ObjectData<Customer>();

//performs update actions

foreach (Customer customer in data.Deleted)
{
Customers.Delete(customer);
}

foreach (Customer customer in data.Updated)
{
Customers.Update(customer);
}

foreach (Customer customer in data.Created)
{
Customers.Insert(customer);
}
}
catch (Exception e)
{
//if were errors then we should to tell client that saving request was unsuccessful
sr.Success = false;
sr.Msg = e.Message;
}

//Convert AjaxResponse to json string and send to client
sr.MakeAnswer();
}


Example with saving in WebService (please pay attention that for UpdateProxy should be set Method=POST and HandleSaveResponseAsXml="true"). The signature of web method can be siffer if using WriteBaseParams (in this case each parameter must be in signature)


[WebMethod]
public AjaxResponse SaveCustomers(string data)
{
//Create instance and passing to the constructor the submitted data
StoreDataHandler gd = new StoreDataHandler(data);

... the same as in above example

//return answer
return sr;
}



2. Saving with internal Store saving model

With this approach all form controls will be submit to server (as in classic postback)

When the UpdateProxy is not specified then will be used internal Store saving. If was specified any IDataSource(SqlDataSource, ObjectDataSource and etc) then they will be used (will be called Insert, Delete, Update of IDataSource). The IDataSource can be specified in DataSourceID of Store

The Store events sequence:
- BeforeAjaxPostBack - fired before any store ajax action: now supported "update" and "refresh" actions. Available next properties in BeforeAjaxPostBackEventArgs: Action, Data (StoreDataHandler which contains submitted data), Parameters (parameters collection passed from client)

if action == "refresh" then
- RefreshData - fired when the should be refresh data. The developer should bind new data on this event. In StoreRefreshDataEventArgs available next properies: Parameters (parameters from client), Start, Limit, Sort, Dir (for suppoerting remote paging and sorting)

if action == "update" then
-- BeforeStoreChanged - fired before all actions related with modifications.In the BeforeStoreChangedEventArgs available: Parameters (parameters from client), DataHandler (StoreDataHandler which contains submitted data). In this event the developer can implenet own logic batch saving (using data from DataHandler). The Developer can set Cancel property of BeforeStoreChangedEventArgs to true for cancel all automatic modification actions

if Cancel from BeforeStoreChangedEventArgs then perform next actions
--- BeforeRecordUpdated - fired for each changed record before updating. If using IDataSource specified in DataSourceID then will be used Update function of IDataSource after this event. If doesn't specified then in this event the developer can specify own logic of updating. In BeforeRecordUpdatedEventArgs available: Record (XmlNode with changed record), keys (id field of changed record), NewValues collection, OldValues (now always empty, in future will be contain old values).
The developer can cancel IDataSource action (set Cancel) or all Update actions (set CancelAll)
--- AfterRecordUpdated - fired after IDataSource update calling (or after BeforeRecordUpdated if IDataSource doesn't specified)

--- BeforeRecordDeleted
--- AfterRecordDeleted

--- BeforeRecordInserted
--- AfterRecordInserted


-- AfterStoreChanged - fired after all modification actions.

- AfterAjaxPostBack - fired after all actions before building response for client. The developer can change AjaxResponse object in AfterAjaxPostBackEventArgs

3. Saving with confirmation

By default the Store on client need only Succes field for confirmation all changes. But there are cases when such behaivor is not applicable (for example, when part of records are saved successfully and part not, or when need to pass new id's to client for inserted records)
For use confirmation need to set UseIdConfirmation="true" for Store. The Store has property RefreshAfterSaving (None, Auto, Always). The auto is default. The RefreshAfterSaving property manages refreshing behaivor after saving: None - no refreshing, Always - always refresh after saving, Auto - refresh if exists deleted or insterted records only. If using UseIdConfirmation="true" then RefreshAfterSaving can set to None.
If developer using confirmation then he must build confirmation list for returning to client.

Example if using UpdateProxy (HttpHandlers, aspx pages, web services and etc)


public void ProcessRequest(HttpContext context)
{
StoreDataHandler gd = new StoreDataHandler(context);
AjaxResponse sr = new AjaxResponse(true);

//build initial confirmation list and passing to constructor id field name
ConfirmationList confirmationList = gd.BuildConfirmationList("CustomerId");

try
{
ChangeRecords<Customer> data = gd.ObjectData<Customer>();
foreach (Customer customer in data.Deleted)
{
Customers.Delete(customer);

//Confirm action for current record
confirmationList[customer.CustomerId].ConfirmRecord();
}

foreach (Customer customer in data.Updated)
{
Customers.Update(customer);

//Confirm action for current record
confirmationList[customer.CustomerId].ConfirmRecord();
}

foreach (Customer customer in data.Created)
{
Customers.Insert(customer);

confirmationList[customer.CustomerId].ConfirmRecord();

//DON'T FORGET TO PASS NEW ID WHEN CONFIRMING INSERTED RECORD
//if Id set on sql server side then need set new id when confirming record
//confirmationList[customer.CustomerId].ConfirmRecord(newId);
}
}
catch (Exception e)
{
sr.Success = false;
sr.Msg = e.Message;
}

StoreResponseData response = new StoreResponseData();
response.Confirmation = confirmationList;

sr.Data = response.ToString();
sr.MakeAnswer();
}


If using internal Store saving model the need confirm records in AfterRecord... events. If IDataSource exist then records will be confirm automatically (based on recordAffected argiment in IDataSource callback function but ned id must be setted by the developer manually). If no IDataSource then developer must confirm all actions



private string insertedValue;
protected void SqlDataSource1_Inserted(object sender, SqlDataSourceStatusEventArgs e)
{
//use e.AffectedRows for ensure success action. The store read this value and set predefined Confirm depend on e.AffectedRows
//The Confirm can be granted or denied in OnAfterRecord.... event
//e.Command.Parameters["@SupplierID"] - output parameter of SqlDataSource
insertedValue = e.Command.Parameters["@SupplierID"].Value != null
? e.Command.Parameters["@SupplierID"].Value.ToString()
: "";
}

protected void Store1_AfterRecordInserted(object sender, AfterRecordUpdatedEventArgs e)
{
//If IDataSource specified then deleted and updated records confirms automatic (depending AffectedRows field)
//But you can override this in AfterRecordUpdated and AfterRecordDeleted event
//For insert we should set new id for refresh on client
//If we don't set new id then old id will be used
if(e.Confirmation.Confirm &amp;&amp; !string.IsNullOrEmpty(insertedValueId))
{
e.Confirmation.ConfirmRecord(insertedValueId);
insertedValue = "";
}
}


Please don't hesitate to post your questions

Timothy
Sep 07, 2008, 4:07 PM
Awesome vlad, thanks for the breakdown.

Cheers,
Timothy

Zarzand
Oct 29, 2008, 9:13 PM
Hi Vlad I have a question where does the Customer come from I working on a simple Update / Delete /Insert but I just don't understand how the UpdateProxy works if you can please send me an sample I't would help me allot.


Thanks

Rod
Oct 30, 2008, 6:22 AM
In case that can help you, have some good examples in the example explorer (especially GridPanel / Service Connections / Handler using) that show this kind of stuff.
U can also see Grid Panel / Misc / Details window.
Gl.

methode
Dec 01, 2008, 1:48 PM
EDIT: This post has been moved to http://forums.ext.net/showthread.php?threadid=4664-16-1.aspx


 Hi Vlad,

hope you can help me, I can't get rid of saving more then one record chenged.

I read your description above but my code does not work.

I'm able to save only the first record changed.

This is my structure: XpoDataSource -> Store -> GridPanel.
I can succesfully fetch data, I use AfterRecordUpdated ajax event to save data.
I understood that code executes so many times the number of records are changed, but event code executes only once.

DataSource:




<dxxpo:XpoDataSource ID="XpoDataSourceTemplateMail" TypeName="it.methode.pareto.ParetoLib.crm.mailing.TemplateMa il"
        runat="server">
    </dxxpo:XpoDataSource>



Store:




<ext:Store ID="StoreTemplateMail" DataSourceID="XpoDataSourceTemplateMail" runat="server"
         OnRefreshData="StoreTemplateMail_RefreshData"
         OnAfterRecordUpdated="StoreTemplateMail_RecordUpdated"
         OnAfterRecordDeleted="StoreTemplateMail_RecordDeleted"
         RefreshAfterSaving="Always" OnAfterAjaxEvent="StoreTemplateMail_AjaxPostBackResult">
        <Reader>
            <ext:JsonReader ReaderID="Oid">
                <Fields>
                    <ext:RecordField Name="Oid" />
                    <ext:RecordField Name="TipoTemplateID" />
                    <ext:RecordField Name="Descrizione" />
                </Fields>
            </ext:JsonReader>
        </Reader>
        <Listeners>
            <LoadException Handler="Ext.Msg.alert('TEMPLATE MAIL - LOAD FAILED', e.message || e );" />
            <CommitFailed Handler="Ext.Msg.alert('TEMPLATE MAIL - ERRORE', msg);" />
        </Listeners>
    </ext:Store>



AjaxEvent:




// this executes ONLY once, even if I changed more than one record
protected void StoreTemplateMail_RecordUpdated(object sender, AfterRecordUpdatedEventArgs e)
        {
            int _id = int.Parse(e.Keys["Oid"].ToString());
            int _tipoTemplateID = int.Parse(e.NewValues["TipoTemplateID"].ToString());

            MyLib.crm.mailing.TemplateMail _tm = this.PersistentSession.GetObjectByKey<MyLib.crm.mailing.TemplateMail>(_id, true);

            if (_tm != null)
            {
                _tm.Descrizione = e.NewValues["Descrizione"].ToString();

                if (_tipoTemplateID != -1)
                    _tm.TipoTemplate = this.PersistentSession.GetObjectByKey<MyLib.crm.mailing.TipoTemplate>(_tipoTemplateID, true);

                _tm.Save();
            }
        }



I also tried this:




protected void StoreTemplateMail_BeforeStoreChanged(object sender, BeforeStoreChangedEventArgs e)
        {
            List<MyLib.crm.mailing.TemplateMail> _records = (List<MyLib.crm.mailing.TemplateMail>)e.DataHandler.ObjectData<MyLib.crm.mailing.TemplateMail>().Updated;

        }



But code above (StoreTemplateMail_BeforeStoreChanged) gives me a TargetInvocationException!

I hope is clear, let me know.
Please, add some annotation for "Inserting" if there is something different compared to update model.
Thanx a lot

Matteo

geoffrey.mcgill
Dec 01, 2008, 2:17 PM
Hi Matteo,

It would be best to start a new thread with your post. 


Post a link cross referencing the threads if they're releated. 

methode
Dec 01, 2008, 2:23 PM
 yesss, immediately


EDIT: See related POST at http://forums.ext.net/showthread.php?threadid=4664-16-1.aspx

animalisme
Jun 11, 2009, 7:04 AM
why i can't fire the event BeforeRecordInserted or else. I write as follow:


ext:Store ID="Store1" runat="server" OnBeforeRecordInserted="Store1_BeforeRecordInserted" OnBeforeRecordUpdated="Store1_BeforeRecordUpdated">

and the.cs code as follow:

protected void Store1_BeforeRecordInserted(object sender, BeforeRecordInsertedEventArgs e)
{
string s = e.Record.ToString();// here is a breakpoint
}

madeofrose
Aug 25, 2012, 10:08 AM
I use version 2. Is this the reason? Is there any replacement property.

I want to update grid because i use entity field's onchanging events

Thanks

prince-sat
Aug 27, 2012, 10:49 PM
Hi ,
Can you give us an Example of this nice descroption Using Razor Engine .

Just a Simple Description Becuse in Razor Engine I did not find the propretie "UpdateProxy"

Thinks.