diff --git a/src/DotVVM.Framework/Controls/GridView.cs b/src/DotVVM.Framework/Controls/GridView.cs index 121f0a275d..247460faeb 100644 --- a/src/DotVVM.Framework/Controls/GridView.cs +++ b/src/DotVVM.Framework/Controls/GridView.cs @@ -11,6 +11,8 @@ using DotVVM.Framework.Binding.Properties; using DotVVM.Framework.Compilation.Javascript; using DotVVM.Framework.Utils; +using Microsoft.Extensions.DependencyInjection; +using DotVVM.Framework.ViewModel; namespace DotVVM.Framework.Controls { @@ -142,13 +144,13 @@ public bool InlineEditing public static readonly DotvvmProperty InlineEditingProperty = DotvvmProperty.Register(t => t.InlineEditing, false); - protected internal override void OnLoad(Hosting.IDotvvmRequestContext context) + protected internal override void OnLoad(IDotvvmRequestContext context) { DataBind(context); base.OnLoad(context); } - protected internal override void OnPreRender(Hosting.IDotvvmRequestContext context) + protected internal override void OnPreRender(IDotvvmRequestContext context) { DataBind(context); // TODO: support for observable collection base.OnPreRender(context); @@ -290,7 +292,7 @@ private static void SetCellAttributes(GridViewColumn column, HtmlGenericControl } } - private void CreateRowWithCells(Hosting.IDotvvmRequestContext context, DataItemContainer placeholder) + private void CreateRowWithCells(IDotvvmRequestContext context, DataItemContainer placeholder) { var isInEditMode = false; if (InlineEditing) @@ -345,21 +347,35 @@ private HtmlGenericControl CreateRow(DataItemContainer placeholder, bool isInEdi return row; } - private bool IsEditedRow(DataItemContainer placeholder) + private PropertyInfo ResolvePrimaryKeyProperty() { - var primaryKeyPropertyName = ((IGridViewDataSet)DataSource).RowEditOptions.PrimaryKeyPropertyName; + var dataSet = (IGridViewDataSet)DataSource; + var primaryKeyPropertyName = dataSet.RowEditOptions.PrimaryKeyPropertyName; if (string.IsNullOrEmpty(primaryKeyPropertyName)) { - throw new DotvvmControlException(this, $"The {nameof(IGridViewDataSet)} must specify the {nameof(IRowEditOptions.PrimaryKeyPropertyName)} property when inline editing is enabled on the {nameof(GridView)} control!"); + throw new DotvvmControlException(this, $"The {nameof(IGridViewDataSet)} must " + + $"specify the {nameof(IRowEditOptions.PrimaryKeyPropertyName)} property " + + $"when inline editing is enabled on the {nameof(GridView)} control!"); } - PropertyInfo prop; - var value = ReflectionUtils.GetObjectPropertyValue(placeholder.DataContext, primaryKeyPropertyName, out prop); + var enumerableType = ReflectionUtils.GetEnumerableType(dataSet.Items.GetType()); + var property = enumerableType.GetProperty(primaryKeyPropertyName); + if (property == null) + { + throw new InvalidOperationException($"Type '{enumerableType}' does not contain a " + + $"'{primaryKeyPropertyName}' property."); + } + return property; + } + private bool IsEditedRow(DataItemContainer placeholder) + { + var property = ResolvePrimaryKeyProperty(); + var value = property.GetValue(placeholder.DataContext); if (value != null) { var editRowId = ((IGridViewDataSet)DataSource).RowEditOptions.EditRowId; - if (editRowId != null && value.Equals(ReflectionUtils.ConvertValue(editRowId, prop.PropertyType))) + if (editRowId != null && value.Equals(ReflectionUtils.ConvertValue(editRowId, property.PropertyType))) { return true; } @@ -368,7 +384,7 @@ private bool IsEditedRow(DataItemContainer placeholder) return false; } - private void CreateTemplates(Hosting.IDotvvmRequestContext context, DataItemContainer placeholder, bool isInEditMode = false) + private void CreateTemplates(IDotvvmRequestContext context, DataItemContainer placeholder, bool isInEditMode = false) { var row = CreateRow(placeholder, isInEditMode); @@ -418,11 +434,17 @@ protected override void RenderContents(IHtmlWriter writer, IDotvvmRequestContext // render on client if (InlineEditing) { + var propertySerialization = context.Services + .GetRequiredService(); + var primaryKeyProperty = ResolvePrimaryKeyProperty(); + var primaryKeyPropertyName = propertySerialization.ResolveName(primaryKeyProperty); + var placeholder = new DataItemContainer { DataContext = null }; placeholder.SetDataContextTypeFromDataSource(GetBinding(DataSourceProperty)); placeholder.SetValue(Internal.PathFragmentProperty, GetPathFragmentExpression() + "/[$index]"); placeholder.SetValue(Internal.ClientIDFragmentProperty, GetValueRaw(Internal.CurrentIndexBindingProperty)); - writer.WriteKnockoutDataBindComment("if", "ko.unwrap(ko.unwrap($gridViewDataSet).RowEditOptions().EditRowId) !== ko.unwrap($data[ko.unwrap(ko.unwrap($gridViewDataSet).RowEditOptions().PrimaryKeyPropertyName)])"); + writer.WriteKnockoutDataBindComment("if", "ko.unwrap(ko.unwrap($gridViewDataSet).RowEditOptions().EditRowId) " + + $"!== ko.unwrap($data['{primaryKeyPropertyName}'])"); CreateTemplates(context, placeholder); Children.Add(placeholder); placeholder.Render(writer, context); @@ -432,7 +454,8 @@ protected override void RenderContents(IHtmlWriter writer, IDotvvmRequestContext placeholderEdit.SetDataContextTypeFromDataSource(GetBinding(DataSourceProperty)); placeholderEdit.SetValue(Internal.PathFragmentProperty, GetPathFragmentExpression() + "/[$index]"); placeholderEdit.SetValue(Internal.ClientIDFragmentProperty, GetValueRaw(Internal.CurrentIndexBindingProperty)); - writer.WriteKnockoutDataBindComment("if", "ko.unwrap(ko.unwrap($gridViewDataSet).RowEditOptions().EditRowId) === ko.unwrap($data[ko.unwrap(ko.unwrap($gridViewDataSet).RowEditOptions().PrimaryKeyPropertyName)])"); + writer.WriteKnockoutDataBindComment("if", "ko.unwrap(ko.unwrap($gridViewDataSet).RowEditOptions().EditRowId) " + + $"=== ko.unwrap($data['{primaryKeyPropertyName}'])"); CreateTemplates(context, placeholderEdit, true); Children.Add(placeholderEdit); placeholderEdit.Render(writer, context); diff --git a/src/DotVVM.Samples.Common/DotVVM.Samples.Common.csproj b/src/DotVVM.Samples.Common/DotVVM.Samples.Common.csproj index b74a5d5cbc..1668d8f754 100644 --- a/src/DotVVM.Samples.Common/DotVVM.Samples.Common.csproj +++ b/src/DotVVM.Samples.Common/DotVVM.Samples.Common.csproj @@ -18,6 +18,7 @@ + diff --git a/src/DotVVM.Samples.Common/ViewModels/ControlSamples/GridView/RenamedPrimaryKeyViewModel.cs b/src/DotVVM.Samples.Common/ViewModels/ControlSamples/GridView/RenamedPrimaryKeyViewModel.cs new file mode 100644 index 0000000000..9804139407 --- /dev/null +++ b/src/DotVVM.Samples.Common/ViewModels/ControlSamples/GridView/RenamedPrimaryKeyViewModel.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DotVVM.Framework.Controls; +using Newtonsoft.Json; + +namespace DotVVM.Samples.BasicSamples.ViewModels.ControlSamples.GridView +{ + public class RenamedPrimaryKeyViewModel + { + public GridViewDataSet Samples { get; set; } = new GridViewDataSet { + RowEditOptions = new RowEditOptions { + PrimaryKeyPropertyName = nameof(SampleDto.Id) + }, + Items = { + new SampleDto + { + Id = "1", + Name = "One" + }, + new SampleDto + { + Id = "2", + Name = "Two" + }, + new SampleDto + { + Id = "3", + Name = "Three" + } + } + }; + + public void Edit(string id) + { + Samples.RowEditOptions.EditRowId = id; + } + + public void Save() + { + Samples.RowEditOptions.EditRowId = null; + } + + public class SampleDto + { + [JsonProperty("id", Required = Required.Default, NullValueHandling = NullValueHandling.Ignore)] + public string Id { get; set; } + + public string Name { get; set; } + } + } +} diff --git a/src/DotVVM.Samples.Common/Views/ControlSamples/GridView/RenamedPrimaryKey.dothtml b/src/DotVVM.Samples.Common/Views/ControlSamples/GridView/RenamedPrimaryKey.dothtml new file mode 100644 index 0000000000..b4d5aca939 --- /dev/null +++ b/src/DotVVM.Samples.Common/Views/ControlSamples/GridView/RenamedPrimaryKey.dothtml @@ -0,0 +1,29 @@ +@viewModel DotVVM.Samples.BasicSamples.ViewModels.ControlSamples.GridView.RenamedPrimaryKeyViewModel + + + + + + + + + + + + + Edit + + + + + Save + + + + + + diff --git a/src/DotVVM.Samples.Tests/Control/GridViewTests.cs b/src/DotVVM.Samples.Tests/Control/GridViewTests.cs index c79f9bc110..635738347f 100644 --- a/src/DotVVM.Samples.Tests/Control/GridViewTests.cs +++ b/src/DotVVM.Samples.Tests/Control/GridViewTests.cs @@ -3,6 +3,7 @@ using DotVVM.Testing.Abstractions; using Riganti.Selenium.Core; using Riganti.Selenium.Core.Abstractions; +using Riganti.Selenium.DotVVM; using Xunit; using Xunit.Abstractions; @@ -533,5 +534,23 @@ public void Control_GridView_LargeGrid() AssertUI.TextEquals(tbody.Last("tr").Last("td").Single("span"), LastCell); }); } + + [Fact] + public void Control_GridView_RenamedPrimaryKey() + { + RunInAllBrowsers(browser => { + browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_GridView_RenamedPrimaryKey); + browser.WaitUntilDotvvmInited(); + + var gridview = browser.Single("gridview", SelectByDataUi); + AssertUI.NotContainsElement(gridview, "input"); + + browser.First("edit-button", SelectByDataUi).Click(); + browser.WaitFor(() => AssertUI.ContainsElement(gridview, "input"), 1000); + + browser.First("save-button", SelectByDataUi).Click(); + browser.WaitFor(() => AssertUI.NotContainsElement(gridview, "input"), 1000); + }); + } } } diff --git a/src/DotVVM.Testing.Abstractions/SamplesRouteUrls.designer.cs b/src/DotVVM.Testing.Abstractions/SamplesRouteUrls.designer.cs index d1f5c6f1ea..b38d95bed9 100644 --- a/src/DotVVM.Testing.Abstractions/SamplesRouteUrls.designer.cs +++ b/src/DotVVM.Testing.Abstractions/SamplesRouteUrls.designer.cs @@ -70,6 +70,7 @@ public partial class SamplesRouteUrls public const string ControlSamples_GridView_GridViewServerRender = "ControlSamples/GridView/GridViewServerRender"; public const string ControlSamples_GridView_GridViewStaticCommand = "ControlSamples/GridView/GridViewStaticCommand"; public const string ControlSamples_GridView_LargeGrid = "ControlSamples/GridView/LargeGrid"; + public const string ControlSamples_GridView_RenamedPrimaryKey = "ControlSamples/GridView/RenamedPrimaryKey"; public const string ControlSamples_HtmlLiteral_HtmlLiteral = "ControlSamples/HtmlLiteral/HtmlLiteral"; public const string ControlSamples_IncludeInPageProperty_IncludeInPage = "ControlSamples/IncludeInPageProperty/IncludeInPage"; public const string ControlSamples_LinkButton_LinkButton = "ControlSamples/LinkButton/LinkButton";