PDA

View Full Version : Set same value for multiple records/refresh GridPanel



Dennis
Jul 23, 2020, 9:04 PM
Hello,
I need to accomplish the following:

I have multiple records in a GridPanel, each record has a property called "Status".

1. In case the user wants to set the same status to more than one record, they should be able to select as many records as they need.

2. Once more than one record is selected an "Update Status" button should appear in the top tool bar.

3. When the user clicks the button a modal form with status drop down should appear, the user would select desired status and click "Save" button. This would update the status of every record that was initially selected in the GridPanel.

4. Once the data is saved, the modal form would disappear and the data in the GridPanel would reflect the change.

See attached screenshots.

I was able to figure out how to do 1, 2, 3. But I can't make the new status appear in the gridpanel records once the window disappears, only once the user reloads the page manually.

Is there an example of something similar to this I can look at? or should I submit a simple test case?

2539525396


Thanks,
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

fabricio.murta
Aug 05, 2020, 1:37 AM
Hello @Dennis!

Cycle through the selected records the way you probably already know, and set the record fields' to the new value you want.

The new values should be reflected to the grid once you then call the grid's .reconfigure() method.

Hope this helps!

p.s.: it would help us understand your question if you posted just the part you couldn't figure out, I am still unsure you need or not help with the other points you show in the question. Assuming here you just have trouble updating records with chosen value throughout all selected records.

Dennis
Aug 05, 2020, 6:02 PM
Hi Fabricio, here is a standalone simplified example. There are two things I need to get working here:
1. Show/hide "Update Status" button based on the grid selection (if no records are selected the button stays hidden, once at least one record is selected it should appear)
2. When the transfer status is updated for selected records StatusEditor -> Edit, and the form disappears. I need the new status to be shown for all the affected records in the grid, without reloading the page.

Please note - my sample below has client status db update functionality commented out and it includes the editable cells functionality you helped me with.

Test.aspx


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

<%@ Register Src="StatusEditor.ascx" TagName="StatusEditor" TagPrefix="uc2" %>

<!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;
};

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></title>
</head>
<body>
<form id="form1" runat="server">

<ext:ResourceManager runat="server" DisableViewState="false" />

<ext:XScript ID="XSCRIPT1" runat="server">
<script>

var MultiClientX = {
statusedit: function (selection) {
this.open();
},

getSelection: function () {
var sel = Ext.encode(#{ GridPanel1 }.getRowsValues({ selectedOnly: true }));

if (sel != null) {
return sel;
}
},

refresh: function () {
try {
#{ GridPanel1 }.getView().refresh();
}
catch (err) {
alert(err);
}
}
};

</script>
</ext:XScript>

<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="Number_Overlay" />
<ext:ModelField Name="Name_Overlay" />
<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="Name_Overlay" Flex="1" TdCls="editable-cell">
<Editor>
<ext:TextField runat="server" />
</Editor>
</ext:Column>
<ext:Column runat="server" Text="Number New" DataIndex="Number_Overlay" 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 runat="server" Text="Update Status">
<Listeners>
<Click Handler="MultiClientX.statusedit(Ext.encode(#{GridPanel1}.g etRowsValues({selectedOnly:true})));" />
</Listeners>
</ext:Button>
<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>

<uc2:StatusEditor ID="StatusEditor1" runat="server" />

</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();
}

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);

ChangeRecords<Client> clients = new StoreDataHandler(changes).BatchObjectData<Client>();

foreach (Client updated in clients.Updated)
{
Store1.GetById(updated.ID).Commit();
}
}
else
{
X.Toast("No records to update.");
}

this.btnEditGrid.Show();
this.btnSaveChanges.Hide();
this.btnCancelEdit.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 partial 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"}
};
}
}

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;
}

}
}


StatusEditor.ascx


<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="StatusEditor.ascx.cs" Inherits="EXTTestCases.StatusEditor" %>
<ext:XScript runat="server" ID="StatusEditXScript">
<script>
Ext.applyIf(MultiClientX, {
open: function () {

win = #{ StatusWinDetails };

win.show();
},

save: function () {
try
{
var sel = this.getSelection();

if (sel != null)
{
alert(sel);
MultiClientX.Edit(sel);
}

#{ StatusWinDetails }.hide();
this.refresh();
}
catch (err)
{
alert(err);
}
}
});
</script>
</ext:XScript>

