{} JSONX

Using Advanced Props

Advanced props let JSONX definitions read dynamic data, format output, render conditionally, and generate component props. Use them when a static JSONX object is not enough.

  • Reference stateful properties dynamically.
  • Format children before rendering.
  • Insert JSONX templates.
  • Create function or component props when the input is trusted.

There are six groups of advanced props:

Do not run untrusted JSONX definitions with evaluation props enabled.

1. Traverse Props

(resourceprops/asyncprops, windowprops, thisprops, thisstate, thiscontext)

Traverse props assign prop values from other objects. For example, you might want an image alt prop to use the current browser URL. Because JXM objects are derived from JSON, window.location.href cannot be read directly inside the static props object.

//assume window.location.href = http://example.com

const JXM = {
  component:'img',
  props:{
    src:'path/to/some/image',
    alt: // ??? http://example.com
  }
};

This is where traverse props are useful. The path reads window.location.href and assigns that value to JXM.props.alt.

Traversing the window object is possible by using the window props traverse prop. The other traverse props are:

  • resourceprops - traverse asynchronous properties passed to components when using JSONX programmatically
  • asyncprops - an alias for resourceprops
  • windowprops - traverse properties on window
  • thisprops - traverse properties on this.props
  • thisstate - traverse properties on this.state
  • thiscontext - traverse properties on this

To reference window.location.href, use this JXM object:

const JXM = {
 component:'img',
 props:{
   src:'path/to/some/image',
 },
 windowprops:{
   alt:['location','href']
 }
}

Traverse props assign values to JXM.props. The traverse prop key is the prop you want to set. The value is the path to read from the source object. For window.location.href, the path is ["location", "href"]; the source object itself is not included in the path.

Some sample use cases are:

  • resourceprops traverses the resources object passed to JSONX methods. asyncprops is an alias.
  • thisprops traverses this.props.
  • thisstate traverses this.state.
  • thiscontext traverses the current this context.
  • windowprops traverses the global window object.
// programmatic example
import * as jsonx from "jsonx";

async function main() {
  const response = await fetch("/path/to/userdata");
  const asyncUserData = await response.json();
  /*
  asyncUserData = {
    user: {
      name: 'jsonx',
      description: 'react without javascript',
    },
    stats: {
      logins: 102,
      comments: 3,
    },
    authentication: 'OAuth2',
  };
  */
  const JXM = {
    component: "div",
    props: {
      id: "generatedJSONX",
      className: "jsonx"
    },
    resourceprops: {
      auth: ["authentication"],
      username: ["user", "name"]
    },
    children: [
      {
        component: "p",
        props: {
          style: {
            color: "red",
            fontWeight: "bold"
          }
        },
        asyncprops: {
          title: ["user", "description"]
        },
        children: "hello world"
      }
    ]
  };

  //render something silly
  jsonx.jsonxRender(JXM, asyncUserData);
  /*
  Renders this JXM object:
  JXM = {
    component: 'div',
    props: {
      id: 'generatedJSONX',
      className:'jsonx',
      auth: 'OAuth2',
      username: 'jsonx',
    },
    children: [
      {
        component: 'p',
        props: {
          style: {
            color: 'red',
            fontWeight:'bold',
          },
          title:'react without javascript',
        },
        children:'hello world',
      },
    ],
  };
  */
}

main();

Example Traverse Props


2. Evaluation Props

(__dangerouslyEvalProps, __dangerouslyBindEvalProps, __dangerouslyEvalAllProps, __dangerouslyInsertFunctionComponents, __dangerouslyInsertClassComponents, __dangerouslyInsertComponents, __dangerouslyInsertReactComponents, __dangerouslyInsertJSONXComponents, _children, __functionProps (legacy), __windowComponents , __windowComponentProps, __spreadComponent, __spread)

Evaluation props compute values and merge them onto JXM.props. They are useful when JSONX needs to describe how dynamic data, state, or functions become props. Use these only with trusted JSONX input.

_children

The _children evaluation property overrides JXM.children. Use it when an advanced prop should provide the rendered children.

//current URL: http://example.com
const JXMWindowLocation = {
  component: "p",
  windowprops: {
    _children: ["location", "href"]
  }
};
// computes: { component:'p', children:'http://example.com', }

__dangerouslyEvalProps, __dangerouslyBindEvalProps, and __dangerouslyEvalAllProps

