ViewBags using #Html.Action to render form on partial view - asp.net-mvc-4

View:
<p>Parent ViewData: #ViewData["Test"]</p>
#Html.Action("MemberSignup","MemberSignupSurface")
PartialView:
<p>PartialView ViewData: #ViewData["Test"]</p>
#using (Html.BeginUmbracoForm<MemberSignupSurfaceController>
("MemberSignupSubmit", "MemberSignupSurfaceController",FormMethod.Post))
{
<!-- Some form controls -->
<input type="submit" value="Signup" />
}
Controller:
public class MemberSignupSurfaceController : SurfaceController
{
public ActionResult MemberSignup()
{
ViewData["Test"] = "From MemberSignup";
// Do database stuff and create model from that
return PartialView("MemberSignupView", model);
}
[HttpPost]
public ActionResult MemberSignupSubmit(MemberViewModel model)
{
ViewData["Test"] = "From MemberSignupSubmit";
if (ModelState.IsValid)
{
// Redirect to success using TempData
}
else
{
return CurrentUmbracoPage();
}
}
}
When my page load MemberSignup is called and the page shows
Parent ViewData:
PartialView ViewData: From MemberSignup
Then when i submit the form on the partial view with invalid input so it won't validate and it calls CurrentUmbracoPage() in the action MemberSignupSubmit
I get the following:
Parent ViewData: From MemberSignupSubmit
PartialView ViewData: From MemberSignup
If i use #Html.Partial to render my partial view both viewbags shows the same value set from the submit action.
I've tried TempDatabut it is not working either. Is there really no way to pass anything back to the partial view after i return from the submit action when using #Html.Action to render a partial view form.
The overall problem I am trying to solve is to popluate a dropdown in my form with values from the database. Using #Html.Partial don't allow me to do this but have a working viewbag.

I did this to render a dynamic dropdown list with values from a database. Maybe it will help someone.
It is a music player which needs a dynamic db populated menu to list the playlists
I made a base controller which all other controllers inherit from. In that base class, I have a PlaylistPopupMenu action which gets the list of playlists from a db.
public PartialViewResult PlaylistPopupMenu()
{
try
{
return PartialView("_PlaylistPopupMenu", db.GetPlaylists(1).ToList());
}
catch (Exception)
{
throw;
}
}
Then I have a _PlaylistPopupMenu partial view as follows:
#model List<OneMusic.Models.GetPlaylists_Result>
#if (Model.Count > 0)
{
<li style="height:2px" class="divider"></li>
foreach (var item in Model)
{
<li style="height:30px">#Html.DisplayFor(p => item.Name)
#Html.ActionLink(item.Name, "AddSong", "Playlist", new { playlistId = #item.PlaylistId, songId = 1 }, "")
</li>
}
}
this renders the dynamic parts of the menu (ie the playlists)
Finally the main page has this to build the dynamic part of the menu:
<ul class="dropdown-menu" style="margin-top:10px"><p class="text-primary" style="margin-left:18px; margin-top:6px">Add To</p>
<!-- other static menu items here-->
<li style="margin-top:-60px; height:0px">#Html.Action("PlaylistPopupMenu")</li>
</ul>

Related

ASP.NET Core - MVC - Model variable in view - null exception

