๐Ÿ”ท Core

coldbox-event-model

Use this skill when working with the ColdBox event object (prc/rc), managing request collections, rendering views and data responses, handling redirects with relocate(), controlling HTTP status codes, or implementing event caching in ColdBox handlers.

$ npx skills add coldbox/skills/coldbox/event-model
$ coldbox ai skills install coldbox/skills/coldbox/event-model
๐Ÿ”— https://skills.boxlang.io/skills/raw/coldbox/skills/coldbox~event-model

ColdBox Event Model

When to Use This Skill

Use this skill when working with the ColdBox request lifecycle, event object, request collections, view rendering, and response management.

Language Mode Reference

Examples use BoxLang (.bx) syntax by default. Adapt for your target language:

ConceptBoxLang (.bx)CFML (.cfc)
Class declarationclass [extends="..."] {component [extends="..."] {
DI annotation@inject above property name="svc";property name="svc" inject="svc";
View templates.bxm suffix.cfm / .cfml suffix
Tag prefix<bx:if>, <bx:output>, <bx:set><cfif>, <cfoutput>, <cfset>

CFML Compat Mode: With BoxLang + CFML Compat module, .bx and .cfc files coexist freely. BoxLang-native classes use class {} (.bx files); CFML-compat classes use component {} (.cfc files).

Core Concepts

The ColdBox event model consists of:

  • Event Object (event) โ€” the RequestContext instance per request
  • Request Collection (rc) โ€” public request data (merged GET + POST + URL params)
  • Private Request Collection (prc) โ€” private handler-to-view data store
  • View Rendering โ€” event.setView() for HTML, event.renderData() for data
  • Relocations โ€” relocate() for redirects

Request Collections

function index( event, rc, prc ) {

    // rc = public request collection (from URL, FORM, etc.)
    var userId   = rc.id ?: 0
    var page     = rc.page ?: 1
    var search   = rc.search ?: ""

    // prc = private REQUEST collection (handler to view data)
    prc.user     = userService.getById( userId )
    prc.users    = userService.list( page = page, search = search )
    prc.pageTitle = "Users"

    // Get/set any rc value
    event.getValue( "id", 0 )             // get with default
    event.setValue( "computedValue", 42 ) // set in rc
    event.getPrivateValue( "user" )       // get from prc
    event.setPrivateValue( "user", user ) // set in prc

    // Check if a value exists
    event.valueExists( "id" )
    event.privateValueExists( "user" )
}

View Rendering

function showProfile( event, rc, prc ) {

    prc.user = userService.getById( rc.id ?: 0 )

    // Set a view
    event.setView( "users/profile" )

    // Set view with layout override
    event.setView(
        view   = "users/profile",
        layout = "AdminLayout"
    )

    // Render no layout (for pages we want no wrapping layout)
    event.setView( "partials/widget" ).noLayout()

    // Override the layout for this event
    event.setLayout( "PrintLayout" )

    // Set view and pass view args
    event.setView(
        view     = "users/profile",
        args     = { showSidebar: true }
    )
}

Data Rendering (REST APIs)

function getUser( event, rc, prc ) {

    var user = userService.getById( rc.id ?: 0 )

    // Render JSON
    event.renderData( data = user.getMemento(), type = "json" )

    // With status code
    event.renderData(
        data       = user.getMemento(),
        type       = "json",
        statusCode = 200
    )

    // XML response
    event.renderData( data = data, type = "xml" )

    // Text response
    event.renderData( data = "Hello World", type = "text" )

    // HTML response
    event.renderData( data = "<h1>Hello</h1>", type = "html" )

    // Binary/file
    event.renderData( data = binaryData, type = "binary" )
}

Relocations (Redirects)

function store( event, rc, prc ) {

    var user = userService.create( rc )

    // Simple relocation by event name
    relocate( "users.index" )

    // Relocation with query string
    relocate( "users.show", { id: user.getId() } )

    // Relocation to a URL
    relocate( uri = "/users/#user.getId()#" )

    // Relocation with status code
    relocate( uri = "/dashboard", statusCode = 302 )

    // Permanent redirect
    relocate( uri = "/new-path", statusCode = 301 )

    // Relocate back (to referring page)
    relocate( back = true )
}

HTTP Request Information

function processRequest( event, rc, prc ) {

    // HTTP method detection
    event.getHTTPMethod()           // GET, POST, PUT, DELETE, etc.
    event.isPost()
    event.isGet()
    event.isPut()
    event.isDelete()
    event.isPatch()

    // Request info
    event.getHTTPHeader( "Authorization" )
    event.getHTTPHeader( "Content-Type" )
    event.getClientIP()
    event.getFullURL()
    event.getCurrentURL()
    event.getCurrentHandler()
    event.getCurrentAction()
    event.getCurrentEvent()         // "handler.action"
    event.getCurrentRoute()

    // AJAX detection
    event.isAjax()

    // SSL detection
    event.isSSL()
}

Response Headers and Status

function apiResponse( event, rc, prc ) {

    // Set response headers
    event.setHTTPHeader( name = "X-Total-Count", value = 100 )
    event.setHTTPHeader( name = "Cache-Control", value = "no-cache" )

    // Set status code
    event.setHTTPHeader( statusCode = 201 )
    event.setHTTPHeader( statusText = "Created" )

    // Get response data
    event.renderData(
        data       = { id: 1, name: "Test" },
        type       = "json",
        statusCode = 201,
        headers    = { "X-Rate-Limit": 100 }
    )
}

Event Caching

class Products extends coldbox.system.EventHandler {

    // Cache the 'index' action output for 60 minutes
    this.EVENT_CACHE_SUFFIX = "products"

    function index( event, rc, prc ) {

        // Cache this event's output
        event.setEventCacheableEntry(
            provider   = "template",
            timeout    = 60,
            lastAccess = 60
        )

        prc.products = productService.list()
        event.setView( "products/index" )
    }

    function show( event, rc, prc ) {

        // Cache per unique ID
        event.setEventCacheableEntry(
            provider   = "template",
            timeout    = 120,
            suffix     = rc.id ?: 0
        )

        prc.product = productService.getById( rc.id ?: 0 )
        event.setView( "products/show" )
    }
}

CFML (.cfc):

component extends="coldbox.system.EventHandler" {

    // Cache the 'index' action output for 60 minutes
    this.EVENT_CACHE_SUFFIX = "products"

    function index( event, rc, prc ) {

        // Cache this event's output
        event.setEventCacheableEntry(
            provider   = "template",
            timeout    = 60,
            lastAccess = 60
        )

        prc.products = productService.list()
        event.setView( "products/index" )
    }

    function show( event, rc, prc ) {

        // Cache per unique ID
        event.setEventCacheableEntry(
            provider   = "template",
            timeout    = 120,
            suffix     = rc.id ?: 0
        )

        prc.product = productService.getById( rc.id ?: 0 )
        event.setView( "products/show" )
    }
}

The Flash Scope

function store( event, rc, prc ) {

    var result = userService.create( rc )

    if( result.hasErrors() ){
        // Store data in flash for redirect
        flash.put( "errors", result.getErrors() )
        flash.put( "data", rc )
        relocate( "users.create" )
        return
    }

    // Success flash message
    flash.put( "success", "User created successfully!" )
    relocate( "users.index" )
}

function create( event, rc, prc ) {

    // Retrieve flash data after redirect
    prc.errors = flash.get( "errors", [] )
    prc.formData = flash.get( "data", {} )
    prc.user   = userService.new( prc.formData )
    event.setView( "users/create" )
}

Executing Events with runEvent()

function dashboard( event, rc, prc ) {

    prc.stats = runEvent(
        event          = "reports.stats",
        private        = true,
        prePostExempt  = true,
        eventArguments = {
            range    : rc.range ?: "30d",
            include  : [ "sales", "users", "retention" ]
        }
    )

    prc.recentOrders = runEvent(
        event          = "widgets.recentOrders",
        cache          = true,
        cacheTimeout   = 5,
        cacheSuffix    = "dashboard-#prc.currentUser.getId()#",
        eventArguments = { limit : 10 }
    )

    event.setView( "dashboard/index" )
}

Viewlets and Reusable Events

function sidebar( event, rc, prc ) {
    return runEvent(
        event          = "widgets.categoryMenu",
        private        = true,
        prePostExempt  = true,
        eventArguments = { selectedSlug : rc.category ?: "" }
    )
}

Event Model Best Practices

  • Use prc for data passed to views (not rc)
  • Use rc for reading input, never write sensitive data to rc
  • Use event.renderData() for REST APIs, event.setView() for HTML
  • Use relocate() for POST-REDIRECT-GET pattern
  • Call event.noRender() to suppress any rendering (for background tasks)
  • Use event.isAjax() to detect and respond to AJAX requests appropriately
  • Use runEvent() for reusable widgets/viewlets and internal event orchestration instead of duplicating handler logic
  • Pass explicit eventArguments structs to internal events rather than mutating rc for side effects