How to put TempData Array in View - c#

I'm trying to post links to View but I need help, first I created an string array for this.
string[] publicPath = new string[] { "/images/" + fileName };
TempData["link"] = publicPath;
ViewBag.link = TempData["link"];
After that this is my .cshtml;
#foreach (var item in ViewBag.link)
{
<textarea cols="102" rows="5" disabled="disabled" style="resize:none;">HELP!</textarea><br />
}
I have no problem putting the variable into TempData, but I could not understand how to use it in View. Thank you in advance, best regards ...

There's no info here about your actual issue, but I'd imagine it's down to working with dynamics. ViewBag is what's referred to as a dynamic. This allows you to just arbitrarily set a property like ViewBag.link, without having to explicitly define a property, but it also means that ViewBag.link doesn't have a known type. As such, you can't just use it in something like a foreach because you must provide an enumerable in that context, and the compiler has no idea if ViewBag.link is an enumerable type or not.
You're going to have to cast it, first, to use it.
#foreach (var item in (string[])ViewBag.link)
Be careful with this, though. Here, you know it's a string[], but if you change the type of what you're stuffing into ViewBag.link, this code here won't complain. It will blow up during runtime, of course, with an InvalidCastException, but you won't know that until it blows up. In general, you should avoid using dynamics, like ViewBag, for this reason. Instead, use a view model class and strongly type your view with that as its model. Then, you don't have to worry about casting, and you'll get all the standard intellisense and compile-time checking you'd expect.

Related

ASP.NET MVC not finding Display Template partial view

I'm not sure what I'm doing wrong here. The default display template for a model I'm using is not being used.
This code is in my main action view:
#if (Model.EmbeddedMediaModels != null)
{
foreach (var mediaItem in Model.EmbeddedMediaModels)
{
BitmapFigureModel bitmap = mediaItem as BitmapFigureModel;
if (bitmap != null)
{
var mm = ModelMetadata.FromLambdaExpression(p => bitmap, this.ViewData);
var modelTypeName = mm.ModelType.Name; // = "BitmapFigureModel"
// Neither resolve the template.
// Html.DisplayFor(m => bitmap);
Html.DisplayFor(m => bitmap, modelTypeName);
}
}
}
The Model.EmbeddedMediaModels property is a collection of EmbeddedMediaModel base types, at present it just contains one object, a BitmapFigureModel which derives from EmbeddedMediaModel.
It's tempting to think that this is confusing matters, but the ModelMetadata instance retrieved is quite able to see the correct BitmapFigureModel model type.
Besides, even if I specify the model type name in the call to DisplayFor it still doesn't work.
And here's proof that a correctly-named display template partial view is in place.
What am I doing wrong?
Contrary to the advice from Brad Wilson (ASP.NET team):
The expression-based versions are primarily used for pulling values
from the model (they are parametrized by the current model, as shown
in the example above). They can also be used for pulling values from
some source other than the model or ViewData (for example, with an
expression like “model => someOtherValue” which ignores the model
entirely). This makes them useful in loops.
http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-1-introduction.html
It actually seems that its not possible to "ignore the model entirely". In the comments under my question DaveParsons suggests to experiment by just newing-up a model instance and passing it into DisplayFor, this leads to the error:
Templates can be used only with field access, property access,
single-dimension array index, or single-parameter custom indexer
expressions.
So it appears that I should stop being a smarty-pants and just use Html.Partial as Ehsan Sajjad suggests.
do like this:
#Html.DisplayFor(m => mediaItem.Name)
if you want to load the partial view:
#Html.Partial("~/Views/Shared/DisplayTemplates/BitmapFigureModel.cshtml", mediaItem)
or:
#Html.RenderPartial("~/Views/Shared/DisplayTemplates/BitmapFigureModel.cshtml", mediaItem)
In your BitmapFigureModel.cshtml:
#model BitmapFigureModel

In an asp.net Razor view, is there anyway to simply continue on error?

So, vb has the much discussed "on error resume next", right now I really need something like that in a C# razor view.
It probably doesn't exists, buy maybe there is another way to accomplish something similar?
I'm trying to create a simple Razor View that is a read only page just for printing. However this view will have a LOT of fields, several hundred rows like this:
<td class="label">Sales Rep #1:</td>
<td class="val">#Model.salesRep1.name</td>
The problem is that salesRep might be null, and this will throw an error. The amount of code on the page will drastically increase if I have wrap each field in an #{if(Model.salesRep1.firstName != null){}} statement.
It would be super slick to just add on error resume next to the top of the page and not worry about the nulls. Is there any similar solution?
Thanks,
View's should only contain view logic, and even that should only extend to outputting properties from the ViewModel.
The controller is often responsible for building up this viewmodel, and it is at that point that you should default any properties where you want to ensure that they are not null when trying to display them.
So for example, in a controller you might have:
var emptySalesRep = new SalesRep();
var model = new MyModel();
model.salesRep1 = repository.GetSalesRep1() ?? emptySalesRep;
Now, it might be that you have a lot of this, so you might choose to instead leave salesRep1 null, and have another way of dealing with this in the View without resporting to lots of if(Model.salesRep1 != null){ ... }.
An extension method could be used (although I prefer the approach above)
public static string DisplayForWhenNotNull<T>(this HtmlHelper html, Func<T> obj, Func<T,object> prop)
{
var item = obj();
if(item == null)
{
return null;
}
return prop(item);
}
Usage:
<td class="label">Sales Rep #1:</td>
<td class="val">#Html.DisplayForWhenNotNull(() => Model.salesRep1, sr => sr.name)</td>
Instead of checking for nulls in the view, or try to ignore errors, I'd use a viewmodel and make sure that salesRep isn't null in there.

