I'm looking for a way to invoke Model binder inside View component. I prefer to use build-in functionality from asp.net core, but this is only available inside controllers, not View Components itself. Any way to resolve that problem?
According to View Components documentation:
View Components don’t use model binding, and only depend on the data you provide when calling into it.
However, you could pass model as an object/parameter into your ViewComponent:
#await Component.InvokeAsync("PriorityList", MyModel)
or
#await Component.InvokeAsync("PriorityList", new { maxPriority = 2, isDone = false })
What do you want to achieve?
As far as I'm aware, this is not possible. The model binding occurs further up in the framework's lifecycle before the controller action is invoked.
I would be really interested in seeing some code as to why you need to do this to see if there are any other potential solutions that wouldn't involve model binding.
You can even use TagHelper
<vc:my-component model="#(Model)"></vc:my-component>
and inside your ViewComponent
public async Task<IViewComponentResult> InvokeAsync(MyModel model)
This is not present inside the official guide
I solved this in my project by using ASP.NET Core's dependency injection to insert my singleton directly into a Razor view.
The code snippet below uses the SignalR JavaScript library to send a notification to the browser if something comes in while they're watching the screen. When the page first loads though, I want to display the notification count in a CSS "bell" image so I get the count from the "singleton" object registered in Startup.cs. I use the List<Notification> object in my back-end code to add new notifications to the single instance object as well as sending the signal to the browser. DI worked great in this case to solve the model binding need.
Startup.cs
services.AddSingleton<List<Notification>>();
NotificationComponent.cshtml
#using PWVault.Service.Notification //optional as you can specify the full namespace instead below
#inject List<Notification> Notifications //this is my registered Singleton from Startup.cs
<link rel="stylesheet" href="~/css/NotificationBadge.css" />
<div class="notificationContainer">
<div class="notification" data-toggle="modal" data-target="#notificationModal">
</div>
<label hidden="hidden" id="notificationCount">#Notifications.Count</label>
</div>
#*modal window*#
#await Html.PartialAsync("_NotificationDetailModal")
<script src="~/lib/signalr/dist/browser/signalr.js"></script>
<script src="~/js/NotificationHub.js"></script>
Related
First please know that I have googled and tried everything that I have found. I have been through solution after solution and read dozens of Stack Overflow questions before posting this. I have no idea why this is not working. That being said I am not super familiar with the Method Handler stuff so I might be making a stupid mistake.
Implementation Details
- .NET Core 2.2
- Microsoft.AspNetCore.Razor.Design 2.2.0
- AngularJS.Core 1.7.8
I am creating an internal portal to aggregate Jenkins build and test data. I want to allow a build of the job that runs Jenkins tests to be kicked off via a button on the page. When I click the button I don't get any errors. I get nothing in the console and don't hit any breakpoints that would be hit if it actually fired the OnPost method.
I have tried using the OnPost and OnPostAsync methods. I have tried using the custom name via the asp-page-handler as well as the default names. I have tried putting the asp-page-handler tag on the form and the button. I have tried using a button and an input. Nothing seems to change the behavior.
The Header of my Razor Page
#page "{handler?}"
#using JenkinsRazorApi.Pages
#using System.Linq;
#using System.Data;
#using System.Web;
#model FslModel
#{
ViewData["Title"] = "fsl";
}
My cshtml markup
<div>
<form method="POST">
<input type="submit" class="btnKickOffBuild" name="kickOffBuild" id="btnKickOffBuild" asp-page-handler="BuildClick" value="Build"/>
</form>
</div>
My method in my model (The PostToApi method is Async which is why I am using the Async version of OnPost)
public void OnPostBuildClickAsync()
{
apiClientCalls.PostToApi("FslSmokeTest", "BuildKickOff");
}
My OnGetAsync method fires when the page loads without issue. Everything that happens in that method works. I am able to reference the Model in the normal Razor fashion so the model is accessible and tied to the page. I just can't get the OnPost method in my Model to fire when I click the btnKickOffBuild button.
If someone could please point me in the right direction I would really appreciate it.
*************EDIT**********************
I tried making the OnPostBuildClickAsync method actually Async and I tried it how I have it above and took the Async off the method name to no avail.
I also tried, as a test, turning all of the Anti-Forgery stuff that is tied to the Razor framework off globally just to rule that out. That didn't work either.
I tried doing this a completely different way by adding the method to my Angular scope wrapped in a javascript function that should only fire if the button is clicked but since it is a server side # tag it runs on page load regardless of the condition in the function.
*************EDIT**********************
This boils down to an Angular thing.
As per the "Submitting a form and preventing the default action" section in the following Link:
https://docs.angularjs.org/api/ng/directive/form
Angular requires an action tag to submit the form to the server. Without it the form is prevented from submitting. I altered my cshtml to:
<div>
<h5 style="margin-left: 50px; float:left;">Test Pass/Fail</h5>
<form method="post" action="">
<button type="submit" class="btnKickOffBuild" asp-page-handler="BuildClick">Build</button>
</form>
</div>
Of particular note is the action="". This forces Angular not to block the submission of the form to the server.
The following question has some detail around Angular Actions also:
ng-action does not add action attribute in the form
Edited:
I have found the root cause of it. You need to create a Razor ViewImport file and import all the razor dependencies in that file. That ways you can use the taghelpers in the application
In my code I have #Html.Partial("_StatusMessage", Model.StatusMessage) but Visual Studio warning me that: Error MVC1000: Use of IHtmlHelper.Partial may result in application deadlocks. Consider using <partial> Tag Helper or IHtmlHelper.PartialAsync.
Should I disable this error or I should really change #Html.Partial to #Html.PartialAsync, and why?
Yes we should,
See below section from their official site
Migrate from an HTML Helper
Consider the following asynchronous HTML Helper example. A collection of products is iterated and displayed. Per the PartialAsync method's first parameter, the _ProductPartial.cshtml partial view is loaded. An instance of the Product model is passed to the partial view for binding.
CSHTML
#foreach (var product in Model.Products)
{
#await Html.PartialAsync("_ProductPartial", product)
}
The following Partial Tag Helper achieves the same asynchronous rendering behavior as the PartialAsync HTML Helper. The model attribute is assigned a Product model instance for binding to the partial view.
CSHTML
#foreach (var product in Model.Products)
{
<partial name="_ProductPartial" model="product" />
}
Copied from
https://learn.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/built-in/partial-tag-helper?view=aspnetcore-2.1
Starting with ASP.NET Core 2.1 ... use #await Html.PartialAsync() instead of #Html.Partial()
#niico, In response to your comment of
Where does the <partial> tag helper fit into this?
Based on what I've been able to find, from documentation and from github it looks like you're supposed to use #Html.PartialAsync() or <partial name="_Post" /> In Place Of #Html.Partial(). However the <partial name="" /> element doesn't seem to work on my version of .NET CORE which is updated as of today (8/23/18)
Please see:
https://learn.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/built-in/partial-tag-helper?view=aspnetcore-2.1
https://learn.microsoft.com/en-us/aspnet/core/mvc/views/partial?view=aspnetcore-2.1
https://github.com/IdentityServer/IdentityServer4/pull/2344
I don't know your code but I think this one should anser your question:
When to use #await Html.PartialAsync in a View in MVC 6
As per the ASP.NET MVC documentation on partial views. https://docs.asp.net/en/latest/mvc/views/partial.html
The PartialAsync method is available for partial views containing asynchronous code (although code in views is generally discouraged):
Also the note on the page.
If your views need to execute code, the recommended pattern is to use a view component instead of a partial view.
So you should use Partial and avoid PartialAsync, and if you find yourself with a PartialAsync you should question yourself whether you're doing something wrong, maybe you should be using a ViewComponent instead or move the logic from the view to the controller.
You can try this method for " Warning MVC1000 Use of IHtmlHelper.Partial " in MVC project.
Source link
<partial name="~/Views/Folder/_PartialName.cshtml" />
<partial name="/Views/Folder/_PartialName.cshtml" />
I have just gotten started with ASP.NET MVC 5 (C#) and have been a little confused by the HTML helpers.
#using (Html.BeginForm("Add", "Song"))
I understand how this can be helpful, but feel that for people who do not know Razor/C#, that this will be a steep learning curve if not properly exposed. I am also aware of the Tag Helpers that are available and like what I see so far.
<form asp-action="Add" asp-controller="Song">
To me, this seems more appropriate, as it means that people who do not understand Razor syntax will be able to more easily read and understand what is happening, as it bears much more similarity to "normal" HTML. That being said, changing each page to use Tag Helpers seems monotonous and unnecessary.
I have recently found the CodeTemplate files, and included them in my project with the intention of adding the Tag Helpers in the template files. However, I soon realized that most Tag Helpers need a specific Controller or whatever to fill out the element attributes. Obviously, this Controller/whatever will change dynamically depending on which Controller/whatever prompted the creation of the View. If I hardcode asp-controller="Song" and then I try to create an Album View from this CodeTemplate, I obviously will have used the wrong controller.
Is there a way to access the dynamic property of whatever Controller, Link, etc called the View? In other words, is there someway of doing this in the CodeTemplate, where This is the Controller/whatever that constructs the View from the modified CodeTemplate?
<form asp-action="This.Action" asp-controller="This.Controller">
Example:
If I had <form asp-action="This.Action" asp-controller="This.Controller"> in my CodeTemplate in all Views, and then created the Views from different Controllers:
Song Controller
Create View - <form asp-action="Add" asp-controller="Song">
Update View - <form asp-action="Edit" asp-controller="Song">
Album Controller
Create View - <form asp-action="Add" asp-controller="Album">
Update View - <form asp-action="Edit" asp-controller="Album">
Is this possible at all? Or am I completely off? Let me know! Thanks!
You can try the solution I found here.
<form asp-controller="#this.ViewContext.RouteData.Values["controller"].ToString())"
asp-action="#this.ViewContext.RouteData.Values["action"].ToString())">
So I have a Layout page
<head>
#RenderSection("HeaderLast", required: false)
</head>
A view
#section HeaderLast
{
<script src="#Url.Content("~/Scripts/knockout-1.2.0.js")"
type="text/javascript"></script>
}
<div id="profile-tab">
#{ Html.RenderPartial("_userProfile"); }
</div>
And a Partial view
#section HeaderLast
{
<script type="text/javascript">
alert('test');
</script>
}
<div......
I figured it couldn't be that simple. Is there a proper way to do this out of box or will this always require some kind of mediator and passing stuff around ViewData to manually make the content bubble up to the layout page?
Bounty started: The bounty will be rewarded to the best solution provided for this short coming. Should no answers be provided I will award it to #SLaks for originally answering this question.
You cannot define sections in partial views.
Instead, you can put the Javascript in ViewBag, then emit any Javascript found in ViewBag in the layout page.
#JasCav: If a partial needs its own CSS, it has no good way to get it rendered.
If that's the reason for its use, it could very well be by design.
You don't want to have a separate CSS file x partial/helper. Remember, each separate CSS file means a separate request to get it from the server, thus an additional round-trip that affects time to render your page.
Also you don't want to emit direct CSS to the HTML from the partial/helper. Instead you want it to have appropriate hooks you can use to define all the look in your site's CSS file.
You can use the same hooks you have available for CSS to activate custom JavaScript behaviors for the elements involved When JavaScript is enabled.
Finally it may be the case what you need is not a Partial View, but an extra Layout you use for some pages. With that approach you would have:
A master Layout that gets set automatically on _ViewStart like you probably has now. This defines the sections like in your sample.
A children Layout page. Here you have both the extra html, css, js you need to have for these views. This uses both #RenderBody() and #section SomeSection { } to structure your common extra layout.
Some views that point to the children layout, and others that use the default master layout.
How to get extra data to the children Layout is out of the scope of the question, but you have several options. Like having a common base for your entities; using ViewBag or calling Html.RenderAction to get that shared logic related to shared dynamic elements in the layout.
It looks like there was a similar question on SO - How to render JavaScript into MasterLayout section from partial view?.
Unfortunately, there is no possibility of declaring sections inside Partial Views. That is because RenderPartial ends up rendering totally separate view page. There is a workaround to this, though a bit ugly. But it can look better if using strongly-typed model instead of ViewData.
Basically, you need to keep track of the reference to the view which called RenderPartial and use the DefineSection method on the object passed to push data to that view.
UPDATE: There is also a blog post about dealing with RenderSection you may find useful.
Here is another approach using helper methods and templated delegate
http://blogs.msdn.com/b/marcinon/archive/2010/12/15/razor-nested-layouts-and-redefined-sections.aspx
As a follow up to my question, the JavaScript/CSS combiner/minifier tool Cassette supports this functionality to allow you to compartmentalize your JavaScript and other assets that are required for partials.
I purchased a site license and use this in all of my MVC applications now.
In asp.net mvc, you can easily call/add a controller from a view, but what is the easiest way to add child/subcontrollers from a controller. What Im getting at is I want to dynamically build a list of child controllers from within the controller itself, not from the view.
The pattern I have in mind is derived from the old ibuyspy portal/dnn where you come up with a list of pluggable modules that you want to inject into the page. Each module is, itself, a controller, and is ignorant that it is a child request. But, dnn uses the ui/views to inject the modules, as where I want to create a list of modules in the controller, then tell the view to inject them.
Thanks in advance,
Jesse
If you have an array of objects in your view model that contains information corresponding to the desired modules, you can inject them into the view using RenderAction like this:
<div id="LeftColumn">
<% foreach (module in Model.Modules) {
Html.RenderAction(module.ActionName, module.ControllerName, new {id = module.id});
} %>
</div>
RenderAction is a method that calls a method on a controller, and injects the result into the page at the location where RenderAction is called. It is part of the ASP.NET MVC Futures assembly.