<ext:Window
ID="StatusWinDetails"
runat="server"
Title="Update Transfer Status"
Icon="Group"
Width="400"
Height="400"
Modal="true"
Hidden="true"
Layout="CenterLayout">
<Items>
<ext:FormPanel ID="StatusPanel" runat="server">
<Items>
<ext:ComboBox
ID="TransferStatus"
runat="server"
FieldLabel="Set Transfer Status To"
AllowBlank="false"
DisplayField="Name"
ValueField="ID"
TypeAhead="true"
QueryMode="Local"
Name="TransferStatus"
ForceSelection="true"
TriggerAction="All"
EmptyText="Select Transfer Status..." Width="200" LabelAlign="Top">
<Store>
<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>
</Store>
</ext:ComboBox>
</Items>
</ext:FormPanel>
</Items>
<Buttons>
<ext:Button ID="btnSave" runat="server" Text="Save" Icon="Disk">
<Listeners>
<Click Handler="MultiClientX.save();" />
</Listeners>
</ext:Button>
<ext:Button ID="btnCancel" runat="server" Text="Cancel" Icon="Cancel">
<Listeners>
<Click Handler="this.up('window').hide();" />
</Listeners>
</ext:Button>
</Buttons>
</ext:Window>


StatusEditor.ascx.cs


using System;
using Ext.Net;
using System.Xml;

namespace EXTTestCases
{
public partial class StatusEditor : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
this.StoreCombo.DataSource = EXTTestCases.TransferStatus.GetAll();
}

[DirectMethod(Namespace = "MultiClientX", IDMode = DirectMethodProxyIDMode.None)]
public void Edit(object _json)
{
Ext.Net.ListItem newStat = this.TransferStatus.SelectedItem;

System.Xml.XmlNode xml = JSON.DeserializeXmlNode("{records:{record:" + _json + "}}");

foreach (XmlNode row in xml.SelectNodes("records/record"))
{
string ID = row.SelectSingleNode("ID").InnerXml;

//this is where transfer status for each selected client is saved to db.

//Data.Client tmp = new Data.Client();
//tmp.updateClientProperty(ID, "TransferStatus_ID", newStat.Value);

}
}
}
}

fabricio.murta
Aug 05, 2020, 9:12 PM
Hello @Dennis!

Thanks for the test case, but I have two points before we get down to it:

1. it looks pretty extensive for just a grid panel with a column having a mapped "status" value that we can mass-change to another value, don't you agree? Is the save/edit logic pertinent in that case? I feel this could be simplified to a single-page code, without custom controls, etc.

2. Have you tried the directions in our last post? I don't see in your last post comments or code block about the result of trying the solution... If it really doesn't help, I believe we could try reducing that test case, I honestly am not sure where to start looking at your issue in all that code.

Hope you understand!

Dennis
Aug 05, 2020, 9:41 PM
Fabricio, I am not sure I am following the suggestion you are referring to. Perhaps if I provide the simple requirements you can suggest a simple solution:

The user needs to be able to select as many records as necessary in the grid (using check boxes), click a button to have a form pop up with a "status" drop down. select the desired status, and click save - this should update all the selected records in db/grid with the chosen status. This is done to allow users mass update records with a status instead of going and updating each record individually.

I realize that it is easier to have a dropdown and "save" button on the same page as the grid and loop through the selected records of the grid while saving/committing the changes (I already made it work) but that is not what the users' requirement is. As always that would be too easy wouldn't it?

Thanks !

fabricio.murta
Aug 05, 2020, 10:25 PM
Hello @Dennis!

Cycle through the selected records the way you probably already know, and set the record fields' to the new value you want.

If you are not sure how to go thru the records, it would be something like:



App.GridPanel1.getSelection().forEach(function(rec ord) {
record.set("FieldName", valueFromComboBox);
});


The new values should be reflected to the grid once you then call the grid's .reconfigure() method. In other words, following the command above,



App.GridPanel1.reconfigure();


If you are mapping the status values from database numbers to human-readable strings, you should update the records with those numbers; when you call reconfigure(), the cell renderer will be evaluated again and the numbers, again, mapped to human-readable values. That is, if in the record field it is stored as a number, you should update it to the corresponding number, and not the "friednly" display string.