The evaluation properties __dangerouslyEvalProps, __dangerouslyBindEvalProps, and __dangerouslyEvalAllProps evaluate strings or functions and assign the result to a JXM object.

__dangerouslyEvalAllProps evaluates a string as a function and assigns the return value to props. Note: When the value is a string, it must be an expression such as (({ jsonx }) => ({})) or (function({ jsonx }) { return {}; }). JSONX passes the current JXM object through the jsonx property.

__dangerouslyEvalProps evaluates each string value and assigns the result to JXM.props. The string must be a valid JavaScript expression. If you evaluate an object literal, wrap it in parentheses: ({ some: "obj" }). If __dangerouslyEvalProps is a function, JSONX calls it with { jsonx }.

__dangerouslyBindEvalProps assigns functions to JXM prop values. This is usually for onClick and onChange handlers. Each value must resolve to a function because JSONX binds the function to the current this context.

These props exist for cases where JSONX is delivered as JSON and JavaScript functions cannot be included directly. Use them sparingly. In many cases, it is better to register functions in code and reference them through thiscontext or another traverse prop.

Example Evaluation Props

__spreadComponent and __spread

__spreadComponent maps one JSONX component over the data in JXM.__spread. Each item in the JXM.__spread array is passed into the child component as JXM.__item. JXM.__spread is usually assigned with a traverse prop.

const JXM = {
  component: 'ul',
  props:{
    __spread: [
      {
        name:'bob smith',
        email:'bob.smith@email.com'
      },
      {
        name:'jane doe',
        email:'jane.doe@email.com'
      },
      {
        name:'billy bob',
        email:'billy.bob@email.com'
      },
    ],
  },
  __spreadComponent:{
    component:'li',
    thisprops:{
      _children:['__item','name']
    }
  },
};
/* => {
  component:'ul', children: [
    {
      component:'li', children:'bob smith',
    },
    {
      component:'li', children:'jane doe',
    },
    {
      component:'li', children:'billy bob',
    }
  ]
};*/

Example Evaluation Props

__dangerouslyInsertFunctionComponents, __dangerouslyInsertClassComponents, __dangerouslyInsertComponents, __dangerouslyInsertReactComponents, __dangerouslyInsertJSONXComponents, __windowComponents, and __windowComponentProps

Component evaluation props assign React elements or components to props. This pattern is common in charting libraries, where a chart accepts a custom label, tick, or tooltip component as a prop.

The most common pattern is a function component passed as a prop. Passing function or class components through JSONX requires the generated component helpers. Read Creating React Components and Component Libraries for more information.

Example Evaluation Props

__functionProps (legacy)

The evaluation prop __functionProps is a legacy way to assign functions to JXM.props. Prefer the newer evaluation props for new work.

predefined functions (legacy)

__functionProps can assign functions that exist on this.props, such as this.props.reduxRouter.push, or functions that exist on window, such as window.console.log.

Properties are assigned by reading a function path from a string prefixed with func:. Function props merge onto jsonx.props after each function string resolves.

const JXM = {
  component: "button",
  props: {
    name: "test"
  },
  __functionProps: {
    onclick: "func:this.props.onClick", // if there's already a defined onClick Function
    printPage: "func:window.print",
    nav: "func:this.props.reduxRouter.push"
  }
};

inline functions (legacy)

__functionProps can also generate functions from strings. This legacy approach is more cumbersome than __dangerouslyEvalProps or __dangerouslyBindEvalProps. Define the function body at JXM.__inline[name], then reference it from __functionProps with func:inline.name. Use __functionargs to bind JXM.props values to inline function arguments.

const JXM = {
  component: "button",
  props: {
    name: "test"
  },
  __functionargs: {
    onClick: ["name"]
  },
  __inline: {
    onClick: ` window.alert("the name of this component from the prop is:" +arguments[0])`
  },
  __functionProps: {
    onClick: "func:inline.onClick"
  }
};

Example Evaluation Props __functionProps


3. Format Props

(___stringifyChildren, ___toStringChildren, ___toNumeral, ___JSDatetoLuxonString, ___ISOtoLuxonString, ___FromLuxonTimeZone)

Format props convert JXM.children values to strings. They are useful when raw values need display formatting before React receives them as children.

___stringifyChildren

The ___stringifyChildren format property converts the JXM.children property to a string by using JSON.stringify.

