POIR
POIR

Reputation: 3190

"Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions" error

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

Answers (8)

DroidRiot
DroidRiot

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

JEuvin
JEuvin

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

user3715342
user3715342

Reputation: 41

use @Html.Raw(item.Description.ToString().Substring(0,100))

Upvotes: 3

Jordan
Jordan

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

RainyTears
RainyTears

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

Rob
Rob

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

Daniel
Daniel

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

Sean Airey
Sean Airey

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

Related Questions