[CLOSED] grid row reordering with remote store?

  1. #1

    [CLOSED] grid row reordering with remote store?

    Hello,
    I'm interested to add row ordering with drag & drop o a grid like it is done in this example

    https://examples3.ext.net/#/DragDrop...ws_Reordering/

    The problem I have is that my grid has paging and remote Store which feeds from a database. This is my actual gridpanel definition


    <ext:GridPanel
    			              	ID="GridSections"
    			              	runat="server"
    			              	Title="Sections"
    			              	Flex="2" 
    			              	MarginSpec="0 15 0 0"
    			              	Width="600"
    			              	Height="350">
    			              		<TopBar>
    					              	<ext:Toolbar runat="server" Flat="true">
    						              	<Items>
    							              	<ext:Button runat="server" Icon="Add">
    								              	<DirectEvents>
    									              	<Click OnEvent="addTemplateSection">
    									              		<EventMask ShowMask="true" />                
    									              	</Click>
    								              	</DirectEvents>		
    								              	<ToolTips>
    								              		<ext:ToolTip runat="server" Html="Click to add a section" />
    								              	</ToolTips>
    							              	</ext:Button>
    							              	<ext:ToolbarSpacer runat="server" />							              							              							              	
    						              	</Items>
    					              	</ext:Toolbar>
    				              	</TopBar>
    			              	<Store>
    				              	<ext:Store ID="Store1" runat="server" RemoteSort="false" PageSize="15">
    					              	<Model>
    						              	<ext:Model runat="server">
    							              	<Fields>
    							              		<ext:ModelField Name="id" />		
    							              		<ext:ModelField Name="sectionname" />							              	
    							              	</Fields>
    						              	</ext:Model>
    					              	</Model>
    					              	<Proxy>
    						              	<ext:AjaxProxy Url="Datagrid.aspx?storeID=TemplateItems">
    							              	<Reader>
    							              		<ext:JsonReader RootProperty="items" TotalProperty="totalCount" />
    							              	</Reader>                 
    						              	</ext:AjaxProxy>
    									</Proxy>
    									<DirectEvents>
    										<Load OnEvent="store_AfterLoad">
    											<EventMask ShowMask="true" />                
    										</Load>
            							</DirectEvents>
    				              	</ext:Store>
    			              	</Store>
    			              	<ColumnModel>
    				              	<Columns>
    					              	<ext:CommandColumn runat="server" Width="40">
    						              	<Commands>                            					  
    						              		<ext:GridCommand Icon="Script" CommandName="EditTemplateSection" />                                					 
    						              	</Commands>
    						              	<DirectEvents>
                									<Command OnEvent="Command_Click">
                    									<EventMask ShowMask="true" /> 
    														<ExtraParams>
    															<ext:Parameter Name="NodeID" Value="record.data.id" Mode="Raw" />
    															<ext:Parameter Name="Command" Value="command" Mode="Raw" />
    														</ExtraParams>
                									</Command>
            								</DirectEvents>	
    					              	</ext:CommandColumn>
    					              	<ext:Column runat="server" Sortable="false" Text="Name" DataIndex="sectionname" Flex="1" />							                
    					              	
    					              	<ext:CommandColumn runat="server" Width="40">
    						              	<Commands>
    						              		<ext:GridCommand Icon="Delete" CommandName="DeleteTemplateSection" />   											 
    						              	</Commands>
    						              	<DirectEvents>
    									              	<Command OnEvent="Command_Click">
    									              		<EventMask ShowMask="true" />    
    															<ExtraParams>
    															<ext:Parameter Name="NodeID" Value="record.data.id" Mode="Raw" />
    															<ext:Parameter Name="Command" Value="command" Mode="Raw" />
    															</ExtraParams>									              		
    									              	</Command>
    								        </DirectEvents>	
    					              	</ext:CommandColumn>
    				              	</Columns>
    			              	</ColumnModel>
    			              	<SelectionModel>
    			              		<ext:RowSelectionModel runat="server" >
    			              		
    				              		<DirectEvents>
    					              		<Select  OnEvent="templateSectionRowSelect">
    						              		<EventMask ShowMask="true" /> 
    						              		<ExtraParams>
    							              		<ext:Parameter Name="NodeID" Value="record.getId()" Mode="Raw" />	
    							            		<ext:Parameter Name="NodeName" Value="record.data.sectionname" Mode="Raw" />	 		
    						              		</ExtraParams>
    					              		</Select >
    				              		</DirectEvents>	
            						</ext:RowSelectionModel >
    			              	</SelectionModel>
    			              	</ext:GridPanel>
    Can provide me some guidelines on how to perform row reorder in a situation like that.

    I reckon that I will need a directevent on the drop of the row, so the server can perfom the necessary database changes, but be consistent the data in the client shoud also be changed. Am I right? The other problem I have is how o reorder a row from page 2 to page 1.

    Is there a example of a scenario like this?
    Last edited by fabricio.murta; Jul 21, 2016 at 5:14 AM.
  2. #2
    Hello @jcanton!

    Well, you can bind a DirectEvent to the 'Drop' event (it will probably be in the dragdrop plugin block) and update the server as the record is dropped.

    I don't see how you can drag and drop something in the previous page in the approach the example you pointed shows so maybe there's not much problem on doing so. If it starts to get complicated, you can just abort the drop operation (return a 'false' from the 'beforeDrop' "listener") and just call a DirectMethod to update the database with the ordering, with a callback that refreshes the grid. I'm not sure how you are keeping the ordering in the database but a order change in a row in the beginning of the list may mean a change to the rest of the n entries in the grid, which may be somehow cumbersome, if the list is too long.

    If these directions do not help, please provide us a fully runnable and simplified test case so we can suggest you on code.
    Fabrício Murta
    Developer & Support Expert
  3. #3
    I think It will be most helpful if you can show how to incorporate row reordering in this ext.net example

    https://examples3.ext.net/#/GridPanel/Update/AutoSave/

    The situation is similar and it will be helpful to show how to incorporate row reorderig when the data is in the server and it is dynamic.
  4. #4
    Hello @jcanton!

    Given the example you pointed, to simulate a persistent database I've changed the data into a static variable -- the data will be kept as long as the server instance (change to Web.config, server restart, rebuild, all will "reset" the database to the initial state).

    Then the data would be turned in

    public static List<TestPerson> TestPersons = new List<TestPerson>
    {
        new TestPerson{Id=1, Email="fred@flintstone.com", First="Fred", Last="Flintstone"},
        new TestPerson{Id=2, Email="wilma@flintstone.com", First="Wilma", Last="Flintstone"},
        new TestPerson{Id=3, Email="pebbles@flintstone.com", First="Pebbles", Last="Flintstone"},
        new TestPerson{Id=4, Email="barney@rubble.com", First="Barney", Last="Rubble"},
        new TestPerson{Id=5, Email="betty@rubble.com", First="Betty", Last="Rubble"},
        new TestPerson{Id=6, Email="bambam@rubble.com", First="BamBam", Last="Rubble"}
    };
    Following, a possible approach is adding a direct method to reorder the entries server-side as they are ordered client-side:

    [DirectMethod]
    public void MoveEntry(int id, int pos)
    {
        var curIndex = TestPersons.FindIndex(m => m.Id == id);
    
        if (curIndex >= 0)
        {
            var movedRec = TestPersons[curIndex];
            TestPersons.RemoveAt(curIndex);
    
            TestPersons.Insert(pos, movedRec);
        }
    }
    Note that this is an alternative that might not work in actual databases, where you would be having to store the order some other way, like having an index column. This approach is just simpler to be implemented with C#'s List. Internally, be aware that inserting an item in the beginning of the list means moving every item after it one position ahead.

    Then, a JavaScript handler will be necessary in order to reflect the client-side change in the server side. We want to bind this to the 'drop' event in the grid. Knowing the function signature (you can add a Handler="console.log('test');" to the event and inspect the generated page to find out the method signature, or refer to Sencha documentation for this), we'll write this:

    var handleDrop = function (node, data, overModel, dropPosition) {
        var droppedRec = data.records[0],
            items = droppedRec.store.data.items,
            dropPos = -1;
    
        for (var i = 0; i < items.length; i++) {
            if (items[i].internalId == droppedRec.internalId) {
                dropPos = i;
                break;
            }
        }
    
        if (dropPos < 0) {
            Ext.Error("Unknown drop position in grid view.");
            return false;
        }
    
        App.direct.MoveEntry(droppedRec.id, dropPos);
    }
    Then, we'll add the drag-drop reorder plugin and grid view as well as the event to the grid:

    <View>
        <ext:GridView runat="server">
            <Plugins>
                <ext:GridDragDrop runat="server" DragText="Drag and drop to reorganize" />
            </Plugins>
            <Listeners>
                <Drop Fn="handleDrop" />
            </Listeners>
        </ext:GridView>
    </View>
    Given that changes to test the page, open it. I've changed the fewer the possible from the original example, so notice I didn't implement a method to sync other changes to the record (like changing names in the editor and reflecting to the static object server-side, etc).

    So move around the records in the grid and notice their positions. An easy way for that is just memorize the sequence of IDs the grid has when you move records around. Then refresh the page, and the entries should be exactly in the same place!

    Unless, of course, you trigger a server side refresh (where the static variable gets reset) as mentioned above.

    Below is the full final source for the edited example:

    <%@ Page Language="C#" %>
    
    <%@ Import Namespace="System.Collections.Generic"%>
    
    <script runat="server">
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!X.IsAjaxRequest)
            {
                this.Session["TestPersons"] = null;
            }
            this.BindData();
        }
    
        public class TestPerson
        {
            public int? Id
            {
                get; set;
            }
    
            public string Email
            {
                get; set;
            }
    
            public string First
            {
                get;
                set;
            }
    
            public string Last
            {
                get;
                set;
            }
        }
    
        //----------------Page------------------------
        public static List<TestPerson> TestPersons = new List<TestPerson>
        {
            new TestPerson{Id=1, Email="fred@flintstone.com", First="Fred", Last="Flintstone"},
            new TestPerson{Id=2, Email="wilma@flintstone.com", First="Wilma", Last="Flintstone"},
            new TestPerson{Id=3, Email="pebbles@flintstone.com", First="Pebbles", Last="Flintstone"},
            new TestPerson{Id=4, Email="barney@rubble.com", First="Barney", Last="Rubble"},
            new TestPerson{Id=5, Email="betty@rubble.com", First="Betty", Last="Rubble"},
            new TestPerson{Id=6, Email="bambam@rubble.com", First="BamBam", Last="Rubble"}
        };
    
        private static int curId = 7;
        private static object lockObj = new object();
    
        private int NewId
        {
            get
            {
                return System.Threading.Interlocked.Increment(ref curId);
            }
        }
    
        private List<TestPerson> CurrentData
        {
            get
            {
                object persons = this.Session["TestPersons"];
    
                if (persons == null)
                {
                    persons = TestPersons;
                    this.Session["TestPersons"] = persons;
                }
    
                return (List<TestPerson>)persons;
            }
        }
    
        private int? AddPerson(TestPerson person)
        {
            lock (lockObj)
            {
                List<TestPerson> persons = this.CurrentData;
                person.Id = this.NewId;
                persons.Add(person);
                this.Session["TestPersons"] = persons;
    
                return person.Id;
            }
        }
    
        private void DeletePerson(int id)
        {
            lock (lockObj)
            {
                List<TestPerson> persons = this.CurrentData;
                TestPerson person = null;
    
                foreach (TestPerson p in persons)
                {
                    if (p.Id == id)
                    {
                        person = p;
                        break;
                    }
                }
    
                if (person == null)
                {
                    throw new Exception("TestPerson not found");
                }
    
                persons.Remove(person);
    
                this.Session["TestPersons"] = persons;
            }
        }
    
        private void UpdatePerson(TestPerson person)
        {
            lock (lockObj)
            {
                if (person.Id % 2 != 0)
                {
                    throw new Exception("SIMULATED ERROR: ODD-numbered id");
                }
    
                List<TestPerson> persons = this.CurrentData;
                TestPerson updatingPerson = null;
    
                foreach (TestPerson p in persons)
                {
                    if (p.Id == person.Id)
                    {
                        updatingPerson = p;
                        break;
                    }
                }
    
                if (updatingPerson == null)
                {
                    throw new Exception("TestPerson not found");
                }
    
                updatingPerson.Email = person.Email;
                updatingPerson.Last = person.Last;
                updatingPerson.First = person.First;
    
                this.Session["TestPersons"] = persons;
            }
        }
    
        private void BindData()
        {
            if (X.IsAjaxRequest)
            {
                return;
            }
    
            this.Store1.DataSource = this.CurrentData;
            this.Store1.DataBind();
        }
    
        protected void HandleChanges(object sender, BeforeStoreChangedEventArgs e)
        {
            List<TestPerson> persons = e.DataHandler.ObjectData<TestPerson>();
    
            if (e.Action == StoreAction.Create)
            {
                foreach (TestPerson created in persons)
                {
                    this.AddPerson(created);
                    e.ResponseRecords.Add(created);
                }
            }
    
            if (e.Action == StoreAction.Destroy)
            {
                foreach (TestPerson deleted in persons)
                {
                    this.DeletePerson(deleted.Id.Value);
                }
            }
    
            if (e.Action == StoreAction.Update)
            {
                foreach (TestPerson updated in persons)
                {
                    this.UpdatePerson(updated);
                    e.ResponseRecords.Add(updated);
                }
            }
            e.Cancel = true;
        }
        
        [DirectMethod]
        public void MoveEntry(int id, int pos)
        {
            var curIndex = TestPersons.FindIndex(m => m.Id == id);
    
            if (curIndex >= 0)
            {
                var movedRec = TestPersons[curIndex];
                TestPersons.RemoveAt(curIndex);
    
                TestPersons.Insert(pos, movedRec);
            }
        }
    </script>
    
    <!DOCTYPE html>
    
    <html>
    <head runat="server">
        <title>Grid with AutoSave - Ext.NET Examples</title>
        <script>
           var updateRecord = function (form) {
                if (form.getForm()._record == null) {
                    return;
                }
    
                if (!form.getForm().isValid()) {
                    Ext.net.Notification.show({
                        iconCls  : "icon-exclamation",
                        html     : "Form is invalid",
                        title    : "Error"
                    });
                    return false;
                }
    
                form.getForm().updateRecord();
           };
    
           var addRecord = function (form, grid) {
                if (!form.getForm().isValid()) {
                    Ext.net.Notification.show({
                        iconCls  : "icon-exclamation",
                        html     : "Form is invalid",
                        title    : "Error"
                    });
    
                    return false;
                }
    
                grid.store.insert(0, new Person(form.getForm().getFieldValues()));
                form.getForm().reset();
           };
    
           var handleDrop = function (node, data, overModel, dropPosition) {
               var droppedRec = data.records[0],
                   items = droppedRec.store.data.items,
                   dropPos = -1;
    
               for (var i = 0; i < items.length; i++) {
                   if (items[i].internalId == droppedRec.internalId) {
                       dropPos = i;
                       break;
                   }
               }
    
               if (dropPos < 0) {
                   Ext.Error("Unknown drop position in grid view.");
                   return false;
               }
    
               App.direct.MoveEntry(droppedRec.id, dropPos);
           }
        </script>
    </head>
    <body>
        <form runat="server">
            <ext:ResourceManager runat="server" />
    
            <h1>Grid with AutoSave</h1>
    
            <p>An Error has been simulated on the server-side: Attempting to update a record having ODD-numbered id will generate this errror. This error can be handled by listening to the "exception" event upon your Store.</p>
    
            <ext:Store
                ID="Store1"
                runat="server"
                AutoSync="true"
                ShowWarningOnFailure="false"
                OnBeforeStoreChanged="HandleChanges">
                <Model>
                    <ext:Model runat="server" IDProperty="Id" Name="Person">
                        <Fields>
                            <ext:ModelField Name="Id" Type="Int" AllowNull="true" />
                            <ext:ModelField Name="Email" />
                            <ext:ModelField Name="First" />
                            <ext:ModelField Name="Last" />
                        </Fields>
                        <Validators>
                            <ext:PresenceValidator Field="Email" />
                            <ext:PresenceValidator Field="First" />
                            <ext:PresenceValidator Field="Last" />
                        </Validators>
                    </ext:Model>
                </Model>
    
                <Listeners>
                    <Exception Handler="
                        var error = operation.getError(),
                            message = Ext.isString(error) ? error : ('(' + error.status + ')' + error.statusText);
                        Ext.net.Notification.show({
                            iconCls    : 'icon-exclamation',
                            html       : message,
                            title      : 'EXCEPTION',
                            autoScroll : true,
                            hideDelay  : 5000,
                            width      : 300,
                            height     : 200
                        });"
    
                        Buffer="10" />
                </Listeners>
            </ext:Store>
    
            <ext:FormPanel
                ID="UserForm"
                runat="server"
                Icon="User"
                Frame="true"
                Title="User -- All fields are required"
                DefaultAnchor="100%"
                Width="500">
                <FieldDefaults LabelAlign="Right" AllowBlank="false" />
                <Items>
                    <ext:TextField runat="server"
                        FieldLabel="Email"
                        Name="Email"
                        Vtype="email"
                        />
    
                    <ext:TextField runat="server"
                        FieldLabel="First"
                        Name="First"
                        />
    
                    <ext:TextField runat="server"
                        FieldLabel="Last"
                        Name="Last"
                        />
                </Items>
    
                <Buttons>
                    <ext:Button
                        runat="server"
                        Text="Save"
                        Icon="Disk">
                        <Listeners>
                            <Click Handler="updateRecord(#{UserForm});" />
                        </Listeners>
                    </ext:Button>
    
                    <ext:Button
                        runat="server"
                        Text="Create"
                        Icon="UserAdd">
                        <Listeners>
                            <Click Handler="addRecord(#{UserForm}, #{GridPanel1});" />
                        </Listeners>
                    </ext:Button>
    
                    <ext:Button
                        runat="server"
                        Text="Reset">
                        <Listeners>
                            <Click Handler="#{UserForm}.getForm().reset();" />
                        </Listeners>
                    </ext:Button>
                </Buttons>
            </ext:FormPanel>
    
            <ext:GridPanel
                ID="GridPanel1"
                runat="server"
                Icon="Table"
                Frame="true"
                Title="Users"
                Height="400"
                Width="500"
                StoreID="Store1"
                StyleSpec="margin-top: 10px">
                <ColumnModel>
                    <Columns>
                        <ext:Column runat="server" Text="ID" Width="40" DataIndex="Id">
                            <Renderer Handler="return record.phantom ? '' : value;" />
                        </ext:Column>
    
                        <ext:Column runat="server" Text="Email" Flex="1" DataIndex="Email">
                            <Editor>
                                <ext:TextField runat="server" />
                            </Editor>
                        </ext:Column>
    
                        <ext:Column runat="server" Text="First" DataIndex="First">
                            <Editor>
                                <ext:TextField runat="server" />
                            </Editor>
                        </ext:Column>
    
                        <ext:Column runat="server" Text="Last" DataIndex="Last">
                            <Editor>
                                <ext:TextField runat="server" />
                            </Editor>
                        </ext:Column>
    
                        <ext:CommandColumn runat="server" Width="70">
                            <Commands>
                                <ext:GridCommand Text="Reject" ToolTip-Text="Reject row changes" CommandName="reject" Icon="ArrowUndo" />
                            </Commands>
                            <PrepareToolbar Handler="toolbar.items.get(0).setVisible(record.dirty);" />
                            <Listeners>
                                <Command Handler="record.reject();" />
                            </Listeners>
                        </ext:CommandColumn>
                    </Columns>
                </ColumnModel>
    
                <TopBar>
                    <ext:Toolbar runat="server">
                        <Items>
                            <ext:Button runat="server" Text="Add" Icon="Add">
                                <Listeners>
                                    <Click Handler="#{Store1}.insert(0, new Person());" />
                                </Listeners>
                            </ext:Button>
    
                            <ext:Button runat="server" Text="Delete" Icon="Exclamation">
                                <Listeners>
                                    <Click Handler="#{GridPanel1}.deleteSelected(); #{UserForm}.getForm().reset();" />
                                </Listeners>
                            </ext:Button>
    
                            <ext:ToolbarSeparator />
    
                            <ext:Button
                                runat="server"
                                Text="Auto Sync"
                                EnableToggle="true"
                                Pressed="true"
                                ToolTip="When enabled, Store will execute Ajax requests as soon as a Record becomes dirty.">
                                <Listeners>
                                    <Toggle Handler="#{Store1}.autoSync = pressed;" />
                                </Listeners>
                            </ext:Button>
                        </Items>
                    </ext:Toolbar>
                </TopBar>
    
                <SelectionModel>
                    <ext:RowSelectionModel runat="server" Mode="Single">
                        <Listeners>
                            <Select Handler="#{UserForm}.getForm().loadRecord(record);" />
                        </Listeners>
                    </ext:RowSelectionModel>
                </SelectionModel>
    
                <View>
                    <ext:GridView runat="server">
                        <Plugins>
                            <ext:GridDragDrop runat="server" DragText="Drag and drop to reorganize" />
                        </Plugins>
                        <Listeners>
                            <Drop Fn="handleDrop" />
                        </Listeners>
                    </ext:GridView>
                </View>
    
                <Buttons>
                    <ext:Button runat="server" Text="Sync" Icon="Disk">
                        <Listeners>
                            <Click Handler="#{Store1}.sync();" />
                        </Listeners>
                    </ext:Button>
                </Buttons>
    
                <Plugins>
                    <ext:CellEditing runat="server" />
                </Plugins>
            </ext:GridPanel>
        </form>
    </body>
    </html>
    I hope this helps!
    Fabrício Murta
    Developer & Support Expert
  5. #5
    Thank you,

    I think that I can figure my way from here. Incorporating necessary changes on the database won't be problem. I already have the same functionality on android and iOs applications.
  6. #6
    Hello @jcanton!

    Glad this helped you out! If you get stuck in the future, don't hesitate to contact us, we're here to help!
    Fabrício Murta
    Developer & Support Expert

Similar Threads

  1. [CLOSED] Remote validation when reordering rows
    By RCN in forum 2.x Legacy Premium Help
    Replies: 12
    Last Post: Jun 12, 2012, 5:11 PM
  2. Grid Row Reordering
    By Rupesh in forum 1.x Help
    Replies: 2
    Last Post: Apr 06, 2012, 11:02 AM
  3. Replies: 4
    Last Post: Jul 28, 2011, 4:36 PM
  4. [CLOSED] Dynamic grid and store with remote paging
    By stoque in forum 1.x Legacy Premium Help
    Replies: 1
    Last Post: Jul 12, 2011, 12:36 PM
  5. Replies: 0
    Last Post: Jul 07, 2011, 8:19 PM

Tags for this Thread

Posting Permissions