Hope this helps!

Dennis
Aug 06, 2020, 4:18 PM
Fabricio, I actually solved part of this issue by calling DirectMethod and bind the data/refresh, it works great. Last issue i am trying to solve is how to make the "Update Status" button appear/disappear based on check box selection in the grid. If there is at least one record selected we would like the button to show up and disappear if no records are selected.

Thanks !

fabricio.murta
Aug 07, 2020, 3:41 AM
Hello again, Dennis!

Use the CheckboxSelectionModel's SelectionChange listener to check whenever the selection change whether the button should be displayed or hidden.

Hope this helps!

Dennis
Aug 10, 2020, 9:37 PM
Fabricio,
I found a close enough example of how to use "selectionchanged" listener but unable to get it to work, can you see what I am missing here (onSelectionChange function):



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

<script runat="server">

protected void Page_Load(object sender, EventArgs e)
{
if (!X.IsAjaxRequest)
{
this.BindData();
}
}

private void BindData()
{
this.Store1.DataSource = Client1.GetAll();
this.Store1.DataBind();
}

public partial class Client1
{
public int ID { get; set; }
public string Name { get; set; }

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

clntList.Add(new Client1() { ID = 10, Name = "Client1"});
clntList.Add(new Client1() { ID = 11, Name = "Client2"});

return clntList;
}
}
</script>

<!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 onSelectionChange = function (grid, selection, eOpts) {
try {
var message = '??';

if (!selection) {
message = 'No selection';
}
else {
message = selection.getCount();
}

Ext.Msg.alert(message);
}
catch (err) {
Ext.Msg.alert(err);
}

};

</script>

<title></title>
</head>
<body>
<form id="form1" runat="server">

<ext:ResourceManager runat="server" DisableViewState="false" />

<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" />
</Fields>
</ext:Model>
</Model>
</ext:Store>
</Store>
<ColumnModel runat="server">
<Columns>
<ext:Column runat="server" Text="Name" DataIndex="Name" Flex="1" />
</Columns>
</ColumnModel>
<TopBar>
<ext:Toolbar runat="server">
<Items>
<ext:Button runat="server" Text="Update Status">
<Listeners>
<Click Handler="alert('It worked!');" />
</Listeners>
</ext:Button>
</Items>
</ext:Toolbar>
</TopBar>
<SelectionModel>
<ext:CheckboxSelectionModel runat="server" Mode="Multi">
<Listeners>
<SelectionChange Fn="onSelectionChange" />
</Listeners>
</ext:CheckboxSelectionModel>
</SelectionModel>
<View>
<ext:GridView runat="server"></ext:GridView>
</View>
<Plugins>
<ext:CellEditing runat="server" ClicksToEdit="1" />
<ext:SelectionReplicator runat="server" />
</Plugins>
</ext:GridPanel>
</form>
</body>
</html>


Thanks!

fabricio.murta
Aug 10, 2020, 10:54 PM
Hello Dennis!

You were pretty close in the test case you provided!

You can change it to the following and get what you probably expect:



var onSelectionChange = function (grid, selection, eOpts) {
try {
var message = '??';

if (selection.length < 1) {
message = 'No selection';
}
else {
message = 'Selected entries: ' + selection.length;
}

Ext.Msg.alert(message);
}
catch (err) {
Ext.Msg.alert('Error: ' + err);
}

};


There were three issues with your test case's code:

1. selection will never be false-ish as it is either an empty array or an array with the selection entries.
2. JavaScript arrays do not implement getCount(), I'm not sure where you got this from. Should use Array's Length property (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length)
3. Ext.Msg.alert() takes a string as its title argument. Providing an object results in what you got: an empty title -- unless that object hat the text property. Which is not the case of a caught javascript exception, it seems. Concatenating the exception object with a string is enough to implicitly cast it, otherwise call its .toString() method (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString).

Hope this helps!

p.s.: as a suggestion, experiment replacing your Ext.Msg.alert() calls to Ext.toast(). It is good for feedback without having to click any confirmation (thus losing components focus, etc).

Dennis
Aug 11, 2020, 4:22 PM
That worked. Thanks!