I am in the habit of using nested loops in classic. Data from the first record set is passed to the second record set. How would I accomplish the same thing in MVC? As far as I can tell, I can only have one model passed to my view.
<%
rs.open "{Call usp_SalesOrder}",oConn
Do While (NOT rs.EOF)
%>
<div><% = rs("SalesOrderNumber")%></div>
<%
rs2.open "{Call usp_SalesOrderLines(" & rs("SOKey") & ")}",oConn
Do While (NOT rs.EOF)
%>
<div><% = rs2("SalesOrderLineNumber")%></div>
<%
rs2.MoveNext
Loop
rs2.close
%>
<%
rs.MoveNext
Loop
rs.close
%>
My suggestion would be to build a more robust model. It is true that you can only pass one model to your view, but your model can contain the results of multiple data sets, provided you have gathered those data sets in your controller and assigned them to the model.
I also suggest staying away from the ViewBag. It's an easy trap to fall into. Trust me when I say you'll regret it later.
For your example, maybe a model defined like this:
public class MyModel
{
public List<SalesOrder> SalesOrders = new List<SalesOrder>();
}
public class SalesOrder
{
public string SOKey = string.Empty;
public List<SalesOrderLine> SalesOrderLines = new List<SalesOrderLine>();
}
And the code to populate the sales orders in the controller:
public Action Index()
{
MyModel model = new MyModel();
model.SalesOrders.AddRange(CallUspSalesOrder());
foreach (SalesOrder salesOrder in model.SalesOrders)
{
salesOrder.SalesOrderLines.AddRange(CallUspSalesOrderLines(salesOrder.SOKey));
}
return View(model);
}
That way, you have access to all sales orders (and their sales order lines) within the view.
I would say that Nathan's post is a good start. Here is what I would do from beginning to end.
This is how I would do my model:
public class SalesOrderModel
{
public List<SalesOrderLines> SOLines = new List<SalesOrderLines>();
public List<SalesOrder> SOHeader = new List<SalesOrder>();
}
My Controller would then do this:
public ActionResult Index()
{
List<SalesOrder> SalesOrder = callSalesOrderUSP.ToList();
List<SalesOrderLines> SalesOrderLines = new List<SalesOrderLines>();
foreach (var thing in SalesOrder)
{
SalesOrderLines.AddRange(callSalesOrderLinesUSP(thing.SOKey).ToList());
}
SalesOrderModel salesOrderModel = new SalesOrderModel
{
SOHeader = SalesOrder,
SOLines = SalesOrderLines
};
return View(salesOrderModel);
}
Then in your view you can do this:
#foreach(var something in Model.SOHeader)
{
foreach (var thing in Model.SOLines.Where(i => i.SOKey == something.SOKey))
{
//display info here
}
}
You can use ViewBag to pass elements not relevant to your model. Also do not be afraid of creating your own ModelView objects that can work between your View and Controller. Your views should not be restricted to what your model has to offer.
Take a look at this for how you can implement a ViewModel in MVC.
And perhaps look at this to see how you can use ViewBag to pass values to your view, not relevant to your model.
Related
My target is, to modify a model in more than one view.
Since sometimes my models have many properties I want to modify them in more than one view. Something like:
first page edits 2 properties, second page edits 3 other properties,...
the model looks like this:
public class LoadViewModel
{
public int CurrentPage { get; set; } = -1;
public PageViewModel PageViewModel { get; set; }
}
public class PageViewModel
{
public string Param1 { get; set; }
public string Param2 { get; set; }
public int Param3 { get; set; }
}
my view on the Index-page looks like this:
#model LoadViewModel
#using(Ajax.BeginForm("Load", "Home", new AjaxOptions {UpdateTargetId = "page"}, new {lvm = Model}))
{
<div id="page"></div>
<input type="submit"/>
}
and this is my action:
public ActionResult Load(LoadViewModel lvm = null)
{
if (lvm == null) lvm = new LoadViewModel();
lvm.CurrentPage += 1;
TempData["CurrentPage"] = TempData["CurrentPage"] == null ? 0 : (int)TempData["CurrentPage"] + 1;
if (!partialViewDict.ContainsKey((int) TempData["CurrentPage"]))
TempData["CurrentPage"] = 0;
return PartialView(partialViewDict[(int)TempData["CurrentPage"]], lvm);
}
the pages are just partials that are mapped:
private Dictionary<int, string> partialViewDict = new Dictionary<int, string>
{
{0, "Pages/_Page1"},
{1, "Pages/_Page2"},
{2, "Pages/_Page3"},
};
and designed like this:
#using WebApplication1.Controllers
#model LoadViewModel
#{
TempData["CurrentPage"] = 0;
}
#Html.DisplayNameFor(m => m.PageViewModel.Param1)
#Html.EditorFor(m => m.PageViewModel.Param1)
this is working. When switching to Page2 the model is correctly set, but when hitting the submit the value of Param1 (that I set in Page1) is resetted to null and only the values I set in the current partial are correct.
This is Page2:
#using WebApplication1.Controllers
#model LoadViewModel
#{
TempData["CurrentPage"] = 1;
}
#Html.DisplayNameFor(m => m.PageViewModel.Param2)
#Html.EditorFor(m => m.PageViewModel.Param2)
When I add a #Html.HiddenFor(m => m.PageViewModel.Param1) into the partial, the value is still set. But I don't want the values to be resetted. I don't want to add an #Html.HiddenFor for all properties a set in a previous view. How can I prevent that the values are resetted when hitting submit without adding #Html.HiddenFor for all not listed attributes? Or is there any other possibility to catch my target?
There's two pieces to this. First, the post itself, and getting that to validate. For that, each step should have its own view model, containing only the properties it's supposed to modify. This allows you to add all the validation you need without causing other steps to fail. In the end, you'll combine the data from all of these into your entity class or whatever.
Which brings us to the second piece. You need some way of persisting the data from each step. The only data that will exist after a POST is the data that was posted and anything in the session (which includes TempData). You could always create a bunch of hidden fields to store the data from the previous steps, but that can get a little arduous. Most likely, you'll just want to use the session.
TempData is basically a specialized instance of Session, so which you use doesn't really matter. With TempData, you'll need to remember call TempData.Keep() for each of the keys you've set for each step or you'll lose the previous steps on the next request. Session will keep them around for the life of the session, but you should remember to remove the keys at the end with Session.Remove().
Do you use #using (Html.BeginForm()) in your .cshtml?
Unfortunately this is MVC. MVC is stateless, which means if you don't render it then you loose it :(
If you use model binding and scaffolding, then you can save some time and work but at the end it will be the same solution.
I am new to using the Entity Framework. I have added my Model, and I need to use two my models/tables in one View Page. So to do that I added this to my AccountViewModels.cs page:
public class category_menuitem
{
public Category Category { get; set; }
public MenuItem MenuItem { get; set; }
}
I am trying to use Values from those two Models/Tables.
My View Page:
using System.Data.SqlClient
#model IEnumerable<YourGoGetterV1.Models.category_menuitem>
#{
ViewBag.Title = "Show Menu" - ViewBag.restaurant_id;
}
<h2>ShowMenu</h2>
<div class="jumbotron">
#foreach (var item in Model)
{
<div><strong>#Html.DisplayFor(item1 => item.Category.Name)</strong>
<div>#Html.DisplayFor(item1 => item.Category.Description)</div>
#{
using (var context = new YourGoGetterContext())
{
SqlParameter sa = new SqlParameter("#p0", ViewBag.restaurant_id);
var menu_items = context.MenuItems.SqlQuery("Select * FROM MenuItems where restaurant_id = #p0", sa).ToList();
var test = "DID IT WORK??";
}
}
</div>
}
</div>
Controller:
public ActionResult ShowMenu(string id, int restaurant_id)
{
ViewBag.Id = id;
ViewBag.restaurant_id = restaurant_id;
return View(Models.category_menuitem.ToList((object(id)));
}
I want to cast the ID, so that it creates a different URL for something that passes in a different ID. But I'm having two problems.
1) I can't even put in the Models.Category_menuitem.ToList() because "No overload for method 'ToList' takes 1 arguments"
2)The Models.Category_menuitem does not contain a definition for ToList.
What do I do?
I think you should do it different. The model should contain the data you use in the view. So do your db requests in the model and give the model to the view to display the data in it. Don't do SQL requests in the view. You also should write classnames always in capital letters because of C# naming conventions.
If I understand it right you want to display a menubar or something like that with categories and each category has many menuitems?
Just create a model menuitems like this:
public class MenuItems {
public List<Category> Categories{get;set;}
}
and a model Category like this:
public class Category {
public string CategoryName {get;set;}
public List<MenuItem> MenuItems {get;set;}
}
Fill the models with data and give the MenuItems Model to the view. In the view you can do something like:
#foreach (var category in Model.Categories)
{
foreach (var menuItem in category.MenuItems)
{
}
}
I hope this helps ;)
To this part of your code:
var menu_items = context.MenuItems.SqlQuery("Select * FROM MenuItems where restaurant_id = #p0", sa).ToList();
I am not sure what you wanna do. You are querying the db for data which should be already in the model or am I wrong? But if you want to use EF you would write:
var items = context.Menuitems.Where(m => m.restaurant_id == id).ToList();
Here's the visual of what I'm trying to do.
So basically what happens here is that you choose a name from the select on the left and a team from the select on the right, then click Submit Assignment and it adds a record. (This information is for reference so you can understand the context. I don't need help with the database inserts or updates.)
This is going to be a PartialView with a ViewModel.
I need to know how to populate those two dropdowns with separate lists. I'm assuming those would be coming over in the ViewModel.
So in my main view, I have a call to the PartialView with RenderAction.
//The action //The controller
#{Html.RenderAction("TeamAssignment", "TeamAssignment");}
The controller that gets called
public class TeamAssignmentController : Controller
{
MyDatabaseEntities db = new MyDatabaseEntities();
[ChildActionOnly]
public ActionResult TeamAssignment()
{
//NEED A VIEW MODEL HERE TO PASS INTO THE PARTIALVIEW
return PartialView();
}
}
In the ViewModel I will need lists that populate the two drop downs.
The linq query on the left for Name would be this.
var nameList = (from p in db.Participant
where p.ParticipantType == "I"
select new {
p.ParticipantID,
p.IndividualName
}).ToList();
The linq query on the right for Team would be this.
var teamList = (from p in db.Participant
where p.ParticipantType == "T"
select new {
p.ParticipantID,
p.TeamName
}).ToList();
How can I pass that information into the PartialView and how do I populate the dropdowns with the ID for value and name for display text?
UPDATE
I belive this may be the ViewModel I need. Is this correct?
public class TeamAssignmentViewModel
{
//Need these ints for the updating and deleting
public int IndividualParticipantID { get; set; }
public int TeamParticipantID { get; set; }
public List<Participant> NameList { get; set; }
public List<Participant> TeamList { get; set; }
}
You need to create an instance of a viewmodel, populate it and pass it to your PartialView:
[ChildActionOnly]
public ActionResult TeamAssignment()
{
// Instantiate A VIEW MODEL HERE TO PASS INTO THE PARTIALVIEW
MyCustomModel model = new MyCustomModel();
model.nameList = (from p in db.Participant
where p.ParticipantType == "I"
select p).ToList();
model.teamList = (from p in db.Participant
where p.ParticipantType == "T"
select p).ToList();
return PartialView("TeamAssignment", model);
}
EDITED: removed use of dynamic class in linq statements
The markup for the Dropdowns might look something like:
#Html.DropDownListFor(model => model.IndividualParticipantID,
new SelectList(Model.NameList, "ParticipantID", "FirstName")
#Html.DropDownListFor(model => model.IndividualParticipantID,
new SelectList(Model.TeamList, "ParticipantID", "Team")
I'll start by admitting that I haven't read everything posted, but I can help with passing data to the PartialView to be used as a model.
In your View, you can build the partial like this:
#Html.Partial("_PartialViewName", currentItem,
new ViewDataDictionary {
{ "ExtraValue", Model.Count },
{ "SecondExtraValue", #ViewBag.ValueToPass });
Then in the PartialView, you can do this:
#model [Type of currentItem from above]
#{
// I'm using var here for shorthand, whereas I do strong typing and cast in my codebase.
var dataValue = ViewData["ExtraValue"];
var secondDataValue = ViewData["SecondExtraValue"];
}
Using this method you can pass in a model, as well as other values that may be needed for rendering.
Hope this helps. :)
It is said that the models should be fat and the Views should be thin.We put our business logic inside the Model (https://stackoverflow.com/questions/235233/asp-net-mvc-should-business-logic-exist-in-controllers).We normally write the LINQ inside the controller,but is it possible that we should write the query in models,If yes then how we will get the results in the View?
Second Question
public ActionResult Index()
{
using (NORTHWNDEntities c = new NORTHWNDEntities())
{
var x = c.Employees.Count();
ViewData["count"] = x;
return View(x);
}
}
When we do this are we passing the variable x to the View?
I tried to access the ViewData in View
<% ViewData["count"] %>
But it gives an error error ,Anyone who can help me with this
Thanks
If you are trying to display the value of ViewData["count"] in your view, you can use the following syntax:
<%= ViewData["count"] %>
Note the = in the opening tag. This is the equivalent of
<% Response.Write(ViewData["count"]) %>
there is better approach for doing this.and is very straight forward.create a Model that meets your needs and pass it to view.
public class MyModel
{
public int Count{get;set;}
}
and your controller can looks like
public ActionResult Index()
{
using (NORTHWNDEntities c = new NORTHWNDEntities())
{
var x = c.Employees.Count();
var model = new MyModel{Count = x};
return View(model);
}
}
and then create an strongly typed view
Razor Syntax :
#model MyModel
#Model.Count
ASPX syntax :
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Models.MyModel>" %>
<%= Model.Count %>
and the code can get even better if you do the following:
public class EmployeeService
{
public int GetEmployeeCount()
{
using (NORTHWNDEntities c = new NORTHWNDEntities())
{
var count = c.Employees.Count();
return count;
}
}
}
and the controller most change as well:
public ActionResult Index()
{
EmployeeService srvc = new EmployeeService();
var x = srvc.GetEmployeeCount();
var model = new MyModel{Count = x};
return View(model);
}
The query have to be into the Data Access Layer and the logic in MVC is into the Controller, not into the Model.
Here you can find an example of a layered architecture with MVC.
At the end you have always to use a Model into the View, don't pass data using the ViewData.
I am developing an asp.net mvc web app in which I want to send 2 objects to View through Controller now I am sending only one object to view through controller
return View(repository.func(id));
and in view I am getting
<% var data = Model.First %>
But now I am confused how to send 2 objects and how to get it.
An excellent occasion to (learn to) use a ViewModel:
class MyViewModel { ... }
// and in the Action:
var view = new MyViewModel();
view.First = repository.func(id) ;
view.Second = ....;
return View(view);
You can use ViewBag (Personally I don't like this approach) or create class which will hold both values and use it for model for your view
I assume your view is strongly-typed to be of the same type as whatever you're returning from:
repository.func(id)
lets say object 'Foo'
Assuming you are using a strongly-typed view:
#model Foo
You can change this to be:
#model IEnumerable<Foo>
Then, your view will be strongly-typed to a collection (IEnumerable) of Foo
and in your view you can do:
foreach(var foo in Model)
{
//Do stuff
}
Naturally, your repository method will have to return a collection of objects (via something that implements IEnumerable - List<> for example)
Just a slight elaboration of Henk's answer
class MyViewModel {
TMyEntityType1 First { get; set; }
IQueryable<TMyEntityType2> Second { get; set; }
}
And then in the action you collect your 2 sets of data and house it in an instance of MyViewModel
var viewModel = new MyViewModel();
viewModel.First = repository.func(id);
viewModel.Second = repository.containing("?");
return View(viewModel);
An in your view you may want to change it to:
<% var dataFirst = Model.First;
var dataSecond = Model.Second;%>
Where Model is now of type MyViewModel and not the return type of repository.func(id)