Pass multiple objects to view through controller - c#

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)

Related

How to pass a strongly typed model and a list of models to a view simultaneously

My end goal is to pass a strongly typed model (to populate a drop-down menu) and a list of models (based on a search query) to a view simultaneously from a controller. The view I am passing it to is the "Clear()" view. Currently in my HomeController.cs I have:
public ActionResult Clear()
{
var states = GetAllStates();
var model = new ProjectClearanceApp.Models.Project();
model.States = GetSelectListItems(states);
return View(model);
}
private IEnumerable<string> GetAllStates()
{
return new List<string>
{
"AL",
// ... (you get the point)
"WY",
};
}
private IEnumerable<SelectListItem> GetSelectListItems(IEnumerable<string> elements)
{
var selectList = new List<SelectListItem>();
foreach (var element in elements)
{
selectList.Add(new SelectListItem
{
Value = element,
Text = element
});
}
return selectList;
}
I read somewhere that that's the best way to get the list of options for a drop-down menu. Now I'd also like a pass a LIST of models to the same view (Clear.cshtml) based on a search query. I'm reading from this Microsoft tutorial to search in the controller action for that view by adding
public ActionResult Index(string searchString)
{
var movies = from m in db.Movies
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
return View(movies);
}
to the controller. How can I pass both the list of drop-down options AND the list of models that fit the search from the controller to the view (or, how can I achieve the same effect without passing both from the controller)?
To achieve your end goal ,that is passing the list of model, viewmodel will be the best solution as per my knowledge.
Some key note above view model:
The ViewModel class, which is the bridge between the view and the model. Each View class has a corresponding ViewModel class. The ViewModel retrieves data from the Model and manipulates it into the format required by the View. It notifies the View if the underlying data in the model is changed, and it updates the data in the Model in response to UI events from the View
The ViewModel class determines whether a user action requires modification of the data in the Model, and acts on the Model if required. For example, if a user presses a button to update the inventory quantity for a part, the View simply notifies the ViewModel that this event occurred. The ViewModel retrieves the new inventory amount from the View and updates the Model. This decouples the View from the Model, and consolidates the business logic into the ViewModel and the Model where it can be tested.
Official definition/ Source: https://msdn.microsoft.com/en-us/library/ff798384.aspx
Additional information with Ex:
http://www.dotnettricks.com/learn/mvc/understanding-viewmodel-in-aspnet-mvc
Kindly let me know your thoughts or feedbacks
Thanks
Karthik
Model is a term that really describes what data is meant to be bound to the view, and what data the model binder will handle when you make a request to/from the view. Because I'm not exactly sure how you are using Index and Clear, I'll just stub these out generally. Please let me know if I've missed the point of your question:
If you are having trouble passing, for example, List<Movies> movieModel along with your List<SelectListItem> selectedStates (or any additional values) to the view, here are 2 common ways to do this.
ViewModel approach -- Create a model class that contains your "main" model along with any other properties, lists, etc. that you'll need in the view.
eg:
public Class MyViewModelClass
{
List<Movie> MoviesList {get;set;}
List<SelectListItem> StatesList {get;set;}
//some other properties/methods can go here as well ...
}
//In Controller
{
MyViewModelClass model = new MyViewModelClass();
model.StatesList = <build your select list>;
model.MovieList = <build your movie list>;
return View(model);
}
In the controller you would then create a new instance of MyViewModelClass model (different name obviously), and populate your movie list, state list, and any other properties, assign them to the model properties, and pass the whole thing as your return View(model);
This is nice because all of the data getting passed to the view can be in one place.
ViewBag approach -- Stick with a single model or List, and pass view-related data (such as dropdown lists, or state bools) in the ViewData or ViewBag.
ex.
//From Controller
public ActionResult SomeMethod()
{
var states = GetAllStates();
var model = <enter movies query here>;
ViewBag.StatesList = GetSelectListItems(states);
//This will be accessible in the view now
return View(model);
}
//In View:
#{
List<SelectListItem> StatesList = (List<SelectListItem>)ViewBag.StatesList; // Can now use this variable to bind to DropDownList, etc.
}
I prefer, for the most part, using a ViewModel to add everything I need. For one, the ViewBag/ViewData requires some casting, and it relies on "magic strings", so you don't have Visual Studio letting you know if you'd typed something wrong with intellisense. That said, either is viable.

