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 }; + } } }