In an attempt to learn ASP.NET Core MVC I've made a simple project and am trying to pass a model instance created in the controller to the view.
Controller Code - I create a simple list, then pass it to the view, being explicit about which view
public class TableController : Controller
{
public IActionResult Index()
{
var modelData = new List<string> {"A", "B"};
ViewBag.Title = "Tables";
return View("/Pages/Table.cshtml", modelData);
}
}
View Code
#page
#model List<string>
<div class="text-center">
<h1 class="display-4">#ViewBag.Title</h1>
#if (Model == null)
{
<p>There is no data to be displayed</p>
}
else
{
<ul>
#foreach (string str in Model)
{
<li>#str</li>
}
</ul>
}
</div>
When I set a breakpoint in the Controller the object I pass in as the model parameter is not null:
However, when I step through into the view code I get this:
I've looked at a few other "Model is null" posts but they were due mismatching types between whats passed in the View() model parameter and whats expected in the view given by the #model declaration.
It's probably something really simple but I'm not sure where I've gone wrong?
I had the same exception, the solution was to remove #page in Index.cshtml and then Boom, everything was there. Took me 5-6 hours to "resolve" this exception but better late, than never.
In asp.net MVC,View does not mean Razor Page.You can use a View page,And add a Table folder in Views.And then add a Index.cshtml(Razor View Page)to it.
Here is a demo worked:
Controller(return View() inIndex action will find a Index.cshtml in Views/Table(Views/ControllerName)):
public class TableController : Controller
{
public IActionResult Index()
{
var modelData = new List<string> { "A", "B" };
ViewBag.Title = "Tables";
return View(modelData);
}
}
View(Don't use #page,it's used in Razor Page):
#model List<string>
#{
ViewData["Title"] = "Table_Index";
}
<div class="text-center">
<h1 class="display-4">#ViewBag.Title</h1>
#if (Model == null)
{
<p>There is no data to be displayed</p>
}
else
{
<ul>
#foreach (string str in Model)
{
<li>#str</li>
}
</ul>
}
</div>
Views folder structure(Each folder means a controller except Shared):
Result:

Returning Different Data To Various View Sections

