Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Binding translation issue in PostBackHandler properties #1878

Merged
merged 6 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions src/Framework/Framework/Compilation/Javascript/ParametrizedCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,20 +141,33 @@ public ParametrizedCode AssignParameters(Func<CodeSymbolicParameter, CodeParamet
for (int i = 0; i < assignment.Length; i++)
{
var a = assignment[i];
if (a.Code == null)
var param = parameters![i];
if (a.Code == null) // not assigned by `parameterAssignment`
{
builder.Add(parameters![i]);
// assign recursively in the default assignment
if (param.DefaultAssignment.Code is {})
{
var newDefault = param.DefaultAssignment.Code.AssignParameters(parameterAssignment);
if (newDefault != param.DefaultAssignment.Code)
builder.Add(new CodeParameterInfo(param.Parameter, param.OperatorPrecedence, param.IsSafeMemberAccess, newDefault));
else
builder.Add(param);
}
else
{
builder.Add(param);
}
builder.Add(stringParts[1 + i]);
}
else
{
var isGlobalContext = a.IsGlobalContext && parameters![i].IsSafeMemberAccess;
var isGlobalContext = a.IsGlobalContext && param.IsSafeMemberAccess;

if (isGlobalContext)
builder.Add(stringParts[1 + i].AsSpan(1, stringParts[i].Length - 1).DotvvmInternString()); // skip `.`
else
{
builder.Add(a.Code, parameters![i].OperatorPrecedence);
builder.Add(a.Code, param.OperatorPrecedence);
builder.Add(stringParts[1 + i]);
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/Framework/Framework/Controls/KnockoutHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,8 @@ private static JsExpression TransformOptionValueToExpression(DotvvmBindableObjec
{
case IValueBinding binding: {
var adjustedCode = binding.GetParametrizedKnockoutExpression(handler, unwrapped: true).AssignParameters(o =>
o == JavascriptTranslator.KnockoutContextParameter ? new ParametrizedCode("c") :
o == JavascriptTranslator.KnockoutViewModelParameter ? new ParametrizedCode("d") :
o == JavascriptTranslator.KnockoutContextParameter ? CodeParameterAssignment.FromIdentifier("c") :
tomasherceg marked this conversation as resolved.
Show resolved Hide resolved
o == JavascriptTranslator.KnockoutViewModelParameter ? CodeParameterAssignment.FromIdentifier("d") :
default(CodeParameterAssignment)
);
return new JsSymbolicParameter(new CodeSymbolicParameter("tmp symbol", defaultAssignment: adjustedCode));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DotVVM.Framework.ViewModel;
using DotVVM.Framework.Hosting;

namespace DotVVM.Samples.Common.ViewModels.FeatureSamples.PostBack
{
public class PostBackHandlerBindingViewModel : DotvvmViewModelBase
{
public bool Enabled { get; set; } = false;

public int Counter { get; set; } = 0;

public string[] Items { get; set; } = new string[] { "Item 1", "Item 2", "Item 3" };
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@viewModel DotVVM.Samples.Common.ViewModels.FeatureSamples.PostBack.PostBackHandlerBindingViewModel, DotVVM.Samples.Common

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>

<p>
<dot:CheckBox Text="Suppress postbacks" Checked="{value: Enabled}" />
</p>
<p>Counter: {{value: Counter}}</p>

<p>Click on grid rows to increment the counter.</p>

<dot:GridView DataSource="{value: Items}">
<Columns>
<dot:GridViewTextColumn HeaderText="Column 1" ValueBinding="{value: _this}" />
</Columns>
<RowDecorators>
<dot:Decorator Events.Click="{command: _parent.Counter = _parent.Counter + 1}" style="cursor: pointer">
<PostBack.Handlers>
<dot:SuppressPostBackHandler Suppress="{value: _parent.Enabled}" />
</PostBack.Handlers>
</dot:Decorator>
</RowDecorators>
</dot:GridView>

</body>
</html>


2 changes: 1 addition & 1 deletion src/Tests/Binding/BindingCompilationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1493,7 +1493,7 @@ public override string ToString()
return SomeString + ": " + MyProperty;
}
}
class TestViewModel3 : DotvvmViewModelBase
public class TestViewModel3 : DotvvmViewModelBase
{
public string SomeString { get; set; }
}
Expand Down
34 changes: 34 additions & 0 deletions src/Tests/Binding/JavascriptCompilationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1395,6 +1395,40 @@ public void JavascriptCompilation_CustomPrimitiveParse()
Assert.AreEqual("VehicleNumber()==\"123\"", result);
}

[TestMethod]
public void JavascriptCompilation_ParametrizedCode_ParentContexts()
{
// root: TestViewModel
// parent: TestViewModel2 + _index + _collection
// this: TestViewModel3 + _index
var context0 = DataContextStack.Create(typeof(TestViewModel));
var context1 = DataContextStack.Create(typeof(TestViewModel2), parent: context0, extensionParameters: [ new CurrentCollectionIndexExtensionParameter(), new BindingCollectionInfoExtensionParameter("_collection") ]);
var context2 = DataContextStack.Create(typeof(TestViewModel3), parent: context1, extensionParameters: [ new CurrentCollectionIndexExtensionParameter() ]);

var result = bindingHelper.ValueBindingToParametrizedCode("_root.IntProp + _parent._index + _parent._collection.Index + _parent.MyProperty + _index + SomeString.Length", context2);

Assert.AreEqual("$parents[1].IntProp() + $parentContext.$index() + $parentContext.$index() + $parent.MyProperty() + $index() + SomeString().length", result.ToDefaultString());

// assign `context` and `vm` variables
var formatted2 = result.ToString(o =>
o == JavascriptTranslator.KnockoutContextParameter ? new ParametrizedCode("context", OperatorPrecedence.Max) :
o == JavascriptTranslator.KnockoutViewModelParameter ? new ParametrizedCode("vm", OperatorPrecedence.Max) :
default(CodeParameterAssignment)
);
Console.WriteLine(formatted2);

var symbolicParameters = result.Parameters.ToArray();
Assert.AreEqual(6, symbolicParameters.Length);
Assert.AreEqual(2, XAssert.IsAssignableFrom<JavascriptTranslator.ViewModelSymbolicParameter>(symbolicParameters[0].Parameter).ParentIndex);
Assert.AreEqual(JavascriptTranslator.KnockoutContextParameter, symbolicParameters[1].Parameter); // JavascriptTranslator.ParentKnockoutContextParameter would also work
Assert.AreEqual(JavascriptTranslator.KnockoutContextParameter, symbolicParameters[2].Parameter); // JavascriptTranslator.ParentKnockoutContextParameter would also work
Assert.AreEqual(JavascriptTranslator.ParentKnockoutViewModelParameter, symbolicParameters[3].Parameter);
Assert.AreEqual(JavascriptTranslator.KnockoutContextParameter, symbolicParameters[4].Parameter);
Assert.AreEqual(JavascriptTranslator.KnockoutViewModelParameter, symbolicParameters[5].Parameter);

Assert.AreEqual("context.$parents[1].IntProp() + context.$parentContext.$index() + context.$parentContext.$index() + context.$parent.MyProperty() + context.$index() + vm.SomeString().length", formatted2);
}

public class TestMarkupControl: DotvvmMarkupControl
{
public string SomeProperty
Expand Down
61 changes: 61 additions & 0 deletions src/Tests/ControlTests/PostbackHandlerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Threading.Tasks;
using CheckTestOutput;
using DotVVM.Framework.Compilation;
using DotVVM.Framework.Controls;
using DotVVM.Framework.Tests.Binding;
using DotVVM.Framework.ViewModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using DotVVM.Framework.Testing;
using System.Security.Claims;
using System.Collections;
using DotVVM.Framework.Binding;
using DotVVM.Framework.Compilation.ControlTree;
using DotVVM.Framework.Hosting;

namespace DotVVM.Framework.Tests.ControlTests
{
[TestClass]
public class PostbackHandlerTests
{
static readonly ControlTestHelper cth = new ControlTestHelper(config: config => {
});
readonly OutputChecker check = new OutputChecker("testoutputs");


[TestMethod]
public async Task ButtonHandlers()
{
var r = await cth.RunPage(typeof(BasicTestViewModel), """
<!-- suppress postback -->
<dot:Button DataContext={value: Nested} Click={staticCommand: 0} Text="Test supress">
<Postback.Handlers>
<dot:SuppressPostBackHandler Suppress={value: _parent.Integer > 100 || SomeString.Length < 5} />
</Postback.Handlers>
</dot:Button>
<!-- confirm -->
<dot:Button DataContext={value: Nested} Click={staticCommand: 0} Text="Test confirm">
<Postback.Handlers>
<dot:ConfirmPostBackHandler Message={value: $"String={_root.String} SomeString={SomeString}"} />
</Postback.Handlers>
</dot:Button>
"""
);

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

public class BasicTestViewModel: DotvvmViewModelBase
{
public int Integer { get; set; } = 123;
public bool Boolean { get; set; } = false;
public string String { get; set; } = "some-string";

public TestViewModel3 Nested { get; set; } = new TestViewModel3 { SomeString = "a" };
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<html>
<head></head>
<body>

<!-- suppress postback -->
<!-- ko with: Nested -->
<input onclick="dotvvm.applyPostbackHandlers((options) => {
0;
},this,[[&quot;suppress&quot;,(c,d)=>({suppress:c.$parent.Integer() > 100 || d.SomeString()?.length < 5})]]).catch(dotvvm.log.logPostBackScriptError);event.stopPropagation();return false;" type="button" value="Test supress">
<!-- /ko -->
<!-- confirm -->
<!-- ko with: Nested -->
<input onclick="dotvvm.applyPostbackHandlers((options) => {
0;
},this,[[&quot;confirm&quot;,(c,d)=>({message:dotvvm.translations.string.format(&quot;String={0} SomeString={1}&quot;, [
c.$parent.String()
, d.SomeString()
])})]]).catch(dotvvm.log.logPostBackScriptError);event.stopPropagation();return false;" type="button" value="Test confirm">
<!-- /ko -->
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
Two handlers
<input data-msg="ahoj" onclick="dotvvm.postBack(this,[],&quot;2suRIvLX7+StPC7x&quot;,&quot;&quot;,null,[[&quot;confirm&quot;,{message:&quot;a&quot;}],[&quot;confirm&quot;,{message:&quot;ahoj&quot;}]],[],undefined).catch(dotvvm.log.logPostBackScriptError);event.stopPropagation();return false;" type="button" value="">
Two handlers, value binding message
<input data-bind="attr: { &quot;data-msg&quot;: Label }" data-msg="My Label" onclick="dotvvm.postBack(this,[],&quot;b80b5Lh9K50jj3q2&quot;,&quot;&quot;,null,[[&quot;confirm&quot;,{message:&quot;a&quot;}],[&quot;confirm&quot;,(c,d)=>({message:(d).Label()})]],[],undefined).catch(dotvvm.log.logPostBackScriptError);event.stopPropagation();return false;" type="button" value="">
<input data-bind="attr: { &quot;data-msg&quot;: Label }" data-msg="My Label" onclick="dotvvm.postBack(this,[],&quot;b80b5Lh9K50jj3q2&quot;,&quot;&quot;,null,[[&quot;confirm&quot;,{message:&quot;a&quot;}],[&quot;confirm&quot;,(c,d)=>({message:d.Label()})]],[],undefined).catch(dotvvm.log.logPostBackScriptError);event.stopPropagation();return false;" type="button" value="">
Two handlers, resource binding message
<input data-msg="My Label" onclick="dotvvm.postBack(this,[],&quot;mH2HraWjqc1sx3p+&quot;,&quot;&quot;,null,[[&quot;confirm&quot;,{message:&quot;a&quot;}],[&quot;confirm&quot;,{message:&quot;My Label&quot;}]],[],undefined).catch(dotvvm.log.logPostBackScriptError);event.stopPropagation();return false;" type="button" value="">
Two handlers, default message
Expand Down
Loading