PDA

View Full Version : Enable/Disable Gridpanel fields on button click.



Dennis
Jul 23, 2020, 8:38 PM
Hello,
I have a gridpanel with two buttons in the topbar/toolbar ("Edit Data" and "Save Data", "Cancel")

Please see attached screenshot(it does not have the "Cancel" button yet).

I have looked through examples and searched through the help topics but could not come up with a solution, here is what i need:


1. When user gets to the page first time - the gridpanel loads with all the fields read-only. "Edit Data" button showing, "Save Data" and "Cancel" buttons hidden.

2. When the user clicks "Edit Data", certain fields ("New Number", "New Name", "Note" on all the records become editable. Editable fields should be visually different from non-editable ones. "Edit Data" button disappears, "Save Data" and "Cancel" buttons appear.

3. If user clicks "Save Data" button, data is updated in database, "Save Data" and "Cancel" buttons disappear, "Edit Data" button appears and all fields on all records become read-only.

4. IF user clicks "Cancel" button, original data is reloaded in the grid, "Save Data" and "Cancel" buttons disappear, "Edit Data" button appears, and all fields on all records become read-only.


Here is what I can't get to work:
1. loop through all the records in the grid (or preferrably all the dirty records) to save data in the db. I could only find how to loop through the selected ones by using selection model. but in my case users may edit data in several rows and not necessarily have them selected.

2. I could not make the buttons disappear, I was only able to disable them. Setting .Visible="false" seems to not do anything.

3. Not sure how to apply different style to editable/read-only fields

4. Not sure how to dynamically (when button is clicked) make fields editable/readonly (it was really easy with a regular asp.net gridview, but i need some help here).

Thanks in advance25394,
Dennis.

geoffrey.mcgill
Aug 04, 2020, 2:30 AM
Hi Dennis,

Can you provided a simplified sample demonstrating as much of this scenario as you can. We can try to fill in the pieces that are not working.

Some tips on creating samples for the forums:
https://forums.ext.net/showthread.php?3440-Forum-Guidelines-For-Posting-New-Topics

Dennis
Aug 04, 2020, 4:43 PM
Geoffrey, here is the sample. Thanks for your help!

Test.aspx


<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Test.aspx.cs" Inherits="EXTTestCases.Test" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<script type="text/javascript">
var template = 'color:{0};';

var change = function (value, meta) {
meta.style = Ext.String.format(template, (value > 0) ? "green" : "red");
return value;
};

var pctChange = function (value, meta) {
meta.style = Ext.String.format(template, (value > 0) ? "green" : "red");
return value + "%";
};

var statusRenderer = function (value) {
var r = App.StoreCombo.getById(value);

if (Ext.isEmpty(r)) {
return "";
}
return r.data.Name;
};
</script>
<title></title>
</head>
<body>
<form id="form1" runat="server">

<ext:ResourceManager runat="server" />

<ext:Store ID="StoreCombo" runat="server">
<Model>
<ext:Model runat="server" IDProperty="ID">
<Fields>
<ext:ModelField Name="ID" />
<ext:ModelField Name="Name" />
</Fields>
</ext:Model>
</Model>
</ext:Store>

<ext:GridPanel ID="GridPanel1" runat="server" Title="Editable GridPanel" Width="1000">
<Store>
<ext:Store ID="Store1" runat="server">
<Model>
<ext:Model runat="server" IDProperty="ID">
<Fields>
<ext:ModelField Name="ID" Type="Int" />
<ext:ModelField Name="Name" />
<ext:ModelField Name="Number" />
<ext:ModelField Name="NumberNew" />
<ext:ModelField Name="NameNew" />
<ext:ModelField Name="Note" />
<ext:ModelField Name="TransferStatus_ID" Type="Int" />
</Fields>
</ext:Model>
</Model>
</ext:Store>
</Store>
<ColumnModel runat="server">
<Columns>
<ext:Column runat="server" Text="Name" DataIndex="Name" Flex="1">
</ext:Column>
<ext:Column runat="server" Text="Number" DataIndex="Number" Flex="1">
</ext:Column>
<ext:Column runat="server" Text="Name New" DataIndex="NameNew" Flex="1">
<Editor>
<ext:TextField runat="server" />
</Editor>
</ext:Column>
<ext:Column runat="server" Text="Number New" DataIndex="NumberNew" Flex="1">
<Editor>
<ext:TextField runat="server" />
</Editor>
</ext:Column>
<ext:Column runat="server" Text="Note" DataIndex="Note" Flex="1">
<Editor>
<ext:TextField runat="server" />
</Editor>
</ext:Column>
<ext:Column runat="server" DataIndex="TransferStatus_ID" Text="Status" Width="200">
<Renderer Fn="statusRenderer"></Renderer>
<Editor>
<ext:ComboBox ID="cbStatuses" StoreID="StoreCombo" DisplayField="Name" ValueField="ID" runat="server" />
</Editor>
</ext:Column>
</Columns>
</ColumnModel>
<TopBar>
<ext:Toolbar runat="server">
<Items>
<ext:Button
ID="btnEditGrid"
runat="server"
Text="Edit Grid"
OnDirectClick="EditGridButton_Click" Disabled="false" />

<ext:Button
ID="btnSaveChanges"
runat="server"
Text="Save Changes"
AutoLoadingState="true"
OnDirectClick="SaveChangesButton_Click" Disabled="true" />
</Items>
</ext:Toolbar>
</TopBar>
<SelectionModel>
<ext:CheckboxSelectionModel runat="server" Mode="Multi" />
</SelectionModel>
<View>
<ext:GridView runat="server"></ext:GridView>
</View>
<Plugins>
<ext:CellEditing runat="server" ClicksToEdit="1" />
</Plugins>
</ext:GridPanel>
</form>
</body>
</html>


Test.aspx.cs


using System;
using System.Collections.Generic;
using Ext.Net;

namespace EXTTestCases
{
public partial class Test : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!X.IsAjaxRequest)
{
this.BindData();
}
}

