diff --git a/src/Framework/Framework/Compilation/Javascript/ParametrizedCode.cs b/src/Framework/Framework/Compilation/Javascript/ParametrizedCode.cs
index 95a0941035..7138d78314 100644
--- a/src/Framework/Framework/Compilation/Javascript/ParametrizedCode.cs
+++ b/src/Framework/Framework/Compilation/Javascript/ParametrizedCode.cs
@@ -50,9 +50,14 @@ public ParametrizedCode(string code, OperatorPrecedence precedence = default)
// TODO(exyi): add WriteTo(StringBuilder)
///
- /// Converts this to string and assigns all parameters using `parameterAssignment`. If there is any missing, exception is thrown.
+ /// Converts this to string and assigns all parameters using `parameterAssignment`.
///
+ /// Thrown when some parameter is not assigned and has no default value.
public string ToString(Func parameterAssignment) => ToString(parameterAssignment, out var _);
+ ///
+ /// Converts this to string and assigns all parameters using `parameterAssignment`.
+ ///
+ /// Thrown when some parameter is not assigned and has no default value.
public string ToString(Func parameterAssignment, out bool allIsDefault)
{
allIsDefault = true;
@@ -205,7 +210,7 @@ private CodeParameterAssignment[] FindAssignment(Func EnumerateAllParameters()
}
}
+ public record MissingAssignmentException(CodeParameterInfo Parameter, ParametrizedCode FullCode): RecordExceptions.RecordException
+ {
+ public override string Message => $"Assignment of parameter '{Parameter.Parameter}' was not found.";
+ }
+
///
/// Builder class with reasonably fast Add operation. Use Build method to convert it to immutable ParametrizedCode
///
diff --git a/src/Framework/Framework/Controls/KnockoutHelper.cs b/src/Framework/Framework/Controls/KnockoutHelper.cs
index e0438393ea..5a40e00242 100644
--- a/src/Framework/Framework/Controls/KnockoutHelper.cs
+++ b/src/Framework/Framework/Controls/KnockoutHelper.cs
@@ -9,6 +9,7 @@
using DotVVM.Framework.Compilation.Javascript.Ast;
using DotVVM.Framework.Configuration;
using DotVVM.Framework.Utils;
+using FastExpressionCompiler;
using Newtonsoft.Json;
namespace DotVVM.Framework.Controls
@@ -263,18 +264,26 @@ options.KoContext is object ?
string SubstituteArguments(ParametrizedCode parametrizedCode)
{
- return parametrizedCode.ToString(p =>
- p == JavascriptTranslator.CurrentElementParameter ? options.ElementAccessor :
- p == CommandBindingExpression.CurrentPathParameter ? CodeParameterAssignment.FromIdentifier(getContextPath(control)) :
- p == CommandBindingExpression.ControlUniqueIdParameter ? uniqueControlId?.GetParametrizedJsExpression(control) ?? CodeParameterAssignment.FromLiteral("") :
- p == JavascriptTranslator.KnockoutContextParameter ? knockoutContext :
- p == JavascriptTranslator.KnockoutViewModelParameter ? viewModel :
- p == CommandBindingExpression.OptionalKnockoutContextParameter ? optionalKnockoutContext :
- p == CommandBindingExpression.CommandArgumentsParameter ? options.CommandArgs ?? default :
- p == CommandBindingExpression.PostbackHandlersParameter ? CodeParameterAssignment.FromIdentifier(getHandlerScript()) :
- p == CommandBindingExpression.AbortSignalParameter ? abortSignal :
- default
- );
+ try
+ {
+ return parametrizedCode.ToString(p =>
+ p == JavascriptTranslator.CurrentElementParameter ? options.ElementAccessor :
+ p == CommandBindingExpression.CurrentPathParameter ? CodeParameterAssignment.FromIdentifier(getContextPath(control)) :
+ p == CommandBindingExpression.ControlUniqueIdParameter ? uniqueControlId?.GetParametrizedJsExpression(control) ?? CodeParameterAssignment.FromLiteral("") :
+ p == JavascriptTranslator.KnockoutContextParameter ? knockoutContext :
+ p == JavascriptTranslator.KnockoutViewModelParameter ? viewModel :
+ p == CommandBindingExpression.OptionalKnockoutContextParameter ? optionalKnockoutContext :
+ p == CommandBindingExpression.CommandArgumentsParameter ? options.CommandArgs ?? default :
+ p == CommandBindingExpression.PostbackHandlersParameter ? CodeParameterAssignment.FromIdentifier(getHandlerScript()) :
+ p == CommandBindingExpression.AbortSignalParameter ? abortSignal :
+ default
+ );
+ }
+ catch (ParametrizedCode.MissingAssignmentException e) when (e.Parameter.Parameter == CommandBindingExpression.CommandArgumentsParameter)
+ {
+ var returnType = expression.GetProperty(ErrorHandlingMode.ReturnNull)?.Type;
+ throw new DotvvmControlException(control, $"The binding {expression} of type {returnType?.ToCode(stripNamespace: true) ?? "?"} requires arguments, but none were provided to the KnockoutHelper.GenerateClientPostback method.", innerException: e) { RelatedBinding = expression };
+ }
}
}