Skip to content
This repository has been archived by the owner on Mar 25, 2022. It is now read-only.

Latest commit

 

History

History
584 lines (470 loc) · 14.5 KB

REVISIONS.md

File metadata and controls

584 lines (470 loc) · 14.5 KB

XO-JS Revision History

New in 2.0

Component Model

XO Form Schemas are now compiled into a full recursive component model, before recursively rendering the component tree.

A tree of components is now instantiated at JSON/JS parsing time, with every component having a parent and children, and the rendering process is fully delegated to the individual controls.

It all starts with the root control (ExoRootControl), that takes care of setting up the page/pages and the navigation control container (ExoNavControl).

There is now a clear separation of parsing and accepting field properties in controls (acceptProperties()), that takes place in the control component constructor, and application of property values (mapAcceptedProperties()), where you can apply the state as passed through the JSON/JS field properties directly, or through databinding, to the state of your component.

As mentioned above, each control that can have children (fieldset, multiinput, group, root, etc.) is now responsible for rendering subcomponents. This means that anyone can create a nesting and the schema can be extended in many ways.

Reactive Databinding

Reactive data binding has been drastically improved. You can now simply bind nearly every schema property directly to a property in one of the model instances.

For instance, enabling controls based on state in the model is now much simpler, because you can now use model variables directly.

See below (enabled: "#/ui/useemail"):

const schema = {
  model: {
    instance: {
      ui: {
        useemail: true
      },
      data: {
        email: ""
      }
    }
  },
  theme: "thin",
  pages: [
    {
      legend: "My First XO Form",
      fields: [
        {
          type: "checkbox",
          caption: "Use email",
          bind: "#/ui/useemail"
        },
        {
          type: "email",
          caption: "Your email",
          bind: "#/data/email",
          required: true,
          placeholder: "john@doe.com",
          enabled: "#/ui/useemail",
          prefix: {
            icon: "ti-email"
          }
        }
      ]
    }
  ]
};

Better Performance & Less Memory used

The XO Form engine has been optimized in many ways,

XO now uses consideraby less memory and cleans up used memory earlier.

UI Tests included

The integrity of the XO platform is fully tested using a built-in test suite.

UI Test Suite

We are continously working on the expansion of the number of tests.

New in 1.6

Themes upgrade

We have completely rewritten XO theming, for easier use and extension.

A new theme, thin has been added, and default form layout has been improved so that custom styling is a lot easier.

See Themes & Styling

Monaco Editor