private void BindData()
{
this.StoreCombo.DataSource = TransferStatus.GetAll();
this.StoreCombo.DataBind();

this.Store1.DataSource = Client.GetAll();
this.Store1.DataBind();
}

public class TransferStatus
{
public int ID { get; set; }
public string Name { get; set; }

public static List<TransferStatus> GetAll()
{
return new List<TransferStatus>
{
new TransferStatus {ID = 1, Name = "Pending"},
new TransferStatus {ID = 4, Name = "Accepted"},
new TransferStatus {ID = 7, Name = "Rejected"}
};
}
}

protected void SaveChangesButton_Click(object sender, DirectEventArgs e)
{
X.Js.Call("Ext.suspendLayouts");
this.btnEditGrid.Disabled = false;
this.btnSaveChanges.Disabled = true;
this.btnEditGrid.Focus();
X.Js.Call("Ext.resumeLayouts", true);
}

protected void EditGridButton_Click(object sender, DirectEventArgs e)
{
X.Js.Call("Ext.suspendLayouts");
this.btnEditGrid.Disabled = true;
this.btnSaveChanges.Disabled = false;
this.btnSaveChanges.Focus();
X.Js.Call("Ext.resumeLayouts", true);
}

}

public partial class Client
{
public int ID { get; set; }
public string Name { get; set; }
public string Number { get; set; }
public string Name_Overlay { get; set; }
public string Number_Overlay { get; set; }
public int TransferStatus_ID { get; set; }
public string Note { get; set; }

public static List<Client> GetAll()
{
List<Client> clntList = new List<Client>();

clntList.Add(new Client() { ID = 10, Name = "Client1", Number = "01", Note = "Client 1 Note", Number_Overlay = "01", Name_Overlay = "Client1", TransferStatus_ID = 1 });
clntList.Add(new Client() { ID = 11, Name = "Client2", Number = "02", Note = "Client 2 Note", Number_Overlay = "02", Name_Overlay = "Client2", TransferStatus_ID = 7 });

return clntList;
}

}
}

