Skip to content

Overriding Introduction

Overriding allows you to override how Iridium resolves your components inside it's rendering pipeline.

We recommend giving the Varients section a quick read before progressing.

Variants

A varient is just the term Iridium uses to describe the state of a component at different points in it's rendering journey. There are three typical varients.

  • Resolvable
  • Concrete
  • View

Resolvable

A resolvable is the entry point to a host of Iridium's components. It's a representation of some state that needs to be resolved at runtime.

A FormInput field on your form is a typical example of a resolvable component. You can provide static state like a label through it's Label method, or a callback function (LabelFn) that can use the current request context to determine the state of the label

go
// static 
FormInput("name").
    Label("Your Name")

// callback
FormInput("name").
    LabelFn(
        func (ctx FieldContext) string {
            // You can access the current request context through the 
            // ctx field here to set your label dynamically 
            if ctx.Request.URL.Path == "/admin/users/create" {
                return "Label is on user's create page"
            }
            return "Your Name"
        }
    )

Behind the scenese, both Label and LabelFn set the same value, either as a callback or static value

go
type InputResolvable[T any] struct {
    LabelStr util.Resolvable[*context.FieldContext[T], string]
}

You can see we defined a resolvable LabelStr field here. util.Resolvable can contain a callback that returns a string when provided that *context.FieldContext[T] struct, or simply a static string.

Are a FormInput and InputResolvable[T] different?

No, a FormInput is a alias for InputResolvable[T] you receive through type generation.

Type generation docs are here

Converting a Resolvable To a Concrete

Once you're finished defining your form for example

Iridium can now take your form definition, and when a user performs a request for your form, can generate that FieldContext struct for you, passes it to your resolvable component, to produce a static form of your input called a concrete input.

Generating context and defining how a resolvable is converted to it's concrete form is an overridable process that you can learn more about here.

Concrete Variants

Concrete varients represent the state of your component after it has resolved all its state based on the current request.

go
type InputConcrete struct {
    LabelStr string
}

You can see now we have a finalized form of that LabelStr.

TIP

Concrete variants can live on their own. In fact, in custom pages, you can call on them directly to borrow our components and place them in your custom pages.

You can learn about re-using our components in your own custom pages here

Concrete Variant to View Variant

Concrete variants register which view (or templ.Component) will be produced from their state in a callback function.

go
bootstrap.RegisterNamedView[*components.InputConcrete](
		views.Input,
        func(u *components.InputConcrete) (templ.Component, error) {
            // Here we provide our concrete input to our view and ask for a templ.Component
            return views.NewInput(u).Component(), nil
		},
	)

and a view component looks like this:

go
// input.templ

type InputView struct {
    *components.InputConcrete
}

func NewInput(i *components.InputConcrete) *InputView {
    return &InputView{i}
}

templ (i *InputView) Component() templ.Component {
    <input type="text" value={i.LabelStr} />
}

If you're looking to do some overriding, you're almost certainly interested in this step. Overriding the resolvable -> concrete step is possible, but not recommended since it is complex and requires you to understand Iridium's internals.

Skip to view overriding here!

Optional Advanced Reading

Please do not consider this page an exhaustive introduction to Iridium's entire rendering and resolution pipeline.

To understand and gain full control over how Iridium renders your components - end-to-end - you have to read our source code for your particular component.

For example, while it's true most components have a three-step journey, some, like the Table require more due to their complex nature. Additionally, internally to keep things orthogonal, we make an effort not to pass concrete forms directly to their views opting in favour of using interfaces.

The good news is, it's very unlikely you'll need to do anything more than change your views! If you need more, the tools are there, but we recommend you question, if at a certain point, you just need to fork our repository. That is your choice 😃

Released under the MIT License.