the generic dev

the generic dev

just another blog

Storing views in Database

17 Feb 2017

I am working on a data analysis and visualization tool since few months. While developing the tool, i figured it out that i needed a way to store static views on the database as data, not as HTML code. To be precise, i need a library with which i can fetch some remote data which represents an UI and render it with React.

When i discussed this idea with my friends who are helping me to build this tool, they ave me this weird WTF look! But i thought of giving it a try and after 12 cups of coffee and 47 songs on spotify, i found out that it’s possible! Actually it’s too damn easy than i initially thought of.

There are only two steps involved, 1.Define a schema and 2.write a function. How easy and elegant JavaScript is, i told my self after i finished writing the same. Actually the code is quite simple that i can fit it here in this blog.

Defining the schema

{
  "$schema": "http://json-schema.org/schema#",
  "title": "Render View Element",

  "definitions": {
    "render_react_element": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "description": "The `nodeName` of the element you want to create. e.g.: div; span; strong. Can be any value accepted as first argument of `React.createElement`."
        },
        "props": {
          "type": "object",
          "additionalProperties": true,
          "description": "The properties of the element you want to create. e.g.: { 'className': 'col-xs-12' }. Can be any value accepted as second argument of `React.createElement`"
        },
        "children": {
          "oneOf": [
            { "type": "string" },
            { "$ref": "#/definitions/json_react_element" },
            { "type": "array", "items": { "$ref": "#/definitions/json_react_element" } }
          ],
          "description": "The children of the element you want to create. If it is a string it will be used as `textContent`; if it is an array it will be mapped using the `json2react` function; if it is an object it will be used as an object described by this schema."
        }
      },
      "required": [ "type" ],
      "additionalProperties": false
    }
  },

  "type": "object",
  "$ref": "#/definitions/render_react_element"
}

Rendering the view based on the schema

"use strict";

function renderViewFromJson(create, mapper, schema) {
  if (typeof schema === "undefined") {
    schema = mapper;
    mapper = null;
  }
  
  if (schema === null) {
    return null;
  }

  if (typeof schema === "string") {
    return schema;
  }

  if (!isPlainObject(schema)) {
    throw new Error("schema must be a string or a plain object");
  }

  var hasNonEmptySchemaType = (
    schema.type &&
    typeof schema.type === "string" &&
    schema.type.trim() !== ""
  );

  if (! hasNonEmptySchemaType) {
    throw new Error("schema.type must be a non-empty string");
  }

  schema.type = schema.type.trim();

  if (schema.props !== undefined && !isPlainObject(schema.props)) {
    throw new Error("schema.props must be a plain object");
  }

  var type = schema.type;
  var props = schema.props || null;
  var children = schema.children && [].concat(schema.children).map(json2react.bind(null, create, mapper));

  mapper && (type = mapper(type, props));

  return create.apply(create, [].concat([type, props]).concat(children));
}

function isPlainObject(maybe) {
  return (
    maybe !== null &&
    typeof maybe === "object" &&
    Object.prototype.toString.call(maybe) == "[object Object]"
  );
}

module.exports = renderViewFromJson;

Rendering it in React

//You can use it with:

//React.render
//As the return value, or part of it, of a stateless component
//As the return value, or part of it, of a component's render method
import { createElement } from "react";
import { renderViewFromJson } from "renderViewFromJson";

const jsonUI = {
  type: "div",
  props: {
    style: { textAlign: "center" },
  },
  children: [
    { type: "h1", children: "It works!" },
    {
      type: "p",
      children: {
        type: "small",
        children: "This component was created from JSON",
      },
    },
  ],
};

ReactDOM.render(renderViewFromJson(createElement, jsonUI), document.body);