Strange behaviour in MVC 4 partial view - vb.net

I got this simple partial view
#ModelType someModel
<div>
#Code
Dim list = //some Ienumerable of SelectListItem
Html.ListBox("MappingFields", list)
End Code
</div>
The same code works in a main view nicely. But when I shift it to a partial view and have an action in the main controller as:
Function RetrurnMyPartialView(id As String) As PartialViewResult
//get model for id
Return PartialView("_partialView", model)
End Function
It is not working!!
I got another partial view with similar implementation and is working fine. Can a ListBox be not used in partial views?
This wasted almost a day. The resulting html for the partial view is an empty div like:
<div>
</div>
I tried to debug, and the model, list items and everything are binding properly.
Please help..Thank you.

Related

Invoke an ASP.NET Core View Component inside a Partial View

I have a partial view (_FormCustomer) that displays a form for creating a customer. I also have a View Component (Countrylist) that generates a options list of countries. Now I want to show the country list in my form. This is what I do:
Index.cshtml
<partial name="_FormCustomer" for="#Model._Customer" />
_FormCustomer.cshtml
<select asp-for="#Model.Land" class="form-control">
#await Component.InvokeAsync("Countrylist");
</select>
CountrylistViewComponent.cs
public async Task<IViewComponentResult> InvokeAsync()
{
return View(await _countryRepository.GetCountriesAsync());
}
(The function GetCountriesAsync() returns a list of countries; this works fine.)
Pages/Componenst/Countrylist/default.cshtml
#model List<Country>
#foreach (Country country in Model)
{
<option value="#country.code">#country.name</option>
}
Unfortunately, select-box stays empty when I call the partial. When I call #await Component.InvokeAsync("Countrylist"); directly from Index.cshtml, however, it works fine.
So it looks like you cannot use a View Component inside a Partial View. Is this conclusion right? Or am I doing something wrong?
Thanks Phantom2018, found the problem after your post.
#0: I'm using Razor pages
#1: this had no effect
#2: this was a typo in my question, not in my code
#3: the debugger shows me that the vie component gets called, so
My actual code is a little different, I want to pre select a country if it's available:
<select asp-for="#Model.Country" class="form-control">
#if (Model == null)
{
await Component.InvokeAsync("Countrylist");
}
else
{
await Component.InvokeAsync("Countrylist", Model.Country);
}
</select>
And after some testing, I found the solution:
<select asp-for="#Model.Country" class="form-control">
#if (Model == null)
{
#await Component.InvokeAsync("Countrylist");
}
else
{
#await Component.InvokeAsync("Countrylist", Model.Country);
}
</select>
Don't know why, but I had to use #'s before the awaits.
I have now tested this scenario and can confirm that the data loads fine - both, when the view component is directly included on the page or when it is included in a partial View. (I have tested this on Razor pages - but it is likely to work the same when using MVC. You have not mentioned if you are using MVC or Razor pages.)
A couple of things you can try to see if the loading works fine:
1) From all "Select"s and "Partials" remove the "for*" attributes. That way you can first check if the data loads & then you can worry about binding to the selected item. (Also, in your provided code, you have omitted the model variables - so it is not possible to comment on them.)
2) Remove the last ";" in your _FormCustomer.cshtml
<select asp-for="#Model.Land" class="form-control">
#await Component.InvokeAsync("Countrylist")
</select>
Note that I have removed the trailing ";" in the await statement. I noticed that the ";" was added as another "option" in the select !
3) I also noticed that even minor syntax errors (not picked up by Intellisense) can cause the select to not load. Debug to see if your InvokeAsync is actually being called - in a scenario where there was a minor syntax error, the InvokeAsync was not even being called.
Also keep in mind that:
"When a partial view is instantiated, it receives a copy of the
parent's ViewData dictionary. Updates made to the data within the
partial view aren't persisted to the parent view. ViewData changes in
a partial view are lost when the partial view returns."

MVC Returning to a view and showing the partial view that was there

Alright, so currently I am working with MVC 4 to make a web app. I am still new to MVC and have hit road blocks and got past them, but I am having trouble finding an answer for my current problem.
My main view, Course, has several partial views, AllCourses and CurrentCourses, that the user can switch between with a couple links. It just displays a list of courses from a database mainly, the links change which will display based on the date. The Course view also has a button that takes them to an entirely new view, Details, which displays the content for the course. In the Details view there is a return link that takes me back to the previous view, Course.
What I need the return link to do is to take them to the previous view, but also display the partial view that was showing when they clicked to view the course details, aka if they were on the AllCourses partial view when they return I want that partial view to show and same if they were on the CurrentCourses.
EDIT::
My Partial views in the main view is handled by:
<ul id="view-options">
<li>View All </li>
<li>View Current</li>
</ul>
<script type="text/javascript">
function getAll() {
$("#result").load("#Url.Action("AllCourses", "Course")");
}
</script>
<script type="text/javascript">
function getCurrent() {
$("#result").load("#Url.Action("CurrentCourses", "Course")");
}
</script>
<div id="result">
.
.
</div>
Just two text links to switch between views.
And my link to the details view is:
<input type="button" class="button" value="View Course" onclick="location.href='#Url.Action("Details", new { id = item.CourseID })'" />
The call back to the main view is similar to the above.
Any thoughts?
So I need to add paging to the view. So I ended up just adding a filter to the page and went with PagedList.Mvc and got rid of the partial views.

mvc - Html.BeginForm postbacks to wrong controller

