NamedCommand programove

Zdravim. Zapasim s vygenerovanim NamedCommand v codeOnly controlce…

DotVVM.Framework.Controls.DotvvmControlException: Internal.ReferencedViewModuleInfoProperty was not set on the NamedCommand.

Delam to blbe? Diky

protected override void OnInit(IDotvvmRequestContext context)
{
base.OnInit(context);

// Vytvoření NamedCommand a jeho připojení k DropCommand
var dropZoneCommand = new NamedCommand
{
    ID= "DropZoneCommandID",
    Name = "DropZoneCommand",
    Command = GetCommandBinding(DropCommandProperty)
};
dropZoneCommand.Parent = this;

this.Children.Add(dropZoneCommand);


context.ResourceManager.AddRequiredResource("MyControlModule");
...

}

protected override void OnLoad(IDotvvmRequestContext context)
{
BuildContent(context);

base.OnLoad(context);

}

private void BuildContent(IDotvvmRequestContext context)
{
NamedCommand cmd = this.FindControlInContainer(“DropZoneCommandID”);
cmd.SetBinding(NamedCommand.CommandProperty, GetCommandBinding(DropCommandProperty));

}

Ta chybová hláška by si celkem zasloužila poupravit… Každopádně používat NamedCommand z code-only controlky víceméně nejde, tam je spíš zamýšlené řešení si napsat knockout binding handler a předat do něj JS funkci. Ostatně NamedCommand dělá přesně tohle with extra steps: dotvvm/src/Framework/Framework/Controls/NamedCommand.cs at main · riganti/dotvvm · GitHub

Přidání knockout binding handleru je nějak zdokumentované v Adding interactivity using Knockout binding handlers | DotVVM Documentation, bohužel koukám, že tam není nic o commandech :confused: V podstatě vám stačí něco takového někde v Render fázi (AddAttributesToRender pokud nemáte důvod to dát jinam):

var dropCommand = KnockoutHelper.GenerateClientPostbackLambda(
    nameof(DropCommandProperty),
    GetCommandBinding(DropCommandProperty),
    this);

writer.WriteKnockoutDataBindComment(
    "my-custom-binding"
    $"{{ dropCommand: {dropCommand}, nejakaDalsiPropertyTreba: {jsExpressionNecehoDalsiho} }}"
);

V JS pak potřebujete něco takového:


ko.bindingHandlers["my-custom-binding"] = {
    init(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        const { dropCommand, nejakaDalsiPropertyTreba } = valueAccessor()
        
        
        async function something() {
            await dropCommand(argumentPokudJePotreba)
        }
    },
    // update
}

Hlavní vlastnost NamedCommandu je jeho vazba na view module (tzn @js direktivu), který je potřeba nějak speciálně inicializovat. Pro normální kontrolky na to nemáme API, protože naše implementace nepodporuje vnořování a code-control můžou obsahovat ITemplate property. Když v kontrolce inicializujete nový view modul, tak by se NamedCommand použitý v ITemplate odkazoval na modul té kontrolky místo toho view kde byl definován.