Composing Umbraco v8 Components

Thursday, February 7, 2019 12:00 PM umbraco

(continuing from Composing Umbraco v8 Collections)

So far, we have explained how to compose Umbraco by registering various elements, such as content finders or property value converters. In other words, how to modify how Umbraco works. But there is one missing bit: how to add functionality. Or, as a tweet asked:

What would be great is to have concrete samples of how to use the code [...] like updating fields on save, etc.

In other words: where should my code go, and how should I hook it into Umbraco?

Components

When one wants to go beyond composing Umbraco, and needs to add functionality... one adds a component to Umbraco. A component implements IComponent. A component:

And so, here is a full example of a component that, whenever a document is saved, would create a corresponding media:

public class MyComponent : IComponent
{
  private readonly IMediaService _mediaService;
  private readonly IMediaTypeService _mediaTypeService;

  // constructor: inject what we need
  public MyComponent(
    IMediaService mediaService,
    IMediaTypeService mediaTypeService)
  {
    _mediaService = mediaService;
    _mediaTypeService = mediaTypeService;
  }

  // initialize: runs once when Umbraco starts
  public void Initialize()
  {
    ContentService.Saved += (sender, args) =>
    {
      var mediaType = _mediaTypeService.Get("foo");
      foreach (var savedContent in args.SavedEntities)
      {
        var media = new Media(savedContent.Name, -1, mediaType);
        _mediaService.Save(media);
      }
    }
  }

  // terminate: runs once when Umbraco stops
  public void Terminate()
  { }
}

Composing Components

How does Umbraco finds components, and in which order are they running? Well, using the mechanisms we described in the previous posts! Umbraco has a collection of components, which is created during composition. So... if I want the above component to run, I need to register it:

public class MyComposer : IUserComposer
{
  public void Compose(Composition composition)
  {
    composition.Components().Append<MyComponent>();
  }
}

The collection is ordered, therefore the order of execution of components is deterministic. If the component is registered by a IUserComposer, you know that it will execute after Core's components. This means, for instance, that you know that the front end cache component has executed and responded to events and updated the cache accordingly.

Moving Cheese

cheese.png

Coming from v7, you may start to see a pattern here. Resolvers are becoming composition. OnApplicationStarting is becoming composers. OnApplicationStarted is becoming components. The only thing that is discovered is composers, and then the rest is explicit.

It may be a little bit more verbose (as in, less easy) but... I have the feeling it is more predictable and robust, i.e. simpler. What do you think?

And, what should the next episode be about?

Note: these posts are not documentation. They introduce concepts, and try to start discussions, in order to gather feedback and ideas. To some extend, they are RFCs. In an ideal world, as things stabilize, these concepts should be properly documented. And then, maybe these posts should just go away as to not clutter Google.

comments powered by Disqus