I have build a searchmodel with a searchstring and decorated it with minlength. In my View I would like to display the requierements for the search string but how can I get to the decoration?
Model:
public class SearchModel
{
[StringLength(50,MinimumLength = 4)]
public string Searchname { get; set; }
}
Razor:
#model Project.Models.SearchModel
<p>
The search value has to be a min length of: ...
</p>
You could do this:
#(typeof(SearchModel).GetProperty("Searchname").GetCustomAttributes(true)
.OfType<StringLengthAttribute>().First().MinimumLength)
Though for the sake of MVC purity, you should probably avoid putting this logic into the view code. Either:
Make your Controller get this information and put it onto the Model itself as a separate property, or
Use a custom ModelMetadataProvider to make the information available via the model metadata.
You can get this value from validator attribute in client side.
$('#Searchname').attr('data-val-length-min')
Or you need this in server side in razor?
#{
var attr = typeof(NAMESPACE.SearchModel).GetProperty("Searchname").GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.StringLengthAttribute), true)[0];
var min = attr.GetType().GetProperty("MinimumLength").GetValue(attr, null);
}
.
.
.
<p>The search value has to be a min length of: #min</p>
Related
I use asp.net mvc 5 and EF 6 .I have following code in my controller
var resultOne = stats.GetPlayerStatsByParam(year, modeOne); //first
ViewData["more"] = stats.GetTeamStatsID(year); //second
return View("StatsNew", resultOne.ToList());
I am able to display result in view using "resultOne". Now I want to pass another data to same view using ViewData[]. its a stored procedure and the result is paasing to "ViewData["more"]".
I have done following code for "ViewData["more"]" in View page
But I am getting an error saying that 'object reference not set'
#foreach (var item in ViewData["more"] as #List<demo.GetTeamStatsID_Result>)
{
#item.Pld;
}
Use a viewmodel and extend it by the data coming from stats.GetTeamStatsID(year);
Do not use ViewBag or ViewData if not necessary for some reason (which I canĀ“t imagine right now)
As the comments have already pointed out, build a ViewModel class, that wraps everthing you need:
//I am using fake types here, sinceI don't know your model classes
//substitute accordingly!
public class MyViewModel
{
public PlayerStatsType PlayerStats { get; set;}
public List<demo.GetTeamStatsID_Result> Teams { get; set;}
}
Then in your action method:
var vm = new MyViewModel();
vm.PlayerStats = stats.GetPlayerStatsByParam(year, modeOne); //first
vm.TeamId = stats.GetTeamStatsID(year); //second
return View("StatsNew", vm);
Amend the model declaration in your view:
#model Namespace.Models.MyViewModel //again use namespace for your vm class
Now you can access both properties from your model:
#foreach (var item in Model.Teams)
{
#item.Pld;
}
I'm using ASP.NET MVC3.
When an user create an account, I need the chosen nickname be unique, so I use the Remote DataAnnotation like this :
public class UserModel
{
[Required]
[Remote("CheckNickname", "Validation", ErrorMessage = "This nickname is already used")]
public string Nickname { get; set; }
// ...
}
I used it in a strongly-typed view, via #Html.TextBoxFor(m => m.Nickname) and it perfeclty works.
However, I created another model with the exact same property.
public class MyOtherModel
{
// ...
[Required]
[Remote("CheckNickname", "Validation", ErrorMessage = "This nickname is already used")]
public string Nickname { get; set; }
}
I used this MyOtherModel.Nickname on a strongly-typed view via :
#Html.TextBoxFor(m => m.MyOtherModel.Nickname)
However, in this case only, the data passed to my CheckNickame() method is always null.
There are only two differences :
In the second case, the property I want to remotely validate is contained in another model (is it a problem ? I don't think so...)
In the second case, the property is displayed inside a modal bootstrap (is it a problem ?)
For information, this is what my CheckNickname() looks like :
public JsonResult CheckNickname(string nickname)
{
UserDAL userDAL = new UserDAL();
bool userIsAvailable = !userDAL.IsUserAlreadyInUse(nickname);
return Json(userIsAvailable, JsonRequestBehavior.AllowGet);
}
As I wrote it before, in the second case only, the parameter nickname is always null whereas it works as expected in the first case.
Is anyone knows why ?
Any help is appreciated.
UPDATE :
I created this method :
public JsonResult CheckNickname2([Bind(Prefix = "MyOtherModel")]string nickname)
{
UserDAL userDAL = new UserDAL();
bool userIsAvailable = !userDAL.IsUserAlreadyInUse(nickname);
return Json(userIsAvailable, JsonRequestBehavior.AllowGet);
}
The call is now :
http://mysite/Validation/CheckNickname2?MyOtherModel.Nickname=Alex
but if I put a breakpoint on CheckNickname2, the nickname paremeter is still null !
However, the call on the working validaton method is :
http://mysite/Validation/CheckNickname?Nickname=Alex
and this one works...
SOLUTION:
Ok, solved by changing [Bind(Prefix = "MyOtherModel")] to [Bind(Prefix = "MyOtherModel.Nickname")] as suggested by Stephen Muecke
In your second example, the html generated will be name="MyOtherModel.Nickname" so the key/value pair posted back will be MyOtherModel.Nickname:yourValue. Change the controller method to
public JsonResult CheckNickname([Bind(Prefix="MyOtherModel.Nickname")]string nickname)
which will effectively strip the prefix and bind correctly to parameter nickname
Note also that the modal usage may be a problem if this is adding dynamic content after the initial page has been rendered (in which case you need to re-parse the validator)
In my MVC 5 app I need to be able to dynamically construct a list of fully qualified external URL hyperlinks, alone with some additional data, which will come from the Model passed in. I figure - I will need to construct my anchor tags something like this:
{{linkDisplayName}}
with AngularJS this would be natural, but, I have no idea how this is done in MVC.
Is there a templating library that can be used for this?
1) Create a model to Hold the Links
public class LinkObject
{
public string Link { get; set; }
public string Description { get; set; }
}
2) In your Action you can use ViewBag, ViewData or even pass the list inside you Model. I will show you how to do using ViewBag
public ActionResult MyDynamicView()
{
//Other stuff and code here
ViewBag.LinkList = new List<LinkObject>()
{
new LinkObject{ Link ="http://mylink1.com", Description = "Link 1"},
new LinkObject{ Link ="http://mylink2.com", Description = "Link 2"},
new LinkObject{ Link ="http://mylink3.com", Description = "Link 3"}
};
return View(/*pass the model if you have one*/);
}
3) In the View, just use a loop:
#foreach (var item in (List<LinkObject>)ViewBag.LinkList)
{
#item.Description
}
Just create a manual one for that, no need to do it from a template. For example, in javascript
function groupAnchor(url,display){
var a = document.createElement("a");
a.href = url;
a.className = "list-group-item";
a.target = "_blank";
a.innerHTML = display;
return a;
}
And then use that function to modify your html structure
<div id="anchors"></div>
<script>
document.getElementById("anchors").appendChild(groupAnchor("http://google.com","Google"));
</script>
Your approach to modification will more than likely be more advanced than this, but it demonstrates the concept. If you need these values to come from server side then you could always iterate over a set using #foreach() and issue either the whole html or script calls there -- or, pass the set from the server in as json and then use that in a function which is set up to manage a list of anchors.
To expand on this, it is important to avoid sending html to the view from a razor iteration. The reason being that html constructed by razor will increase the size of the page load, and if this is done in a list it can be a significant increase.
In your action, construct the list of links and then serialize them so they can be passed to the view
public ActionResult ViewWithLinks()
{
var vm = new ViewModel();
vm.Links = Json(LinkSource.ToList()).Data;
//or for a very simple test for proof of concept
var Numbers = Json(Enumerable.Range(0,100).ToList()).Data;
ViewData["numbers"] = Numbers ;
return View(vm);
}
where all you need is an object to hold the links in your view model
public class ViewModel
{
public ICollection<Link> Links { get; set; }
}
public class Link
{
public string text { get; set; }
public string href { get; set; }
}
and then in your view you can consume this json object
var allLinks = #Html.Raw(Json.Encode(Model.Links));
var numbersList = #Html.Raw(Json.Encode(ViewData["linkTest"]));//simple example
Now you can return to the above function in order to place it on the page by working with the array of link objects.
var $holder = $("<div>");
for(var i = 0; i < allLinks.length; i++){
$holder.append(groupAnchor(allLinks[i].href,allLinks[i].text));
}
$("#linkArea").append($holder);
The benefit is that all of this javascript can be cached for your page. It is loaded once and is capable of handling large amounts of links without having to worry about sending excessive html to the client.
I found this post about Display and EditorTemplates for MVC:
http://www.growingwiththeweb.com/2012/12/aspnet-mvc-display-and-editor-templates.html
It creates a display template to easily display a decimal formatted with currency sign.
The model used in the example:
public class TestModel
{
public decimal Money { get; set; }
}
The display template:
Views/Shared/DisplayTemplates/decimal.cshtml:
#model decimal
#{
IFormatProvider formatProvider =
new System.Globalization.CultureInfo("en-US");
<span class="currency">#Model.ToString("C", formatProvider)</span>
}
In my website I have a helper class with a method to retrieve a formatted currency string from a decimal, so I would replace the above with something like:
#model decimal
#(MyHelperClass.GetCurrencyString(Model))
And finally the view where we want to see the formatted currency:
#model TestModel
#Html.DisplayFor(e => e.Money)
Output:
<span class="currency">$3.50</span>
I can implement this without any problem. But of course i have different views where i want to view a formatted currency. But in some cases I don't want to show the currency sign.
My question now is how i should implement this small variation without to much overkill in code.
Here is my current implementation:
I've changed my display template to this:
#model decimal
#{
bool woCurrency = (bool)ViewData["woCurrency"];
}
#(MyHelperClass.GetCurrencyString(Model)Model,woCurrency))
Of course i've also changed to GetCurrencyString method to accept this additional attribute.
In my view I now have to provide this attribute too:
#Html.DisplayFor(m => m.Money, new { woCurrency = true })
So actually I everything works like it should work. But somehow I don't like this sollution that makes the view more complex.
My question to you: Is there any other method to implement something like this? Or any advice to possible optimise my current sollution?
Thanks!
You need to apply DisplayFormat attribute to your Money property. For example:
[DisplayFormat(DataFormatString = "{0:C}")]
public decimal Money { get; set; }
For more information have a look at these two links:
DisplayFormatAttribute.DataFormatString (The example at the bottom of the page uses currency formatting as an example)
ASP.NET MVC - DisplayFormat attribute
How about HtmlHelper that checks the ViewData["woCurrency"] automatically and outputs the correct result?
public static string Currency(this HtmlHelper helper, decimal data, string locale = "en-US", bool woCurrency = false)
{
var culture = new System.Globalization.CultureInfo(locale);
if (woCurrency || (helper.ViewData["woCurrency"] != null && (bool)helper.ViewData["woCurrency"]))
return data.ToString(culture);
return data.ToString("C", culture);
}
Then:
#Html.Currency(Model.Money);
I am using ASP.NET MVC 3 with razor. I am also using the latest version of Telerik MVC.
I have a grid on my view displaying a list of applications. Each application has a state. I need to write a helper method to display links in each row of the grid depending on each application's current state. If the state is 1 then I need to display an Edit link. The helper that I have looks like this:
public static string ActionLinks(this HtmlHelper helper, string grantApplicationId, string grantApplicationStateId)
{
List<TagBuilder> linkList = new List<TagBuilder>();
string actionLinks = string.Empty;
if (grantApplicationStateId == "1")
{
// Edit link
TagBuilder editLink = new TagBuilder("a");
editLink.MergeAttribute("href", "/GrantApplication/Edit/" + grantApplicationId);
editLink.InnerHtml = "Edit";
linkList.Add(editLink);
}
foreach (TagBuilder link in linkList)
{
actionLinks += link.ToString() + "<br>";
}
return actionLinks;
}
The grid column in my Telerik grid looks like this:
column.Bound(x => x.Id)
.ClientTemplate(#Html.ActionLinks("<#= Id #>", "<#= GrantApplicationStateType.Id #>"))
.Title("Actions");
My view model looks like:
public class GrantApplicationListViewModel
{
// Just partial properties
public GrantApplicationStateType GrantApplicationStateType { get; set; }
}
And GrantApplicationStateType looks like:
public class GrantApplicationStateType : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
When the above helper method is called then the value of grantApplicationStateId is "<#= GrantApplicationStateType.Id #>". How would I get the value that was passed through? What I mean is, if the value is 1 that was passed through, how would I get 1 because currently it is "<#= GrantApplicationStateType.Id #>"?
UPDATE 2012-02-06
I tried Darin's link, used the exact same sample in my code, but changed the following:
column.ActionLink("Open", "Edit", "GrantApplication", item => new { id = item.Id, applicationStateId = item.GrantApplicationStateType.Id });
I need to pass through 2 values. I need to do a couple of if statements on the grant application state id, and then return the specific action links to the client. But it fails when looping through the values in:
if (memberExpression.Expression is ParameterExpression)
value = string.Format("<#= {0} #>", memberExpression.Member.Name);
else
value = GetValue(memberExpression);
The first parameter passed in goes through the first/true part of the if statement:
value = string.Format("<#= {0} #>", memberExpression.Member.Name);
..but the second parameter goes through the false part of the if:
value = GetValue(memberExpression);
What's the difference between the 2?
And then it fails at the GetValue method with the following message:
variable 'item' of type MyProject.ViewModels.GrantApplicationListViewModel' referenced from scope '', but it is not defined
I can't get this to work, and I looked for some more samples and couldn't find any.
You can't do this using a helper. In ASP.NET MVC helpers run on the server. Notice the ClientTemplate name in the Telerik grid? That is meant to run on the client.
What it does is that it simply uses <#= Id #> as a placeholder to a server side helper which will generate some HTML and on the client side, the Telerik grid will do a simple string replace in order to put the value which is only known on the client.
At the moment your server side ActionLinks helper is invoked, the Telerik grid cannot pass you the actual value which is known only on the client.
You may take a look at the following blog post for a nice extension.