Reputation: 3190
Why am I receiving the error:
Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions
at this code:
@model IEnumerable<ArtSchoolProject.Models.Trainer>
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_PageLayout.cshtml";
}
<h2>Index</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<ul class="trainers">
@foreach (var item in Model) {
<li>
<div>
<div class="left">
<a href="@Url.Action("Details", "Details", new { id = item.ID })">
<img src="~/Images/Trainer/@item.Picture" />
</a>
</div>
<div class="right">
@Html.ActionLink(item.Name,"Details",new {id=item.ID})
<br />
@Html.DisplayFor(modelItem=>@string. item.Description.ToString().Substring(0,100))
</div>
</div>
</li>
}
</ul>
at line:
@Html.DisplayFor(modelItem=>item.Description.ToString().Substring(0,100))
Update:
Problem solved. I added to my code :
@{
string parameterValue = item.Description.ToString().Substring(0, 100);
}
@Html.DisplayFor(modelItem=>parameterValue)
My new code:
@foreach (var item in Model) {
<li>
<div>
<div class="left">
<a href="@Url.Action("Details", "Details", new { id = item.ID })">
<img src="~/Images/Trainer/@item.Picture" />
</a>
</div>
<div class="right">
@Html.ActionLink(item.Name,"Details",new {id=item.ID})
<br />
@{
string parameterValue = item.Description.ToString().Substring(0, 100);
}
@Html.DisplayFor(modelItem=>parameterValue)
</div>
</div>
</li>
}
This is only one possibility. Just for curiosity is there another solution for solving the error?
Upvotes: 149
Views: 166302
Reputation: 1
If you want to get a substring on a view without creating a template, this will work. Concat()
was used for personal use just to see if it would work.
Substring(0, 100)
would work only if the data itself is represented as a single string. Instead, the database takes a long string NVARCHAR(MAX)
/VARCHAR(MAX)
as an array of strings or list of strings, so Substring()
and ToCharArray()
will fail because both methods are looking for the length of the first string in the list of strings.
This way you create a variable based on the model and return that to a list in which you use the Take()
method to grab the characters within that list. Please refer yourself to the above methods in dotnet to get a better understanding.
It should not matter what C# or .NET version you use. I am on .NET Core 6.
<tbody>
@foreach (var item in Model)
{
var it = item.Contents.ToList().Take(100).Concat("...");
<tr>
<td>
@Html.DisplayFor(modelItem => it)
</td>
<
</tr>
}
</tbody>
Upvotes: 0
Reputation: 1038
The correct syntax for converting a DateTime to shortdate follows:
@foreach (var item in Model)
{
@{var endDate= !item.EndDate.ToShortDateString();}
@Html.DisplayFor(m => endDate,)
}
Upvotes: 0
Reputation: 9911
The ...For
extension methods on the HtmlHelper (e.g., DisplayFor
, TextBoxFor
, ElementFor
, etc...) take a property and nothing else. If you don't have a property, use the non-For
method (e.g., Display
, TextBox
, Element
, etc...).
The ...For
extension methods provides a way of simplifying postback by naming the control after the property. This is why it takes an expression and not simply a value. If you are not interested in this postback facilitation then do not use the ...For
methods at all.
Note: You should not be doing things like calling ToString
inside the view. This should be done inside the view model. I realize that a lot of demo projects put domain objects straight into the view. In my experience, this rarely works because it assumes that you do not want any formatting on the data in the domain entity. Best practice is to create a view model that wraps the entity into something that can be directly consumed by the view. Most of the properties in this view model should be strings that are already formatted or data for which you have element or display templates created.
Upvotes: 1
Reputation: 189
Fill in the service layer with the model and then send it to the view. For example: ViewItem=ModelItem.ToString().Substring(0,100);
Upvotes: 0
Reputation: 859
I ran into a similar problem with the same error message using following code:
@Html.DisplayFor(model => model.EndDate.Value.ToShortDateString())
I found a good answer here
Turns out you can decorate the property in your model with a displayformat then apply a dataformatstring.
Be sure to import the following lib into your model:
using System.ComponentModel.DataAnnotations;
Upvotes: 12
Reputation: 9521
I had the same problem with something like
@foreach (var item in Model)
{
@Html.DisplayFor(m => !item.IsIdle, "BoolIcon")
}
I solved this just by doing
@foreach (var item in Model)
{
var active = !item.IsIdle;
@Html.DisplayFor(m => active , "BoolIcon")
}
When you know the trick, it's simple.
The difference is that, in the first case, I passed a method as a parameter whereas in the second case, it's an expression.
Upvotes: 126
Reputation: 6372
The template it is referring to is the Html helper DisplayFor
.
DisplayFor expects to be given an expression that conforms to the rules as specified in the error message.
You are trying to pass in a method chain to be executed and it doesn't like it.
This is a perfect example of where the MVVM (Model-View-ViewModel) pattern comes in handy.
You could wrap up your Trainer
model class in another class called TrainerViewModel
that could work something like this:
class TrainerViewModel
{
private Trainer _trainer;
public string ShortDescription
{
get
{
return _trainer.Description.ToString().Substring(0, 100);
}
}
public TrainerViewModel(Trainer trainer)
{
_trainer = trainer;
}
}
You would modify your view model class to contain all the properties needed to display that data in the view, hence the name ViewModel.
Then you would modify your controller to return a TrainerViewModel
object rather than a Trainer
object and change your model type declaration in your view file to TrainerViewModel
too.
Upvotes: 83