Generic use of DropDownListFor with reflections

I've always used DropDownListFor like this
Html.DropDownListFor(m => m.PropertyOfTheModel, SelectionList)
this works fine as long as you know the exact name of the property you're trying to build a dropdown list on (in this case: PropertyOfTheModel).
Now I have a different task. My model contains a fixed property, declared as object and called FormModel. Using reflections assume I'd like to build a dropdown for everyone of the properties contained in FormModel. Thanks to the attributes I've managed to solve the SelectionList part, I now have to write the first argument but I have no idea on how to do it.
foreach (var property in Model.FormModel.GetType().GetProperties())
{
#Html.DropDownListFor(m => m.FormModel.GetType().GetProperty(property.Name), SelectList)
}
The code above is not working: how should I write it? I have no experience in writing lambda expressions: is there any alternative to generate a dropdownlist which automagically gets the correct selected attribute just by passing in the property and the selection list? Or do I have to write the expression? Thanks!
this is the error I get:
Templates can be used only with field access, property access,
single-dimension array index, or single-parameter custom indexer
expressions.
I haven't had time to try this out yet, but the stumbling block may be that the code is trying to bind PropertyInfo, not the actual property on the class. I would give this a shot:
foreach (var property in Model.FormModel.GetType().GetProperties())
{
#Html.DropDownListFor(m => property.Name, SelectList)
}
I think it is as simple as this:
#Html.DropDownList(property.Name, SelectList)
Don't use the DropDownListFor helper, but DropDownList instead.

(PartialView) The model item passed into the dictionary is of type 'Customer', but this dictionary requires a model item of type 'UserProfile'

#model Customer
#Html.Partial("_UserProfile", (UserProfile)Model.UserProfile)
When i run this code, i get this error:
The model item passed into the dictionary is of type 'Customer', but this dictionary requires a model item of type 'UserProfile'.
Partial View _UserProfile is strongly typed.
I want to be able to edit these field.
Any suggestions?
Make sure your Model.UserProfile is not null.
I found your post trying to debug the same error, and it turned out I hadn't initialised my "Model.UserProfile" equivalent.
I guess what's happening here, is that if a null model is passed to RenderPartial, it defaults to using the main view's model? Can anyone confirm this?
If Model.UserProfile is null, it will attempt to pass in your customer model.
Two ways to get around this:
#model Customer
#Html.Partial("_UserProfile", (UserProfile)Model.UserProfile, new ViewDataDictionary())
Or:
#model Customer
if (Model.UserProfile != null)
{
#Html.Partial("_UserProfile", (UserProfile)Model.UserProfile)
}
I ran into this problem when dealing with parts of a user profile such as Name and Address records. If the user had an incomplete profile I want the account management view to detect a null Address record and display an Action link to create a new Address or display whatever address data is available.
As described by others when null is passed the overload for Html.RenderPartial gets triggered and the parent View Model is passed through. I ended up converting my partial views to Display and Editor Templates to get around it. Here are some How-To articles from: Hansleman and codeguru
You get better re-usability from this method and it preserves the null values:
In your View:
#Html.DisplayFor( m=> m.Address)
Then handle the null value in the DisplayTemplate.
#model Namespace.Models.MyObject
...
if(#Model != null){
...
}else{
...
}
I have faced the same problem but finally I had figured it out.
There is a type mismatch in passed models .. Your View accepts model of type Customer but you partial view is passing the model Userprofile so what you have to do is pass the same model in both or.... create a model that have all properties of both models. Surely your problem will be solved.
It will fallback on initial model if passed item is null.
Try this:
#Html.Partial("_UserProfile", (UserProfile)Model.UserProfile ?? new UserProfile())
Your trying to case a Customer type object to a UserProfile type object. By default this wont work as the framework has no idea how to cast these objects. If you absolutely must do it this way the only option is to provide explicit cast operator like:
public static explicit operator Digit(byte b) // explicit byte to digit conversion operator
{
Digit d = new Digit(b); // explicit conversion
System.Console.WriteLine("Conversion occurred.");
return d;
}
You can read more about it here.
Add the keyword "virtual" to the UserProfile property on the Customer model.
It is the easyest way overcome the lazy loading, but performance..

MVC Strongly typed IQueryable<IGrouping<TKey, TElement>> model

I'm trying to create a strongly typed model for one of my Views in MVC. The model is the result of a LINQ GroupBy query so it is the type shown below (grouping employees by first letter of surname).
#model IQueryable<IGrouping<string, Employee>>
I'm unsure why but it doesn't let me have a model of this type. The error message I get is:
An opening "<" is missing the corresponding closing ">". Which is incorrect.
I know I can create a view specific model and populate that instead but I'd like to know why this model doesn't seem to work?
By default, a very limited set of namespaces are available for direct use in razor views. Try to expand it to fully qualified names and see if the problem persists:
#model System.Linq.IQueryable<System.Linq.IGrouping<string, Name.Space.Employee>>
I don't know why you'd be getting this error, since you appear to be using correct Razor code. It's possible that there's actually a bug elsewhere in the page that is being made manifest through this incorrect error message.
A workaround, which may help you determine the real source of the bug, would be to create your own strongly-typed model class, which could have this data as its property:
public class EmployeeListViewModel
{
public IQueryable<IGrouping<string, Employee>> EmployeesByCompanyTitle {get;set;}
}
(There are those who would argue that this is a better approach anyway, since you can now add information to your view model more easily.)

Categories

Resources