Bring Your Own (BYO) 7.22 Implementation Examples

Overview

The Bring Your Own (BYO) 7.22 implementation examples page contains a list of arguments for custom elements. Use this list as a base for building your own components using our BYO Component SDK guide.

The Bring Your Own framework is intended for developers who have a strong understanding of the Unqork Designer Platform and JavaScript JavaScript is an object-oriented computer programming language. It is most-commonly used for interactive effects in the browser..

This example is for Creators using the 7.22 Unqork Designer Platform release. For environments using the 7.24 release, view our BYO 7.24 Implementation Examples.

Simple UI

args Example

arg type Object Type Description

input

string componentKey

Subscribe to external textfield value, based on which updates occur in the heading text.

output

string componentKey

On button-click, updates the external component value.

Implementation

Copy

export class SimpleUI extends HTMLElement {
  constructor() {
    super()
    this.attachShadow({ mode: 'open' })
  }

  /**
   * This is the only method that is required from "unqork" in order to pass the api and config.
   * The developer does not need this is in order to make a basic web component.
   * @param {*} initialConfig
   * @param {*} api
   */
  initialize(initialConfig, api) {
    this.config = initialConfig
    this.api = api

    this.setExternalComponentValue('This was set when component was initialized')

    this.subscribeToValueChange()
    this.subscribeToValidationChange()
    this.subscribeToInputData()
  }

  /**
   * This is the web component lifecycle method,
   * it's recommended for initializing view and events.
   */
  connectedCallback() {
    this.initialView()
    this.setupEvents()
  }

  disconnectedCallback() {
    this.setExternalComponentValue('This was set when component was destroyed')
  }

  /**
   * Subscribe to this component value state change,
   * its only needed if the component will have updated values set using set_property
   */
  subscribeToValueChange = () => {
    this.api.state.state$('value').subscribe((value) => {
      if (value) this.shadowRoot.getElementById('custom-input').value = value
    })
  }

  /**
   * Subscribe to this component to validate state change and render errors
   */
  subscribeToValidationChange = () => {
    this.api.state.state$('validation').subscribe((validation) => {
      const errors = this.shadowRoot.getElementById('errors')

      if (!validation) return

      if (validation.result.isValid) errors.innerHTML = ''
      else errors.innerHTML = validation.result?.errors?.map((e) => `<div>${e.message}</div>`)
    })
  }

  /**
   * Subscribe to the external component values
   */
  subscribeToInputData = () => {
    const inputRefKey = this.config.args.input
    const setHeading = (c) => {
      const heading = this.shadowRoot.getElementById('heading')
      heading.innerHTML = c.value ? `Hello, ${c.value}!` : ''
    }

    if (inputRefKey) console.warn('data input not defined')

    setHeading(this.api.state.resolveByKey(inputRefKey))
    this.api.state.resolveByKey$(inputRefKey).subscribe(setHeading)
  }

  /**
   * Set validation for a state
   * @param {*} isValid
   * @param {*} errors
   */
  setValidation = (isValid, errors) => {
    this.api.state.set((state) => {
      return merge(state, 'validation.result', {
        isValid,
        errors,
      })
    })
  }

  /**
   * For the given component key, update its value
   * Avoid changing every component property and expect it to work
   * it depends on the component implementation if the state update will be respected
   *
   * @param {*} key
   * @param {*} value
   */
  setExternalComponentValue(value) {
    this.api.state.updateExternalComponentState(this.config.args.output, { value })
  }

  /**
   * Render initial DOM structure
   */
  initialView() {
    this.shadowRoot.innerHTML = `
        <h1 id="heading"></h1>
        <button id="fireEventBtn">Fire Event</button>
        <button id="updateOutputBtn">Set output</button>
        <button id="setInvalidBtn">Make component invalid</button>
        <button id="setValidBtn">Make component valid</button>
        </br>
        <input type="text" id="custom-input"/>
        <div id="errors"></div>
    `
  }

  /**
   * Bind events
   */
  setupEvents() {
    const fireEventBtn = this.shadowRoot.getElementById('fireEventBtn')
    const updateOutputBtn = this.shadowRoot.getElementById('updateOutputBtn')
    const setInvalidBtn = this.shadowRoot.getElementById('setInvalidBtn')
    const validBtn = this.shadowRoot.getElementById('setValidBtn')
    const input = this.shadowRoot.getElementById('custom-input')

    // Add an 'onclick' event listener
    fireEventBtn.onclick = () => this.api.events.emit({ name: 'customEvent' })
    updateOutputBtn.onclick = () => this.setExternalComponentValue('This was set from an output')
    setInvalidBtn.onclick = () => this.setValidation(false, [{ type: 'customError', message: 'errorMessage' }])
    validBtn.onclick = () => this.setValidation(true, [])
    input.addEventListener('keyup', (e) => this.api.state.set({ value: e.target.value }))
  }
}

export default SimpleUI

Ag Grid

args

arg type Object Type Description

input

string componentKey

Subscribe to the external component with the component definition array An array is a type of object that stores one or more data types. Data types supported in arrays include numbers, strings, and objects..

output

string componentKey

Subscribe to the external component with the data the grid should display.

Implementation Example

Copy
// << Import/Insert the AgGrid lib  here.>>
// << For readability purposes it has been removed.>>

export class AgGrid extends HTMLElement {
  constructor() {
    super()
    this.attachShadow({ mode: 'open' })

    this.gridOptions = {
      columnDefs: [],
      rowData: [],
    }
  }

  initialize(initialConfig, api) {
    this.config = initialConfig
    this.api = api

    this.renderGrid(this.shadowRoot, this.gridOptions)
    this.subscribeColumnDefs()
    this.subscribeData()
  }

  subscribeColumnDefs() {
    this.api.state.resolveByKey$(this.config.args.columnDefs).subscribe((columnDefs) => {
      this.gridOptions = {
        rowData: this.gridOptions.rowData,
        columnDefs: columnDefs.value || [],
      }
      this.updateGrid(this.gridOptions)
    })
  }

  subscribeData() {
    this.api.state.resolveByKey$(this.config.args.value).subscribe((rowData) => {
      this.gridOptions = {
        rowData: rowData.value || [],
        columnDefs: this.gridOptions.columnDefs,
      }
      this.updateGrid(this.gridOptions)
    })
  }

  updateGrid(gridOptions) {
    console.log('Updating AG Grid', gridOptions)
    this.gridApi.updateGridOptions(gridOptions)
  }

  async renderGrid(document, gridOptions) {
    console.log('Rendering AG Grid component', gridOptions)

    document.innerHTML = this.view()
    this.gridEl = document.querySelector('#myGrid')
    this.gridApi = agGrid.createGrid(this.gridEl, gridOptions)
    console.log('Created AG Grid', {
      document,
      gridOptions,
      gridEl: this.gridEl,
      gridApi: this.gridApi,
    })
  }

  view() {
    return `
      <div id="myGrid" style="height: 500px"></div>
    `
  }
}

export default AgGrid