const JXM = {
  component: "div",
  children: { "some-non-string": "data" },
  ___stringifyChildren: true
}; // => { component:'div', children: '{"some-non-string":"data"}' };

___toStringChildren

The ___toStringChildren format property converts the JXM.children property to a string by calling toString().

const JXM = {
  component: "div",
  children: [1, 2, 3, 4],
  ___toStringChildren: true
}; // => { component:'div', children: '1,2,3,4' };

___toNumeral

The ___toNumeral format property converts the JXM.children property to a string by calling numeral(JXM.children).format(JXM.___toNumeral). See numeral formatting options on numeraljs.com.

const JXM = {
  component: "div",
  children: 15204.39084,
  ___toNumeral: "0,0.00"
}; // => { component:'div', children: '15,204.39' };

___JSDatetoLuxonString

The ___JSDatetoLuxonString format property converts the JXM.children property to a string by calling Luxon.DateTime.fromJSDate(JXM.children).toFormat(JXM.___JSDatetoLuxonString). See luxon formatting options from the luxon formatting docs.

const JXM = {
  component: "div",
  children: new Date("2020-03-03"),
  ___JSDatetoLuxonString: "LLL d, yyyy"
}; // => { component:'div', children: 'Mar 3, 2020' };

___ISOtoLuxonString & ___FromLuxonTimeZone

The ___ISOtoLuxonString format property converts the JXM.children property to a string by calling Luxon.DateTime.fromISO(JXM.children).toFormat(JXM.___ISOtoLuxonString). Set the time zone with the ___FromLuxonTimeZone format prop. See Luxon formatting options in the Luxon formatting docs.

const JXM_NY = {
  component: "div",
  children: "2020-03-03T14:30:00.000Z",
  ___ISOtoLuxonString: "ff",
  ___FromLuxonTimeZone: "America/New_York"
}; // => { component:'div', children: 'Mar 3, 2020, 9:30 AM' };

const JXM_LA = {
  component: "div",
  children: "2020-03-03T14:30:00.000Z",
  ___ISOtoLuxonString: "ff",
  ___FromLuxonTimeZone: "America/Los_Angeles"
}; // => { component:'div', children: 'Mar 3, 2020, 6:30 AM' };

Example Format Props


4. Utility Props

(__template, passprops, debug, test)

Utility props support rendering behavior without directly becoming regular React props.

debug

The debug flag logs the JXM object and computed advanced props when JXM.debug === true.

const JXM = {
    component: 'div',
    children: 'Debug JXM Data',
    __dangerouslyEvalAllProps:`(
      ()=>({ style:{ color:"blue" } })
    )`,
    debug:true,
};

//outputs to console:
/* {
  jsonx: {
    component: "div",
    children: "Debug JXM Data",
    __dangerouslyEvalAllProps: "(()=>({ style:{ color:"blue" } }))"
    debug: true
  },
  {
    computedProps: {
      style: {color: "blue"}
    }
  }
}*/

test

The test flag outputs the calculated render data as a string when JXM.test === true.

const JXM = {
    component: 'div',
    children: 'Test JXM Data',
    test:true,
};

//outputs as a string component:
/* {
  element: "div",
  children: "Debug JXM Data",
  test: true
}*/

passprops

The passprops flag passes props from the parent JXM object to each child JXM object except JXM.props.style.

const JXM = {
  component: 'div',
  props:{
    type:'radio',
    size:'large',
    extraOne:'ok',
    title:'my radio',
    style:{
      background:'red'
    }
  },
  passprops:true,
  children:[
    {
      component:'input',
    }
  ]
};

/* computes:
const JXM = {
  component: 'div',
  props:{
    type:'radio',
    size:'large',
    extraOne:'ok',
    title:'my radio',
    style:{
      background:'red'
    }
  },
  passprops:true,
  children:[
    {
      component:'input',
      props:{
        type:'radio',
        size:'large',
        extraOne:'ok',
        title:'my radio',
      },
    }
  ]
};
*/

You can pass only selected props by listing the prop names.

const JXM = {
  component: 'div',
  props:{
    type:'radio',
    size:'large',
    title:'my radio',
    style:{
      background:'red'
    }
  },
  passprops:['type','title'],
  children:[
    {
      component:'input',
    }
  ]
};

