Lightweight Bar Chart in Vanilla CSS

There are a hundred and one JavaScript charting libraries out there that can do amazing things with your data. Sometimes, though, a whole library is overkill. Libraries come at a cost and for simple charts you can often do what you need without any third party code at all.

Here’s an example of how you can create a bar chart using just HTML and CSS.

The HTML

First off we’ll need some HTML to manipulate with our CSS. You’ll probably be using server side rendering and for this example I was using Razor pages. My .cshtml looked
something like this:

<div class="chart">
  <div class="bars">
    @foreach (var point in Model.Data)
    {
    <div class="bar" style="height:@(point.ValueAsPercentOfMax)%;"></div>
    }
  </div>
  <div class="labels">
    @foreach (var point in Model.Data)
    {
    <span class="label">@point.Label</span>
    }
  </div>
</div>

…which outputs HTML like this…

<div class="chart">
  <div class="bars">
    <div class="bar" style="height:50%;"></div>
    <div class="bar" style="height:20%;"></div>
    <div class="bar" style="height:70%;"></div>
    <!-- etc -->
  </div>
  <div class="labels">
    <span class="label">Bar 1</span>
    <span class="label">Bar 2</span>
    <span class="label">Bar 3</span>
    <!-- etc -->
  </div>
</div>

One important thing to note is that we are translating our data values into %age heights on the .bar divs. Combined with some CSS this will turn these divs into the bars in our bar chart.

That’s all we need to do for the DOM but so far it doesn’t look great

plain-dom

Let’s add some styles!

The CSS

First things first: we need to be able to see the bars. At the moment the .bar divs have a %age height set but their containing element .bars has zero height. A %age of zero is zero, so no bars!

We want the bars to take up all available space above the labels so let’s turn .chart into a vertical flexbox and set the .bars to flex so they fill up anything left over from the labels.

Note: I’ve given the chart a fixed height & width, but we’ll be writing this so that it will grow or shrink to fit the available space. I’ve also given each .bar a background color so we can see where they are!

.chart {
  height: 100px;
  width: 200px;
  display: flex;
  flex-direction: column;
}

.bars {
  flex: 1;
}

.bar {
  background: red;
}

bar-height

Now the .bars container is the right height (pushing the labels to the bottom) but we still can’t see them. This time they have height but zero width.

We can get .bars to space it’s components out horizontally, then have each .bar grow to take up the available space with flex: 1. By setting all child elements of .bars to have the same flex value they will all be assigned the same share of the available space.

.bars {
  flex: 1;
  display: flex;
  flex-direction: row;
}
.bar {
  flex: 1;
  background: red;
}

horizontal-spacing

Ok, now we’re getting somewhere: we have bars! We don’t want them at the top though, so let’s set .bars to align it’s items to flex-end to get them to the bottom.

.bars {
  /* ...as above... */
  align-items: flex-end;
}

vertical-align

Better. Those labels need to line up with the columns though, so let’s apply the same horizontal flex to .labels-container. We can add some text alignment and a little padding while we’re about it.

.labels {
  display: flex;
  flex-direction: row;
}
.label {
  flex: 1;
  padding: 3px;
  text-align: center;
}

labels

Much better, but the “big red square” column style isn’t the best. Let’s give each column some padding, border and a nice background color and…

.bar {
  flex: 1;
  border-radius: 3px 3px 0 0;
  background: rgba(98, 144, 200, 1);
  border: 1px solid rgba(55, 105, 150, 1);
  border-bottom: none;
  margin: 0 18px;
}

pretty-bars

Pretty good! We just want one final touch to add a bit of polish: animation. We can make our bars grow to their full height as the page loads by animating the translateY property from 100% (i.e. entirely outside the parent element) to 0 (i.e. the desired final position).

@keyframes grow-column {
  0% {
    transform: translateY(100%);
  }
  100% {
    transform: translateY(0);
  }
}

.bars {
  /* ...as above ... */
  overflow: hidden;
}

.bar {
  /* ... as above ... */
  animation: grow-column 1s;
}

We need to set the containing .bars element to hide all overflow content; otherwise we would see the columns sliding in over the top of the labels.

