Skip to content

Commit

Permalink
data context matching based on types: fixed few problems
Browse files Browse the repository at this point in the history
  • Loading branch information
exyi committed Sep 11, 2022
1 parent cfae6e1 commit 3ee2344
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 10 deletions.
27 changes: 25 additions & 2 deletions src/Framework/Framework/Binding/BindingHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,31 @@ internal static (int stepsUp, DotvvmBindableObject target) FindDataContextTarget
{
// only count changes which are visible client-side
// server-side context are not present in the client-side stack at all, so we need to skip them here
changes++;
lastAncestorContext = ancestorContext;

// don't count changes which only extend the data context, but don't nest it

var isNesting = ancestorContext.IsAncestorOf(lastAncestorContext);
if (isNesting)
{
changes++;
lastAncestorContext = ancestorContext;
}
#if DEBUG
else if (!lastAncestorContext.DataContextType.IsAssignableFrom(ancestorContext.DataContextType))
{
// this should not happen - data context type should not randomly change without nesting.
// we change data context stack when we get into different compilation context - a markup control
// but that will be always the same viewmodel type (or supertype)

var previousAncestor = control.GetAllAncestors(includingThis: true).TakeWhile(aa => aa != a).LastOrDefault();
var config = (control.GetValue(Internal.RequestContextProperty) as Hosting.IDotvvmRequestContext)?.Configuration;
throw new DotvvmControlException(
previousAncestor ?? a,
$"DataContext type changed from '{lastAncestorContext.DataContextType.ToCode()}' to '{ancestorContext.DataContextType.ToCode()}' without nesting. " +
$"{previousAncestor?.DebugString(config)} has DataContext: {lastAncestorContext.DataContextType.ToCode(stripNamespace: true)}, " +
$"{a.DebugString(config)} has DataContext: {ancestorContext.DataContextType.ToCode(stripNamespace: true)}");
}
#endif
}

if (bindingContext.Equals(ancestorContext))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
Expand Down Expand Up @@ -108,6 +108,21 @@ public IEnumerable<Type> Parents()
}
}

public bool IsAncestorOf(DataContextStack x)
{
var c = x.Parent;
while (c != null)
{
if (this.hashCode == c.hashCode)
{
if (this.Equals(c))
return true;
}
c = c.Parent;
}
return false;
}

ITypeDescriptor IDataContextStack.DataContextType => new ResolvedTypeDescriptor(DataContextType);
IDataContextStack? IDataContextStack.Parent => Parent;