Transitioning from classic asp to asp.net MVC

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.

Getting error attempting to add multiple models to a view

I need to have 2 models in my View. But since we could only add 1 view, i took the following approach;
#model Tuple<My.Models.Mod1,My.Models.Mod2>
#Html.DropDownListFor(m => m.Item2.humanKind,Model.Item2.allHuman)
#Html.TextBoxFor(m => m.Item1.food)
But, what i end up getting is the following error;
The model item passed into the dictionary is of type 'My.Models.Mod2', but this dictionary requires a model item of type 'System.Tuple`2[My.Models.Mod1,My.Models.Mod2]'.
What is this, and how can i solve this?
UPDATE
public ActionResult Index()
{
var model2 = new Mod2 { allHuman = allHumans() };
var model1 = new Mod1(); // JUST NOW I ADDED THIS, BUT IT DOESn't WORK
return View(model1,model2);
}
You can only have one model per view. You need to instantiate the Tuple as Ufuk suggested.
However I would suggest creating a new model that has the other models as a property.
The view in question is being called from a controller action that only passes in a My.Models.Mod2 rather than a Tuple<My.Models.Mod1,My.Models.Mod2>.
Double-check the specific controller action that calls this view.
UPDATE
Your controller code
return View(model1,model2);
should be
return View(new Tuple<My.Models.Mod1,My.Models.Mod2>(model1, model2>);
You are passing model1 and model2 as separate parameters rather than as a Tuple.
Build a view model that contains both:
Public class CompositeViewModel{
Public Mod1 mod1 {get;set;}
Public Mod2 mod2 {get;set}
}
Then construct and pass CompositeViewModel to view. Set views to use CompositeViewModel as the model #model CompositeViewModel
Using a Tuple doesn't easily allow you to expand or change what you are doing.
It maybe even looks like you have one ViewModel that has data, and then some associated IEnumerable<SelectListItem>. If that is the case then name the ViewModel like CreateAnimalTypeViewModel which contains all the properties you need to create it, then have various select lists.
If you need to map from something to the ViewModel e.g. if you were doing an edit of an existing item you could use AutoMapper.
You are not creating a tuple instance before sending it to the view.
public ActionResult Index()
{
var model2 = new Mod2 { allHuman = allHumans() };
var model1 = new Mod1();
return View(new Tuple<Mod1,Mod2>(model1,model2));
}

Accessing an object passed into a view

I am passing an object (a List) into a view like so:
private readonly CarsContext _db = new CarsContext();
public ActionResult Index()
{
ViewBag.Message = "Foobar";
var result = from cars in _db.CarProfiles
where cars.CarOfTheWeek
select cars;
return View(result.ToList());
}
How do I access this object from within my Index view? I am thinking I need to add it to my dynamic viewbag, and access it that way instead of passing it as an argument, is this correct or if I keep my controller code above as it is, can I access this object from within the view?
Thanks
Make your view strongly typed to the class/ type you are returning to, from your action method.
In your case you are returning a List of CarProfile class objects. So in your view(index.cshtml), drop this as the first line.
#model List<CarProfile>
Now you can access the list of CarProfile using the keyword Model.for example, you want to show all items in the list, in a loop, you can do like this
#model List<CarProfile>
#foreach(var item in CarProfile)
{
<h2>#item.Name</h2>
}
Assuming Name is a property of CarProfile class.
In your Index.cshtml view, your first line would be:
#model YOUR_OBJECT_TYPE
So for example, if you pass a string in your line:
return View("hello world");
Then your first line in Index.cshtml would be:
#model String
In your case, your first line in Index.cshtml would be:
#model List<CarProfile>
This way, if you type #Model in your Index.cshtml view, you could access to all the properties of your list of CarProfile. #Model[0] would return your first CarProfile in your list.
Just a tip, the ViewBag shouldn't be used most of the time. Instead, you should create a ViewModel and return it to your view. If you want to read about ViewModels, here's a link: http://msdn.microsoft.com/en-us/vs2010trainingcourse_aspnetmvc3fundamentals_topic7.aspx
You can access the object in your view by passing it in ViewBag ( The way that you have wrote your controller ). But I think, it is usually better to have a ViewModel and pass data to view, via that ViewModel.
Passing Business objects directly to model ( the way you have done ), make model dependent to business object, that it is not optimal in the most cases. Also having ViewModel instead of using ViewBag has the advantage of using StrongTypes.
private readonly CarsContext _db = new CarsContext();
public ActionResult Index()
{
var result = from cars in _db.CarProfiles
where cars.CarOfTheWeek
select cars;
MyViewModel myViewModel = new MyViewModel( );
myViewModel.Message = "Foobar";
myViewModel.ResultList = result.ToList();
return View( myViewModel );
}

ViewData and ViewModel in MVC ASP.NET

I'm new to .Net development, and now are following NerdDinner tutorial. Just wondering if any of you would be able to tell me
What is the differences between ViewData
and ViewModel
(all I know is they are used to pass some form of data from controller to view) and perhaps tell me on what situation should I use ViewData instead of ViewModel and vice versa
Thanks in advance!
Sally
What is ViewData ?
dictionary object that you put data into, which then becomes
available to the view.
ViewData Sample
Controller Action method likes :
public class HomeController : Controller
{
public ActionResult Index()
{
var featuredProduct = new Product
{
Name = "Smart Phone",
QtyOnHand = 12
};
ViewData["FeaturedProduct"] = featuredProduct;
return View();
}
}
How to use ViewData on View ?
#{
var viewDataProduct = ViewData["FeaturedProduct"] as Product;
}
<div>
Today's Featured Product is!
<h3>#viewDataProduct.Name</h3>
</div>
What is a ViewModel ?
Allow you to shape multiple entities from one or more data models or
sources into a single object
Optimized for consumption and rendering by the view
Its like :
How to use ViewModel with MVC 3 ?
Domain Model
public class Product
{
public Product() { Id = Guid.NewGuid(); Created = DateTime.Now; }
public Guid Id { get; set; }
public string ProductName { get; set; }
}
ViewModel
public class ProductViewModel
{
public Guid VmId { get; set; }
[Required(ErrorMessage = "required")]
public string ProductName { get; set; }
}
Controller Action Method
[HttpGet]
public ActionResult AddProduct()
{
//for initialize viewmodel
var productViewModel = new ProductViewModel();
//assign values for viewmodel
productViewModel.ProductName = "Smart Phone";
//send viewmodel into UI (View)
return View("AddProduct", productViewModel);
}
View - AddProduct.cshtml
#model YourProject.ViewModels.ProductViewModel //set your viewmodel here
Conclusion
By using ViewModel can pass strongly-typed data into View
But ViewData is Loosely Typed.So Need to cast data on View
ViewModel can use for Complex scenarios such as merging more than one
domain model
But ViewData can be used only for simple scenarios like bring data
for the drop down list
ViewModel can use for attribute-based validation scenarios which
needed for Ui
But Cannot use ViewData for such kind of validations
As a best practices always try to use strongly typed data with
Views.ViewModel is the best candidate for that.
ViewData:
In short, use ViewData as support data, such as a datasource to a SelectList.
ViewModel:
ASP.NET MVC ViewModel Pattern
When a Controller class decides to render an HTML response back to a
client, it is responsible for
explicitly passing to the view
template all of the data needed to
render the response. View templates
should never perform any data
retrieval or application logic – and
should instead limit themselves to
only have rendering code that is
driven off of the model/data passed to
it by the controller.
[...]
When using [the "ViewModel"] pattern we create strongly-typed
classes that are optimized for our
specific view scenarios, and which
expose properties for the dynamic
values/content needed by our view
templates. Our controller classes can
then populate and pass these
view-optimized classes to our view
template to use. This enables
type-safety, compile-time checking,
and editor intellisense within view
templates.

Categories

Resources