Antaris / AspNetCore.Mvc.Widgets

Prototype Widget framework for ASP.NET Core Mvc
17 stars 5 forks source link

InvokeGet object values are null #2

Open PromontoryProtean opened 8 years ago

PromontoryProtean commented 8 years ago

I am attempting to pass a few properties to a widget from a view. In my example I am passing a settings dictionary to a contact form. I am calling the following overload from my view: @await Widget.InvokeAsync("ContactForm", settingsDictionary)

I believe that is equivalent to the following overload in DefaultWidgetHelper: public async Task<HtmlString> InvokeAsync(Type widgetType, object values = null)

In the contact form widget, I have the following:

public IWidgetResult InvokeGet(ContactFormSettingsDictonary settingsDictionary)
{
    return View("Form", new ContactFormViewModel(contactFormSettingsDictionary));
}

Problem is that the settingsDictionary parameter is always delivered to InvokeGet as null, so I am unable to initialize the ContactFormViewModel. Also, I think the model binder is touching the settingsDictionary because I have some getters in ContactFormViewModel that I had to comment out due to some exceptions that got tossed up. I tried to go through the source and figure out where the problem lies, but I got a little lost. Anyway, I have done something very similar using view components, so I figured I'd check to see if this is intended to be a supported scenario before I dig any deeper.

Antaris commented 8 years ago

Ah, you've been relying on the old way of passing arguments ala RC1 ViewComponents. In RC1, ViewComponents would accepts a params object[] array as the input, and it would use these objects and map them (in the order they are provided to the parameters of the associated MethodInfo of the target ViewComponent method).:

@await Component.InvokeAsync("ContactForm", settingsDictionary) => public async Task<IViewComponentResult InvokeAsync(ContactFormSettingsDictionary settingsDictionary) { }

To handle model binding, we couldn't accept arguments this way, because we need to be able to map the parameters by name. Instead, in RC2 ViewComponents and this Widget framework, we accept arguments using the following syntax:

@await Widget.InvokeAsync("ContactForm", new { settingsDictionary = settingsDictionary })
// or @await Widget.InvokeAsync("ContactForm", new { settingsDictionary }) with inferred name

This means you can provide arguments to named widget parameters, and the rest will be filled in by the model binder. Currently, because you are passing that dictionary straight in, it is not being mapped at all, meaning the model binder will fill in the rest. If the model binder couldn't get a value from the value providers - it'll give you null.

PromontoryProtean commented 8 years ago

Ok, I think I follow. The confusing part is that the method is publicly available and appears to be usable, so there was no way to tell from the method signature that it was going to try to model bind directly to the params object[]. But it looks like this will be much clearer and more flexible in RC2.

I'll take a look at the RC2 branch. There is a lot of churn going on right now in the Microsoft repositories so I don't envy you trying to build against all the renaming that is going on. I'm definitely looking forward to RC2 though, some great changes happening. I'll keep an eye on this repository and get you some more feedback as the dust settles.