Skip to content

Commit

Permalink
Merge pull request #752 from riganti/fix/gridview-inline-editing
Browse files Browse the repository at this point in the history
Allow rename of a IRowEditOptions.PrimaryKeyPropertyName property through JsonProperty
  • Loading branch information
cafour authored Sep 9, 2019
2 parents 9759711 + a5f2bf0 commit 4488ebe
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 12 deletions.
47 changes: 35 additions & 12 deletions src/DotVVM.Framework/Controls/GridView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -142,13 +144,13 @@ public bool InlineEditing
public static readonly DotvvmProperty InlineEditingProperty =
DotvvmProperty.Register<bool, GridView>(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);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
Expand All @@ -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);

Expand Down Expand Up @@ -418,11 +434,17 @@ protected override void RenderContents(IHtmlWriter writer, IDotvvmRequestContext
// render on client
if (InlineEditing)
{
var propertySerialization = context.Services
.GetRequiredService<IPropertySerialization>();
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);
Expand All @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/DotVVM.Samples.Common/DotVVM.Samples.Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<None Remove="Views\ComplexSamples\NestedComboBox\OuterWrapper.dotcontrol" />
<None Remove="Views\ControlSamples\ComboBox\HeavilyNested.dothtml" />
<None Remove="Views\ControlSamples\ComboBox\Nullable.dothtml" />
<None Remove="Views\ControlSamples\GridView\RenamedPrimaryKey.dothtml" />
<None Remove="Views\ControlSamples\Literal\Literal_NumberBinding.dothtml" />
<None Remove="Views\ControlSamples\MultiSelect\binded.dothtml" />
<None Remove="Views\ControlSamples\MultiSelect\hardcoded.dothtml" />
Expand Down
Original file line number Diff line number Diff line change
@@ -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<SampleDto> Samples { get; set; } = new GridViewDataSet<SampleDto> {
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; }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
@viewModel DotVVM.Samples.BasicSamples.ViewModels.ControlSamples.GridView.RenamedPrimaryKeyViewModel

<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<dot:GridView DataSource="{value: Samples}"
InlineEditing="true"
data-ui="gridview">
<dot:GridViewTextColumn ValueBinding="{value: Name}" HeaderText="Name" />
<dot:GridViewTemplateColumn>
<ContentTemplate>
<dot:LinkButton Click="{command: _root.Edit(Id)}"
data-ui="edit-button">
<span>Edit</span>
</dot:LinkButton>
</ContentTemplate>
<EditTemplate>
<dot:LinkButton Click="{command: _root.Save()}"
data-ui="save-button">
<span>Save</span>
</dot:LinkButton>
</EditTemplate>
</dot:GridViewTemplateColumn>
</dot:GridView>
</body>
</html>
19 changes: 19 additions & 0 deletions src/DotVVM.Samples.Tests/Control/GridViewTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
});
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 4488ebe

Please sign in to comment.