How to access properties in the model passed to the view? - c#

I'm sending the following model to my view.
return View(new { url = returnUrl });
In the view, I'm don't want to specify any particular class for my object (since I wish to keep it flexible and elastic for now). So, the #Model is the apparently an object and as such, it's got no url property. Hence the error.
Additional information: 'object' does not contain a definition for 'url'
Now, I do know that the thing inside the object has url property. I have assigned it to it and I also see it when watching the variable as the exception's been thrown.
My question is how to access the field. Is my only option declaring a class and type the model using #model Something? I can't use as keyword to type it to var...
In "plain" C# we can do something like this.
var some = new {thing = "poof"};
string output = some.thing;
How do I do the equivalent of it in CSHTML file under Razor?

Strongly-typed view models are the way to go. Create a type that suits the needs of the view and treat reusability/duplication as a secondary concern.
However, let me explain why your attempt did not work.
It is legal to pass an anonymous type--even between assemblies[1]--as long as it is cast to object. In fact, the MVC framework assemblies consume anonymous types in many helper methods. Those anonymous types are then evaluated using reflection (optimized by caching).
1: I believe there are some caveats to this, and it certainly isn't good practice in most cases.
A view is compiled into a class so that it can be executed. Part of the class's contract is the type of model it expects, as indicated by #model in your view markup.
This presents a problem with anonymous types, as you cannot indicate their type in your view and type object doesn't expose the properties you set when declaring the type. Thus, you end up with a view that only knows that its model is an object.
Again, use strongly-typed models, or the ViewBag if you truly only need one or two values.
However, to prove that the anonymous type can be passed to the view, look at this (horrible) example:
Controller
return View( new { property1 = "hello world"} );
View
#model object
#{
var rvd = new RouteValueDictionary( Model );
}
#rvd["property1"]
We passed an anonymous type to the view as an object, and then read the object's properties using RouteValueDictionary.

You can use ViewData and ViewBag to send objects to the view page, in your case you can write in the controller something like this:
ViewData["url"] = url ; //Or whatever
return View();
Now in the view you can simply use your object example:<div>#ViewData["url"]</div>
But mainly in MVC it is more recommended to use strongly typed View Models

You may want to look into using the dynamic type in C#. See https://msdn.microsoft.com/en-us/library/dd264736.aspx for details.
While the standard would be to use a strongly-typed view model, there are some scenarios where you might want to use dynamic as your model type (or as a property of your strongly-typed view model), such as in a CMS where the properties are built dynamically by the CMS Provider.
Example view:
#model dynamic
<p>
Url: #Model.url
</p>

Related

Can you use dot notation with ViewData in Razor Pages?

I'm using Razor Pages and trying to access a property from ViewData:
#{
ViewData["Property"] = "Value";
}
I know this works:
<h1>#ViewData["Property"]
However, the app crashes when I try to access it with dot notation:
<h1>#ViewData.Property</h1>
Why does this hapen?
I know dot notation works with ViewBag since it allows me to access properties dynamically.
ViewData is object of type ViewDataDictionary which is actually IDictionary<string,object> while ViewBag is dynamic. dynamic allows to use dot notation while dictionary in C# allows to get values only via [key], so your app crashes because there is no such property Property in ViewDataDictionary even if there is such key.
The ViewData is IDictionary which means you can access data using key (ViewData[yourkey]). But your concern is also valid, we want to access property so we can reduce risk of some teammates mis type the key.
And for that reason, I always prefer to use #Model.YourProperty. Then next question, if you got a common used field for all pages such as Languague, LoginName, ... Then I suggest to have a base Model to contain all those properties. And all page model needs to inherit/implement those properties.

Returning multiples of the same model to view [duplicate]

I've been able to successfully return a model to a view and display the results in a strongly-typed fashion.
I have never seen an example where multiple models get returned. How do I go about that?
I suppose the controller would have something like this:
return View(lemondb.Messages.Where(p => p.user == tmp_username).ToList(), lemondb.Lemons.Where(p => p.acidity >= 2).ToList());
Does MVC let you return multiple models like that?
And then in the view I have this line at the top of the file:
#model IEnumerable<ElkDogTrader.Models.Message>
And I frequently make calls to "model" in the view.
#foreach (var item in Model)
If there were 2 models, how would I refer to them separately?
Is this possible with multiple models, or is this why people use ViewBag and ViewData?
You can create a custom model representing the data needed for your view.
public class UserView
{
public User User{get;set;}
public List<Messages> Messages{get;set;}
}
And then,
return View(new UserView(){ User = user, Messages = message});
In the view:
Model.User;
Model.Messages;
The ViewBag is useful because it is dynamically typed, so you can reference members in it directly without casting. You do, however, then lose static type checking at compile time.
ViewData can be useful if you have a one-off on your view data types and know the type and will be doing a cast in the view anyway. Some people like to keep the actual typed view pure in a sense that it represents the primary model only, others like to take advantage of the type checking at compile time and therefore make custom models needed for the view.
I believe ViewModel should be the way to go. Within the customary ViewModel, you can reference other models or define all the related domain models in the viewModel itself.

How do you put a ViewBag item into a Text Box?

