0

The project is currently MVC3 but could be upgraded if required.

I'm trying to get a content editor for a simple cms improved. At the moment we use

@Html.EditorFor(model=>model.Content) //Content is a List<EditableContent>

EditableContent has a (brand new) property of string EditorView. The idea is this will name suitable editors for particular items of content.

What I want to do is use my default EditableContent template if EditorView is null, otherwise use the EditorView as the named template. I'm aware of the overload for EditorFor that takes a template, but that's going to use the same template for each item of content, but that's not what I want.

I did try taking my default template and basically doing

//Shared/EditorTemplates/EditableContent.cshtml
@model Website.Areas.Admin.EditableContent

@if (!string.IsNullOrWhiteSpace(Model.EditorView))
{
<text>
    @Model.EditorView
    @Html.EditorForModel(Model.EditorView)
</text>
}
else
{
  //Original template
}

Weirdly in this case, the view is located (I have a slightly custom view engine which appears to locate the view correctly). The name of the view (via @Model.EditorView ) is emitted to the page, but @Html.EditorForModel() doesn't produce anything.

Any ideas how I can use a custom template per item using EditorFor or similar ?

6
  • 1
    I feel like your trying to bypass something that should already be well implemented in the MVC framework. What exactly are you trying to acheive at a higher level? Perhaps we could guide you in the right direction instead of helping you making this hack work? (Or perhaps I'm off the track and what you are doing is right! :D) Commented Oct 1, 2012 at 12:15
  • What is the full path to the view being referenced in Model.EditorView in your test case? EditorForModel(string) looks in the controller's editor templates folder, then in 'Views\Shared\EditorTemplates', and if not found in either of those locations, the default template is used. Commented Oct 1, 2012 at 12:54
  • Additionally, since you are calling EditorForModel from the deafault template already, i'm not sure how MVC will behave when it tries to find the default template. Or if it will even try find and use the specified template assuming it's in one of the paths it searches as mentioned above. Commented Oct 1, 2012 at 12:55
  • @pluc I'm trying to include different editorfor templates against a collection (so that the actual template rendered can be different for each item in the collection, depending on data within that item) My hack is horrid, but I'm sure its not covered in the framework Commented Oct 1, 2012 at 13:30
  • @GiscardBiamby the template is in an area, but in Views\SharedEditorTemplates and is found by the custom view engine. I think your second comment is more the issue. Commented Oct 1, 2012 at 13:31

2 Answers 2

2

Interface

interface IEditableContent
{
    public virtual DatabaseContext.Content Content { get; set; }
}

Implementation

class SomeTypeEditableContent : IEditableContent
{
    public SomeTypeEditableContent(DatabaseContext.Content c)
    {
        Content = c;
    }
}

Service (Repository or Controller action)

List<IEditableContent> lEditableContent = new List<IEditableContent>();
foreach(var c in db.Contents)
{
    switch (c.EditorView)
    {
    case "SomeType":
        lEditableContent.Add(new SomeTypeEditableContent(c));
        break;
    default:
        lEditableContent.Add(new DefaultEditableContent(c));
        break;
    }
}

View

@Html.EditorFor(model=>model.ListContent) //List<IEditableContent>

Editable SomeType Template

@model SomeTypeEditableContent
//Display this as a editable string

Default Editable Content Template

@model IEditableContent
//Use default display

I'm sorry I do not have the time to test it. Let me know how it goes and i'll edit / delete this answer if it does not. I would have posted this as a comment but I needed anotation and formating. But at least it gives you an idea of what I had in mind.

-Edit-

The point of this exemple is to show you that MVC framework should be able to determine the proper template for a implementated interface even if simply providing the object typed as the interface. It will use refelction (i believe?) to find the highest class definition then look for the template.

-Edit 2 (based off comments)-

Changed the exemple to match new specifications

Sign up to request clarification or add additional context in comments.

7 Comments

I like this method of handling it. But one drawback is that you'd have to modify the code that loads these IEditableContent objects from the db, so that it constructs the proper concrete class based on whether or not the EditorView property in the database has a value. I think it would be easier to use a delegating helper method in this case.
In my exemple, EditorView is no longer used as it will base the object's view on which implementation of IEditableContent it is. From the OP, it is not clear wether EditorView is stored in the database (Which I would highly discourage in the first place) or determined in the business logic. The way I see it, he is loading data, builds a list of EditableContent which contains the Entity + the type of display based on X conditions then uses a template to display the proper view for each element.
You're right, it depends on how the OP's setup looks. he didn't describe if the EditorView, the name of the view to use when he wants to override the default template, was stored in the database for each EditableContent item.
Each Entity Framework Content item is functionally identical, it has a contentType (which is where the EditorView is stored) and a number of fields such as BodyText, Heading, Image, HeadingLink, ImageLink etc. No inheritance here (but could/should have been done that way). The code that projects this into EditableContent could do as @pluc suggests here. The Templates will just show some fields, specific labels, help text etc and have behaviours tailored to the content type.
In that case pluc's solution would work pretty well. I was thinking each instance of a content item could have a different value for EditorView.
|
1

It seems weird that you are trying to override the editor template from a view that is already overriding the template. I'm not sure if that will work (it might, but I have never tried so I don't know). I've already added a couple comments to your question above.

If it turns out that trying to specify templates from the EditorTemplates folder doesn't work, which would be understandable, I think you could achieve what you want by introducing a custom EditorForModel() override. You can use David Ebbo's Razor Generator VS extension to define a razor helper that will be usable across your entire project.

/// /Views/Helpers/AndiEditorForExtensions.cshtml
@helper AndiEditorFor(HtmlHelper html, Website.Areas.Admin.EditableContent model) {
    if (!string.IsNullOrWhiteSpace(Model.EditorView)) {
    <text>
        @Model.EditorView
        @Html.EditorForModel(Model.EditorView)
    </text>
    }
    else {
        // Call @Html.EditorFor(), which will use the default editor template (i.e., NOT your custom one defined in the database)
        @Html.EditorFor(model) 
    }
}

Then from your normal code that loads the EditableContent items from the db and displays them, call your custom extension:

@Html.AndiEditorFor(model)

UPDATE:

A better option might be to skip the Razor Generator altogether. Just define a custom HtmlHelper, mimicking the EditorExtensions methods. Add a similar if/else to handle Model.EditorView having a value or not.

2 Comments

I agree its weird, and probably why it doesn't work. Not sure I want the Razor Generator as I already have custom view engines. Isn't that going to be trouble? But I'll see if I can run with the idea
I'm not sure if it's going to be trouble, but I don't see why it would be unless you've disabled the Razor view engine in your project. The Razor Editor isn't necessary. Since this AndiEditorFor extension will just delegate view rendering, it can just be a custom HtmlHelperExtension, inside a C# class called HtmlHelperExtensions for example.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.