I'd like to limit the number of displayed characters in DropDownList :
#Html.DropDownList("domaines", Model.Domaines, new { #class = "form-control", #id = "domaines", autocomplet = "autocomplet",maxlength = 21 })
this is the scenario :
if the number of characters <= 18 : the whole word is displayed
Else if number of characters > 18 : the first 18 characters will be displayed concatenated to an ellipsis (...).
How can I do this?
You need to prepare your model before you send it to the view. You need to pass an IEnumerable<SelectListItem> to DropDownList(), not your own type. You can use the SelectList(IEnumerable, string, string) constructor for that.
How to truncate strings with ellipsis has been answered in How do I truncate a .NET string? and Ellipsis with C# (ending on a full word).
In your controller:
// ... initialize model.
foreach (var domainModel in model.Domaines)
{
// Assuming the display member you want to truncate is called `DisplayString`.
// See linked questions for Truncate() implementation.
domainModel.DisplayString = domainModel.DisplayString.Truncate(18);
}
// Assuming the `Domaines` type has a `Value` member that indicates its value.
var selectList = new SelectList(model.Domaines, "Value", "DisplayString");
// Add a `public SelectList DomainSelectList { get; set; }` to your model.
model.DomainSelectList = selectList;
return View(model);
In your view:
#Html.DropDownList("domaines", Model.DomainSelectList, new { ... })
Related
I'm running asp.net 4 mvc and I've created a DropDownList of dates that defaults to the first entry in the list. When I select an entry, I invoke a controller function and do some processing. However, when my page does the PostBack, instead of displaying the list item I selected, it displays the original default list item again.
How do I get my page to display the last item I selected from the list? I've spent two full days searching this site and the Internet for a solution but nothing I try seems to work. Any assistance would be greatly appreciated.
My Html View
#Html.DropDownList("selectList", Model.ReverseMonthsLists(),
new { #onchange = "CallChangefunc(this.value)" })
<script>
function CallChangefunc(val) {
window.location.href = "/dashboard/Report_Performance?id=" + val;
}
</script>
My ViewModel
public SelectList MonthList { get; set; }
public IEnumerable<SelectListItem> ReverseMonthsLists()
{
var selectListItems = GetDates()
.Select(_ => _.ToString("MMM yyyy"))
.Select((dateString, index) => new SelectListItem { Selected = index == 0, Text = dateString, Value = dateString })
.ToList();
return selectListItems;
}
public static IEnumerable<DateTime> GetDates()
{
DateTime startDate = new DateTime(2017, 6, 1).Date;
var currentDate = DateTime.Now.Date;
int numberOfMonthsToShow = (currentDate.Year - startDate.Year) * 12 + currentDate.Month - startDate.Month;
var dates = new List<DateTime>(numberOfMonthsToShow);
currentDate = currentDate.AddMonths(-1);
for (int i = 0; i < numberOfMonthsToShow; i++)
{
dates.Add(currentDate);
currentDate = currentDate.AddMonths(-1);
}
return dates;
}
My Controller
[RequireLogin]
public ActionResult Report_Performance(string id)
{
DateTime newDate = DateTime.Now.Date.AddMonths(-1);
if (id != null)
newDate = DateTime.Parse(id);
var aVar = Models.Reporting.ListingStatsReportingViewModel.GetStats(userCurrentService.CompanyId.Value, Models.Reporting.DateTimePeriod.Monthly, newDate);
return this.View(aVar);
}
You can change your code as follows:
Let's say your model class that is being returned by GetStats method in the Report_Performance action is MyStats which contains a string property named SelectedDateString (you need to add this property to your view model class).
Updated view markup:
#model MyStats
#Html.DropDownList("SelectedDateString", Model.ReverseMonthsLists(),
new { #onchange = "CallChangefunc(this.value)" })
<script>
function CallChangefunc(val) {
window.location.href = "/dashboard/Report_Performance?id=" + val;
}
</script>
Updated controller:
[RequireLogin]
public ActionResult Report_Performance(string id)
{
DateTime newDate = DateTime.Now.Date.AddMonths(-1);
if (id != null)
newDate = DateTime.Parse(id);
var aVar = Models.Reporting.ListingStatsReportingViewModel.GetStats(userCurrentService.CompanyId.Value, Models.Reporting.DateTimePeriod.Monthly, newDate);
//This will make sure that the model returns the correct value of the property as a string.
aVar.SelectedDateString = id;
return this.View(aVar);
}
A Html.DropDownList() works by getting data from a string property in the model which is of the same name as the name of the DropDownList itself.
In your case, you need to set the DropDownList value using javascript or jquery as it's not connected to a model property.
Let me give you an example:
A drop down list in MVC can be created by using either
#Html.DropDownListFor(m => m.PreferredContactMethod, Model.PreferredContactMethods, "")
or
#Html.DropDownList("PreferredContactMethod", Model.PreferredContactMethods, "")
In both cases, PreferredContactMethod is a string property in my model that is connected to the view - which is done by specifying #model PreferredContactModel at the top of the view.
In your case, your list name is selectList and if the specified model is connected to the view and if there's a property in the model that gets the selected date, then you need to change the name of your drop down list to it.
I hope it makes sense, if there's any issue, please comment back. I want to help with this.
The problem is here:
window.location.href = "/dashboard/Report_Performance?id=" + val;
This essential tells the browser to navigate to a new page, which is an HttpGet operation. Thus, there is no correlation between your current settings and those of a new page.
It's as if you had just gone up to the address bar and hit enter. It issues a new page with all new defaults.
There are many ways you can address this problem. The easiest would be to have some javascript that looks at the URL and extracts the id query parameter, then selects the item in the dropdown box that corresponds with the id.
Another option is to set the dropdownlist's selected value based on the ID in your controller.
In controller:
ViewBag.SelectedItem = id;
In View:
#Html.DropDownList("SelectedItem", Model.ReverseMonthsLists(), ...)
I need to save the Contact Number in a registration form wherein i'm having 2 TextBox, one for Country Code and another for Number. Now i need to combine both and bind into a single property which i have in my class. How can i do that?
View:
<div class="form-group">
#Html.LabelFor(m => m.Phone, "Contact Number:")
#Html.TextBoxFor(m => m.xxxx,new { #class = "form-control", #id = "txtContactCode", required="required", type ="number" })-
#Html.TextBoxFor(m => m.Phone,new { #class = "form-control", #id = "txtContactNumber", required="required", type ="number" })
</div>
Property,
public string Phone { get; set; }
Now what should i bind the property in the code of the phone number field to concatenate as one? Is there any way or i should declare another property named Code and then proceed?
In general, you should not try to split/join things. You're just introducing a potential point of failure into your application. If you only care about storing a single combined Phone, then let the user enter their phone number directly in a field for Phone. If you care about ensuring that every phone number has a country code, you can use a phone number validation library like this port of Google's libphonenumber library, to parse the user entered phone numbers and standardize how they're stored in the database.
The problem with trying to combine two fields into one is that you then have to split that one field back into two. Especially with something like a country code that can be variable length, that's going to be really difficult to do reliably. However, if you insist on going down this path, I'd recommend using a view model like:
public string Phone
{
get { return String.Format("{0} {1}", CountryCode, Number); }
set
{
CountryCode = null;
Number = null;
if (value != null)
{
var parts = value.Split(new[] { ' ' }, 2);
if (parts.Length == 2)
{
CountryCode = parts[0];
Number = parts[1];
}
}
}
}
public string CountryCode { get; set; }
public string Number { get; set; }
Then, you would bind to CountryCode and Number, respectively, in your view. The Phone custom getter and setter will take care of translating back and forth between combined and constituent parts.
You can easily do this by using the BindModel() method which is available in the IModelBinder interface.
IModelBinder.BindModel: Binds the model to a value by using the specified controller context and binding context.
Also see this explanation with real-time example.
Note: In the above example, first_name, middle_name and last_name were bound to the full name property. You can bind your required two properties to the one property in the same way.
Is it possible with either Bootstrap, HTML5 or jQuery to have a currency prefix within the input field as a display element and not part of the actual input value?
For example I wish to display £550.00 in the Amount field, but the data as 550.00
I have tried using w2ui, jQuery Price Format and jQuery maskMoney, all 3 do the same job basically, they can indeed prefix or suffix, but they don't actually store a value in the input, so posting data returns a null value.
Model.cs
[DisplayFormat(DataFormatString = "{0:F2}", ApplyFormatInEditMode = true)]
public double Amount { get; set; }
HTML
<div class="col-sm-3">
<div class="input-group">
<span class="input-group-addon">Amount</span>
#Html.TextBoxFor(m => m.Amount, new { #class = "form-control", #Value = #ViewBag.Amount, placeholder = "Amount", #Id = "Amount" })
</div>
</div>
// Dynamically setting value of field
$('#Amount').val(data.Amount);
// w2ui example
$('#Amount').val(data.Amount).w2field('money', { moneySymbol: '£' });
// jQuery Price Format example
$('#Amount').val(data.Amount).priceFormat({
prefix: '£'
});
I realise I can use another field to store the numeric value, but I have quite a few fields I need to display which are initially dynamically populated, but can be overridden by the user. So duplicating fields, one for display input and one hidden to store inputted data seems over kill.
Any assistance would be much appreciated :-)
I recommend wrapping your input with a span:
<span class="currency">£ #Html.TextBoxFor()</span>
And modifying that with css to make it format nicely for yourself. Such as this.
Or just add it as a label:
<label for="currencyInput">Amount in £</label>
Otherwise, you'll be stuck trying to remove / add the prefix everywhere. Keep your input as the actual value.
The issue wasn't with any of the jQuery libraries but the fact that the form was posting a string, whereas the model was expecting a double.
So I have implemented a ModelBinder to allow the post of string data containing the '£' pound sign and convert to a double.
MyModelBinder.cs
namespace WebApplication1.Models
{
public class MyModelBinder : DefaultModelBinder
{
protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
{
if (propertyDescriptor.ComponentType == typeof(MyModel))
{
if (propertyDescriptor.Name == "Amount")
{
var obj = bindingContext.ValueProvider.GetValue("Amount");
return Convert.ToDouble(obj.AttemptedValue.Replace("£", ""));
}
}
return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
}
}
}
HomeController.cs
[HttpPost]
public ActionResult Index([ModelBinder(typeof(MyModelBinder))]MyModel model)
HTML
$('#Amount').val(data.Amount).priceFormat({
prefix: '£'
});
I hope this proves useful to others.
I will try and explain my situation as best as I can with the information I have. The short of it is that based on the value in the text box, I need to hide options in a dropdown list.
I have a text box with a building limit. It has an ID and then a location number as there are multiple buildings on the same page:
<div class="col-md-6 col-sm-6">
<label>Limit</label>
#Html.TextBoxFor(m => m.Building.Limit, htmlAttributes: new { id = "buildingLimit-" + Model.LocationNum })
</div>
Below this I have a dropdown list with the a different ID but with the same location number:
<div class="col-md-6 col-sm-6">
<label>Occurrence</label>
#Html.DropDownListFor(m => m.Earthquake.Occurrence, new SelectList(Model.Earthquake.AggregateList, "Value", "Text"), htmlAttributes: new { id = "EarthquakeOcc-" + Model.LocationNum })
</div>
Right now, that dropdown list is not at all tied to the buildingLimit. It displays several different values which can be selected. What I would like to do is if the buildingLimit is say $250,000 then the dropdown list should not have values greater than that. If they change it again to $1,000,000 then the dropdown list needs to adjust to have values no greater than $1,000,000
I have really been struggling on this one now. It sounds simple enough but I can't figure out how to do this.
This is what I have so far:
$("[id^='buildingLimit-']").change(function () {
var location = $(this).attr("id").split("-")[1];
var buildingLimit = $(this).val();
var occArray = [];
$("[id^='EarthquakeOcc-']").each(function (i, selected) {
occArray[i] = $(selected).val();
alert(occAccary[i]);
if (occArray[i] > buildingLimit) {
$("[id^='EarthquakeOcc'] option[value = occArray[i]").remove();
}
});
I know that there are several things wrong with it: it only works when the text box is changed and it just removes. Does not add back through .append(). Also, the values are hard coded:
public List<SelectListItem> AggregateList
{
get
{
if (aggregateList == null)
{
aggregateList = new List<SelectListItem>();
aggregateList.Add(new SelectListItem { Text = "$100,000", Value = "100000" });
aggregateList.Add(new SelectListItem { Text = "$250,000", Value = "250000" });
aggregateList.Add(new SelectListItem { Text = "$500,000", Value = "500000" });
aggregateList.Add(new SelectListItem { Text = "$1,000,000", Value = "1000000" });
aggregateList.Add(new SelectListItem { Text = "$2,000,000", Value = "2000000" });
aggregateList.Add(new SelectListItem { Text = "$3,000,000", Value = "3000000" });
aggregateList.Add(new SelectListItem { Text = "$4,000,000", Value = "4000000" });
aggregateList.Add(new SelectListItem { Text = "$5,000,000", Value = "5000000" });
aggregateList.Add(new SelectListItem { Text = "$6,000,000", Value = "6000000" });
aggregateList.Add(new SelectListItem { Text = "$7,000,000", Value = "7000000" });
aggregateList.Add(new SelectListItem { Text = "$8,000,000", Value = "8000000" });
aggregateList.Add(new SelectListItem { Text = "$9,000,000", Value = "8000000" });
aggregateList.Add(new SelectListItem { Text = "$10,000,000", Value = "10000000" });
aggregateList.Add(new SelectListItem { Text = "$25,000,000", Value = "25000000" });
}
return aggregateList;
}
set { aggregateList = value; }
}
First things first, you're going to need an immutable list of all possible values. Trying to remove and recreate options, while also keeping everything ordered properly and such is an unnecessary pain. If you have a list of possible values, you can simply pull from that list, up to the defined threshold, based on the text box entry, to recreate the entire list of options. I would also recommend using a nice little JS library called accounting.js. It's not a strict requirement to make this work, but it makes converting back and forth between formatted currency string values and actual numeric values a ton easier.
Basically, you just need a list of the values in JS:
var MyNamespace = MyNamespace || {};
MyNamespace.AggregateList = [
100000,
250000,
500000,
...
];
If you would prefer to create this list in your MVC view model and pass it to the view you can use something like the following instead:
MyNamespace.AggregateList = #Json.Encode(Model.AggregateListValues);
Where AggregateListValues would be a List<int>, or similar.
Next, the way you're selecting the building limit and earthquake occurrence fields is highly inefficient. It's better to put each pair of fields inside a wrapper such that you can select the other based on its proximity:
<div class="location">
<input id="buildingLimit" />
<select id="EarthquakeOcc"> ... </select>
</div>
That code is intentionally simplified to illustrate what's happening; you're still free to use whatever other HTML you currently have wrapping those fields. Just make sure that both are grouped together within one parent. Then:
$('[id^=buildingLimit]').on('change', function () {
var parent = $(this).closest('.location');
var select = $('[id^=EarthquakeOcc]', parent);
});
Also, for what it's worth, this frees you up to use the original ids, without having to manually specify the id, or and probably better, you could add a class to the fields instead, such that you can select them based on that class rather than their id attributes starting with a certain string, which would be a much cheaper selector for jQuery to process. (When you select by attribute, your selector is actually *[attribute=value]. In other words, jQuery must select every element in the DOM and then filter out the ones that don't have matching id values. Using a selector like input[attribute=value] would be better, as that narrows the field down to just input elements before jQuery much search each one. However, classes will always be faster, as jQuery can just rely on the native document.getElementsByClassName.)
For your event handler:
$('[id^=buildingLimit]').on('change', function () {
var parent = $(this).closest('.location');
var select = $('[id^=EarthquakeOcc]', parent);
// if you choose not to use accounting.js, you just need to ensure that the value
// gets turned into a number, using a combination of something like `replace` with
// a regex and `parseInt`, etc.
var limit = accounting.unformat($(this).val());
var output = [];
var i = 0;
while (MyNamespace.AggregateList[i] <= limit) {
// If you choose not to use accounting.js, you just need to implement your
// own logic for formatting the number as you want
output.push('<option value="' + MyNamespace.AggregateList[i] + '">$' + accounting.formatNumber(MyNamespace.AggregateList[i]) + '</option>');
i++;
}
select.html(output.join(''));
});
I have a create view with multiple DropDownListFors. Each time a new object is created only 1 of the DropDownListFors should have a value, I want the others to return 0 as the result when the optionLabel is left selected.
How do I assign 0 as the value for a DropDownListFor's optionLabel?
EDIT:
Here is an example of my DropDownListFor code in my view:
#Html.DropDownListFor(model => model.cardReward.ID, new SelectList(ViewBag.cardReward, "Id","Name"), "None")
When I render the page it creates the list with None at the top like this:
<option value>None</option>
I want it to be like this:
<option value="0">None</option>
In the documentation for DropDownFor the optionLabel parameter (where you're passing "None") is described as:
The text for a default empty item.
So this is designed to always be an empty item. You will need to add an additional item into your select list in order to get a 0 value.
I have used the following extension method to accomplish this (sorry untested, there may be minor errors):
public IEnumerable<SelectListItem> InsertEmptyFirst(this IEnumerable<SelectListItem> list, string emptyText = "", string emptyValue = "")
{
return new [] { new SelectListItem { Text = emptyText, Value = emptyValue } }.Concat(list);
}
You would use it like this:
#Html.DropDownListFor(model => model.cardReward.ID, new SelectList(ViewBag.cardReward, "Id","Name").InsertEmptyFirst("None", "0"))
Insert a new empty string, here's an example.
#Html.DropDownListFor(x => x.ProjectID, Model.Projects, string.Empty)