Trying to force text in certain fields in a view that is adding records to the DB via Entity framework to uppercase.
Can I do it in the view EditorFor or can I do it in the controller to all fields easily before firing db.SaveChanges()?
You could loop through the properties on your ViewModel server side using a helper method.
public T ViewModelToUpper<T>(T viewModel) where T : class
{
foreach (var property in viewModel.GetType().GetProperties())
{
var value = property.GetValue(viewModel, null);
if (value is string)
{
property.SetValue(viewModel, value.ToString().ToUpper());
}
}
return viewModel;
}
Then you can call viewModel = ClassName.ViewModelToUpper(viewModel). Now you don't have to worry about doing it for every string property as this will happen anyway.
You can do this in the UI with jQuery if you'd like. It's a little weird from a user perspective to be typing and have everything converted to uppercases (I'd check my caps lock). Convert to uppercase as user types using javascript
Your other option, better IMO, is to do this in the controller. You can use ToUpper()on the strings. Keep Globalization in mind.
Related
I am working on an asp.net mvc 5.2.2. web application using Razor view engine. I am trying to modify the default Html.DisplayFor to show substring of 100 character followed by "..." on certain views ? so is this possible to achieved ?
Thanks
Html.DisplayFor is a templated helper. MVC has some default templates built-in for the most common C# types. For a string, it's pretty much just dumps the string value as-is, though.
You can override the built-ins by adding your own display templates to Views\Shared\DisplayTemplates\. The views you add need to be named after one of three things:
A type (either standard C# types or you own classes), e.g. String.cshtml, DateTime.cshtml, Boolean.cshtml, etc.
A member of the DataType enum, e.g. EmailAddress.cshtml, ImageUrl.cshtml, etc. (this requires that the property be decorated with the appropriate DataType attribute)
Virtual anything you want in conjunction with decorating the property with the UIHint attribute. For example [UIHint("Foo")] would use a display template named Foo.cshtml.
In your case, you could use String.cshtml with contents of something like:
#model String
#Model.Substring(0, 100)…
However, I don't think you really want this to apply to every string rendered on your site, everywhere. Using UIHint with a custom display template is probably a better choices here. Create a display template named something like TruncatedString.cshtml and then decorate any properties you want displayed truncated like this with [UIHint("TruncatedString")].
That's only if you insist on using Html.DisplayFor. Really, it would probably be better to add a string extension like:
public static class StringExtensions
{
public static string Truncate(this string s, int length, string suffix = "...", bool html = false)
{
s = s ?? string.Empty;
s = System.Net.WebUtility.HtmlDecode(s);
if (s.Length > length)
{
s = s.Substring(0, length + 1);
s = s.Substring(0, Math.Min(s.Length, s.LastIndexOf(" ") == -1 ? 0 : s.LastIndexOf(" ")));
s = s + suffix;
}
if (html)
{
return System.Net.WebUtility.HtmlEncode(s.Trim());
}
else
{
return s.Trim();
}
}
}
And then, you can just do the following in your view:
#Model.SomeLongString.Truncate(100)
You can achieve that using Editor Templates and UIHint's:
http://www.codeguru.com/csharp/.net/net_asp/mvc/using-display-templates-and-editor-templates-in-asp.net-mvc.htm
http://www.hanselman.com/blog/ASPNETMVCDisplayTemplateAndEditorTemplatesForEntityFrameworkDbGeographySpatialTypes.aspx
Personally my preference would be to create another read-only property in the model and have that return a truncated value. Probably using the truncate extension mentioned here.
you can user the next sentence:
#Html.DisplayFor(Function(modelItem) item.name_field).ToString().Substring(1, n)
#Html.DisplayFor(modelItem => item.CreateDate).ToString().Substring(0,10)
I have a view model like this:
public class EditVM
{
public Media.Domain.Entities.Movie Movie { get; set; }
public IEnumerable<Genre> Genres { get; set; }
}
Movie is the real entity I wish to edit. Genres is simply present to populate a drop down. I would prefer that when I call:
#Html.TextBoxFor(m => m.Movie.Title)
inside my strongly typed view that the input control have a name = "Title" instead of "Movie.Title"
I do not wish to split my view into partial views or lose my strongly typed view by using ViewData or the like.
Is there a way to express to the View that I do not wish to have the Movie. prefix? I noticed that you can set:
ViewData.TemplateInfo.HtmlFieldPrefix = "x";
in the controller, but unfortunately it seems only to allow adding an additional prefix. Setting it to "" does nothing.
Is there any work around for this? Or am I stuck with the unfortunate prefix that isn't really necessary in this case if I wish to keep strongly typed views and lambdas?
Thanks for any help.
Update:
Here's the controller actions to maybe make things a bit clearer.
public ActionResult Edit(int? id)
{
var vm = new EditVM
{
Movie = id.HasValue ? _movieSvc.Find(id.Value) : new Movie(),
Genres = AppData.ListGenres()
};
return View(vm);
}
[HttpPost]
public void Edit([Bind(Prefix = "Movie")]Movie m)
{
_movieSvc.AddOrUpdateMovie(m); //Exceptions handled elsewhere
}
No, in order to do what you want you would have to rewrite the Html helpers, and then you would have to write your own model binder. Seems like a lot of work for minimal gain.
The only choice is a Partial view in which you pass the Movie object as the model. However, this would require you to write your own model binder to have it be recognized.
The reason you have to do m.Movie.Title is so that the ID has the correct name, so the model binder can recognize it as a member of your model.
Based on your update:
Your options are:
Use non-strongly typed helpers.
Use a partial view.
Rewrite the stronly typed helpers
Don't use the helpers at all, and write the values to the HTML
Personally, i'd just use 1 or 2, probably 2.
EDIT:
Based on your update above. Change your code to this (note, Genres does not get posted back to the server, so m.Genres will just be null on postback):
[HttpPost]
public void Edit(EditVM m)
{
_movieSvc.AddOrUpdateMovie(m.Movie); //Exceptions handled elsewhere
}
EDIT:
I did just think of an alternative to this. You could simply do this:
#{ var Movie = Model.Movie; }
#Html.TextBoxFor(m => Movie.Title)
However, if there was a validation error, you would have to recreate your EditVM.
I have a view model like this
I think that you might have some misunderstanding about what a view model is. A view model shouldn't contain any reference to your domain models which is what those Movie and Genre classes seem to be. I mean creating a new class that you suffix with VM and in which you stuff all your domain models as properties is not really a view model. A view model is a class that is specifically designed to meet the requirements of your view.
A much more correct view model would looks like this:
public class EditVM
{
public string MovieTitle { get; set; }
public IEnumerable<GenreViewModel> Genres { get; set; }
}
and in your view you would have:
#Html.EditorFor(x => x.MovieTitle)
#Html.EditorFor(x => x.Genres)
Another option is to either use the TextBox(string name, object value) overload instead of the TextBoxFor:
#Html.TextBox("Title", Model.Movie.Title)
You could also specify the input tag HTML instead of using a helper.
Another option is to take EditVM as your postback parameter. This is what I would do. My post action parameter is always the same type of the .cshtml model. Yes there will be properties like lists that are null, but you just ignore those. It also allows you to gracefully handle post errors as well because if there is an error you'll need to return an instance of that view model anyhow, and have the values they submitted included. I usually have private methods or DB layer that handles retrieving the various lists that go into the ViewModel, since those will be empty on postback and will need to be repopulated, while not touching the properties that were in the post.
With your post method as it is now, if you need to return the same view, you've gotta create a new EditVM and then copy any posted values into it, and still populate the lists. With my method, you eliminate one of those mapping steps. If you are posting more than one thing, are you going to have umpteen different parameters on your post action? Just let them all come naturally into a single parameter typed to the EditVM of the View. While maybe having those null properties in the VM during the postback feels icky, you get a nice predictable consistency between View and postback IMO. You don't have to spend alot of time thinking about what combination of parameters on your post method will get you all the pieces of data from the form.
I'm trying to display a view model using an editor template that wraps the model in a fieldset before applying a base Object editor template.
My view:
#model Mvc3VanillaApplication.Models.ContactModel
#using (Html.BeginForm())
{
#Html.EditorForModel("Fieldset")
}
Uses a fieldset template (Views/Shared/EditorTemplates/Fieldset.cshtml):
<fieldset>
<legend>#ViewData.ModelMetadata.DisplayName</legend>
#Html.EditorForModel()
</fieldset>
Which in turn uses a basic template for all objects (Views/Shared/EditorTemplates/Object.cshtml):
#foreach (var prop in ViewData.ModelMetadata.Properties.Where(x =>
x.ShowForEdit && !x.IsComplexType && !ViewData.TemplateInfo.Visited(x)))
{
#Html.Label(prop.PropertyName, prop.DisplayName)
#Html.Editor(prop.PropertyName)
}
That's my intent anyway. The problem is that while the page renders with a fieldset and a legend, the Object template isn't applied so no input controls are displayed.
If I change the view to not specify the "Fieldset" template then my model's properties are rendered using the Object template, so it's not that my Object template can't be found.
Is it possible to pass the same model through multiple templates?
For what it's worth, the view model looks like this:
namespace Mvc3VanillaApplication.Models
{
[System.ComponentModel.DisplayName("Contact Info")]
public class ContactModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
I implemented what you have, and was able to reproduce it. I set a break point in Object.cshtml so I could inspect it and I was caught off guard to realize that it wasn't even hitting the object template when the fieldset template was being used. Then I stepped through the fieldset template and saw it was calling the template just fine, so something must be happening in the code which prevents it from displaying the object template.
I opened up the MVC3 source code, searched for EditorForModel and found the correct function.
public static MvcHtmlString EditorForModel(this HtmlHelper html) {
return MvcHtmlString.Create(TemplateHelpers.TemplateHelper(html, html.ViewData.ModelMetadata, String.Empty, null /* templateName */, DataBoundControlMode.Edit, null /* additionalViewData */));
}
Obviously this wasn't it, so I pressed F12 on TemplateHelpers.TemplateHelper, and once there again I pressed F12 on single line call which brings you to the meat of the function. Here I found this short bit of code starting on line 214 of TemplateHelpers.cs:
// Normally this shouldn't happen, unless someone writes their own custom Object templates which
// don't check to make sure that the object hasn't already been displayed
object visitedObjectsKey = metadata.Model ?? metadata.RealModelType;
if (html.ViewDataContainer.ViewData.TemplateInfo.VisitedObjects.Contains(visitedObjectsKey)) { // DDB #224750
return String.Empty;
}
Those comments are actually in the code, and here we have the answer to your question: Can one model be passed through multiple editor templates?, the answer is no*.
That being said, this seems like a very reasonable use case for such a feature, so finding an alternative is probably worth the effort. I suspected a templated razor delegate would solve this wrapping functionality, so I tried it out.
#{
Func<dynamic, object> fieldset = #<fieldset><legend>#ViewData.ModelMetadata.DisplayName</legend>#Html.EditorForModel()</fieldset>;
}
#using (Html.BeginForm())
{
//#Html.EditorForModel("Fieldset")
//#Html.EditorForModel()
#fieldset(Model)
}
And viola! It worked! I'll leave it up to you to implement this as an extension (and much more reusable) method. Here is a short blog post about templated razor delegates.
* Technically you could rewrite this function and compile your own version of MVC3, but it's probably more trouble than it's worth. We tried to do this on the careers project when we found out that the Html.ActionLink function is quite slow when you have a few hundred routes defined. There is a signing issue with the rest of the libraries which we decided was not worth our time to work through now and maintain for future releases of MVC.
In first cshtml template we can recreate ViewData.TemplateInfo (and clear VisitedObjects list)
var templateInfo = ViewData.TemplateInfo;
ViewData.TemplateInfo = new TemplateInfo
{
HtmlFieldPrefix = templateInfo.HtmlFieldPrefix,
FormattedModelValue = templateInfo.FormattedModelValue
};
now we can call another template with same model
#Html.DisplayForModel("SecondTemplate")
I am working on an ASP.NET MVC based CMS that presents a rather extreme case. The system must allow the user to add custom content types based on different fields, and for every field, one can add options and validations. The thing is that everything is stored in a complex DB and extracted at runtime using LINQ.
I am pretty fresh with ASP>NET MVC so the following dilemma came to mind
How should I make the content creation view so that form helpers are not predefined int he view code but are loaded based on the type of the field ? Do I have to create a factory class that checks the value of the type property of the field, and then returns a helper based on that or there's a better way to do it. This one seems pretty rigid to me , because anytime I make a change in the Fieldtypes table, I will have to make sure to create a check for that new type too.
public class CType {
string Name; //e.g Post Article etc
List<ContentData> data ;
...
}
public class ContentData {
string Data; // Basically this is the data stored for each field
FieldInstance fieldInstance;
...
}
public class FieldInstance {
string Title; // e.g Title Body etc.
FieldType Type ; // e.g textbox textarea image checkbox etc
...
}
public class FieldType {
string Type; // e.g textbox textarea image checkbox etc
...
}
I see an HTML Helper in your future. The HTML Helper can work through your Model at runtime and output the appropriate HTML for the View. If you go that route, I suggest you get to know the StringBuilder and TagBuilder classes. They'll simplify things and help make your HTML Helper much more readable.
I did not know about the concept of templated helpers. This is what happens when you're new to something. Pretty much, this is what fixed my problem
http://msdn.microsoft.com/en-us/library/ee308450%28VS.100,printer%29.aspx
I have settled on trying to use ASP.NET MVC but the first part I want to replace is the Model. I am using LLBL Pro for the model.
I have a table called "Groups" that is a simple look up table. I want to take thhe results of the table and populate a list in MVC. Something that should be very simple... or so I thought.... I've tried all kinds of things as I was getting errors like:
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[glossary.EntityClasses.GroupEntity]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[glossary.CollectionClasses.GroupCollection]'.
private GroupCollection gc = new GroupCollection();
public ActionResult Index()
{
gc.GetMulti(null);
return View( gc.?????? );
}
This is all I am trying to do, I've tried lots of variations, but my goal is simply to take the data and display it.
Not sure if this would work, but you could try wrapping the EntityCollection into a ViewModel class and passing it to the View like so:
public class GroupsViewModel()
{
public GroupCollection Groups { get; set; }
// other items in your view model could go here
}
then convert your controller method to
public ActionResult Index()
{
GroupCollection gc = new GroupCollection();
gc.GetMulti(null);
GroupsViewModel vm = new GroupsViewModel();
vm.Groups = gc;
return View(vm);
}
I like this approach because each ViewModel is an object in-and-of itself.
You can use the AsEnumerable extension where your ????? are or change the type of your ViewUserControl(in the markup) to be of type System.Collections.Generic.List. Basically what you need to correct is the mismatch between the type of the View and the Model being passed in.
I'm not sure about your exact error, but I'd venture a guess that one of two things are happenging:
You are making some sort of invalid / illegal call on your LLBLGen object. If this is the case make sure you are setting it up right / calling right method / property etc.
The model you are passing to the veiw is too hairy for it to deal with. In this case, and in general, you should create a light 'View Model' class with just the data you want displayed and populate it from your LLBLGen object first then pass it to the view, which will be able to easily handle your view model class.
Here are some references:
http://stephenwalther.com/blog/archive/2009/04/13/asp.net-mvc-tip-50-ndash-create-view-models.aspx
http://nerddinnerbook.s3.amazonaws.com/Part6.htm
http://www.codinginstinct.com/2008/10/view-model-inheritance.html
Stemming off what Yuriy said, it looks like your view is strongly typed to a "collection" of a collection of your groupentity, and you are trying to pass just the collection of your groupentities. Make sure your "collection" type (IEnumerable, IList, etc) matches what type of collection you are sending in your controller, along with the type of the actual object in the collection.
View:
System.Collections.Generic.List1[glossary.EntityClasses.GroupEntity]
Controller:
System.Collections.Generic.List1[glossary.EntityClasses.GroupEntity]
Just a thought