Отрисовка искрового вида в строку - PullRequest
2 голосов
/ 29 ноября 2010

Я пытаюсь отобразить частичное представление в виде строки, чтобы оно могло быть возвращено в виде HTML для вызова jjery ajax.После долгих поисков я нашел этот код.

public string RenderAsString(string viewName, string modelName, object model)
{
    // Set up your spark engine goodness.
    var settings = new SparkSettings().SetPageBaseType(typeof(SparkView));
    var templates = new FileSystemViewFolder(Server.MapPath("~/Views"));
    var engine = new SparkViewEngine(settings) { ViewFolder = templates };


    // "Describe" the view (the template, it is a template after all), and its details.
    var descriptor = new SparkViewDescriptor().AddTemplate(@"Shared\" + viewName + ".spark");

    // Create a spark view engine instance
    var view = (SparkView)engine.CreateInstance(descriptor);

    // Add the model to the view data for the view to use.
    view.ViewData[modelName] = model;

    // Render the view to a text writer.
    var writer = new StringWriter(); view.RenderView(writer);

    // Convert to string
    return writer.ToString();
}

Но при выполнении следующей строки:

var view = (SparkView)engine.CreateInstance(descriptor);

я получаю следующую ошибку:

Ошибка компиляции динамического представления.Ссылка на объект требуется для нестатического поля, метода или свойства 'DomainModel.Entities.Region.Id.get.

Это мое частичное представление:

<ViewData Model="Region" />

<div id="${ Region.Id }" class="active-translation-region-widget" >
    <label>${Region.RegionName}</label>
        ${ Html.CheckBox("Active") }
</div> 

Кажется, он не распознает модель.

PS Когда я вызываю вид из родительского вида, вот так

<for each="var region in Model">
    <ActiveTranslationRegion Region="region" if="region.Active==true"></ActiveTranslationRegion>
</for>

Он прекрасно воспроизводится.Что я делаю не так?

Ответы [ 2 ]

2 голосов
/ 30 ноября 2010

Просто глядя на это, я думаю, что следующая строка является проблемой:

<ViewData Model="Region" />

Вместо этого следует читать:

<viewata model="Region" />

Обратите внимание на строчную "модель". Это потому, что model является особым случаем, поскольку за сценой он выполняет приведение к строго типизированной модели представления. Верхний определит переменную с именем Model в сгенерированном классе представления и присвоит ему значение Region. Использование нижнего регистра ниже создаст переменную Model, но также приведёт ее к строго типизированному экземпляру Region, который взят из словаря ViewData.

Примечание При использовании Model в коде, как и в цикле for each, он должен быть в верхнем регистре, что правильно в вашем случае. Еще раз, это единственный особый случай, потому что он извлекает строго типизированную модель из словаря ViewData.

Еще одна вещь - <viewata model="Region" /> должна быть объявлена ​​в родительском представлении, и она может быть определена только один раз для каждой страницы, поэтому вы не можете переопределить ее в частичном представлении. Если это частичное представление, вам лучше использовать его, передавая часть модели, как вы делали во втором примере выше.

Причина вашего исключения выше заключается в том, что он пытается получить свойство Id в качестве статического элемента из Region Типа вместо того, чтобы запрашивать свойство Id в вашем экземпляр из Region как часть вашей модели представления.

В качестве примечания, код для того, чтобы добраться туда, куда вы хотите, немного искажен. Вы можете найти более точные способы сделать то, что вы хотите, проверив некоторые из примеров прямого использования , но я понимаю, что это, вероятно, просто всплеск, чтобы увидеть, как это работает ...:)

Обновление в ответ на ваш ответ на вопрос / ответ

Я вполне уверен, что проблема заключается в передаче Region в следующий вызов:

<ActiveTranslationRegion Region="region" if="region.Active==true">

... снова до именования. Да, у вас может быть только один model на просмотр, как я уже говорил ранее, поэтому вам нужно удалить из верхней части вашей части следующее:

<viewdata model="Region" />

Вот что вызывает проблему. Затем я бы переименовал элемент, входящий в вашу частичку, примерно так:

<ActiveTranslationRegion ActiveRegion="region" if="region.Active==true">

и тогда ваш частичный будет выглядеть так:

<form action="/Translation/DeactivateRegion" class="ui-widget-content active-translation-region-widget">
    <input type="hidden" name="Id" value="${ActiveRegion.Id}" />        
    <label class="region-name">${ ActiveRegion.RegionName }</label>
    <input class="deactivate-region-button" type="image" src=${Url.Content("~/Content/Images/Deactivate.png")} alt="Deactivate" />
</form>

Примечание Я использую ActiveRegion, потому что в синтаксическом анализаторе Spark ActiveRegion объявляется как переменная и присваивается значение region в текущей области видимости при циклическом просмотре for loop. Нет необходимости неукоснительно придерживаться model - потому что вы прошли и передали часть model теперь, когда вы объявили как ActiveRegion. О, и вы могли бы придерживаться имени Region, если вы действительно хотите, я просто изменил его, чтобы подчеркнуть, и потому что у вас есть Type с именем Region в вашем коде, и я не большой Поклонник изворотливых проблем, использующих то же имя для переменной, что и тип. Плюс это делает это немного яснее.