animation

✨ tada! ✨ A nice looking simple bar chart at a cost of a few bytes of CSS!

Advertisements

Composite Pattern in .NET Core with Dependency Injection

Following on from my previous post where I implemented a decorator pattern using .NET Core dependency injection I realised that I could use the same method to create a composite pattern in a developer-friendly way.

Composite Pattern

Similar to the decorator pattern, the Composite Pattern let’s you wrap existing implementations of an interface to augment the functionality.

The difference between the two is that the decorator wraps a single instance of the interface; a composite wraps many.

interface IService {
  void DoSomething(string value);
}

class Decorator : IService {
  public Decorator(IService wrappedService) {
    //...
  }
}

class Composite : IService {
  public Composite(IEnumerable<IService> wrappedServices) {
    //...
  }
}

This is useful where you have a number of implementations of your service and you don’t want dependent classes to know whether they should call one, some or all of them.

For example, if you have a report generator that wants to send results to multiple sources you might implement several instances of IReporter:

interface IReporter {
  void Send(IReport report);
}

class ConsoleReporter : IReporter {
  public void Send(IReport report) {
    //write details to console
  }
}

class TelemetryReporter : IReporter {
  public void Send(IReport report) {
    //write stats to a telemetry service
  }
}

class EmailReporter : IReporter {
  public void Send(IReport report) {
    //send a report email to stakeholders
  }
}

Your composite reporter would construct on all other implementations of IReporter and call them in order:

class CompositeReporter : IReporter {
  private IEnumerable<IReporter> _reporters;

  public CompositeReporter(IEnumerable<IReporter> reporters) {
    _reporters = reporters;
  }

  public void Send(IReport report) {
    foreach (var reporter in _reporters)
      reporter.Send(report);
  }
}

This means that anything that needs to send a report can request a single IReporter and let the CompositeReporter worry about routing the report through the correct concrete implementations.

Default DI Behaviour

As discussed in the previous post, the default behaviour of the .NET Core Dependency Injection framework is to provide the last-registered copy of an interface, or all registered copies for an IEnumerable.

services.AddScoped<IService, ConcreteService1>();
services.AddScoped<IService, ConcreteService2>();
services.AddScoped<IService, ConcreteService3>();

//later
const service = serviceProvider.GetRequiredService<IService>();
// service is instance of ConcreteService3

const allServices = serviceProvider.GetRequiredService<IEnumerable<IService>>();
// allServices contains one instance of all 3 registered implementations

What we need is a way to register a new type to replace the existing registrations and take them in as a constructor dependency.

class Composite : IService {
  public class Composite(IEnumerable<IService> services) {
    //...
  }
}

//BAD - throws StackOverflowException when resolved!
services.AddScoped<IService, Composite>();

Unfortunately the default behaviour of the DI framework is to attempt to fulfil the request for all IService implementations with…another instance of Composite! One StackOverflowException later and we’re back to the drawing board.

How can we make this play nicely with DI?

Borrowing from the Last Post

After some digging through the ASP.NET Core source code in the last post we came up with a couple of useful helpers that we can re-use here: CreateFactory and CreateInstance.

ActivatorUtilities.CreateFactory generates a factory function to create an instance of ConcreteType from the service provider with some services provided explicitly.

var objectFactory = ActivatorUtilities.CreateFactory(
  typeof(ConcreteType), 
  new[] { typeof(IService) });

CreateInstance creates an instance of a service from a ServiceDescriptor.
We can get instances of ServiceDescriptor from the service collection and use these to create previously-registered types.

public static object CreateInstance(this IServiceProvider services, ServiceDescriptor descriptor)
{
  if (descriptor.ImplementationInstance != null)
    return descriptor.ImplementationInstance;

  if (descriptor.ImplementationFactory != null)
    return descriptor.ImplementationFactory(services);

  return ActivatorUtilities.GetServiceOrCreateInstance(services, descriptor.ImplementationType);
}

With these tools we can define our desired composite behaviour.

Extract Existing Registrations

When we register a new composite we want to

  1. Remove all existing registered services for the same interface
  2. Insert the composite implementation
  3. Pass instances of all removed implementations into the constructor of the composite