I have a "Most Popular" div currently displaying the most popular auction items and an "Ending Soon" div that I want to display those auctions ending soon. I have successfully queried the database for the "Most Popluar" and returned those results to the page. And I know what the query is for those ending soon, but how do I return both sets of data to the page for the partial view "_AuctionTile" to use?
The following update reflects the suggestions made by Vinutha N --
The View
#model IEnumerable<MyAuctionApp.Models.Auction>
#{
ViewBag.Title = "Home Page";
}
<div class="row">
<div id="popular" class="col-md-4 col-lg-4">
<h2>Most Popular Items</h2>
#foreach (var item in Model.mostPopularItems)
{
#Html.Partial("_AuctionTile", item)
}
</div>
<div id="ending" class="col-md-4 col-lg-4">
<h2>Auctions Ending Soon</h2>
#foreach (var item in Model)
{
#Html.Partial("_AuctionTile", item)
}
</div>
The ViewModel
using MyAuctionApp.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MyAuctionApp.ViewModels
{
public class AuctionViewModel
{
public static List<Auction> mostPopularItems;
public static List<Auction> endingSoon;
public AuctionViewModel(AuctionsDataContext db)
{
mostPopularItems = db.Auctions.Where(x => x.EndTime >
DateTime.Today).OrderByDescending(x => x.viewCount).ToList();
endingSoon = db.Auctions.Where(x => x.EndTime >
DateTime.Today).OrderByDescending(x => x.EndTime).ToList();
}
}
}
The Controller
using MyAuctionApp.Models;
using MyAuctionApp.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MyAuctionApp.Controllers
{
[AllowAnonymous]
public class HomeController : Controller
{
[AllowAnonymous]
public ActionResult Index()
{
var db = new AuctionsDataContext();
var dataModuleObject = new AuctionViewModel(db);
return View(dataModuleObject);
//var auctions = db.Auctions.ToArray();
//var auctions = db.Auctions.Where(x => x.EndTime >
//DateTime.Today).OrderByDescending(x => x.viewCount);
//return View(auctions);
}
}
As you can see from the attached screenshot however, the 'mostPopularItems'
property that was initialized in the AuctionViewModel, is not being picked
up by Intellisense as existing, in the line
#foreach (var item in Model.mostPopularItems)
What do I still not have quite right?
Thanks,
CM
Use child actions. First, create actions like the following on your AuctionController:
[ChildActionOnly]
public ActionResult MostPopular()
{
// get most popular auctions
return PartialView("_AuctionTile", auctions);
}
[ChildActionOnly]
public ActionResult EndingSoon()
{
// get auctions ending soon
return PartialView("_AuctionTile", auctions);
}
Then, in your view, where you want each to display:
#Html.Action("MostPopular")
#Html.Action("EndingSoon")
Your main view's model should only focus on what it needs to do, not asides like these.
UPDATE
Sorry, I wasn't paying attention to your HTML. Basically, you would need to create a partial for each section, then. For example:
_MostPopular.cshtml
#model IEnumerable<Auction>
<div id="popular" class="col-md-4 col-lg-4">
<h2>Most Popular Items</h2>
#foreach (var item in Model)
{
#Html.Partial("_AuctionTile", item)
}
</div>
Then, change the child action to return this instead:
return PartialView("_MostPopular", auctions);
The key here is that the child action creates a separate context, where you can build a view model specifically for this partial view, without affecting what's going on in the main view.
BONUS
Display templates can help a lot here too. For example, if you could create a view like Views\Shared\DisplayTemplates\Auction.cshtml. Then, any time you call Html.DisplayFor with an instance of Auction, it would automatically use that view to render it. Additionally, Html.DisplayFor can handle enumerables as well. It will simply use the display template for the contained type to render each item in the list. So in your code, instead of iterating over the items in Model and rendering a partial view for each, you could then simply do:
#model IEnumerable<Auction>
<div id="popular" class="col-md-4 col-lg-4">
<h2>Most Popular Items</h2>
#Html.DisplayForModel()
</div>
Firstly you need to declare 2 list they are,
list Popularitem and list endingSoon in view model which you are using(if not create and use its a best practice. because u cannot do in the controller)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace DataViewModel.ViewModels
{
public class DataViewModel
{
public list<Auctions> Popularitem { get; set; }
public list<Auctions> endingSoon { get; set; }
public CustomerViewModel((datatype)db)
{
list<Auctions> Popularitem=db.Auctions.Where(x => x.EndTime > DateTime.Today).OrderByDescending(x => x.viewCount);
list<Auctions> endingSoon =db.Auctions.Where(x => x.EndTime > DateTime.Today).OrderByDescending(x => x.viewCount);
}
}
}
in controller
public ActionResult Index()
{
var db = new AuctionsDataContext();
datamoduleobject= new Dataviewmodel(db);
return View(datamoduleobject);
}
in view :
#MyProject.Web.ViewModels.DataViewModel
<div id="popular" class="col-md-4 col-lg-4">
<h2>Most Popular Items</h2>
#foreach (var item in Model.Popularitem)
{
#Html.Partial("_AuctionTile", item)
}
</div>
<div id="ending" class="col-md-4 col-lg-4">
<h2>Auctions Ending Soon</h2>
#foreach (var item in Model.endingSoon)
{
#Html.Partial("_AuctionTile", item)
}
</div>
and please go through the link i have provided which help u to understand http://tutlane.com/tutorial/aspnet-mvc/how-to-use-viewmodel-in-asp-net-mvc-with-example
In your model declare 2 list and assign the data to lists. In html page use the specific list property in foreach.
Like,
model.Popularitems
Model.Endingitems
Either create view model as suggested above or you can pass extra data in view bag as in controller you can set
ViewBag.endingSoon= yourCollection
and use it in your view
hope It'll help

creating a partial view with a form post that planning on using in several places

How can I create a partial view with a form post that I plan on using in several places?
The partial view will have a form that creates an entry in data storage and displays the persisted data underneath this form.
So after submitting the form I ll see my entry in a grid like structure under the form without switching the parent view.
If the model is not valid the error will be shown also. The trick here is, how do I stay in my current page without creating an action
In the controller of each view that shows the partial view?
I will be using this partial view in say 10 different parent views.
Below, i provide some of the codes that will help community to make sense the question exactly.
How should i configure my code to achieve my goal.
Thanks
This is the partial view sample
#model ViewModels.CommentViewModel
#using (Html.BeginForm("Index", "Comment", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary()
<div class="form-group">
<label class="control-label col-md-2">Please Type Your Name</label>
<div class="col-md-10">
#Html.TextBoxFor(model => model.Name, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Name)
</div>
</div>
<input id="addComment" type="submit" value="Add" />
</div>
}
#foreach (var item in Model.Comments)
{
<p>
#item.Name
</p>
}
Here is Controller
public PartialViewResult Index(int id)
{
var model = new CommentViewModel() { Comments= db.Comments.Where(x=> x.NewsId == id && x.isApproved== true )};
return PartialView("_Comments", model);
}
[HttpPost]
public PartialViewResult Comment(int id, CommentViewModel model)
{
if (ModelState.IsValid)
{
var comment = new Comment()
{
Name = model.Name,
Title = model.Title,
CommentContent = model.Content,
Email = model.Email,
CreationDate = DateTime.Now,
RefId = Guid.NewGuid(),
isApproved = false,
NewsId = id
};
db.Comments.Add(comment);
db.SaveChanges();
return PartialView();
}
return PartialView();
}
If you want to do things like submit a form and retrieve updated data without reloading the page, then you're talking about AJAX. The fact that this is a partial view is meaningless in that context. It doesn't matter how many different views this partial view will be rendered in, you just need one action in one controller that can respond to an AJAX request. Then, you'll just need to do something like the following with JavaScript that can be included via an external file in whatever views need this form:
$('#MyPartialViewForm').on('submit', function (e) {
// prevents form from submitting standard way, causing page refresh
e.preventDefault();
$.post('/url/that/handles/form', $(this).serialize(), function (results) {
// results will be a rendered partial with the data here,
// which you can use to replace the content of a div or
// something on your page.
$('#DivWhereSubmittedDataIsDisplayed').html(results);
});
});
Then, in your action that responds to the AJAX request:
[HttpPost]
public ActionResult ActionForAjaxForm(FormModel model)
{
// do something like save the posted model
return PartialView("_PartialViewThatRendersData", model);
}

