Skip to content

Commit

Permalink
hierarchyrepeater: fix server-side rendering datacontexts
Browse files Browse the repository at this point in the history
  • Loading branch information
exyi authored and cafour committed Apr 22, 2022
1 parent 188ff74 commit 54d129c
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 41 deletions.
75 changes: 39 additions & 36 deletions src/Framework/Framework/Controls/HierarchyRepeater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ private void SetChildren(IDotvvmRequestContext context, bool renderClientTemplat

if (DataSource is not null)
{
AddServerLevel(this, context, GetIEnumerableFromDataSource()!);
this.AppendChildren(CreateServerLevel(context, GetIEnumerableFromDataSource()!));
}

if (renderClientTemplate)
Expand Down Expand Up @@ -217,8 +217,7 @@ private void SetChildren(IDotvvmRequestContext context, bool renderClientTemplat
}
}

private void AddServerLevel(
DotvvmControl parent,
private DotvvmControl CreateServerLevel(
IDotvvmRequestContext context,
IEnumerable items,
ImmutableArray<int> parentPath = default,
Expand All @@ -237,7 +236,6 @@ private void AddServerLevel(
var level = new HierarchyRepeaterLevel {
ForeachExpression = foreachExpression
};
parent.Children.Add(level);
{
DotvvmControl levelWrapper = string.IsNullOrEmpty(LevelTagName)
? new PlaceHolder()
Expand All @@ -247,15 +245,15 @@ private void AddServerLevel(
var index = 0;
foreach (var item in items)
{
AddServerItem(levelWrapper, context, item, parentPath, index);
levelWrapper.AppendChildren(CreateServerItem(context, item, parentPath, index));
index++;
}
}
}
return level;
}

private void AddServerItem(
DotvvmControl parent,
private DotvvmControl CreateServerItem(
IDotvvmRequestContext context,
object item,
ImmutableArray<int> parentPath,
Expand All @@ -264,38 +262,42 @@ private void AddServerItem(
DotvvmControl itemWrapper = string.IsNullOrEmpty(ItemTagName)
? new PlaceHolder()
: new HtmlGenericControl(ItemTagName);
parent.Children.Add(itemWrapper);
var dataItem = new DataItemContainer { DataItemIndex = index };
itemWrapper.Children.Add(dataItem);
dataItem.SetDataContextTypeFromDataSource(GetDataSourceBinding());
{
var dataItem = new DataItemContainer { DataItemIndex = index };
itemWrapper.Children.Add(dataItem);
dataItem.SetDataContextTypeFromDataSource(GetDataSourceBinding());
var placeholder = new PlaceHolder { DataContext = item };
{
var placeholder = new PlaceHolder { DataContext = item };
{
var parentSegment = parentPath.IsDefaultOrEmpty
? string.Empty
: $"/[{string.Join("]/[", parentPath)}]";
placeholder.SetValue(
Internal.PathFragmentProperty,
$"{GetPathFragmentExpression()}{parentSegment}/[{index}]");
ItemTemplate.BuildContent(context, placeholder);
}
dataItem.Children.Add(placeholder);

var foreachExpression = ((IValueBinding)ItemChildrenBinding!
.GetProperty<DataSourceAccessBinding>()
.Binding)
.GetKnockoutBindingExpression(dataItem);

// if the item has children then recurse down
parentPath = parentPath.Add(index);
var itemChildren = GetItemChildren(item);
if (itemChildren.Any())
{
AddServerLevel(dataItem, context, itemChildren, parentPath, foreachExpression);
}
var parentSegment = parentPath.IsDefaultOrEmpty
? string.Empty
: $"/[{string.Join("]/[", parentPath)}]";
placeholder.SetValue(
Internal.PathFragmentProperty,
$"{GetPathFragmentExpression()}{parentSegment}/[{index}]");
ItemTemplate.BuildContent(context, placeholder);
}
dataItem.Children.Add(placeholder);
}
// if the item has children then recurse down
var itemChildren = GetItemChildren(item);
if (itemChildren.Any())
{
var foreachExpression = ((IValueBinding)ItemChildrenBinding!
.GetProperty<DataSourceAccessBinding>()
.Binding)
.GetParametrizedKnockoutExpression(dataItem)
.ToString(p =>
p == JavascriptTranslator.KnockoutViewModelParameter ? CodeParameterAssignment.FromIdentifier($"ko.unwrap($foreachCollectionSymbol)[{index}]()") :
p is JavascriptTranslator.ViewModelSymbolicParameter vm ?
JavascriptTranslator.GetKnockoutViewModelParameter(vm.ParentIndex - 1).DefaultAssignment :
default
);

itemWrapper.AppendChildren(
CreateServerLevel(context, itemChildren, parentPath.Add(index), foreachExpression)
);
}
return itemWrapper;
}

private DotvvmControl GetClientItemTemplate(IDotvvmRequestContext context)
Expand All @@ -304,6 +306,7 @@ private DotvvmControl GetClientItemTemplate(IDotvvmRequestContext context)
{
dataItem.DataContext = null;
dataItem.SetValue(Internal.PathFragmentProperty, $"{GetPathFragmentExpression()}/[$indexPath]");
// TODO: this does not work, we need to set a data binding into the Internal.ClientIDFragmentProperty
dataItem.SetValue(Internal.ClientIDFragmentProperty, "$indexPath.map(function(i){return i();}).join(\"_\")");
dataItem.SetDataContextTypeFromDataSource(GetDataSourceBinding());

Expand All @@ -329,7 +332,7 @@ private DotvvmControl GetClientItemTemplate(IDotvvmRequestContext context)
.Binding)
.GetParametrizedKnockoutExpression(dataItem)
.ToString(p =>
p == JavascriptTranslator.KnockoutViewModelParameter ? CodeParameterAssignment.FromIdentifier("$item") :
p == JavascriptTranslator.KnockoutViewModelParameter ? CodeParameterAssignment.FromIdentifier("$item()") :
p is JavascriptTranslator.ViewModelSymbolicParameter vm ?
JavascriptTranslator.GetKnockoutViewModelParameter(vm.ParentIndex - 1).DefaultAssignment :
default
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public override Task Load()
return base.Load();
}

public string GlobalLabel { get; set; } = "Test";

public void ClickNode(Node node)
{
node.ClickCount++;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
ItemTagName="li"
RenderSettings.Mode="Server"
PostBack.Update="true">
<span RenderSettings.Mode="Client">{{value: Name}}</span>
<dot:Button Click="{command: _root.ClickNode(_this)}" Text="{value: ClickCount}" />
<span RenderSettings.Mode="Client" title={value: _root.GlobalLabel}>{{value: Name}}</span>
<dot:Button Click="{command: _root.ClickNode(_this)}" Text="{value: ClickCount}" title={value: $"{_root.GlobalLabel}: {Name}"} />
</dot:HierarchyRepeater>

<h2>Client Rendering</h2>
Expand All @@ -21,15 +21,15 @@
LevelTagName="ul"
ItemTagName="li"
RenderSettings.Mode="Client">
<span>{{value: Name}}</span>
<span title={value: _root.GlobalLabel}>{{value: Name}}</span>
<dot:Button Click="{command: _root.ClickNode(_this)}" Text="{value: ClickCount}" />
</dot:HierarchyRepeater>

<h2>No Item and Level Tags</h2>
<dot:HierarchyRepeater DataSource="{value: Roots}"
ItemChildrenBinding="{value: Children}"
RenderSettings.Mode="Client">
<p>{{value: Name}}</p>
<p title={value: _root.GlobalLabel}>{{value: Name}}</p>
</dot:HierarchyRepeater>

<h2>Empty</h2>
Expand All @@ -38,7 +38,7 @@
LevelTagName="ul"
ItemTagName="li">
<ItemTemplate>
<span>{{value: Name}}</span>
<span title={value: _root.GlobalLabel}>{{value: Name}}</span>
</ItemTemplate>
<EmptyDataTemplate>
<strong>There are no nodes.</strong>
Expand All @@ -50,5 +50,8 @@
<span>{{value: Name}}</span>
<dot:Button Click="{command: _root.ClickNode(_this)}" Text="{value: ClickCount}" />
</dot:Repeater>


<p> This should be propagated to all nodes <dot:TextBox Text={value: GlobalLabel} />
</dot:Content>

0 comments on commit 54d129c

Please sign in to comment.