Недостаток вызова метода Html.RenderPartial не сразу очевиден. Одна вещь, которую вы теряете, это 3-проходный рендеринг, который обеспечивает Spark. Если вы используете синтаксис тега (который является предпочтительным), вы сможете размещать партиалы внутри партиалов на нескольких уровнях, передавая переменные, которые передают каждому частичку то, что им нужно в стеке. Он становится действительно мощным - начинайте думать о структурах типа сетки данных, где строки и ячейки являются отдельными частями, которые получают переменные, которые им нужны из модели, и хранятся в хорошем и чистом виде в отдельных управляемых файлах представления. Но не останавливайтесь на этом, начните думать о таргетинге содержимого заголовка и нижнего колонтитула на основе переменных или трех макетов столбцов, которые создают панель мониторинга, которая отображает все виды по индивидуально сложенным частям на многих уровнях.

Вы теряете всю эту гибкость, когда используете стандартный болтовой метод ASP.NET MVC Helper Html.RenderPartial() - будьте осторожны, есть решение, подобное приведенному выше.

Дайте мне знать, если это работает ...
Всего наилучшего
Роб Г

0 голосов
/ 01 декабря 2010

Я изменил код и просмотрел немного. В конце концов, все, что я действительно пытаюсь добиться - это иметь родительское представление (не показано) для итерации IEnumerable и для каждой итерации отображать частичное представление (ActiveTranslationRegion), которое отображает некоторый Html для представления модели региона.

Я также хочу иметь возможность вызывать метод действия с помощью вызова ajax для визуализации отдельного частичного представления ActiveTranslationRegion, передавая ему модель отдельного региона. Я рефакторинг кода соответственно.

Частичное представление (_ActiveTranslationRegion.spark)

<viewdata model="Region" />

<form action="/Translation/DeactivateRegion" class="ui-widget-content active-translation-region-widget">
    <input type="hidden" name="Id" value="${Model.Id}" />        
    <label class="region-name">${ Model.RegionName }</label>
    <input class="deactivate-region-button" type="image" src=${Url.Content("~/Content/Images/Deactivate.png")} alt="Deactivate" />
</form>

Обратите внимание, что с помощью я могу ссылаться на Model в представлении, как предложил RobertTheGrey (см. Выше).

Я удалил весь код, чтобы вернуть представление в виде строки, и просто создал метод метода действия, который возвратил частичное представление:

[UnitOfWork]
public PartialViewResult ActivateRegion(int id)
{
    var region = _repos.Get(id);
    if (region != null)
    {    
        region.Active = true;
        _repos.SaveOrUpdate(region);
    }

    return PartialView("_ActiveTranslationRegion", region);
}

Одна вещь, которую я должен был сделать, это изменить вид моего родителя так:

<div id="inactive-translation-regions-panel">
    <h3 class="ui-widget-header">Inactive Regions</h3>
    <for each="var region in Model">
        <span if="region.Active==false">
                # Html.RenderPartial("_InActiveTranslationRegion", region);
        </span>
    </for>
</div>

Где раньше у меня было следующее:

<div id="inactive-translation-regions-panel">
   <for each="var region in Model">
          <ActiveTranslationRegion Region="region" if="region.Active==true"></ActiveTranslationRegion>
   </for>
</div>

Обратите внимание, что я должен вызывать Html.RenderPartial, а не использовать элемент. Если я пытаюсь использовать элемент (что я предпочел бы сделать), я получаю следующую ошибку:

Only one viewdata model can be declared. IEnumerable<Region> != Region

Есть ли способ обойти эту проблему?

Обновление:

Я испробовал твою рекомендацию, но безуспешно. Чтобы вспомнить проблему, я хочу использовать частичное в 2 разных ситуациях. В первом случае у меня есть родительское представление, которое использует модель IEnumerable<Region>, частичное просто использует Region в качестве своей модели. Поэтому в родительском элементе я перебираю IEnumerable и передаю Region частичному. Во втором случае я хочу вызвать PartialView("_ActiveTranslationRegion", region) из метода действия. Если я удаляю <viewdata model="Region" /> из частичного, я получаю сообщение об ошибке с жалобой на модель. Лучший способ обойти найденную проблему - добавить привязку к файлу bindings.xml:

<element name="partial"># Html.RenderPartial("@name", @model);</element>

(Примечание. Кажется очень важным сохранить эту запись в файле привязок на одной строке)

Таким образом, я по-прежнему могу вызывать частичное из метода действия, как описано выше, и передавать его Region в качестве модели, но я также могу заменить свой вызов Html.RenderPartial в родительском представлении более «html» подобным тегом. :

<partial name="_ActiveTranslationRegion" model="region" />

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

<div id="inactive-translation-regions-panel">
    <h3 class="ui-widget-header">Inactive Regions</h3>
    <for each="var region in Model">
        <span if="region.Active==false">
               <partial name="_ActiveTranslationRegion" model="region" />
        </span>
    </for>
</div>

Конечно, под капотом все еще звонит

# Html.RenderPartial("_ActiveTranslationRegion", region);

Но это лучшее решение, которое мы могли бы придумать.

С уважением, Simon

...