fabricio.murta
Aug 05, 2020, 1:24 AM
Hello @Dennis, and thanks for the test case!

First I will point each issue you had with a possible way to address it, then I will post a suggestion of how your example could look like with these implemented.


1. loop through all the records in the grid (or preferrably all the dirty records) to save data in the db. I could only find how to loop through the selected ones by using selection model. but in my case users may edit data in several rows and not necessarily have them selected.

You want the grid store's dirty records. There are several examples which shows some ways you can sync stores to the server. Here are some:

- GridPanel > Saving_Variations > HttpHandler (https://examples5.ext.net/#/GridPanel/Saving_Variations/HttpHandler/)
- GridPanel > Saving_Variations > StoreCustomLogic (https://examples5.ext.net/#/GridPanel/Saving_Variations/StoreCustomLogic/)
- GridPanel > Saving_Variations > StoreEvents (https://examples5.ext.net/#/GridPanel/Saving_Variations/StoreEvents/)
- GridPanel > Saving_Variations > WebService (https://examples5.ext.net/#/GridPanel/Saving_Variations/WebService/)
- GridPanel > Update > SqlDataSource (https://examples5.ext.net/#/GridPanel/Update/SqlDataSource/)
- GridPanel > Update > AutoSave (https://examples5.ext.net/#/GridPanel/Update/AutoSave/)

One particular approach to saving the grid data would be highlighted in this example:

- GridPanel > Update > Batch (https://examples5.ext.net/#/GridPanel/Update/Batch/)

That is, just submitting the changed data. If you are not interested in new rows (if your grid won't allow adding new records), you may want just getUpdatedRecords() instead of getChangedData() in the direct event parameter.


2. I could not make the buttons disappear, I was only able to disable them. Setting .Visible="false" seems to not do anything.

What you're missing here is that you are just setting properties/configs to already rendered components. They are already rendered, they won't re-read those until you re-render the component at all. What you want here is to call their respective setter methods. So change this.btnSaveChanges.Hidden = true; to this.btnSaveChanges.Hide(); and you should get what you want.

You may notice just setting the config in some situations just works, and that would be true. But that's not always true and it may be confuse to guess which components get or not the "auto-call-setter" feature. So best rule of thumb would be to try to call the setter itself when there's one available. .Hide(), .Show(), .Disable(), .Enable(), or when there's client-side setter but not server-side, say, for a Html setting, you could this.MyComponent.Call("setHtml", "my inner content");. true/false and integer numbers are valid second arguments you can pass to the .Call() method.


3. Not sure how to apply different style to editable/read-only fields

One way through this is to give the editable cells (or the read-only ones) a CSS class. Just to "mark" them. Then you can draw a javascript function to loop throughout them and apply another CSS class with the effect you want (or just set the desired CSS styling). Likewise, another single "dim" method could step thru the elements with that "marker class" and de-apply the effect class or styles.

Adapted sample

The sample below should comply to your requirements, with some remarks:

- When you click 'save' we are just getting and showing back the submitted value, you should parse them back accordingly (the batch save example (https://examples5.ext.net/#/GridPanel/Update/Batch/) should help with other details like clearing the 'dirty' state if desired and mapping it to an actual C# structure).
- The "save" button is initially disabled. The grid store will enable and disable it as changes are triggered -- so that it is always disabled if there's nothing to submit.
- The visual effects on editable cells are toggled by the save button's show/hide event.
- The "editability" of cells in turn, are defined by the save button's hidden state. Technically the editable cells will always trigger edit when clicked, but they will just be denied until the "save" button is displayed -- "signalling" the page it is in "edit mode".
- The "cancel" handler just calls again the data binding method, so the store is replaced (actually refreshed with whatever data is in the server at the moment it is called) with a whole new set of data. You can alternatively cycle thru the store data records and discard() the changes to every dirty record, but then new or removed records would need additional specific logic (but no server round-trip would be required when cancelling changes).
- I have merged the two code blocks into a single block, with embedded code-behind, should be easier to copy-paste, and namespace-agnostic.



<%@ Page Language="C#" %>

<!DOCTYPE html>

<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
if (!X.IsAjaxRequest)
{
this.BindData();
}
}

private void BindData()
{
this.StoreCombo.DataSource = TransferStatus.GetAll();
this.StoreCombo.DataBind();

this.Store1.DataSource = Client.GetAll();
this.Store1.DataBind();
}

public class TransferStatus
{
public int ID { get; set; }
public string Name { get; set; }

public static List<TransferStatus> GetAll()
{
return new List<TransferStatus>
{
new TransferStatus {ID = 1, Name = "Pending"},
new TransferStatus {ID = 4, Name = "Accepted"},
new TransferStatus {ID = 7, Name = "Rejected"}
};
}
}

protected void SaveChangesButton_Click(object sender, DirectEventArgs e)
{
var changes = e.ExtraParams["data_changes"];

if (changes != null)
{
X.Toast("Got these records to update in database: " + changes);
}
else
{
X.Toast("No records to update.");
}

this.btnEditGrid.Enable();
this.btnSaveChanges.Hide();
}

protected void EditGridButton_Click(object sender, DirectEventArgs e)
{
this.btnEditGrid.Hide();
this.btnSaveChanges.Show();
this.btnCancelEdit.Show();
}

protected void CancelEditButton_Click(object sender, DirectEventArgs e)
{
BindData();
this.btnEditGrid.Show();
this.btnSaveChanges.Hide();
this.btnCancelEdit.Hide();
}

public class Client
{
public int ID { get; set; }
public string Name { get; set; }
public string Number { get; set; }
public string Name_Overlay { get; set; }
public string Number_Overlay { get; set; }
public int TransferStatus_ID { get; set; }
public string Note { get; set; }

public static List<Client> GetAll()
{
List<Client> clntList = new List<Client>();

clntList.Add(new Client() { ID = 10, Name = "Client1", Number = "01", Note = "Client 1 Note", Number_Overlay = "01", Name_Overlay = "Client1", TransferStatus_ID = 1 });
clntList.Add(new Client() { ID = 11, Name = "Client2", Number = "02", Note = "Client 2 Note", Number_Overlay = "02", Name_Overlay = "Client2", TransferStatus_ID = 7 });

return clntList;
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<script type="text/javascript">
var template = 'color:{0};';

var change = function (value, meta) {
meta.style = Ext.String.format(template, (value > 0) ? "green" : "red");
return value;
};

var pctChange = function (value, meta) {
meta.style = Ext.String.format(template, (value > 0) ? "green" : "red");
return value + "%";
};

var statusRenderer = function (value) {
var r = App.StoreCombo.getById(value);

if (Ext.isEmpty(r)) {
return "";
}
return r.data.Name;
};

var highlightEditable = function () {
document.querySelectorAll(".editable-cell").forEach(function (el) {
el.classList.add("editable-cell-highlight");
});
}

var dimEditable = function () {
document.querySelectorAll(".editable-cell").forEach(function (el) {
el.classList.remove("editable-cell-highlight");
});
}
</script>
<style type="text/css">
.editable-cell { }
.editable-cell-highlight {
background-color: #f9ffd77f;
}
</style>
<title>62941 - Disable Grid Cell Editing & more</title>
</head>
<body>
<form id="form1" runat="server">

<ext:ResourceManager runat="server" />

<ext:Store ID="StoreCombo" runat="server">
<Model>
<ext:Model runat="server" IDProperty="ID">
<Fields>
<ext:ModelField Name="ID" />
<ext:ModelField Name="Name" />
</Fields>
</ext:Model>
</Model>
</ext:Store>

<ext:GridPanel
ID="GridPanel1"
runat="server"
Title="Editable GridPanel"
Width="1000">
<CustomConfig>
<ext:ConfigItem Name="editingDisabled" Value="true" Mode="Raw" />
</CustomConfig>
<Store>
<ext:Store ID="Store1" runat="server">
<Model>
<ext:Model runat="server" IDProperty="ID">
<Fields>
<ext:ModelField Name="ID" Type="Int" />
<ext:ModelField Name="Name" />
<ext:ModelField Name="Number" />
<ext:ModelField Name="NumberNew" />
<ext:ModelField Name="NameNew" />
<ext:ModelField Name="Note" />
<ext:ModelField Name="TransferStatus_ID" Type="Int" />
</Fields>
</ext:Model>
</Model>
<Listeners>
<DataChanged Handler="if (App.btnSaveChanges) App.btnSaveChanges.setDisabled(!this.isDirty())" />
</Listeners>
</ext:Store>
</Store>
<ColumnModel runat="server">
<Columns>
<ext:Column runat="server" Text="Name" DataIndex="Name" Flex="1">
</ext:Column>
<ext:Column runat="server" Text="Number" DataIndex="Number" Flex="1">
</ext:Column>
<ext:Column runat="server" Text="Name New" DataIndex="NameNew" Flex="1">
<Editor>
<ext:TextField runat="server" />
</Editor>
</ext:Column>
<ext:Column
runat="server"
Text="Number New"
DataIndex="NumberNew"
Flex="1"
TdCls="editable-cell">
<Editor>
<ext:TextField runat="server" />
</Editor>
</ext:Column>
<ext:Column
runat="server"
Text="Note"
DataIndex="Note"
Flex="1"
TdCls="editable-cell">
<Editor>
<ext:TextField runat="server" />
</Editor>
</ext:Column>
<ext:Column
runat="server"
DataIndex="TransferStatus_ID"
Text="Status"
Width="200"
TdCls="editable-cell">
<Renderer Fn="statusRenderer"></Renderer>
<Editor>
<ext:ComboBox ID="cbStatuses" StoreID="StoreCombo" DisplayField="Name" ValueField="ID" runat="server" />
</Editor>
</ext:Column>
</Columns>
</ColumnModel>
<TopBar>
<ext:Toolbar runat="server">
<Items>
<ext:Button
ID="btnEditGrid"
runat="server"
Text="Edit Grid"
OnDirectClick="EditGridButton_Click" />
<ext:Button
ID="btnSaveChanges"
runat="server"
Text="Save Changes"
AutoLoadingState="true"
Disabled="true"
Hidden="true">
<DirectEvents>
<Click OnEvent="SaveChangesButton_Click">
<ExtraParams>
<ext:Parameter name="data_changes" Value="function() { return App.Store1.getChangedData(); }" Mode="Raw" />
</ExtraParams>
</Click>
</DirectEvents>
<Listeners>
<Hide Fn="dimEditable" />
<Show Fn="highlightEditable" />
</Listeners>
</ext:Button>
<ext:Button
ID="btnCancelEdit"
runat="server"
Text="Cancel"
AutoLoadingState="true"
OnDirectClick="CancelEditButton_Click"
Hidden="true" />
</Items>
</ext:Toolbar>
</TopBar>
<Listeners>
<BeforeEdit Handler="if (App.btnSaveChanges.hidden === true) return false" />
</Listeners>
<SelectionModel>
<ext:CheckboxSelectionModel runat="server" Mode="Multi" />
</SelectionModel>
<View>
<ext:GridView runat="server"></ext:GridView>
</View>
<Plugins>
<ext:CellEditing runat="server" ClicksToEdit="1" />
</Plugins>
</ext:GridPanel>
</form>
</body>
</html>


Hope this helps!

Dennis
Aug 05, 2020, 5:00 PM
Fabricio, using your example I got this functionality working. Thanks !