Using Ajax.BeginForm with MVC 4 - adding to my model collection asynchronously isn't working

I am trying to make a small football site where the user can create a new team and then asynchronously in another div it shows all the teams the user has created. So basically a team is created then added to the list of teams. All of this is in the model.
Now, I would like to do this asynchronously because its a nice to have but it's not working in my code. I am either missing something or it's not possible with what I am doing.
Controller
public ActionResult TeamManagement()
{
modelTeamSelect modelTeamSelect = new modelTeamSelect();
return View(modelTeamSelect);
}
[HttpPost]
public ActionResult TeamManagement(string btnSubmit, modelTeamSelect modelTeamSelect)
{
switch (btnSubmit)
{
case "Add Team":
// For now - add to collection but not use DAL
modelTeamSelect.teams.Add(modelTeamSelect.team);
//modelTeamSelect.team.TeamName = string.Empty;
break;
}
return View(modelTeamSelect);
}
View
#model Website.Models.modelTeamSelect
#{
ViewBag.Title = "Football App";
}
#section featured {
}
#using (Ajax.BeginForm(new AjaxOptions
{
HttpMethod = "POST",
Url = "Home/TeamManagement",
OnComplete = "teamAdded()"
}))
{
<div id="divTeams" style="float:left">
<h3>Create a new team:</h3>
#Html.LabelFor(m => m.team.TeamName)
#Html.TextBoxFor(m => m.team.TeamName)
<input type="submit" value="Add Team" name="btnSubmit" />
</div>
<div id="divCreatedTeams" style="float:left">
<h3>Your created teams:</h3>
#if (Model.teams.Count > 0)
{
for (int i = 0; i < Model.teams.Count; i++)
{
#Html.TextBoxFor(m => m.teams[i].TeamName)
}
}
</div>
<div id="divLeagues">
</div>
}
Model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Website.Models
{
public class modelTeamSelect
{
public modelTeamSelect()
{
teams = new List<modelTeam>();
team = new modelTeam();
}
public List<modelTeam> teams { get; set; }
public modelTeam team { get; set; }
}
}
I have the right javascript references being used in the project as I recently fixed this.
Why isn't my UI changing to reflect new contents of list?
I dont get the idea of passing the submit button string to the Action. But in order to pass a ViewModel to the Action I think you have to write your own model binder. If you want you can try getting the models seperately in the action and combining them in the Action
public ActionResult TeamManagement(List<modelTeam> teams, modelTeam team)
and combine them in the action in the viewModel.
Just a sugestion If you want to retrieve them async with ajax what I do is return partial view (i think better in your case) or json

Create ViewModel for Navigation