I'm facing a very annoying issue in my mvc 4 application.
What I'm doing:
I have a main view (Index) and a partial view added to main view. on partial view a form is added to post data to the action in some controller but, it posts to Home controller each time.
Note: both of the controllers are on root.
Here is how my code looks like:
form on partial view:
#using (Html.BeginForm("SearchLaptop", "SearchControl", FormMethod.Post))
{
<input type="submit" value="Search">
}
Action in SearchControl controller:
[HttpPost]
public ActionResult SearchLaptop()
{
}
but on clicking the button in form it never goes to the SearchControl controller but, goes to Home controller always.
i.e. the url is supposed to be:
/SearchControl/SearchLaptop
but, it is always looks like:
/Home/SearchLaptop
Can anyone please help in this regard. Thanks
I managed to solve this issue by moving partial view to Shared folder.

submit model with RenderPartial on View

So imagine this:
There is a View.
There is a model with this view in the following form:
Id
List<Food>
within each Food, there is:
Id
Name
List<Ingredients>
each ingredient contains:
Id
Name
Qty
This is in 1 model.
I have a view which takes in this Model.
I then have a partial view which takes in the List and renders it on the screen.
That works however when the form is submitted (the button is on the main view), the data fails to bind/is not shown in the Model.
what is the correct way to be able to bind the data back?
it does work when I take the whole thing and put it in the main view itself but due to reusability reasons, it makes sense having it in a partial view so I can just "drop" it on any page and pass the data.
The partial view takes in this:
#model List<FoodProject.Web.Models.FoodViewModel>
Thanks
UPDATE
I tried using the EditorTemplate and it seems to almost respect the model binding conventions as before using the Html.Partial was not doing so. That was producing things like:
[0].PropertyName
instead of:
ModelName[0].PropertyName
The template editor is almost there but gives me:
ModelName.[0].Id
I believe this is why when posting, I get null back in the model collection
how can I make it respect the model binding? where am I going wrong?
You should be close with the Editor Template because that worked for me. I'll show my example and maybe it will help you see what you have wrong.
The Models:
public class TestModelA
{
public List<TestModelB> PropA { get; set; }
}
public class TestModelB
{
public string PropB { get; set; }
}
The Editor Template (TestModelB.cshtml) placed in Views/Shared/EditorTemplates:
#model MvcTest.Models.TestModelB
#Html.EditorFor(m => m.PropB)
The main view:
#model MvcTest.Models.TestModelA
#using (Html.BeginForm())
{
#Html.EditorFor(m => m.PropA)
<input type="submit" value="Submit" />
}

MVC Partial view with controller, ajax - how do I ge the partial controller to get data?

I'm learning MVC and am stumped by this. I'm trying to factor some common code that gets data and displays it in a WebGrid into a partial view that i can use on multiple pages.
My home controller Index method just does a return View(). The Home view looks like this:
#using (Ajax.BeginForm("SearchAction", "Search",
new AjaxOptions { UpdateTargetId = "data-grid", HttpMethod = "Post" }))
{
#Html.TextBoxFor(model => model.name)
<input type="submit" value="Search" />
}
#{
<div id="data-grid">
#Html.Partial("SearchResults", Model)
</div>
}
I'm trying to use Ajax to avoid losing my search form data when clicking a WebGrid pager link, which are rendered as normal links.
My SearchController looks like this:
public ActionResult SearchAction(string name)
{
return RedirectToAction("SearchResults", new { name = name });
}
public ActionResult SearchResults(string name)
{
//does database query and sticks results in the viewbag
//filter on optional name parameter
VieweBag.Members = MyQueryResults;
return PartialView();
}
My SearchResults shared view, data is passed in via ViewBag.Members:
#{
var grid = new WebGrid(null, rowsPerPage: ViewBag.Pagesize);
grid.Bind(ViewBag.Members);
#grid.GetHtml(// etc. etc.)
}
The results I'm getting is that the ViewBag.Pagesize and ViewBag.Members binding fails since there is no data in the viewbag. Obviously, my partial controller is not being called to do the initial query and put stuff in the ViewBag when the home page is first loaded. How do I make that happen?
The other weird thing is that if I just copy the database query code into my home controller (where it originally was) to force the original query, then if I put some text into the search field and do a search, the partial view renders by itself on a new page. Why is that happening, I thought it would only render as part of my home page.
I've cobbled this partial view together from various answers/places and have no doubt gotten something horribly wrong :\
The partial page won't pass through a controller, but simply render the view directly. If you want to pass view data to the partial view, there is an overloaded function that takes a viewdata dictionary. I'm sorry I can't be more detailed, but I'm on my mobile (waiting for my son to fall asleep in the other room) :)
Update:
If you want to trigger a GET action for your partial view, you can use Html.Action. Here are some useful links:
MSDN RenderAction
Difference between RenderPartial and RenderAction
Further, it would probably make sense for you to move your form tags into your partial view, but those are details for when you clean up the code.
Jonass is right, the ViewBag only propagates between the Controller and the View.
One thing you can do is make the model of the partial view be the same as the type of data you're putting into the ViewBag.
So if for example your MyQueryResults is of type:
IEnumerable<Result>
In your partial view you'd add
#Model IEnumerable<Result>
And then in the main view, you'd pass it through the Render method:
#Html.Partial("SearchResults", ViewBag.Members);
You'll need to tweak this a bit to make sure it's the right type, but this should do the trick.
Good luck!