Reputation: 577
I'm creating an application where the user has to complete a series of steps sequentially. Within the controller I check if each step is complete or in progress and then based on this information, I add the style attributes to the ViewModel which are then passed to the view.
Here's the view with the steps:
So in the controller I have the following code that is currently populating the view:
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";
The controller goes on and on as the viewModel is populated for each step.
The html in view for each panel look like this:
<div class="panel [email protected]">
<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>
Although this approach is working fine, the controller contains over 500 lines of code, just to populate the viewModel depending on what step the user is at.
Is there a better approach to doing this?
Upvotes: 2
Views: 2199
Reputation:
Not only could you reduce the controller code to just a few lines, you can significantly reduce the view code as well.
Rather than having a view models with multiple properties for each step, use a collection to represent the steps
public enum StepStatus
{
Upcoming,
Current,
Complete
}
public class Step
{
public int Number { get; set; }
public StepStatus Status { get; set; }
}
Then in the method which generates the view
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);
}
And the view simply uses a loop to generate each step
@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>
and add some css to mimic the panel-succcess
, panel-infoand panel-default
bootstap classes
.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;
}
You have not indicated how your rendering the content within the <div class="panel-body">
element, but this can simply be done using @Html.Action()
if you want to include content for all steps, or using ajax to load the content as required. Using some conventions, the controller method could simply be
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);
}
where your partials are named _Step1Complete.cshtml
, _Step1Current.cshtml
etc (I assume you have different content based on the status), and the to generate the content in the view
<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>
Upvotes: 1
Reputation: 696
I would suggest to do the styling via javascript or jQuery and CSS. By evaluating your ViewModel. Let the browser do the handling of UI styling which was designed to that.
Upvotes: 0