I have an MVC 4 application with several views. I.e. Products, Recipes, Distrubutors & Stores.
Each view is based around a model.
Let's keep it simple and say that all my controllers pass a similar view-model that looks something like my Product action:
public ActionResult Index()
{
return View(db.Ingredients.ToList());
}
Ok so this is fine, no problems. But now that all of my pages work I want to change my navigation (which has dropdowns for each view) to load the items in that model.
So I would have a navigation with 4 Buttons (Products, Recipes, Distrubutors & Stores).
When you roll over each button (let's say we roll over the products button) then a dropdown would have the Products listed.
To do this I need to create some type of ViewModel that has all 4 of those models combined. Obviously I can't just cut out a PartialView for each navigation element and use
#model IEnumerable<GranSabanaUS.Models.Products>
And repeat out the Products for that dropdown, because then that navigation would only work in the Product View and nowhere else.
(After the solution)
AND YES ROWAN You are correct in the type of nav I am creating, see here:
Introduction
I'm going to be making a few assumptions because I don't have all the information.
I suspect you want to create something like this:
Separating views
When you run into the issue of "How do I put everything into a single controller/viewmodel" it's possible that it's doing too much and needs to be divided up.
Don't treat your a final page as one big view - divide the views up into smaller views so they are doing 'one thing'.
For example, the navigation is just one part of your layout. You could go even further to say that each dropdown menu is a single view that are part of the navigation, and so on.
Navigation overview
Suppose you have a _Layout.cshtml that looks like this:
<body>
<div class="navbar">
<ul class="nav">
<li>Products</li>
<li>Recipes</li>
</ul>
</div>
#RenderBody()
</body>
As you can see we have a simple navigation system and then the main body is rendered. The problem that we face is: How do we extract this navigation out and give it the models it needs to render everything?
Extracting the navigation
Let's extract the navigation into it's own view. Grab the navigation HTML and paste it into a new view called __Navigation.cshtml_ and put it under ~/Views/Partials.
_Navigation.cshtml
<div class="navbar">
<ul class="nav">
<li>Products</li>
<li>Recipes</li>
</ul>
</div>
Create a new controller called PartialsController. Create a new action to call our navigation.
PartialsController.cs
[ChildActionOnly]
public class PartialsController : Controller
{
public ActionResult Navigation()
{
return PartialView("_Navigation");
}
}
Update our Layout to call the navigation.
_Layout.cshtml
<body>
#Html.Action("Navigation", "Partials")
#RenderBody()
</body>
Now our navigation is separated out into its own partial view. It's more independent and modular and now it's much easier to give it model data to work with.
Injecting model data
Suppose we have a few models such as the ones you mentioned.
public class Product { //... }
public class Recipe { //... }
Let's create a view-model:
NavigationViewModel.cs
public class NavigationViewModel
{
public IEnumerable<Product> Products { get; set; }
public IEnumerable<Recipe> Recipes { get; set; }
}
Let's fix up our action:
PartialsController.cs
public ActionResult Navigation()
{
NavigationViewModel viewModel;
viewModel = new NavigationViewModel();
viewModel.Products = db.Products;
viewModel.Recipes = db.Recipes;
return PartialView("_Navigation", viewModel);
}
Finally, update our view:
_Navigation.cshtml
#model NavigationViewModel
<div class="navbar">
<ul class="nav">
#foreach (Product product in Model.Products)
{
#<li>product.Name</li>
}
#foreach (Recipe recipe in Model.Recipes)
{
#<li>recipe.Name</li>
}
</ul>
</div>
public class MenuContents
{
public IEnumerable<Products> AllProducts { get; set; }
public IEnumerable<Recepies> AllRecepies { get; set; }
public IEnumerable<Distributors> AllDistributors { get; set; }
public IEnumerable<Stores> AllStores { get; set; }
private XXXDb db = new XXXUSDb();
public void PopulateModel()
{
AllProducts = db.Products.ToList();
AllRecepies = db.Recepies.ToList();
AllDistributors = db.Distributors.ToList();
AllStores = db.Stores.ToList();
}
}
Then in your controller
public ActionResult PartialWhatever()
{
MenuContents model = new MenuContents();
model.PopulateModel();
return PartialView("PartialViewName", model);
}
Then in your partial view
#Model MenuContents
... do whatever here