根据来自控制器的逻辑在 MVC 中设置视图样式

Styling a view in MVC based on logic from the controller

我正在创建一个应用程序,用户必须按顺序完成一系列步骤。在控制器中,我检查每个步骤是否完成或正在进行,然后根据此信息,我将样式属性添加到 ViewModel,然后将其传递给视图。

以下是包含步骤的视图:

所以在控制器中,我有以下代码当前正在填充视图:

switch (WhatStep)
{
    case 1:
        ViewModel.WhatStep = 1;

        ViewModel.StepOneComplete = false;
        ViewModel.StepOnePanelColour = "info";
        ViewModel.StepOneStatusText = "<span class=\"text-info pull-right\"><strong>Next Step</strong></span>";
        ViewModel.StepOneCollapsedText = "open";

        ViewModel.StepTwoComplete = false;
        ViewModel.StepTwoPanelColour = "default";
        ViewModel.StepTwoStatusText = "<span class=\"text-default pull-right\"><strong>Upcoming</strong></span>";
        ViewModel.StepTwoCollapsedText = "collapsed";

        ViewModel.StepThreeComplete = false;
        ViewModel.StepThreePanelColour = "default";
        ViewModel.StepThreeStatusText = "<span class=\"text-default pull-right\"><strong>Upcoming</strong></span>";
        ViewModel.StepThreeCollapsedText = "collapsed";

        ViewModel.StepFourComplete = false;
        ViewModel.StepFourPanelColour = "default";
        ViewModel.StepFourStatusText = "<span class=\"text-default pull-right\"><strong>Upcoming</strong></span>";
        ViewModel.StepFourCollapsedText = "open";

        ViewModel.StepFiveComplete = false;
        ViewModel.StepFivePanelColour = "default";
        ViewModel.StepFiveStatusText = "<span class=\"text-default pull-right\"><strong>Upcoming</strong></span>";
        ViewModel.StepFiveCollapsedText = "collapsed";

        ViewModel.StepSixComplete = false;
        ViewModel.StepSixPanelColour = "default";
        ViewModel.StepSixStatusText = "<span class=\"text-default pull-right\"><strong>Upcoming</strong></span>";
        ViewModel.StepSixCollapsedText = "collapsed";

        break;
    case 2:
        ViewModel.WhatStep = 2;

        ViewModel.StepOneComplete = true;
        ViewModel.StepOnePanelColour = "success";
        ViewModel.StepOneStatusText = "<span class=\"text-success pull-right\"><strong>✔ Complete</strong></span>";
        ViewModel.StepOneCollapsedText = "collapsed";

        ViewModel.StepTwoComplete = false;
        ViewModel.StepTwoPanelColour = "info";
        ViewModel.StepTwoStatusText = "<span class=\"text-info pull-right\"><strong>Next Step</strong></span>";
        ViewModel.StepTwoCollapsedText = "open";

随着为每个步骤填充 viewModel,控制器不断运行。

每个面板的 html 视图如下所示:

<div class="panel panel-@Model.StepOnePanelColour">
    <div class="panel-heading">
        <h4 class="panel-title">
            <strong>Step One:</strong>
            <a data-toggle="collapse" data-parent="#accordion" href="#collapseOne" class="@Model.StepOneCollapsedText"></a>
            @Html.Raw(Model.StepOneStatusText)
        </h4>
    </div>
    <div id="collapseOne" class="panel-collapse collapse" style="height: 0px;">
        <div class="panel-body">
            <p>Instructions here<p>
        </div>
    </div>
</div>

虽然这种方法工作正常,但控制器包含 500 多行代码,只是为了根据用户所处的步骤填充 viewModel。

有更好的方法吗?

我建议通过 javascript 或 jQuery 和 CSS 进行样式设置。通过评估您的 ViewModel。让浏览器处理专门为此设计的 UI 样式。

您不仅可以将控制器代码减少到几行,还可以显着减少视图代码。

与其为每个步骤使用具有多个属性的视图模型,不如使用集合来表示步骤

public enum StepStatus
{
    Upcoming,
    Current,
    Complete
}
public class Step
{
    public int Number { get; set; } 
    public StepStatus Status { get; set; }
}

然后在生成视图的方法中

public ActionResult Index(int currentStep)
{
    List<Step> model = new List<Step>();
    for (int i = 0; i < 10; i++) // generates 10 steps
    {
        model.Add(new Step() { Number = i + 1 });
    }
    // Set the status of the steps based on the current step
    for (int i = 0; i < currentStep; i++)
    {
        model[i].Status = StepStatus.Complete;
    }
    model[currentStep - 1].Status = StepStatus.InProgress;
    return View(model);
}

而视图只是使用一个循环来生成每个步骤

@model List<Step>
....
<div id="accordion">
    @for (int i = 0; i < Model.Count; i++)
    {
        <div class="panel @Model[i].Status.ToString().ToLower()"> // adds class name based on the status
            <div class=" panel-heading">
                <h4 class="panel-title">
                    <strong>Step @Model[i].Number</strong>
                    @{ string className = Model[i].Status == StepStatus.Current ? "open" : "collapsed"; }
                    <a data-toggle="collapse" data-parent="#accordion" href="#step@(Model[i].Number)" class="@className"></a>
                    <span class="text-default pull-right">
                        @if (Model[i].Status == StepStatus.Complete)
                        {
                            <span class="glyphicon glyphicon-ok" aria-hidden="true"></span> // adds the tick mark
                        }
                        <strong>@Model[i].Status</strong>
                    </span>
                </h4>
            </div>
            <div id="step@(Model[i].Number)" class="panel-collapse collapse">
                <div class="panel-body">
                    <p>Instructions here<p>
                </div>
            </div>
        </div>
    }
</div>

并添加一些 css 来模仿 panel-succcess、panel-infoand panel-default bootstap 类

.complete {
    border-color: #d6e9c6;
}
.complete > .panel-heading {
    color: #3c763d;
    background-color: #dff0d8;
    border-color: #d6e9c6;
}
.current {
    border-color: #bce8f1;
}
.current > .panel-heading {
    color: #31708f;
    background-color: #d9edf7;
    border-color: #bce8f1;
}
.upcoming {
    border-color: #ddd;
}
.upcoming > .panel-heading {
    color: #333;
    background-color: #f5f5f5;
    border-color: #ddd;
}

您没有说明如何在 <div class="panel-body"> 元素中呈现内容,但是如果您想包含所有步骤的内容,可以使用 @Html.Action() 或使用 ajax 根据需要加载内容。使用一些约定,控制器方法可以简单地是

public PartialViewResult Step(int number, StepStatus status)
{
    var model = .... // get the model based on the step number
    string viewName = string.Format("_Step{0}{1}", number, status)
    return PartialView(viewName, model);
}

你的部分内容被命名为 _Step1Complete.cshtml_Step1Current.cshtml 等(我假设你根据状态有不同的内容),以及在视图中生成内容的

<div class="panel-body">
    @{ Html.RenderAction("Step", new { number = Model[i].Number, status = Model[i].Status }) // assumes the Step() method is in the same controller
</div>