I have the following code and I get an error saying:
has no applicable method named 'TextBoxFor' but appears to have an extension method by that name.
My Code:
#Html.TextBoxFor(ViewBag.taglist)
Why don't you use strongly typed model in your view instead of ViewBag. This will make your life easier.
In fact, you must use a model to with TextBoxFor, otherwise it just won't work. See the definition of TextBoxFor - as a second parameter it takes a lambda expression that takes a property form a model.
If you want just a text box, two options:
#Html.TextBox("NameOfTheTextbox", (String)ViewBag.SomeValue)
or just go
<input type="text" value="#ViewBag.SomeValue" />
No complex solutions required.
I agree with other suggestions of using a strongly-typed model, because the compile-time error support is so much better than debugging exceptions. Having said that, in order to do what you want, you can use this:
#Html.TextBox("NameOfTextBox", (string)ViewBag.taglist)
Update: A Simple Example
Now that you've provided some details in your comments, I've taken a guess at what you might be doing, in order to provide a simple example.
I'm assuming you have a list of tags (like SO has per question) that you'd like to display neatly in a textbox, with each tag separated by a space. I'm going to assume your Tag domain model looks something like this:
public class Tag
{
public int Id { get; set; }
public string Description { get; set; }
}
Now, your view will need a list of the tags but will likely need some other information to be displayed as well. However, let's just focus on the tags. Below is a view model to represent all the tags, taking into account that you want to display them as a string inside a textbox:
public class SomeViewModel
{
public string Tags { get; set; }
// Other properties
}
In order to get the data you want you could grab all of the tags like this:
public ActionResult Index()
{
using (YourContext db = new YourContext())
{
var model = new SomeViewModel();
model.Tags = string.Join(" ", db.Tags.Select(t => t.Description).ToList());
return View(model);
}
}
Notice how I'm directly passing model to the view.
The view is now very simple:
#model SomeViewModel
#Html.EditorFor(m => m.Tags)
The model directive is what signifies that a view is strongly-typed. That means this view will expect to receive an instance of SomeViewModel. As you can see from my action code above, we will be providing this view the type that it wants. This now allows us to make use of the strongly-typed HtmlHelper (i.e. Html.XxxFor) methods.
In this particular case, I've used Html.EditorFor, as it will choose an appropriate input element to render the data with. (In this case, because Description is a string, it will render a textbox.)
You cannot use Html.TextBoxFor without explicitly setting a type for your model within the view. If you don't specify a type it defaults to dynamic. If you want to do model binding then you must use an explicit type rather than a dynamic type like ViewBag. To use Html.TextBoxFor you must define a model type that defines the property that you wish to bind. Otherwise you have to use Html.TextBox and set the value manually from ViewBag. As others have said, you will make your life much easier if you use a statically typed model and take advantage of the inbuilt MVC model binding.
You have to use a lambda expression to select the property, plus you will have to cast the ViewBag member to the correct type.
#Html.TextBoxFor(model => (string)ViewBag.taglist)

Accessing Controller variable in View MVC

I have started to use MVC and i have set up a simple project that has a controller than returns a value from entity FrameWork. I have a controller for the index page which visual studio setup as default in its template.
I am hoping that this value will be returned to the index page of the website.
Controller code
MISEntities db = new MISEntities();
public ActionResult Index()
{
ViewBag.Message = "Real Time Production";
var scrap = from r in db.tbl_dppIT
select r.CastGood;
return View(scrap);
}
How do i access the var scrap using razor?
I have looked at the viewbag method and other razor syntax but cant seem to find how i access this item.
Just because the controller variable is declared using the var keyword doesn't mean it doesn't have a type. Rather, this tells C# that the type should be inferred by its context (in this case, an IEnumerable<T> of whatever type tbl_dppIT.CastGood is).
So, the other three answers are partly correct, you need to define the type of model being passed into the View via the razor keyword #model as noted by the other answers:
// In your view Razor:
#model IEnumerable</* type of CastGood */>
There is an alternative to the three answers already specified, and that is sticking the scrap variable into the ViewBag in your controller:
ViewBag.Scrap = scrap;
When you access the expando property Scrap from the ViewBag in the view, it will be a dynamic object, so you won't get the aid of IntelliSense. But, it is another way, just so that you know all the possibilities.
UPDATE:
Based on your comments below, it looks like if CastGood is a database column that is allowed to be null and whose type is int, then you'd want:
#model IEnumerable<int?>
HTH.
You need to declare the type of the variable scrap as the model inside the View using the #model keyword.
// View.cshtml
#model IEnumerable<Namespace.ScrapType>
#foreach(var item in Model) {
<p>#item.SomeProperty</p>
}
See Part 3: Views and ViewModels of the MVC Music Store example.
You need to strongly type your view and it will available as the Model instance.
#model IEumerable<MyType>
#foreach(var item in Model)
{
#*do something with it *#
}

How to reference implicitly typed model in view

I'm learning MVC and I'm trying to create an implicitly typed variable for my model but not sure how to reference it in the view #model IEnumerable<???>
Controller...
public ActionResult Users() {
var model = from MembershipUser u in Membership.GetAllUsers()
select new {user = u, roles = string.Join(", ", Roles.GetRolesForUser(u.UserName))};
View(model);
}
You can't reference an anonymous-typed variable in the view (or anywhere outside its local scope). It's possible to use a dynamic type:
#model IEnumerable<dynamic>
But this way you don't have Intellisense/compile-time type checking; the best approach would be to just create a class for your model.
That's an anonymous type you're returning. I believe you'll want to use an actual type. If you don't have one for what you're selecting in your query, you'll want to create one.
Once you have your type, here's an example of using it in the view.
This is the first line in my view:
#model IEnumerable<AutoTrackerCommon.Entities.TrackerJob>
And I'm using it in a WebGrid:
#{
var grid = new #WebGrid(
source: Model,
rowsPerPage: 10);
}
You reference it as Model, but why would you do such a thing?
Use ViewModels, don't go with untyped or implicitly typed models. Its a nightmare to work with: you lose everything from IDE support to automatic validation. Don't complicate your life unnecessarily. View Models (or strongly typed views) are the way to go.
Good article to read about this: View Models in ASP.NET MVC.
Hope this is of help to you & good luck

Categories