Dynamic CSS in an ASP.NET Core MVC View Component

This post shows how a view with dynamic css styles could be implemented using an MVC view component in ASP.NET Core. The values are changed using a HTML form with ASP.NET Core tag helpers, and passed into the view component which displays the view using css styling. The styles are set at runtime.

Code: https://github.com/damienbod/AspNetCoreMvcDynamicViews

2019-09-14: Updated to ASP.NET Core 3.0

2019-01-30: Updated to ASP.NET Core 2.2

Creating the View Component

The View Component is a nice way of implementing components in ASP.NET Core MVC. The view component is saved in the \Views\Shared\Components\DynamicDisplay folder which fulfils some of the standard paths which are pre-defined by ASP.NET Core. This can be changed, but I always try to use the defaults where possible.

The DynamicDisplay class implements the ViewComponent class, which has a single async method InvokeAsync that returns a Task with the IViewComponentResult type.

using AspNetCoreMvcDynamicViews.Views.Shared.Components.DynamicDisplay;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

namespace AspNetCoreMvcDynamicViews.Views.Home.ViewComponents
{
    [ViewComponent(Name = "DynamicDisplay")]
    public class DynamicDisplay : ViewComponent
    {
        public async Task<IViewComponentResult> InvokeAsync(DynamicDisplayModel dynamicDisplayModel)
        {
            return View(await Task.FromResult(dynamicDisplayModel));
        }
    }
}

The view component uses a simple view model with some helper methods to make it easier to use the data in a cshtml view.

namespace AspNetCoreMvcDynamicViews.Views.Shared.Components.DynamicDisplay
{
    public class DynamicDisplayModel
    {
        public int NoOfHoles { get; set; } = 2;

        public int BoxHeight { get; set; } = 100;

        public int NoOfBoxes { get; set; } = 2;

        public int BoxWidth { get; set; } = 200;

        public string GetAsStringWithPx(int value)
        {
            return $"{value}px";
        }

        public string GetDisplayHeight()
        {
            return $"{BoxHeight + 50 }px";
        }

        public string GetDisplayWidth()
        {
            return $"{BoxWidth * NoOfBoxes}px";
        }
    }
}

The cshtml view uses both css classes and styles to do a dynamic display of the data.

@using AspNetCoreMvcDynamicViews.Views.Shared.Components.DynamicDisplay
@model DynamicDisplayModel

<div style="height:@Model.GetDisplayHeight(); width:@Model.GetDisplayWidth()">
    @for (var i = 0; i < Model.NoOfBoxes; i++)
    {
    <div class="box" style="width:@Model.GetAsStringWithPx(Model.BoxWidth);height:@Model.GetAsStringWithPx(Model.BoxHeight);">
        @if (Model.NoOfHoles == 4)
        {
            @await Html.PartialAsync("./FourHolesPartial.cshtml")
        }
        else if (Model.NoOfHoles == 2)
        {
            @await Html.PartialAsync("./TwoHolesPartial.cshtml")
        }
        else if (Model.NoOfHoles == 1)
        {
            <div class="row justify-content-center align-items-center" style="height:100%">
                <span class="dot" style=""></span>
            </div>
        }
    </div>
    }
</div>

Partial views are used inside the view component to display some of the different styles. The partial view is added using the @await Html.PartialAsync call. The box with the four holes is implemented in a partial view.

<div class="row" style="height:50%">
    <div class="col-6">
        <span class="dot" style="float:left;"></span>
    </div>
    <div class="col-6">
        <span class="dot" style="float:right;"></span>
    </div>
</div>

<div class="row align-items-end" style="height:50%">
    <div class="col-6">
        <span class="dot" style="float:left;"></span>
    </div>
    <div class="col-6">
        <span class="dot" style="float:right;"></span>
    </div>
</div>

And CSS classes are used to display the data.

.dot {
	height: 25px;
	width: 25px;
	background-color: #bbb;
	border-radius: 50%;
	display: inline-block;
}

.box {
	float: left;
	height: 100px;
	border: 1px solid gray;
	padding: 5px;
	margin: 5px;
	margin-left: 0;
	margin-right: -1px;
}

Using the View Component

The view component is then used in a cshtml view. This view implements the form which sends the data to the server. The view component is added using the Component.InvokeAsync method which takes only a model as a parameter and then name of the view component.

@using AspNetCoreMvcDynamicViews.Views.Shared.Components.DynamicDisplay
@model MyDisplayModel
@{
    ViewData["Title"] = "Home Page";
}

<div style="padding:20px;"></div>

<form asp-controller="Home" asp-action="Index" method="post">
    <div class="col-md-12">

        @*<div class="form-group row">
            <label  class="col-sm-3 col-form-label font-weight-bold">Circles</label>
            <div class="col-sm-9">
                <select class="form-control" asp-for="mmm" asp-items="mmmItmes"></select>
            </div>
        </div>*@

        <div class="form-group row">
            <label class="col-sm-5 col-form-label font-weight-bold">No of Holes</label>
            <select class="col-sm-5 form-control" asp-for="DynamicDisplayData.NoOfHoles">
                <option value="0" selected>No Holes</option>
                <option value="1">1 Hole</option>
                <option value="2">2 Holes</option>
                <option value="4">4 Holes</option>
            </select>
        </div>

        <div class="form-group row">
            <label class="col-sm-5 col-form-label font-weight-bold">Height in mm</label>
            <input class="col-sm-5 form-control" asp-for="DynamicDisplayData.BoxHeight" type="number" min="65" max="400" />
        </div>

        <div class="form-group row">
            <label class="col-sm-5 col-form-label font-weight-bold">No. of Boxes</label>
            <input class="col-sm-5 form-control" asp-for="DynamicDisplayData.NoOfBoxes" type="number" min="1" max="7" />
        </div>

        <div class="form-group row">
            <label class="col-sm-5 col-form-label font-weight-bold">Box Width</label>
            <input class="col-sm-5 form-control" asp-for="DynamicDisplayData.BoxWidth" type="number" min="65" max="400" />
        </div>

        <div class="form-group row">
            <button class="btn btn-primary col-sm-10" type="submit">Update</button>
        </div>

    </div>

    @await Component.InvokeAsync("DynamicDisplay", Model.DynamicDisplayData)

</form>

The MVC Controller implements two methods, one for the GET, and one for the POST. The form uses the POST to send the data to the server, so this could be saved if required.

public class HomeController : Controller
{
	[HttpGet]
	public IActionResult Index()
	{
		var model = new MyDisplayModel
		{
			DynamicDisplayData = new DynamicDisplayModel()
		};
		return View(model);
	}

	[HttpPost]
	public IActionResult Index(MyDisplayModel myDisplayModel)
	{
		// save data to db...
		return View("Index", myDisplayModel);
	}

Running the demo

When the application is started, the form is displayed, and the default values are displayed.

And when the update button is clicked, the values are visualized inside the view component.

Links:

https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-2.1

https://docs.microsoft.com/en-us/aspnet/core/mvc/views/partial?view=aspnetcore-2.1

https://docs.microsoft.com/en-us/aspnet/core/mvc/views/razor?view=aspnetcore-2.1

3 comments

  1. […] Dynamic CSS in an ASP.NET Core MVC View Component – Damien Bowden […]

  2. […] Dynamic CSS in an ASP.NET Core MVC View Component (Damien Bowden) […]

  3. […] Du CSS dynamique avec un View Component ASP.NET Core MVC. […]

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.