MVC3 HtmlHelpers с использованием Generics и LINQ - PullRequest
3 голосов
/ 29 июня 2011

ОБНОВЛЕНО

Я пытаюсь создать расширение HtmlHelper, которое может принимать коллекцию TModels.Затем установите выражение, которое будет извлекать объявленное свойство foreach в коллекции и распечатывать их.

Этот код ниже работает.Тем не менее, единственное, что мне не нравится в шаблоне, который у меня есть на данный момент, это то, что обобщенные типы не выводятся.Я должен объявить типы для моего метода расширения.

Я пытаюсь следовать типу шаблона, который Telerik использовал для своей Grid, когда при объявлении модели выводятся общие типы.

Я хотел бы сделать это в моемview:

@model List<MyProject.MyModels.UserModel>
@(Html.MyExtensions()
      .PrintUsers(Model)
      .FirstName(m => m.FirstName)
      .LastName(m => m.LastName)
)

С моим текущим шаблоном я должен сделать это:

@model List<MyProject.MyModels.UserModel>
@(Html.MyExtensions<List<MyProject.MyModels.UserModel>, MyProject.MyModels.UserModel>()
      .PrintUsers(Model)
      .FirstName(m => m.FirstName)
      .LastName(m => m.LastName)
)

Так что мне нужно выяснить лучший шаблон, чтобы мои типы выводились.Есть идеи?

UserModel выглядит так:

public class UserModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Итак, у меня есть HtmlHelper, который выглядит так:

public static class UIExtension
{
    public static ComponentFactory<TModel, T> MyExtensions<TModel, T>(this HtmlHelper<TModel> html) 
        where TModel : IEnumerable<T>
        where T : class
    {
        return new ComponentFactory<TModel, T>(html);
    }
}

Итак, один из моих компонентоввзял бы List, а затем повторил бы печать каждого объявленного свойства из выражения Linq на страницу.

В моем ComponentFactory у меня есть это:

public class ComponentFactory<TModel, T>
    where T : class
{
    private HtmlHelper<TModel> _html;

    public ComponentFactory()
    {
    }

    public ComponentFactory(HtmlHelper<TModel> html)
    {
        this._html = html;
    }

    public ListComponent<T> PrintUsers(IEnumerable<T> model)
    {
        return new ListComponent<T>(model);
    }

    /* Add other ui components to this factory */
}

Тогда мой ListComponent

public class ListComponent<T> : IHtmlString
{
    private Expression<Func<T, string>> _firstname;
    private Expression<Func<T, string>> _lastname;

    private IEnumerable<T> _model;

    public ListComponent(IEnumerable<T> model)
    {
        this._model = model;
    }

    public ListComponent<T> FirstName(Expression<Func<T, string>> property)
    {
        this._firstname = property;
        return this;
    }

    public ListComponent<T> LastName(Expression<Func<T, string>> property)
    {
        this._lastname = property;
        return this;
    }

    public override MvcHtmlString Render()
    {
        TagBuilder container = new TagBuilder("div");

        TagBuilder title = new TagBuilder("div");
        title.InnerHtml = "<b>Names</b>";
        container.InnerHtml = title.ToString(TagRenderMode.Normal);

        TagBuilder ul = new TagBuilder("ul");

        foreach (var item in this._model)
        {
            TagBuilder li = new TagBuilder("li");
            li.SetInnerText(String.Format("{0} {1}", _firstname.Compile()(item), _lastname.Compile()(item)));
            ul.InnerHtml += li.ToString(TagRenderMode.Normal);
        }

        container.InnerHtml += ul.ToString(TagRenderMode.Normal);

        return MvcHtmlString.Create(container.ToString(TagRenderMode.Normal));
    }
}

Спасибо за любую помощь и предложения!

Ответы [ 3 ]

2 голосов
/ 30 июня 2011

Что-то вроде этого:

public static class UIExtension
{
    public static ComponentFactory<TModel> MyExtensions<TModel>(this HtmlHelper<IEnumerable<TModel>> html) where TModel : class

