MVC 3 with Razor question about partial views.
I have this :
#model MvcGroupie.Models.Message
#{
ViewBag.Title = "Details";
}
<h2>Details</h2>
<fieldset>
<legend>Message</legend>
<div class="display-label">postCreator</div>
<div class="display-field">#Model.postCreator</div>
<div class="display-label">postDate</div>
<div class="display-field">#String.Format("{0:g}", Model.postDate)</div>
<div class="display-label">postSubject</div>
<div class="display-field">#Model.postSubject</div>
<div class="display-label">postBody</div>
<div class="display-field">#Model.postBody</div>
</fieldset>
#Html.Partial("~/Views/Shared/replyPartial.cshtml")
<p>
#if(Model.postCreator == User.Identity.Name) {#Html.ActionLink("Edit", "Edit", new { id=Model.postID } + " | ")}
#Html.ActionLink("Reply", "Reply", new { id=Model.postID }) |
#Html.ActionLink("Back to List", "Index")
</p>
For a very simple post and reply MVC app im playing with for learning. I cant get a partial to display for replies :/
If i add the partial i get 'MvcGroupie.Models.Message', but this dictionary requires a model item of type 'MvcGroupie.Models.Reply'. Ok, so you cant ever use diff models on the same page? The first line starts with #model MvcGroupie.Models.Message so i can access the model.postSubject and the like. But if i want to add the replies and have people able to reply from the same page it doesnt allow it, they would fall under #model MvcGroupie.Models.Reply...
Curious how to get around this... I tried #Html.Partial("~/Views/Shared/replyPartial.cshtml", Model.Reply) but it doesnt recognize Model.Reply ....
Serious roadblock in my way of learning any help?
When you make the call to render a partial view that takes a different model you need to pass the model to that view. The default behavior is that the partial view will use the same model as the view that called it, but that won't work in your case because the models are different.
Try this:
#Html.RenderPartial("~/Views/Shared/replyPartial.cshtml", Model.Replies)
I'm assuming your Message object has a Replies property. Don't forget to do a null check in your partial view in case the message doesn't have any replies.
Related
I have a View MyView.cshtml with the following content:
#using MyProject.ViewModels
#model MyProject.ViewModels.MyViewViewModel
<form asp-action="Test" method="Post">
<component type="typeof(MyProject.Views.Home.Test)" render-mode="ServerPrerendered" />
<input type="submit" value="send"/>
</form>
And I have the Razor Component Test.razor with the following content (with Blazor Syntax):
#page "/Test"
<div class="form-group top-buffer #Visible">
<div class="row">
<div class="col-2">
<label asp-for="TestName" class="control-label"></label>
</div>
<div class="col-3">
<input asp-for="TestName" class="form-control" />
<span asp-validation-for="TestName" class="text-danger"></span>
</div>
</div>
</div>
<button #onclick="Show">Show</button>
#code {
public string Visible { get; set; } = "hidden";
protected async Task Show()
{
Visible = "";
}
}
The Class MyViewViewModel would look like this:
namespace MyProject.ViewModels
{
public class MyViewViewModel
{
[Display(Name = "Test Name:")]
public string TestName { get; set; }
}
}
Works all pretty fine so far. However I now want to use this component as part of a Web form which will be sent to the controller after submission. That's why I need to access and change properties of my ViewModel 'MyViewViewModel'. Unfortunately I did not find any answer in the internet on how to do that. I can't use #model MyProject.ViewModels.MyViewViewModel like in the view because this will give me a compilation error. I wonder if I need to use #inject, but if yes, I don't know how...
(parts are used from this example: https://jonhilton.net/use-blazor-in-existing-app/)
When you mix Blazor in a Razor Page, you can do the following:
Render a Razor Component
Interact with a Razor Component
Pass a Razor Component values
Please keep in mind that you are dealing with two different life-cycles. So if you do work inside of a Razor Component, the component will update but not effect the Razor Page it is hosted inside of. So mixing Razor Components and Pages with forms would be difficult.
More specifically to the OP. To pass data from your ViewModel to the component you may use the following method.
#using MyProject.ViewModels
#model MyProject.ViewModels.MyViewViewModel
<form asp-action="Test" method="Post">
<component type="typeof(MyProject.Views.Home.Test)"
render-mode="ServerPrerendered"
param-Name="#Model.TestName"/>
<input type="submit" value="send"/>
</form>
Test.razor
<h3>HelloWorld</h3>
Hello #Name
#code {
[Parameter]
public string Name { get; set; } = "undefined";
}
About life cycles
Basically when you have a button in Blazor, it will trigger an event which causes the component to re-render. You could imagine it like an iframe, or update-panel. When you have a button in a Razor page, it does a HTTP call round trip and reloads the page entirely. There is no event system in place to tell Blazor to invoke an HTTP call round trip to refresh the Razor page's content and vise versa. You can only one-way data-bind from Razor pages to Blazor, think write-only, and only when the page loads.
To hopefully add to the info. With a ASP.Net Core MVC project host Blazor webassembly, I was trying to pass a viewmodel into a razor component using this code in my view cshtml file:
<component Type="typeof(Leave)" render-mode="WebAssembly" model="new { model = (MyViewModel)#Model})"/>
But it would fail to render the razor component if I tried to access data in the viewmodel from the razor component with an Object not set exception. I think it was accessing the data before the view model has been initialized. Maybe if I set a default value this could avoided?
I found by using this instead I was able to get it working.
#(await Html.RenderComponentAsync<Leave>(RenderMode.WebAssembly,new { model = (MyViewModel)#Model}))
Edit
Seems you also need to register the viewModel class in the services in the Blazor WASM project in the Program.cs file.
builder.Services.AddScoped(sp => new HttpClient {BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddScoped<MyViewModel,MyViewModel>(); // <= add this line
await builder.Build().RunAsync();`
Without that I would get an error saying the property could not be found.
Hopefully this saves someone else some time :-)
I have a pretty simple scenario, Model for my view is a List.
Loop through List like
#foreach(CustomObject obj in Model)
{
Html.Partial("_TrackingCustomObject",obj)
}
So i was expecting to have number of partial views according to my list.
Partial View has been developed accordingly.
There is no error on page. It just does not show any data that is supposed to display by partial views.
What is the reason of not showing any data?
You are missing an #:
#foreach(CustomObject obj in Model)
{
#Html.Partial("_TrackingCustomObject", obj)
}
But why writing foreach loops when you can use editor/display templates? Like this:
#model IEnumerable<CustomObject>
#Html.EditorForModel()
and then simply define the corresponding editor template (~/Views/Shared/EditorTemplates/CustomObject.cshtml) that will automatically be rendered for each element of your model:
#model CustomObject
<div>
#Html.EditorFor(x => x.Foo)
</div>
Simple and conventional :-)
You're missing the Razor symbol #:
#foreach(CustomObject obj in Model)
{
#Html.Partial("_TrackingCustomObject",obj)
}
Also make sure your partial view is using the object type CustomObject as the Model.
#model MyProject.Models.CustomObject
<h1>Yeah we're in a partial! #Model.SomeProperty </h1>
To try and drill down to where the error is, try placing some static text inside the PartialView.
<p>Some text</p>
If your collection has 10 items, then you should see 10 of these paragraphs. Next once this works, focus on displaying some property in each item.
#model MyProject.Models.CustomObject
<p>Some text</p>
<p>#Model.SomeProperty</p>
When you are creating html form using #Html.BeginForm() you have to wrap the remaining stuf inside a <div> or other container else the html elements won't get rendered.
Ex.
this won't work
#using(Html.BeginForm())
{
Html.EditorFor(m => m.Name)
}
this will work
#using(Html.BeginForm())
{
<div>
#Html.EditorFor(m => m.Name)
</div>
}
Bit late in the day, but this worked for me in MVC 4:
#foreach (var p in #Model.RelatedCards)
{
Html.RenderPartial("_ThumbPartial", p);
}
Try this:
#Html.RenderPartial("_TrackingCustomObject",obj)
This is too old but someone can use it.
#foreach(CustomObject obj in Model)
{
<text>
Html.Partial("_TrackingCustomObject",obj)
</text>
}
I'm new to MVC (MVC3) so not sure about the best way to implement this.
I want to create a single "main" view (not strongly-typed). This "main" view will contain multiple strongly-typed partial views that each contain a form. Each partial view will therefore post back to their own POST action that does whatever. The problem I see is that when a partial view posts back, it needs to only update the partial view itself and not affect the other partial views on the page.
When I postback from a partial view now, it just returns the partial view alone back, rather than the entire "main" page.
How can this functionality be achieved in MVC3? (from a high-level perspective)
Thanks
You can post data by AJAX.
In my example I use jQuery:
<div id="first-form" class="form-container">
#Html.Partial("FirstPartial")
</div>
<div id="second-form" class="form-container">
#Html.Partial("SecondPartial")
</div>
// and here go rest forms
Your partial view may be following:
#model YourModelClass
#using (Html.BeginForm())
{
// some fields go there
}
<input type="button" value="Save Form Data" class="save-button"/>
Js would be following:
$("input.save-button").on("click", function () {
var button = $(this);
var container = button.closest("div.form-container");
var url = container.find("form").attr("action");
container.busy($.post(url, function (response) {
container.html(response);
}));
return false;
});
I'm having problems when trying to change the model of my view in MVC 3.
First view (index.cshtml) :
#model IEnumerable<MyProgram.MyFrogCollection>
<h1>Welcome to my frog collection</h1>
#foreach(MyProgram.Frog frog in Model)
{
<div class="frogDetails">
#RenderPage("ShowFrogDetails.cshtml", frog);
</div>
}
Second view (ShowFrogDetails.cshtml), that I would like to use all over the site :
#model MyProgram.Frog
<h3>Name:</h3><div class="detail">#Model.Name</div>
<h3>Colour:</h3><div class="detail">#Model.Colour</div>
However when I try to run the page index.cshtml after passing in a list of frog objects I get the following error when getting to the #RenderPage line :
Server Error in '/' Application. The model item passed into the
dictionary is of type
'System.Collections.Generic.List`1[MyProgram.Frog]', but this
dictionary requires a model item of type 'MyProgram.Frog'.
If I were to remove the code from ShowFrogDetails.cshtml and place it in-line within the foreach loop of index.cshtml the results are what I would expect. However this doesn't reuse existing code.
Is there anyway I can change the model to a single Frog object for use in the RenderPage ?
Cheers!
Try like this:
<div class="frogDetails">
#Html.Partial("ShowFrogDetails", frog)
</div>
I feel stupid asking this but I cant seem to get a partial view rendering in a page.
I have created a partial view that im trying to load into my index page. I have called my pv _BusinessDetails basically its a view that returns some customer data.
My pv looks like
#model MyMVC.Models.BusinessModel
<div class="grid">
<div class="grid-header">
<div class="gh-l"></div>
<div class="gh-m">Business Details</div>
<div class="gh-r"></div>
</div>
<div class="grid-row">
<label class="labelBold">Busines Name</label>
<label>#Model.BusinesName</label>
</div>
</div>
From my index page I am trying to call the pv using
#Html.Partial("_BusinessDetails")
which fails so if I add
#Html.Partial("_BusinessDetails",new MyMVC.Models.BusinessModel())
The partial view is loaded however with no data as the controller isn't been hit. In my controller I have tried
public ActionResult _BusinessDetails()
{
return PartialView("_BusinessDetails");
}
public PartialViewResult _BusinessDetails()
{
return PartialView("_BusinessDetails");
}
However neither of them are hit. What have I done wrong?
When rendering a partial view and passing a view model, that view model should already be populated. No controllers/action methods are invoked when using #Html.Partial().
Since you are using this strongly-typed partial view on your home page, consider building its view model in your HomeController's Index() method. Is your index page strongly-typed as well? If so, you can add your partial view's view model as a property of your index page's view model, and pass that when calling #Html.Partial().
On your index page, it would look something like:
#model MyMVC.Models.IndexViewModel
<!-- some HTML here -->
#Html.RenderPartial("_BusinessDetails", Model.BusinessModel)
If your index page is not strongly-typed, you can use the ViewBag object or you can strongly-type it to MyMVC.Models.BusinessModel and use #Html.RenderPartial("_BusinessDetails", Model) (which, while simple, could cause confusion).
Rachel Appel has a nice blog post, as does Mike Brind, if you would like more information.
It's tricky. I've had success with using a model on the main view as a container object:
class MainPageModel {
public BusinessDetailModel BusinessDetails { get; set; }
// ...
}
and then just passing the whole model like #Html.Partial("_BusinessDetails", Model) to my partial views.
When you wrote this,
#Html.Partial("_BusinessDetails",new MyMVC.Models.BusinessModel())
The data is not loaded as your model is empty, so before passing model BusinessModel,fill it before.