See XO Studio now uses the Microsoft Monaco Editor (yes, the one that powers #VSCode), complete with custom XO Form intellisense!

Monaco

The underlying XO Form Control is xoformeditor, which is a wrapper around the new monacoeditor, which is more generic.

{
  type: "monacoeditor",
  caption: "Monacoeditor",
  bind: "#/data/html",
  language: "html",
  class: "full-height",
  options: {
    minimap: {
      enabled: false
    }
  }
}

Note: See Monaco documentation for IEditorConstructionOptions for the options property.

New in 1.5

Breaking Change in Model Binding and Rules

Databinding syntax

Binding syntax is now: #/instancename/property instead of instance.instancename.property

Also, the binding syntax is globally the same, so you no longer need to use a '@' outside the bind property.

Model Binding shortcutting syntax

So if your model looks like this:

model: {
  instance: {
    data: {
      email: "john@doe.com";
    }
  }
}

... You can bind the email property using this syntax:

{
    type: "email",
    caption: "Email address",
    bind: "#/data/email"
}

Rule Engine

We added a rules engine that model based. Rules can be triggered based on state in your model.

Default xo.form.run() options

You can now add defaults for the options passed to each form xo.form.run() when creating the xo form context:

const context = await xo.form.factory.build({
  defaults: {
    validation: "inline",
  },
  runOptions: {
    rules: {
      actions: {
        myCustomAction: (a, b) => {
          // my custom implementation
        },
      },
    },
  },
});

In the example above, the Rules Engine is being instructed that, apart from the internal actions the XO Rules Engine provides, a myCustomAction action exists, and all forms that are run can use it.

Just don't forget to pass the context created to the form runner:

let fr = await xo.form.run(schema, {
  context: context,
});

Event ruleContextReady

The ruleContextReady is dispatched when the Rules Engine is ready to execute rules. Use it to get access to the context of the rules engine:

let fr = await xo.form.run(schema, {
  on: {
    ruleContextReady: (e) => {
      this.ruleContext = e.detail.context;
    },
  },
  rules: {
    actions: {
      myCustomAction: (a, b) => {
        alert(this.ruleContext.var(a));
      },
    },
  },
});

Sandbox subform property

The sandbox control now has a subform property (boolean). Setting subform to true will make the form loaded in the sandbox bind to a node in the master model.

const schema = {
  model: {
    instance: {
      data: {
        test: "My Data",
        sub: {
          text: "Subform Control Data",
        },
      },
    },
  },
  pages: [
    {
      fields: [
        {
          caption: "Test Field",
          type: "text",
          bind: "#/data/test",
        },
        {
          type: "sandbox",
          bind: "#/data/sub",
          form: {
            schema: {
              pages: [
                {
                  fields: [
                    {
                      type: "text",
                      caption: "Subform Textbox",
                      bind: "#/data/text",
                    },
                  ],
                },
              ],
            },
          },
        },
      ],
    },
  ],
};

Note the bind: "#/data/text" in the subform: the subform cannot have a model of itself in this case, and the node used in the bind property translates to an instance in the subform called 'data'.

Tags control supports autocompletion

The tags control now also supports autocomplete, like all text controls.

const schema = {
  submit: true,
  model: {
    instance: {
      you: {
        pets: ["Cat"],
      },
    },
  },
  pages: [
    {
      fields: [
        {
          type: "tags",
          bind: "#/you/pets",
          caption: "What pets do you have?",
          autocomplete: {
            strict: true,
            items: [
              "Dog",
              "Cat",
              "Parrot",
              "Rabbit",
              "Pig",
              "Snake",
              "Turtle",
              "Other",
            ],
          },
          info: "You can select multiple pets",
        },
      ],
    },
  ],
};

Autocomplete settings (all text controls)

  • The autocomplete settings now include a strict boolean setting, to allow only values from the autocomplete items.

Rules Engine

  • The action navigate was added to the list of available actions in the Rules Engine.

Listview edit

You can now turn a listview control into a master-detail editor. See ListView Control

Model Binding

You can now point an instance to a URL. See Data Binding

LiveEditor

The XO Form LiveEditor can now be automatically started, using the auto: true setting.

const schema = {
  model: {
    instance: {
      data: {
        name: "",
      },
    },
  },
  pages: [
    {
      fields: [
        {
          type: "text",
          bind: "#/data/name",
          caption: "Enter your name",
        },
      ],
    },
  ],
};
document.getElementById("form").appendChild(
  await xo.form.run(schema, {
    on: {
      post: (e) => {
        alert(JSON.stringify(e.detail.postData, null, 2));
      },
      interactive: (e) => {
        let exo = e.detail.host;
        new xo.form.factory.LiveEditor(exo, {
          auto: true,
        }).on("schema-change", (e) => {
          debugger;
        });
      },
    },
  })
);

Revamped Studio

Portal

The XO Studio finally has side-by-side code & rendering, for immediate feedback on your code changes.

Also, you will now directly see model changes as well as posted form values in the 'Data' panel.

Improved Error handling

We have done a lot to improve the error handling & debugging experience. Errors in your schema now lead to a friendlier and clearer message shown in the form rendering panel.

OpenAPI support

XO now has better support for OpenAPI. For instance, when selecting OpenAPI in the Start wizard, you will now be able to select a schema to work with in the OpenAPI definition.

New Router with history and hash support

A new Router is available, and the PWA Router has been rewrittenn to use this history and hash routing suporting router. The new router can also be used outside the PWA component structure.

  • Autocomplete search results can now return a complete HTML element, using the element property of the returned object array.

A new function getInstance(), is in fact a shortcut to get the data of any bound model instance.

return await xo.form.run(schema, {
  on: {
    post: (e) => {
      let contact = e.detail.host.getInstance("contact");
      // work with contact instance data
    },
  },
});

Adding details to triggered events in actions

  • You can now pass details when calling trigger action to trigger an event in the second parameter:
{
  type: "button",
  caption: "Log in",
  value: "clicked",
  bind: "#/temp/loginbutton",
  actions: [
    {
      if: {
          is: "clicked"
      },
      do: {
          trigger: ["signed-in", {
              account: "#/temp/userName"
          }]
      }
    }
  ]
}

addcaption property on list inputs

List inputs (radiobuttonlist, checkboxlist) now allow adding entries to the underlying array of items.

const schema = {
  model: {
    instance: {
      data: {
        who: "",
        people: ["John", "Pete", "Harry"],
      },
    },
  },
  pages: [
    {
      legend: "Welcome to XO",
      intro: "This is a generated web form",
      fields: [
        {
          type: "radiobuttonlist",
          bind: "#/data/who",
          items: "#/data/people",
          addcaption: "Other person",
        },
      ],
    },
  ],
};

AddCaption for live addition of list options

List inputs with addcaption now dispath events on the adding behavior:

const schema = {
  model: {
    instance: {
      data: {
        who: "",
        selected: {},
        people: ["John", "Pete", "Harry"],
      },
    },
  },
  pages: [
    {
      legend: "Using 'addcaption'",
      intro: "This radiobuttonlist allows you to add options",
      fields: [
        {
          type: "radiobuttonlist",
          bind: "#/data/who",
          items: "#/data/people",
          addcaption: "Other person",
        },
        {
          type: "multiinput",
          name: "editor",
          disabled: true,
          bind: "#/data/selected",
          caption: "Edit",
          fields: {
            name: {
              type: "text",
              caption: "First name",
              disabled: true,
            },
            fullname: {
              type: "text",
              caption: "Full name",
            },
            email: {
              type: "email",
              caption: "Email address",
            },
          },
        },
      ],
    },
  ],
};

const fr = await xo.form.run(schema, {
  on: {
    dataModelChange: (e) => {
      this.exo = e.detail.host;

      if (e.detail.changeData?.path === "#/data/who") {
        let person = {
          fullname: e.detail.changeData?.newValue + " Doe",
        };
        e.detail.model.instance.data.selected = person;
        e.detail.host.get("editor").disabled = false;
      }
    },
    dom: {
      "add-item": (e) => {
        debugger;
      },
      "select-add": (e) => {
        this.exo.get("editor").disabled = true;
      },
    },
  },
});

this.app.UI.areas.main.add(fr);

Other changes

  • Progress mode steps: fix in navigating to shown page headers
  • New Rules Engine action: focus. See Rules Engine
  • Command: submit: false, to suppress the automatically generated submit button. The command needs to be placed at the top level of your schema.
const schema = {
  submit: false,
  pages: [
    {
      fields: [
        {
          type: "text",
          name: "name",
          caption: "Your name",
          placeholder: "John Doe",
        },
      ],
    },
  ],
};

Fixes

  • Fix in databinding: multiple fields binding to single value in model.
  • Fixed scrolling in autocomplete controls
  • Error handling in getInstance() method on form improved
  • Validation checking didn't take disabled and invisible controls out of the equasion
  • Rules engine didn't change visible and disabled state on control, but instead directly modified DOM state.
  • Fixed trigger action in rules engine.
  • Fixed bug in PWA Router route method

Older versions

See Revisions Archive