Expand Down
2 changes: 1 addition & 1 deletion src/Samples/Tests/Tests/Complex/TaskListTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public void Complex_TaskList_ServerRenderedTaskList()
//add task
browser.SendKeys("input[type=text]", "DotVVM");
browser.Click("input[type=button]");
browser.Click("input[type=submit]");
browser.FindElements(".table tr").ThrowIfDifferentCountThan(4);
Expand Down
31 changes: 28 additions & 3 deletions src/Tests/ControlTests/MarkupControlTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public class MarkupControlTests
_ = Repeater.RenderAsNamedTemplateProperty;
config.Resources.RegisterScriptModuleUrl("somemodule", "http://localhost:99999/somemodule.js", null);
config.Markup.AddMarkupControl("cc", "CustomControl", "CustomControl.dotcontrol");
config.Markup.AddMarkupControl("cc", "CustomControlWithStaticCommand", "CustomControlWithStaticCommand.dotcontrol");
config.Markup.AddMarkupControl("cc", "CustomControlWithCommand", "CustomControlWithCommand.dotcontrol");
config.Markup.AddMarkupControl("cc", "CustomControlWithProperty", "CustomControlWithProperty.dotcontrol");
config.Markup.AddMarkupControl("cc", "CustomControlWithInvalidVM", "CustomControlWithInvalidVM.dotcontrol");
Expand Down Expand Up @@ -68,14 +69,14 @@ public async Task MarkupControl_PassingStaticCommand()
{
var r = await cth.RunPage(typeof(BasicTestViewModel), @"
<cc:CustomControlWithCommand DataContext={value: Integer} Click={staticCommand: s.Save(_parent.Integer)} Another={value: _this} />
<cc:CustomControlWithStaticCommand DataContext={value: Integer} Click={staticCommand: s.Save(_parent.Integer)} Another={value: _this} />
<dot:Repeater DataSource={value: Collection}>
<cc:CustomControlWithCommand Click={staticCommand: s.Save(_this)} Another={value: _root.Integer} />
<cc:CustomControlWithStaticCommand Click={staticCommand: s.Save(_this)} Another={value: _root.Integer} />
</dot:Repeater>
",
directives: $"@service s = {typeof(TestService)}",
markupFiles: new Dictionary<string, string> {
["CustomControlWithCommand.dotcontrol"] = @"
["CustomControlWithStaticCommand.dotcontrol"] = @"
@viewModel int
@baseType DotVVM.Framework.Tests.ControlTests.CustomControlWithCommand
@wrapperTag div
Expand All @@ -86,6 +87,30 @@ @wrapperTag div
check.CheckString(r.FormattedHtml, fileExtension: "html");
}

[TestMethod]
public async Task MarkupControl_CommandInRepeater()
{
var r = await cth.RunPage(typeof(BasicTestViewModel), @"
<cc:CustomControlWithCommand DataContext={value: Integer} Click={command: s.Save(_parent.Integer)} Another={value: _this} />
<dot:Repeater DataSource={value: Collection}>
<cc:CustomControlWithCommand Click={command: s.Save(_this)} Another={value: _root.Integer} />
</dot:Repeater>
",
directives: $"@service s = {typeof(TestService)}",
markupFiles: new Dictionary<string, string> {
["CustomControlWithCommand.dotcontrol"] = @"
@viewModel int
@baseType DotVVM.Framework.Tests.ControlTests.CustomControlWithCommand
@wrapperTag div
<dot:Button Click={command: _control.Click()} Text={resource: $'Button with number = {_control.Another}'} />"
}
);

check.CheckString(r.FormattedHtml, fileExtension: "html");
}


[TestMethod]
public async Task MarkupControl_UpdateSource()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
<ul data-bind="foreach: { data: $parent.Products()?.Items }" id="Products__input">
<li>
<label class="form-check-label">
<input class="form-check-input" data-bind="dotvvm-checkedItems: $parents[1].Products, checkedArrayContainsObservables: true, dotvvm-checkbox-updateAfterPostback: true, dotvvm-checked-pointer: 'dotvvm-checkedItems', checkedValue: Value" type="checkbox">
<input class="form-check-input" data-bind="dotvvm-checkedItems: $parent.Products, checkedArrayContainsObservables: true, dotvvm-checkbox-updateAfterPostback: true, dotvvm-checked-pointer: 'dotvvm-checkedItems', checkedValue: Value" type="checkbox">
<span data-bind="text: DisplayName"></span>
</label>
</li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
<ul data-bind="dotvvm-validation: Products, dotvvm-validationOptions: { invalidCssClass: &quot;is-danger&quot;, setToolTipText: true }, foreach: { data: $parent.Products()?.Items }" id="Products__input">
<li>
<label class="checkbox">
<input data-bind="dotvvm-checkedItems: $parents[1].Products, checkedArrayContainsObservables: true, dotvvm-checkbox-updateAfterPostback: true, dotvvm-checked-pointer: 'dotvvm-checkedItems', checkedValue: Value" type="checkbox">
<input data-bind="dotvvm-checkedItems: $parent.Products, checkedArrayContainsObservables: true, dotvvm-checkbox-updateAfterPostback: true, dotvvm-checked-pointer: 'dotvvm-checkedItems', checkedValue: Value" type="checkbox">
<span data-bind="text: DisplayName"></span>
</label>
</li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<ul data-bind="foreach: { data: $parent.Countries()?.Items }" id="CountryId__input">
<li>
<label>
<input data-bind="dotvvm-checkedItems: $parents[1].CountryId, checkedArrayContainsObservables: true, dotvvm-checkbox-updateAfterPostback: true, dotvvm-checked-pointer: 'dotvvm-checkedItems', checkedValue: Value" type="checkbox">
<input data-bind="dotvvm-checkedItems: $parent.CountryId, checkedArrayContainsObservables: true, dotvvm-checkbox-updateAfterPostback: true, dotvvm-checked-pointer: 'dotvvm-checkedItems', checkedValue: Value" type="checkbox">
<span data-bind="text: DisplayName"></span>
</label>
</li>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<html>
<head></head>
<body>

<!-- ko with: int -->
<div>
<input onclick="dotvvm.postBack(this,[&quot;$parent.int&quot;],&quot;A0nd+6BTPk/xOJQH&quot;,&quot;c7&quot;,null,[],[],undefined).catch(dotvvm.log.logPostBackScriptError);event.stopPropagation();return false;" type="button" value="Button with number = 10000000">
</div>
<!-- /ko -->
<div data-bind="foreach: { data: Collection }">
<div>
<input onclick="dotvvm.postBack(this,[&quot;Collection/[$index]&quot;],&quot;A0nd+6BTPk/xOJQH&quot;,&quot;c9&quot;+'_'+ko.contextFor(this).$index()+'_'+&quot;c11&quot;,null,[],[],undefined).catch(dotvvm.log.logPostBackScriptError);event.stopPropagation();return false;" type="button" value="Button with number = 10000000">
</div>
</div>
</body>
</html>

0 comments on commit 3ee2344

Please sign in to comment.