Привязать значения свойства моей модели из общего шаблона EditorFor - PullRequest
0 голосов
/ 09 октября 2018

Я пытаюсь написать общую платформу для помощи в разработке форм в стиле мастера.

У меня есть модель со свойствами, представляющими каждый шаг в мастере, например,

public class ExampleWizardTransaction : WizardTransaction
{
    public override TransactionType TransactionType { get; set; } = TransactionType.ExampleWizard;
    public override string ControllerName { get; set; } = "WizardExample";

    [DisplayName("Client Details")]
    public ClientDetails ClientDetails { get; set; } = new ClientDetails();

    [DisplayName("Client Questions")]
    public ClientQuestions ClientQuestions { get; set; } = new ClientQuestions();

    [DisplayName("Client Preferences")]
    public ClientPreferences ClientPreferences { get; set; } = new ClientPreferences();
}

[Step(1)]
public class ClientDetails : IStep
{
    [Display(Description = "Please enter your first name")]
    public string FirstName { get; set; }

    [Display(Description = "Please enter your last name")]
    public string LastName { get; set; }
}

[Step(2)]
public class ClientQuestions : IStep
{
    [DisplayName("What is your favourite car?")]
    public string FavouriteCar { get; set; }

    [DisplayName("What is your favourite holiday destination?")]
    public string FavouriteDestination { get; set; }
}

[Step(3)]
public class ClientPreferences : IStep
{
    [DisplayName("Red or Blue?")]
    public Colours Colour { get; set; }

    [DisplayName("Do you like Indian food")]
    public bool LikeFood { get; set; }
}

Изначально у меня было частичное представление для каждого шага мастера, которое выглядело так:

@model Web.Models.ExampleWizard.Create

<div class="row">
    <div class="col-md-6">
        @Html.EditorFor(x => x.ExampleWizardTransaction.ClientDetails)
    </div>
</div>

Используя это, значения моей формы связываются правильно, так как, когда я публикую ее, MVC знает контекст привязки.

В моей форме я отрисовываю частичное представление, передавая номер шага, например

Html.RenderPartial($"_CreateWizard_{Model.ExampleWizardTransaction.CurrentStep}", Model);

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

Для этого я выполняю действие, которое определяет, какой тип связан с шагом мастера, и возвращаю частичное представление:

Html.RenderAction("GetStep", "ExampleWizard", Model.ExampleWizardTransaction);

В моем частичном представлении указывается интерфейс, который каждыйшаг мастера реализует:

_WizardStep.cshtml

@model Services.ViewModels.Wizard.IStep

<div class="row">
    <div class="col-md-6">
        @Html.EditorFor(x => x)
    </div>
</div>

Когда я использую вышеупомянутое, форма отображается правильно, но значения больше не связываются на POST,что я предполагаю, потому что он не имеет привязки контекста для свойств (например, идентификатор и имя типов ввода не полностью определены).

У меня есть шаблон EditorFor для свойств строки на шагах мастера, который отображает текстовое поле:

@model string
<div class="col-md-12">
    <div class="form-group">
        <label class="m-b-none" for="@ViewData.Model">
            @ViewData.ModelMetadata.DisplayName
        </label>
        <span class="help-block m-b-none small m-t-none">@ViewData.ModelMetadata.Description</span>
        <div class="input-group">
            @Html.TextBox("", Model, new {@class = "form-control"})
            <div class="input-group-addon">
                <i class="fa validation"></i>
            </div>
        </div>

    </div>
</div>

Можно ли использовать мой общий частичный вид "_WizardStep.cshtml" и по-прежнему связыватьсясвойства текущего шага к моей модели?

Мой контроллер выглядит следующим образом:

[HttpPost]
public virtual ActionResult CreateWizard(Create model, string action)
{
    var createModel = CreateModel<Create>();
    switch (createModel.Save(action))
    {
        case WizardState.Finished:
            return RedirectToActionWithMessage("List", "Transaction", "Completed", ToastNotificationStatus.Success);
        case WizardState.Ongoing:
            return RedirectToAction(MVC.ExampleWizard.CreateWizard(
                model.ExampleWizardTransaction.Id,
                model.ExampleWizardTransaction.GetNextStep(action)));
        default:
            model.MapExistingTransactions<ExampleWizardTransaction>();
            return View(model);
    }
}

Моя модель «Создать» содержит мое свойство «ExampleWizardTransaction», которое содержит каждый из шагов мастера, реализующих интерфейс IStep.

1 Ответ

0 голосов
/ 10 октября 2018

Вдохновившись ответом @ StephenMuecke, я выбрал следующий подход.

В моем представлении «CreateWizard.cshtml» я отображаю шаг со следующей строкой:

@Html.WizardPartialFor(x => x.ExampleWizardTransaction.GetStepObject<IStep>(), "_WizardStep", Model.ExampleWizardTransaction)

Это вызывает метод расширения «WizardPartialFor»:

public static MvcHtmlString WizardPartialFor<TModel, TProperty>(this HtmlHelper<TModel> helper,
    Expression<Func<TModel, TProperty>> expression, string partialViewName, IWizardTransaction wizardTransaction)
{
    var compiled = expression.Compile();
    var result = compiled.Invoke(helper.ViewData.Model);

    PropertyInfo currentStep = wizardTransaction.GetCurrentStepPropertyInfo();

    string currentStepName = currentStep.PropertyType.Name;

    var name = $"{currentStep.DeclaringType.Name}.{currentStepName}";

    var viewData = new ViewDataDictionary(helper.ViewData)
    {
        TemplateInfo = new TemplateInfo { HtmlFieldPrefix = name }
    };

    return helper.Partial(partialViewName, result, viewData);
}

public static PropertyInfo GetCurrentStepPropertyInfo(this IWizardTransaction wizardTransaction)
{
    var properties = wizardTransaction.GetType().GetProperties()
        .Where(x => x.PropertyType.GetCustomAttributes(typeof(StepAttribute), true).Any());

    return properties.FirstOrDefault(x => ((StepAttribute)Attribute
        .GetCustomAttribute(x.PropertyType, typeof(StepAttribute))).Step == wizardTransaction.CurrentStep);
}

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

public static TProperty GetStepObject<TProperty>(this IWizardTransaction wizardTransaction)
    where TProperty : class
{
    var properties = wizardTransaction.GetType().GetProperties()
        .Where(x => x.PropertyType.GetCustomAttributes(typeof(StepAttribute), true).Any());

    var @object = properties.FirstOrDefault(x => ((StepAttribute)Attribute
            .GetCustomAttribute(x.PropertyType, typeof(StepAttribute))).Step == wizardTransaction.CurrentStep);

    return @object.GetValue(wizardTransaction) as TProperty;
}

Это успешно отображает мой общий частичный просмотр _WizardStep и также успешно связывает данные в POST.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...