I have a solution which has both an ASP.NET Core 3.1 web application project as well as a Razor Client Library (RCL) project. I am trying write a view component which will be distributed with the Razor Client Library, but can be referenced from the ASP.NET Core web application.
I can successfully render this ViewComponent when I call the InvokeAsync() Tag Helper on the web application's _Layout.cshtml:
#await Component.InvokeAsync("NavBar", new {})
But I want to make it so that the RCL is not dependent on a string name provided on the web application. Instead, I would like to call the ViewComponent via an extension method like this:
#{ await Component.RenderMyViewComponentAsync(); }
To me, this way is more beneficial to whoever will use this shared RCL Library, as they don't need to specify the exact name of the ViewComponent, and can rely on IntelliSense to complete the extension method.
I have created a helper class that takes the ViewComponent object and simply calls its InvokeAsync() method within an extension method:
public static class PartialHelper
{
public static async Task<IHtmlContent> RenderMyViewComponentAsync(this IViewComponentHelper vcHelper)
{
return await vcHelper.InvokeAsync("NavBar", new { });
}
}
And, of course, inside of NavBarViewComponent.cs, I have implemented the InvokeAsync() method:
public class NavBarViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync()
{
return View();
}
}
Here's the problem, though: I'm not seeing my view render on the screen from the latter method, even though both ways of doing it will still hit my NavBarViewComponent.InvokeAsync().
From what I see, both ways of returning the ViewComponents are functionally equivalent and use the same methods, except #{ await Component.RenderMyViewComponentAsync(); } goes through a helper function first.
I've tried debugging both ways, but to no avail. I am stuck!
Is there any way to achieve what I'm asking for here? If any clarification is needed, please ask.
Your extension method is actually working exactly like it should. The issue is in how you're calling it from your _Layout.cshtml. You're using the following syntax:
#{ await Component.RenderMyViewComponentAsync(); }
That treats it as though it's within a script block, instead of rendering the IHtmlContent to the view. The IHtmlContent is correctly returned, but it isn't assigned to anything or written to the output.
Instead, you simply need to use the same style syntax you used when calling InvokeAsync():
#await Component.RenderMyViewComponentAsync()
Then this will render exactly like you're expecting.
Had the same issue with the extension method, so thank you for the answer.
One addition - instead of using a string, use the generic to catch errors at compile time:
await Component.InvokeAsync<NavBar>();
or directly from the controller:
public IActionResult Index()
{
return ViewComponent(typeof(NavBar));
}
In addition to answering your specific question, I also want to answer the question behind your question. Your general objective seems to be the simplification of the syntax, without relying on strings which don't provide any design- or compile-time validation.
I really like your approach of using extension methods as a solution for this, and will likely borrow that myself. But another approach that solves a similar objective is to invoke your ViewComponent as a Tag Helper:
<vc:NavBar />
This gives you basic IntelliSense support in Visual Studio, and is much cleaner than the InvokeAsync() approach.
That said, it's worth noting that the Tag Helper approach does have a couple of critical limitations, as I've discussed in a previous answer:
It doesn't support excluding any optional parameters you may have defined on your InvokeAsync() method*.
Neither Visual Studio nor the compiler will provide (obvious) warnings if you're missing any parameters.
Given that, your approach of using extension methods may still be preferable if your view components have any parameters—and, especially, if any of those parameters have logical defaults.
*Note: As of ASP.NET Core 6 Preview 6, View Components invoked as Tag Helpers will now honor optional parameters (source).
Related
I inherited a rather old MVC project that had a Grid package installed, Grid.MVC. It is used extensively, and taking it out or replacing it is not an option (client won't pay for it.)
We just built out a new portal section to the site, and in it, we used a new (and better) grid, NonFactors.Grid.Core.MVC5. Much more features and options.
But here's the problem. In all the places where the old grid is used, I now get this run-time error:
The call is ambiguous between the following methods or properties: 'NonFactors.Mvc.Grid.MvcGridExtensions.Grid(System.Web.Mvc.HtmlHelper, System.Collections.Generic.IEnumerable)' and 'GridMvc.Html.GridExtensions.Grid(System.Web.Mvc.HtmlHelper, System.Collections.Generic.IEnumerable)'
This should be a simple fix. All I need to do is tell the old grids which one they are. But i'm not figuring out the syntax. In the view's, they all have a #using which points to the correct (old) version.
#using GridMvc.Html
....
#Html.Grid(Model.Leads).Named("userGrid").Selectable(false).Columns(c =>
{
....
}
A pic might be more helpful:
I've tried various means of a full path in the view, but the syntax is never right...
#GridMvc.Html.GridExtensions.Grid(Model.Leads).... // Nope
#Html.GridMvc.Html.GridExtensions.Grid(Model.Leads).... // Nope
etc etc etc.
Again, this is probably simple. I'm just not getting it.
Try passing the HtmlHelper instance as first argument to the extension method:
#NonFactors.Mvc.Grid.MvcGridExtensions.Grid(Html, Model.Leads)
#GridMvc.Html.GridExtensions.Grid(Html, Model.Leads)
Extension methods are callable as standard static methods, but just take the parent type as a first parameter, so you should be able to pass the HtmlHelper as so:
#GridMvc.Html.GridExtensions.Grid(Html, Model.Leads)
Disable one of the grid templates from the web.config
in the views folder.
-->
In an asp.net core application, I have a pair of controller methods that respond to an Edit action. One for GET, which takes a string parameter for the entity id:
public async Task<IActionResult> Edit(string id)
and the other for receiving a POST of updated entity values:
[HttpPost]
[ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditSave(string id)
Inside the postable action method, I call
var bindingSuccess = await TryUpdateModelAsync(vm);
And that works fine.
Now, I'm trying to write a test for this, but am finding that TryUpdateModelAsync requires lots of things from HttpContext and the controller to be fleshed out. I've tried mocking those out, but after looking at the source code for TryUpdateModelAsync, I realize that I'd essentially need to mock everything down to the metadata, which isn't proving to be straightforward.
I'm wondering if perhaps this difficulty is telling me something: TryUpdateModelAsync makes it hard to test, so I should refactor the controller method to not rely on this helper. Instead, I could add another parameter to the method for my viewmodel and decorate it with [FromBody], so the model binding would happen from the post fields when present, but I would be able to pass in a view model when testing. However, I like the TryUpdateModelAsync method, because it does the busy work of merging the post fields into my view model. The other way I can think to accomplish the merging is write my own Merge method. Okay, no big deal, but I'd prefer not to have to do this for each entity (or reinvent the wheel writing a reflection based merger) and really, I'm wondering if I just missed the boat on how to write a unit test against this. I could fire up a whole TestServer, like I do for integration tests, but I'm not sure this is the right direction and feels like I would just be complicating my unit tests further. However, maybe it is justified in this scenario?
I've seen answers that worked with previous versions of .net mvc, where all they needed to do was mock an IValueProvider and attach it to the controller, but in .net core it appears the TryUpdateModelAsync was reworked and requires more moving parts.
In sum, I see three options:
Continue mocking and stubbing out all the pieces that
TryUpdateModelAsync needs. This might be a dead end, it seems to
be so far.
Use TestServer and make this test from a little
higher altitude using an HttpClient
Refactor this method to use
[FromBody] on a view model parameter, then write my own merge
methods for each entity, there by side stepping
TryUpdateModelAsync altogether
These all have their drawbacks, so I'm hoping there's a 4th entry to this list that I don't see because I am ignorant.
After looking at the source for ControllerBase, I noticed that the troublesome method in question relies on the static method ModelBindingHelper.TryUpdateModelAsync (seriously!!!!? I thought we were more evolved by now.)
This as you have already painfully discovered makes testing your controller some what of a bother.
I'm wondering if perhaps this difficulty is telling me something
well stop wondering. It is. :)
Here is another option you may have over looked. Abstract/Adapt away that difficulty back to the depths from which it came.
public interface IModelBindingHelperAdaptor {
Task<bool> TryUpdateModelAsync<TModel>(ControllerBase controller, TModel model) where TModel : class;
}
an implementation can look like this
public class DefaultModelBindingHelperAdaptor : IModelBindingHelperAdaptor {
public virtual Task<bool> TryUpdateModelAsync<TModel>(ControllerBase controller, TModel model) where TModel : class {
return controller.TryUpdateModelAsync(model);
}
}
Inject the IModelBindingHelperAdaptor into your controller as a dependency and let it call the demon spawned method.
var bindingSuccess = await modelBindingHelper.TryUpdateModelAsync(this, vm);
You are now free to mock your abstraction without all the tight coupling, which is what I think they should have done in the first place.
Now I assume you already know to setup the necessary things in your startup to allow for the above suggestion to work so it should not be a difficult task for you to get that up and running.
Whilst looking at a theme I downloaded from the Orchard CMS gallery, I noticed that a Layout.cshtml file had this block of code at the top of the file:
#functions {
// To support the layout classifaction below. Implementing as a razor function because we can, could otherwise be a Func<string[], string, string> in the code block following.
string CalcuClassify(string[] zoneNames, string classNamePrefix)
{
var zoneCounter = 0;
var zoneNumsFilled = string.Join("", zoneNames.Select(zoneName => { ++zoneCounter; return Model[zoneName] != null ? zoneCounter.ToString() : ""; }).ToArray());
return HasText(zoneNumsFilled) ? classNamePrefix + zoneNumsFilled : "";
}
}
I know what the declared function does (calculates which zones are populated in order to return the width of each column), my question is- what is the correct use of the #function block, and when should I ever use it?
The #functions block lets you define utility functions directly in the view, rather than adding them as extensions to the #Html helper or letting the controller know about display properties. You'd want to use it when you can meet these conditions:
The functionality is tied closely to the view and is not generally useful elsewhere (such as "How wide do I make my columns").
The functionality is more than a simple if statement, and/or is used in multiple places in your view.
Everything that the function needs to determine it's logic already exists in the Model for the view.
If you fail the first one, add it as a #Html helper.
If you fail the second one, just inline it.
If you fail the third one, you should do the calculation in your controller and pass the result as part of the model.
Others have explained what #functions does so I won't rehash that. But I would like to add this:
If your view is typed to a viewmodel, I think a viable option would be to move this logic into the viewmodel to avoid cluttering your markup with too much code. Otherwise your views start to look more and more like classic ASP and I don't think anybody wants that.
I don't think there's anything wrong with using #functions or #helper in your view, but once you get beyond a couple of methods in your view, or even if the function is somewhat complicated, it might be worth refactoring to the viewmodel if at all possible. If it's code that can be reused, it may be a good idea to to pull it out into a helper class or an extension to the HtmlHelper class. One thing that is a bummer is realizing you just rewrote a piece of code that already existed because you didn't know it was hidden away in some arbitrary view.
From msdn blogs, #functions block is to let you wrap up reusable code, like the methods and properties
In this particular case, the people who have created the theme you are using probably were trying to keep it as a simple theme (only views, css and images).
If you need to write some code for a theme for Orchard, you have to turn to a module (as stated here: http://docs.orchardproject.net/Documentation/Anatomy-of-a-theme) unless you write this code in the view.
I am not sure it is worth the time to switch from a theme to a module only to get the size of a column.
In ASP.NET MVC3 C#, I want to have an action with this signature:
public ActionResult Restore<T>(int entityId, string redirect) where T : class
I cannot figure out how to call this action properly from the view through a user action. Is it possible to pass the type like that from a view?
Perhaps with something similar to:
#using (Html.BeginForm("Restore", "Global", new { T = #m }))
Perhaps the signature could change while retaining the functionality?
No, it's not possible to do this out of the box. What it boils down to is that the routing code is not generic type aware. Generic types are stored internally much different than they are represented in code, as such method names are not compared the same way.
It could be possible if you wrote your own extension points, but there could be other problems with code that doesn't expect this behavior. You would also have to write many of your own helper methods and not use existing ones.
In short, it's not worth trying to do in my opinion.
I'm writing a view helper based on the ideas about partial requests from this blog post: http://blog.codeville.net/2008/10/14/partial-requests-in-aspnet-mvc/
In the controller action I prepare a widget by running:
AddWidget<Someontroller>(x => x.OtherActionName(new List<int>()));
Then in my view I can run the action and render the view output by doing some form of:
Html.RenderWidget...
And here comes my question, what compiler checked syntax would you choose from the following to use in the view:
Html.RenderWidget<SomeController, List<int>>(x => x.OtherActionName);
Html.RenderWidget<SomeController>(x => x.OtherActionName(null));
Html.RenderWidget<SomeController>(x => x.OtherActionName(It.IsAny<List<int>>);
Can anyone name some pros and cons? Or is it better to go with strings as the original Partial Request implementation does?
PS. Don't take the naming of It.IsAny> to litteraly, I just thought it was best described using the Moq naming.
Using the strings is significantly faster than using so-called strongly typed helpers (really; it's like 10 times faster), unless you implement some kind of caching for your Expression parsing. Note, though, that MVC 2 may have something along these lines RSN. So one option, if you can, is to just wait and see what's in the next preview drop. At the very least, you'll want to look like the rest of MVC, and the MVC team may end up doing your work for you.
("So called" because under the covers they're going to end up as strings in a RouteValueDictionary anyway.)