/* computes:
const JXM = {
  component: 'div',
  props:{
    type:'radio',
    size:'large',
    extraOne:'ok',
    title:'my radio',
    style:{
      background:'red'
    }
  },
  passprops:['type','title'],
  children:[
    {
      component:'input',
      props:{
        type:'radio',
        title:'my radio',
      },
    }
  ]
};
*/

___template

The ___template advanced prop loads JXM objects from an external file. It is mainly useful on the server. It can also load a URL in the browser, but that browser request is synchronous.


const JXM = {
  component:'div',
  ___template:'path/to/some/jxm/json/file'
}
// path/to/some/jxm/json/file = { component: "section", children: "from external template"}
/* computes:
{
  component:'div',
  children:[{ component: "section", children: "from external template"}]
}
*/

Example Utility Props


5. Display Props

(comparisonprops, comparisonorprops)

Display props determine whether a React element rendered from a JXM object should be shown. They add conditional rendering based on prop values.

comparisonprops and comparisonorprops

The display prop comparisonprops conditionally renders elements when comparisons pass. By default, every comparison must be true. If JXM.comparisonorprops is true, only one comparison needs to be true.

Comparison values can be literal values or references to JXM.props values. References use the same path-array format as traverse props, with JXM.props as the source object.

//and conditions
jsonx = {
  component: "div",
  children: "evals to false, so it will not render",
  comparisonprops: [
    {
      left: ["bigNum"],
      operation: "lte",
      right: ["smallNum"]
    }, // false (10000 <= 100)
    {
      left: ["smallNum"],
      operation: "<=",
      right: ["bigNum"]
    } // true (100 <= 10000)
  ] // false and true === false, so it won't render
};
//or conditions
jsonx = {
  component: "div",
  children: "evals to true, so this will render",
  comparisonorprops: true,
  comparisonprops: [
    {
      left: ["truthy"],
      operation: "eq",
      right: ["falsey"]
    }, // = false
    {
      left: ["smallNum"],
      operation: "eq",
      right: ["smallNum"]
    } // true
  ] // false or true === true, so render element
};

// All comparison operations
switch (opscompares.operation) {
  case "eq":
  case "==":
    return opscompares.left == opscompares.right;
  case "dneq":
  case "!=":
  case "!":
    return opscompares.left !== opscompares.right;
  case "dnseq":
  case "!==":
    return opscompares.left !== opscompares.right;
  case "seq":
  case "===":
    return opscompares.left === opscompares.right;
  case "lt":
  case "<":
    return opscompares.left < opscompares.right;
  case "lte":
  case "<=":
    return opscompares.left <= opscompares.right;
  case "gt":
  case ">":
    return opscompares.left > opscompares.right;
  case "gte":
  case ">=":
    return opscompares.left >= opscompares.right;
  case "dne":
  case "undefined":
  case "null":
    return opscompares.left === undefined || opscompares.left === null;
  case "!null":
  case "!undefined":
  case "exists":
  default:
    //'exists'
    return opscompares.left !== undefined && opscompares.left !== null;
}

Example Display Props


6. Applied Props

(useformregister, useremoveprops, useincludeprops)

Applied props are helper properties that modify other JSONX properties.

useformregister

The applied prop useformregister passes a React Hook Form register function to a component. It avoids manually wiring the form reference.

jsonx = {
  component: "input",
  props:{
    name:'firstName',
  },
  useformregister: true,
};

// is equivalent to
jsonx = {
  component: "input",
  props:{
    name:'firstName',
  },
  thiscontext:{
    ref: ['reactHookForm', 'register']
  },
};

useremoveprops

The applied prop useremoveprops removes a list of props from the JXM object. It is usually used with passprops when child components should not receive every parent prop.

jsonx = {
  component: "input",
  props:{
    name:'firstName',
    removeThis:true,
    extraProp:'remove me',
  },
  useremoveprops: ['removeThis','extraProp'],
};

// is equivalent to
jsonx = {
  component: "input",
  props:{
    name:'firstName',
  },
};

useincludeprops

The applied prop useincludeprops removes all props from the JXM object except the listed props. It is usually used with passprops when a child component should receive only a small prop set.

jsonx = {
  component: "input",
  props:{
    name:'firstName',
    removeThis:true,
    extraProp:'remove me',
    keepMe:'just this prop',
  },
  useincludeprops: ['keepMe'],
};

// is equivalent to
jsonx = {
  component: "input",
  props:{
    keepMe:'just this prop',
  },
};

Next: External And Custom Components