I have a model that I am using to present an index of a model from a database and have given a display name to some of the rows that may need spaces in them, (I.e. "weekstarting" in a db would be given a display name of "Week Starting").
So I set the display name for my model like this:
[DisplayName("Week Starting")]
public DateTime WeekStarting { get; set; }
and then in the table headers for my table I use the following line of code to display the field name using its given display name:
#Html.LabelFor(x => x.First().WeekStarting)
The above all works fine. But I am using the W3C validator and it is giving me the following error for the example I have given:
The for attribute of the label element must refer to a form control.
Forgive me if it is obvious but what am I doing wrong here? I am not using a form I am simply displaying an index of items in a table. I have tried to look for an answer and saw someone suggest that the form controls being referred to need ids (even though I'm not using a form) but this would not be applicable in this instance because if I tried to set an id in the index it would be duplicated with each item in the index:
foreach (var item in Model.Tbms)
{
<tr><td>#item.value</td><tr>.... would be repeated for each item, and also unsure where I would put the id in any case, the td?
}
Or is there a better way to label the field header, with my preferred display name in the first place? I guess I could just swap #Html.LabelFor... for Hard code field name but do I have to?
It's inserting a label element, which should correspond to a valid input on the page. You can (a) either not worry about it, (b) output the text directly without using LabelFor, (c) write a custom helper that extracts the label text from the model without wrapping it in a label, or (d) unwrap it with javascript on the client (which will make it pass validation, but only after running the script).
Here's an extension based on the LabelFor code that should help with (c). Untested.
public static class HelperExtensions
{
public static MvcHtmlString TableHeaderFor<TModel,TValue)( this HtmlHelper<TModel> html, Expression<Func<TModel,TValue>> expression )
{
var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
string headerText = metadata.DisplayName ?? metadata.PropertyName ?? ExpressionHelper.GetExpressionText(expression);
return new MvcHtmlString( headerText );
}
}
Is it just a case issue? Your line:
#Html.LabelFor(x => x.First().Weekstarting)
contains 'Weekstarting', whereas the actual property is called 'WeekStarting'.
Related
I've got an issue with my fieldset in Zend Framework 2.
The user is able to save his personal data over a form. If he already save this, they should be prefilled with the data from database. This worked fine as it was only a form, but I need the address data in a fieldset, so that I can use it at other parts of my program. Now the input fields stays empty.
At the beginning, I fill the personal data in a session. My data looks like this:
object(Application\Model\Product\PersonalData)#247 (3) {
["tel":protected]=> string(0) ""
["birthday":protected]=> string(10) "2013-01-01"
["address":protected]=> object(Application\Model\Account\Addresses)#248 (15) {
["firstname":protected]=> string(5) "Ernie"
["surname":protected]=> string(6) "Muppet"
...
}
}
As you can see, the data is already bind to the given objects, PersonalData as main, and Addresses for the fieldset. This seems to work then.
Then I put it in my form:
$oForm->setData($oForm->getHydrator()->extract($_SESSION->getPersonalData()));
return new ViewModel(array('form'=>$oForm));
The addressFieldset has a hydrator and a binding, which does work, because all objects are perfectly filled. The only problem is, that when I open the page, the input-fields are empty, only birthday and telephone are filled, which are directly on the form
My form implements the address-fieldset like this:
$addressFieldset = new AddressFieldset($lang);
$addressFieldset->setUseAsBaseFieldset(true);
$addressFieldset->setName('address');
$this->add($addressFieldset);
I think that it might be just a problem with the correct addressing of my fieldset, but I can't explain why it would be filled correctly after posting the data then. All I want is that he fill the setData in my Fieldset.
I hope you understand my question and could help me.
Thanks a lot,
Svenja
EDIT:
I analysed it a bit more now, it's very strange and I don't know what went wrong.
When I debug I can see that $fieldset->getValue() returns all data I need. So I thought that the binding might be wrong and I did this to debug it step by step:
$values = $this->form->get('address')->getValue();
$addressFieldset = $this->form->get('address');
$aValues = $addressFieldset->getHydrator()->extract($values);
$addressFieldset->bindValues($aValues);
I went to the Fieldset.php and bindValues does perfectly what it should (it is only a recapitulation anyway(?)), call the hydrator and fill my object. But if I show in the elements, all values are NULL.
I already checked my elements twice. The only different between the model and the elements is a different order of declaration. I call the method setUseAsBaseField(true) in the fieldset and the form, too. I can't understand why the data is in values but not in elements.
It's very strange, because I have something and it good work. Are you confident that the expression $_SESSION->getPersonalData() to return the desired result? You are using a very strange session.
http://framework.zend.com/manual/2.1/en/modules/zend.session.container.html
To EDIT (I'm sorry my english):
You can use different types of hydrator, for example
ArraySerializable (by default, your entity must have getArrayCopy()
and exchangeArray() methods, in your case) ArraySerializable is the hydrator by default.
First you have to bind a form with entity $form->bind(new Entity()); The entity will bind to the base fieldset. If the base fieldset not specified, the entity will bind with the form, because the form inherits fieldset. If the entity is an aggregate, ie, its properties contain another objects, for each of these objects should be your fildset.
In constructor this children fieldsets you should use
$this->setObject(new MyChildrenEntity());
As a result, the entity properties will be extracted to the form
elements.
After that, you should only work with the form, but not with its
elements or fieldsets.
You can pass any data in the form, so form elements will get this
values. $form->setData($this->getRequest()->getPost()); This method use internal populateValues() method. If the form has elements with an appropriate name, they will be assigned to these values. If the form has fieldsets, they will also be transferred to these values.
As long as the form fails to validate, the entity of these values
will not be assigned. These values are assigned to entities only
in case of successful verification. IsValid () method uses the
internal method bindValues () if the validation was successful.
After successful validation, you can get the entity using getData()
method $entity = $form->getData();
P.S.: If you are doing a complex "haсk", do not be offended by this simple explanation.
I finally found the solution!
At first, this link helped me:
Populating fieldsets
I didn't integrate the fieldset Input-Filter in the Form Input Filter.
But that wasn't all I have to do. My Form-Datamodel looks like this:
interface IPersonalData
{
public function getTel();
public function getBirthday();
public function getAddress();
public function setTel($tel);
public function setAddress(IAddresses $address);
public function setBirthday($birthday);
}
This is only the interface, but Address is an object. And that is the problem. When the form tries to fill the fieldset, he only accept arrays. So I have to extract my object in the getter-method to an array.
I don't think that this is very useful, because I normaly want to get my object with this method. So I write a method "getAddressAsArray()" which looks like this:
public function getAddressAsArray()
{
$oAddressHydrator = new AddressHydrator();
if(isset($this->address))
{
return $oAddressHydrator->extract($this->address);
}
return array();
}
The extract-method of my hydrator changed like this:
public function extract($object)
{
if(!$object instanceof IPersonalData)
{
throw new \InvalidArgumentException('$object must be an instance of Application\Model\Product\IPersonalData');
}
return array(
'telephone' => $object->getTel(),
'address' => $object->getAddressAsArray(),
'birthday' => $object->getBirthday(),
);
}
I have a plain textfield in Tridion that can have multiple values. The itemtype is a SingleLineTextField.
In the TBB code I have the following (removed the non-essential parts):
ItemFields itemFields = new ItemFields(folder.Metadata, folder.MetadataSchema);
foreach (ItemField itemField in itemFields)
{
string itemFieldValue = string.Empty;
switch (Utilities.GetFieldType(itemField))
{
case FieldType.SingleLineTextField:
itemFieldValue = itemField.ToString();
break;
}
}
Now the result in case of two entries is just two strings with a character line break in it.
String A
String B
The method used is a generic one, which also works on other fields, so I was looking for some way to find out if a SingleLineTextField has more values in it.
You can cast the field to a SingleLineTextField type, then iterate through the Values collection, something along these lines:
SingleLineTextField field = (SingleLineTextField)itemField;
foreach(string value in field.Values)
{
// do something with value
}
// or if all you want is the count of values
int i = field.Values.Count;
Firstly, I would advise against relying on the ToString() method on objects unless it is specifically documented. In this case it works with the abstract class ItemField, but this may not always be the case.
The TOM.Net API only defines Definition and Name properties for ItemField, so you need to cast your ItemField object to something more specific.
the TextField abstract class, which SingleLineTextField inherits from, defines a ToString() method, but also Value and Values properties, which are much better suited to what you're trying to do. Looking at the documentation, we can see that Values will give us an IList<String> of the values, even if your field is not multi-valued. Perfect!
So, to answer your question, "I was looking for some way to find out if a SingleLineTextField has more values in it", you need to cast your ItemField as a TextField and check the number of Values it provides, thus:
TextField textField = (TextField)itemField;
// If you need to deal with multi-valued fields separately
if (textField.Values.Count > 1)
{
//Logic to deal with multiple values goes here
}
else
{
//Logic to deal with single valued goes here
}
// Much better... If you can deal with any number of values in a generic fashion
foreach (string value in textField.Values)
{
// Generic code goes here
}
If I have in my model class a property of type DateTime how can I render it in a specific format - for example in the format which ToLongDateString() returns?
I have tried this...
#Html.DisplayFor(modelItem => item.MyDateTime.ToLongDateString())
...which throws an exception because the expression must point to a property or field. And this...
#{var val = item.MyDateTime.ToLongDateString();
Html.DisplayFor(modelItem => val);
}
...which doesn't throw an exception, but the rendered output is empty (although val contains the expected value, as I could see in the debugger).
Thanks for tips in advance!
Edit
ToLongDateString is only an example. What I actually want to use instead of ToLongDateString is a custom extension method of DateTime and DateTime?:
public static string FormatDateTimeHideMidNight(this DateTime dateTime)
{
if (dateTime.TimeOfDay == TimeSpan.Zero)
return dateTime.ToString("d");
else
return dateTime.ToString("g");
}
public static string FormatDateTimeHideMidNight(this DateTime? dateTime)
{
if (dateTime.HasValue)
return dateTime.Value.FormatDateTimeHideMidNight();
else
return "";
}
So, I think I cannot use the DisplayFormat attribute and DataFormatString parameter on the ViewModel properties.
You could decorate your view model property with the [DisplayFormat] attribute:
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}",
ApplyFormatInEditMode = true)]
public DateTime MyDateTime { get; set; }
and in your view:
#Html.EditorFor(x => x.MyDate)
or, for displaying the value,
#Html.DisplayFor(x => x.MyDate)
Another possibility, which I don't recommend, is to use a weakly typed helper:
#Html.TextBox("MyDate", Model.MyDate.ToLongDateString())
If all you want to do is display the date with a specific format, just call:
#String.Format(myFormat, Model.MyDateTime)
Using #Html.DisplayFor(...) is just extra work unless you are specifying a template, or need to use something that is built on templates, like iterating an IEnumerable<T>. Creating a template is simple enough, and can provide a lot of flexibility too. Create a folder in your views folder for the current controller (or shared views folder) called DisplayTemplates. Inside that folder, add a partial view with the model type you want to build the template for. In this case I added /Views/Shared/DisplayTemplates and added a partial view called ShortDateTime.cshtml.
#model System.DateTime
#Model.ToShortDateString()
And now you can call that template with the following line:
#Html.DisplayFor(m => m.MyDateTime, "ShortDateTime")
Simple formatted output inside of the model
#String.Format("{0:d}", model.CreatedOn)
or in the foreach loop
#String.Format("{0:d}", item.CreatedOn)
I use the following approach to inline format and display a date property from the model.
#Html.ValueFor(model => model.MyDateTime, "{0:dd/MM/yyyy}")
Otherwise when populating a TextBox or Editor you could do like #Darin suggested, decorated the attribute with a [DisplayFormat] attribute.
If all your DateTime types are rendered the same way you can use a custom DateTime display template.
In your Views folder create a folder named "DisplayTemplates" either under your controller specific views folder, or under "Shared" folder (these work similar to partials).
Inside create a file named DateTime.cshtml that takes DateTime as the #model and code how you want to render your date:
#model System.DateTime
#Model.ToLongDateString()
Now you can just use this in your views and it should work:
#Html.DisplayFor(mod => mod.MyDateTime)
As long as you follow the convention of adding it to the "DisplayTemplates" folder and naming the file to match the type your are displaying, MVC will automatically use that to display your values. This also works for editing scenarios using "EditorTemplates".
Here's some more information on templates.
My preference is to keep the formatting details with the view and not the viewmodel. So in MVC4/Razor:
#Html.TextBoxFor(model => model.DateTime, "{0:d}");
datetime format reference:
http://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.71).aspx
Then I have a JQuery datepicker bound to it, and that put's the date in as a different format...doh!
Looks like I need to set the datepicker's format to the same formatting.
So I'm storing the System.Globalization formatting in a data-* attribute and collecting it when setting up the
#Html.TextBoxFor(
model => model.DateTime.Date,
"{0:d}",
new
{
#class = "datePicker",
#data_date_format=System.Globalization.CultureInfo
.CurrentUICulture.DateTimeFormat.ShortDatePattern
}));
And here's the sucky part: the formats of .net and datepicker do not match, so hackery is needed:
$('.datePicker').each(function(){
$(this).datepicker({
dateFormat:$(this).data("dateFormat").toLowerCase().replace("yyyy","yy")
});
});
that's kind of weak, but should cover a lot of cases.
works for me
<%=Model.MyDateTime.ToString("dd-MMM-yyyy")%>
Had the same problem recently.
I discovered that simply defining DataType as Date in the model works as well (using Code First approach)
[DataType(DataType.Date)]
public DateTime Added { get; set; }
In MVC5 I'd use, if your model is the datetime
string dt = Model.ToString("dd/MM/yyy");
Or if your model contains the property of the datetime
string dt = Model.dateinModel.ToString("dd/MM/yyy");
Here's the official meaning of the Formats:
https://msdn.microsoft.com/en-us/library/8kb3ddd4(v=vs.110).aspx
you can do like this #item.Date.Value.Tostring("dd-MMM-yy");
if I just want to display the date in short format I just use
#Model.date.ToShortDateString() and it prints the date in
If all you want to do is display the date with a specific format, just call:
#Model.LeadDate.ToString("dd-MMM-yyyy")
#Model.LeadDate.ToString("MM/dd/yy")
It will result in following format,
26-Apr-2013
04/26/13
this will display in dd/MM/yyyy format in your View
In View:
instead of DisplayFor use this code
<td>
#(item.Startdate.HasValue ? item.Startdate.Value.ToString("dd/MM/yyyy") : "Date is Empty")
</td
it also checks if the value is null in date column, if true then it will display Date is Empty or the actual formatted date from the column.
Hope helps someone.
#{
string datein = Convert.ToDateTime(item.InDate).ToString("dd/MM/yyyy");
#datein
}
Only View File Adjust like this. You may try this.
#Html.FormatValue( (object)Convert.ChangeType(item.transdate, typeof(object)),
"{0: yyyy-MM-dd}")
item.transdate it is your DateTime type data.
There are a few posts about this, but after hours of searching I still can't find what I need.
The answer in the following post almost gets me what I want:
Combobox for Foreign Key in DataGridView
Question 1:
Going off that example where a Product has many Licenses, my database mappings are all many-to-one relationships which means my License class holds a reference to the Product class. The License class does not have a property for the ProductId since that can be retrieved via the Product reference. I don't want to muck up the License class with both a reference to Product and a ProductId property just to make binding in the UI easier.
Because of this I can't set the DataPropertyName to an Id field. It needs to be the class reference name like so:
DataGridViewComboBoxColumn dataGridViewComboBoxColumn =
(DataGridViewComboBoxColumn)myDataGridView.Columns("LicenseComboBoxColumn");
dataGridViewComboBoxColumn.DataPropertyName = "License"; // not LicenseID
****Update****
I was able to get this to partially work without creating the ProductId property by specifying the Product.Id as the DataPropertyName like so:
dataGridViewComboBoxColumn.DataPropertyName = "License.Id";
However, when doing so, it broke databinding which caused me to manually get and set the cell value.
I've also seen posts about binding to the DataGridView cell, but databinding breaks when I do that and the datasource itself is never updated:
// populate the combo box with Products for each License
foreach (DataGridViewRow row in myDataGridViewProducts.Rows)
{
IProduct myProduct = row.DataBoundItem as IProduct;
DataGridViewComboBoxCell cell = (DataGridViewComboBoxCell)row.Cells("myProductCol");
cell.DataSource = getListOfILicenseObjectsFromDao(myProduct.Id);
cell.Value = myProduct.License.Id;
}
Maybe I'm doing something wrong, or maybe there's a different way. Can anyone help here?
Question 2:
How do I display a different list of Licenses for each Product?
In other words, the combobox list of Licenses will be different for each Product in the grid. I'd like to do this using databinding so I don't have to get and set the values myself.
I found the answer myself. I had this same issue a while ago and found the solution in some old code I dug up. The solution was to add a Self property to the object I wanted to databind to in the combobox (in the example above it would be the License class) and use that property as the ValueMember like so:
foreach (DataGridViewRow row in myDataGridViewProducts.Rows)
{
IProduct myProduct = row.DataBoundItem as IProduct;
DataGridViewComboBoxCell cell = (DataGridViewComboBoxCell)row.Cells("myProductCol");
cell.DataSource = getListOfILicenseObjectsFromDao(myProduct.Id);
cell.DataPropertyName = "License";
cell.DisplayMember = "Name";
cell.ValueMember = "Self"; // key to getting the databinding to work
// no need to set cell.Value anymore!
}
The License class now looks like this:
Public class License
{
public string Name
{
get; set;
}
public ILicense Self
{
get { return this; }
}
// ... more properties
}
Granted I had to "muck" up the Business classes with a property named Self, but that's much better (less confusing to the programmer) than having both a reference to License and a LicenseId property in the Product class IMO. Plus it keeps the UI code much much simpler as there's no need to manually get and set the values - just databind and done.
I am trying to understand the best way of implementing a DropDownList in ASP.NET MVC 2 using the DropDownListFor helper. This is a multi-part question.
First, what is the best way to pass the list data to the view?
Pass the list in your model with a SelectList property that contains the data
Pass the list in via ViewData
How do I get a blank value in the DropDownList? Should I build it into the SelectList when I am creating it or is there some other means to tell the helper to auto create an empty value?
Lastly, if for some reason there is a server side error and I need to redisplay the screen with the DropDownList, do I need to fetch the list values again to pass into the view model? This data is not maintained between posts (at least not when I pass it via my view model) so I was going to just fetch it again (it's cached). Am I going about this correctly?
Your best bet is to create a SelectList in your Controller - use my extension method here:
http://blog.wekeroad.com/2010/01/20/my-favorite-helpers-for-aspnet-mvc
Pop that into ViewData using the same key as your property name:
ViewData["statusid"]=MySelectList
Then just use Html.DropDownFor(x=>x.StatusID) and you're all set.
Answering in parts:
The best way IMHO is to pass the list in the ViewModel like this:
public SelectList Colors
{
get
{
// Getting a list of Colors from the database for example...
List<Color> colors = GetColors().ToList();
// Returning a SelectList to be used on the View side
return new SelectList(colors, "Value", "Name");
}
}
To get a blank or default option like ( -- Pick a color -- ), you can do this on the view side:
#Html.DropDownListFor(m => m.Color, Model.Colors, "-- Pick a color --")
You'll have to fetch/populate the list again if it's part of the ViewModel.
Take a look at the following blog post. It can give you some tips:
Drop-down Lists and ASP.NET MVC
You could do something like:
<%= Html.DropDownListFor((x => x.ListItems), Model.ListItems, "")%>
or
<%= Html.DropDownList("ListItems", Model.ListItems, "")%>
The last param 'optionLabel' makes a blank list item
In this case, you can see ListItems is a property of the model.
I have made the view strongly typed to the model also.
(You know this already!)
Pass the list in your model with a SelectList property that contains the data
Yes, add it when you build the SelectList. (If you build the list using LINQ, Union might come in handy.)
Yes do do, and yes you are.
I find it more intuitive to work with a sequence of SelectListItems (rather than a SelectList).
For example, this would create an IEnumerable<SelectListItem> from a sequence of customer objects that you can pass to the Html.DropDownListFor(...) helper. The 'Selected' property will optionally set the default item in the dropdown list.
var customers = ... // Get Customers
var items = customers.Select(c => new SelectListItem
{
Selected = (c.Id == selectedCustomerId),
Text = c.Email,
Value = c.Id.ToString()
});