    {
        return new ComponentFactory<TModel>(html);
    }
}
public class UserModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
public class ComponentFactory<TModel>
where TModel : class
{
    private HtmlHelper _html;

    public ComponentFactory()
    {
    }

    public ComponentFactory(HtmlHelper<IEnumerable<TModel>> html)
    {
        this._html = html;
    }

    public ListComponent<TModel> PrintUsers(IEnumerable<TModel> model)
    {
        return new ListComponent<TModel>(model);
    }

    /* Add other ui components to this factory */
}
public class ListComponent<T> : IHtmlString
{
    private Expression<Func<T, string>> _firstname;
    private Expression<Func<T, string>> _lastname;

    private IEnumerable<T> _model;

    public ListComponent(IEnumerable<T> model)
    {
        this._model = model;
    }

    public ListComponent<T> FirstName(Expression<Func<T, string>> property)
    {
        this._firstname = property;
        return this;
    }

    public ListComponent<T> LastName(Expression<Func<T, string>> property)
    {
        this._lastname = property;
        return this;
    }



public override MvcHtmlString Render()
{
    TagBuilder container = new TagBuilder("div");

    TagBuilder title = new TagBuilder("div");
    title.InnerHtml = "<b>Names</b>";
    container.InnerHtml = title.ToString(TagRenderMode.Normal);

    TagBuilder ul = new TagBuilder("ul");

    foreach (var item in this._model)
    {
        TagBuilder li = new TagBuilder("li");
        li.SetInnerText(String.Format("{0} {1}", _firstname.Compile()(item), _lastname.Compile()(item)));
        ul.InnerHtml += li.ToString(TagRenderMode.Normal);
    }

    container.InnerHtml += ul.ToString(TagRenderMode.Normal);

    return MvcHtmlString.Create(container.ToString(TagRenderMode.Normal));
}
}

Тогда используется следующее:

Html.MyExtensions().PrintUsers(Model).FirstName(p=>p.FirstName).LastName(p=>p.LastName)
1 голос
/ 01 июля 2011

Посмотрев на ответ @Tomas, он помог мне понять, чего мне не хватало в шаблоне. Хотя ответ @Tomas действительно дал мне синтаксис, который я хотел в View, где Model выводился правильно, единственным недостатком было то, что весь ViewModel был привязан к IEnumerable<T>. Так как я хотел бы привязать к свойству в ViewModel свойство IEnumerable, я внес пару изменений, и это сработало.

Расширение HtmlHelper:

public static class UIExtension
{
   public static ComponentFactory MyExtensions(this HtmlHelper html)
   {
       return new ComponentFactory(html);
   }

   public static ComponentFactory<TModel> MyExtensions<TModel>(this HtmlHelper<TModel> html) 
      where TModel : class
   {
       return new ComponentFactory<TModel>(html);
   }
}

ComponentFactory:

public class ComponentFactory<TModel>
{
   private HtmlHelper<TModel> _html;
   public ComponentFactory(HtmlHelper<TModel> html)
   {
      this._html = html;
   }

   public ListComponent<TObj> PrintUsers<TObj>(IEnumerable<TObj> model)
   {
      return new ListComponent<TObj>(model);
   }
}

Итак, основное изменение заключается в том, что для метода PrintUsers я использую другой универсальный тип для обработки этой части. Таким образом, мой Model может быть тем, что решит разработчик, и они просто устанавливают Property для использования в качестве datasource для ListComponent.

Так что теперь в View он очень гибкий, я могу сделать что-то вроде этого ...

@(Html.MyExtensions()
      .PrintUsers(Model.MyList)
      .FirstName(m => m.FirstName)
      .LastName(m => m.LastName)
)
0 голосов
/ 29 июня 2011

Вы можете использовать два параметра типа, один для списка и один для типа элемента:

ListComponent<TList, TItem> Print<TList, TItem>(TList list) where TList : IEnumerable<TItem>

ОБНОВЛЕНИЕ: Возможно, так будет работать вывод типа:

ListComponent<TItem> Print<TItem>(IEnumerable<TItem> list)
...