Remove Existing Registrations

IServiceCollection extends IEnumerable so we can filter it down to get the services that match the interface of the composite class.

public static void AddComposite<TInterface, TConcrete>(this IServiceCollection services)
  where TInterface : class
  where TConcrete : class, TInterface
{
  //get a list of existing registrations matching the target interface
  var wrappedDescriptors = services
    .Where(s => s.ServiceType == typeof(TInterface))
    .ToList();

  //remove each from the service collection
  foreach (var descriptor in wrappedDescriptors)
    services.Remove(descriptor);

  //...
}

We call ToList to we get a persistent list of the items and then remove them from the original collection.

Add Composite Implementation

Next up we want to insert the definition of our composite class, and we’re going to use the ActivatorUtilites helper mentioned above.

var objectFactory = ActivatorUtilities.CreateFactory(
  typeof(TConcrete),
  new[] {
    typeof(IEnumerable<TInterface>)
  });

Here we create a factory function that can be used with a service provider to resolve an instance of TConcrete (i.e. our composite class) with any parameters of type IEnumerable{TInterface} manually specified by us.

The objectFactory forms the basis of a new ServiceDescriptor to add to the collection.

var compositeDescriptor = ServiceDescriptor.Describe(
  typeof(TInterface),
  serviceProvider => (TInterface)objectFactory(serviceProvider, new [] {
    /* todo: inject original services here */
  },
  ServiceLifetime.Scoped);
);

services.Add(compositeDescriptor);

Note: in this example I have hard-coded a lifetime of Scoped for the service. We can improve on this below but it will do for now.

Inject Original Services

We still need to inject instances of the original services that we removed. We recorded their service descriptors in wrappedDescriptors and we can now combine those with the CreateInstance extension method above to populate our constructor parameter.

var compositeDescriptor = ServiceDescriptor.Describe(
  typeof(TInterface),
  serviceProvider => (TInterface)objectFactory(serviceProvider, new [] {
    wrappedDescriptors
      .Select(d => serviceProvider.CreateInstance(d))
      .Cast<TInterface>()
  },
  ServiceLifetime.Scoped);
);

Now all of the wrapped services will be created through the service provider and passed to our composite.

This approach may seem long winded but it has the advantage that any other dependencies of either the wrapped services or our composite will also be injected from the service provider with no further input from us!

Calculate Lifetime Scope

The hard-coded lifetime scope isn’t ideal, and whilst we could push the onus onto the caller to specify a scope we can do slightly better and infer it from the existing registrations.

If the composite depends on a Scoped instance then it can be either Scoped or Transient without a problem, but cannot be Singleton as it would not have access to scoped dependencies.

We can infer the maximum scope of the composite by taking the most specific scope of it’s dependencies. The ServiceLifetime enum is defined with the least specific scope (Singleton) as 0 so we can select the maximum value to get the most specific.

Pull it All Together

Combining all of the above we get the following extension method:

public static void AddComposite<TInterface, TConcrete>(this IServiceCollection services)
  where TInterface : class
  where TConcrete : class, TInterface
{
  var wrappedDescriptors = services.Where(s => s.ServiceType == typeof(TInterface)).ToList();
  foreach (var descriptor in wrappedDescriptors)
    services.Remove(descriptor);

  var objectFactory = ActivatorUtilities.CreateFactory(
    typeof(TConcrete), 
    new[] { typeof(IEnumerable<TInterface>) });

  services.Add(ServiceDescriptor.Describe(
    typeof(TInterface),
    s => (TInterface)objectFactory(s, new[] { wrappedDescriptors.Select(d => s.CreateInstance(d)).Cast<TInterface>() }),
    wrappedDescriptors.Select(d => d.Lifetime).Max())
  );
}

Now we can wrap up as many services as we want in a composite with one line

var services = new ServiceCollection();
services.AddSingleton<IReporter, ConsoleReporter>();
services.AddScoped<IReporter, TelemetryReporter>();
services.AddTransient<IReporter, EmailReporter>();

services.AddComposite<IReporter, CompositeReporter>();