Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Design suggestion #3

Open
mmc41 opened this issue Oct 25, 2016 · 11 comments
Open

Design suggestion #3

mmc41 opened this issue Oct 25, 2016 · 11 comments

Comments

@mmc41
Copy link

mmc41 commented Oct 25, 2016

@Anthropic Hi Anthropic
I just completed a schema based form editor for angular 2 for a future project and I would like to discuss my solution with you - if you are interested? - since I think some of my ideas might be useful for this project and I am interested in co-operation.

My proposed solution consist of:

  1. A generic gui model in typescript that can be easily consumed and used to draw a form. The gui model only contains representational data + mappings back to the schema. No validation stuff.
  2. A typescript translator that reads a JSON schema file and translates it into a gui model.
  3. A web form in angular 2 that process the gui model, shows the corresponding form and calls into AJV (https://github.com/epoberezkin/ajv) at roughly each keystroke to validate and show validation errors.

This solution is very simple yet works fine for my yet-to-be announced OS project. See example of an Angular2 form below using this approach. The code could be extended to work with any web framework with modifications to step 3. The main idea, which I think could affect this core project is to introduce a gui model - or a IR (intermediate representation) in other words - as a translation target for the core AND to simplify things by not putting validations stuff into it but still requiring a fast schema validator to be used in the gui rather than standard validation api's for form elements.

Example of dynamic from produced from a json schema:

screen shot 2016-10-25 at 08 32 09

An example of how an instance of the corresponding gui model could look like is shown below:

const complex_gui_model1: SettingsGuiModel = {
  kind: 'group',
  name: '',
  controlType: 'group',
  label: '',
  tooltip: '',
  settingsObjectPath: '',
  required: true,
  elements: [
    { kind: 'group', name: 'authentication', controlType: 'group', label: 'Authentication', tooltip: 'an authentication description here', settingsObjectPath: 'authentication', isRoot: false, required: true,
      elements: [ { kind: 'field', name: 'user', controlType: 'input', label: 'User', tooltip: 'a username', settingsObjectPath: 'authentication.user', defaultValue: '', values: undefined, required: true, type: 'string', subType: 'none' },
                  { kind: 'field', name: 'password', controlType: 'input', label: 'Password', tooltip: 'a password', settingsObjectPath: 'authentication.password', defaultValue: '', values: undefined, required: true, type: 'string', subType: 'none' },
                  { kind: 'field', name: 'scheme', controlType: 'input', label: 'scheme',  tooltip: '', settingsObjectPath: 'authentication.scheme', defaultValue: 'basic', values: undefined, required: true, type: 'string', subType: 'none' },
                  { kind: 'field', name: 'preemptive', controlType: 'yesno', label: 'preemptive',  tooltip: '', settingsObjectPath: 'authentication.preemptive', defaultValue: true, values: undefined, required: true, type: 'boolean', subType: 'none'}
                ]
    },
    { kind: 'group', name: 'server', controlType: 'group', label: 'Server', tooltip: '', settingsObjectPath: 'server', isRoot: false, required: true,
      elements: [ { kind: 'field', name: 'host', controlType: 'input', label: 'host', tooltip: '', settingsObjectPath: 'server.host', defaultValue: '', values: undefined, required: true, type: 'string', subType: 'none' },
                  { kind: 'field', name: 'port', controlType: 'input', label: 'port', tooltip: '', settingsObjectPath: 'server.port', defaultValue: 80, values: undefined, required: true, type: 'integer', subType: 'none' },
                  { kind: 'field', name: 'protocol', controlType: 'dropdown', label: 'protocol', tooltip: '', settingsObjectPath: 'server.protocol', defaultValue: 'http', values: ['http', 'ftp'], required: true, type: 'string', subType: 'none' }
                ]
    }
  ],
  errors: [],
  isRoot: true
};

And the corresponding source schema looks like is shown here:

{
  '$schema': 'http://json-schema.org/draft-04/schema#',
  'type': 'object',
  'properties': {
    'authentication': {
      'type': 'object',
      'title': 'Authentication',
      'description': 'an authentication description here',
      'properties': {
        'user': {
          'type': 'string',
          'minLength': 1,
          'default': '',
          'title' : 'User',
          'description': 'a username',
        },
        'password': {
          'type': 'string',
          'minLength': 1,
          'default': '',
          'title' : 'Password',
          'description': 'a password',
        },
        'scheme': {
          'type': 'string',
          'default': 'basic'
        },
        'preemptive': {
          'type': 'boolean',
          'default': true
        }
      },
      'required': [
        'user',
        'password',
        'scheme',
        'preemptive'
      ]
    },
    'server': {
      'type': 'object',
      'title': 'Server',
      'properties': {
        'host': {
          'type': 'string',
          'default': ''
        },
        'port': {
          'type': 'integer',
          'multipleOf': 1,
          'maximum': 65535,
          'minimum': 0,
          'exclusiveMaximum': false,
          'exclusiveMinimum': false,
          'default': 80
        },
        'protocol': {
          'type': 'string',
          'default': 'http',
          'enum' : ['http', 'ftp']
        }
      },
      'required': [
        'host',
        'port',
        'protocol'
      ]
    }
  },
  'required': [
    'authentication',
    'server'
  ],
  'additionalProperties': false
}

Let me know if you think this sounds interesting?. If so, we could co-operate on a "standardised" gui model and a translator from json schema to that model. As noted, I have an initial version working.

/Morten

@severinkehding
Copy link

Hey Morten,

i am currently looking for a solution to do exactly what you already did. I stumbled upon some repos all with more or less exciting results i am almost ready to implement it myself. If you dont mind i would like to take a look at your working version we migth find a mutual agreement to further advance this dynamic rendering.

Cheers!

@mmc41
Copy link
Author

mmc41 commented Oct 26, 2016

@severinkehding Thanks for your interest. The code is currently a part of a closed-source project but I can be persuaded to work on extracting it and releasing it as OS on github and npm if someone (you?) will commit to help with docs and maintenance. Otherwise I do not have time. The code is high-quality with tests but currently no docs apart from some comments.

@Anthropic
Copy link
Member

@mmc41 send me a PM in Gitter and we can discuss, although I am in Australia so being 1am I am off to sleep now so I will follow up tomorrow :)

Frankly I have been looking at two half started implementations and haven't decided yet which way to go, so I am open to understanding more solutions further to try to find the best end solution for users.

@mmc41
Copy link
Author

mmc41 commented Oct 26, 2016

@Anthropic Will send you a msg tomorrow. What is your Gitter channel / username ?

@Anthropic
Copy link
Member

@mmc41
Copy link
Author

mmc41 commented Nov 7, 2016

@severinkehding @Anthropic I took a little time to repackage the code for the proposal as a independent project which has been released on npm as "json-schema-js-gui-model". See the source on github.

I am still interested in combining efforts, in particular on the json schema ui extensions.

@Anthropic
Copy link
Member

@mmc41 had a quick look on the weekend, I like what I see, I need to find more time to wrap my head around TypeScript. Can you export a TS lib and then still using TS es6 syntax import it into another lib? Ran into issues with the core and importing it into ASF in es6 format and everything breaking due to babel changes in v6, just wondering if you know how well TS would handle making a bundle you can bundle in another bundle :)

@mmc41
Copy link
Author

mmc41 commented Nov 30, 2016

Note sure about your particular use case, but yes you can export typescript from one module and import it in other modules. Generally, you just add an index.d.ts file in addition to the index.js file and an types entry to the package.json file. You can see how it is done in the json-schema-js-gui-model project. BTW: I am working on a v2.0 of my libraries that transpiles into es5 to work better in browsers. The prerelease code is committed to GitHub but not released as npm yet.

P.S. The approach I suggest of compiling json schema to a gui model which in turn is used to construct a UI form, is similar to a Intermediate Language (IR) of a classic compiler and is a well-known way to decouple things. It makes it much easier to innovate and contribute.

@nicklasb
Copy link
Member

I import typescript left and right, and runtime-compiles in my solution, IMO, basically all ways work.

@mmc41 One could call the XHTML representation of the DOM model the intermediate language.
That is why it is so easy to extend ASF?

@mmc41
Copy link
Author

mmc41 commented Dec 2, 2016

@nicklasb Yes, in fact there are many intermediate representations/languages at different abstraction levels. For instance, microcode, assembler, and java byte code are all representations of essentially "the same thing" at different abstraction levels. The point is that a good IR is well suited for a particular use at a particular abstraction level. The DOM is for a different use and for a different abstraction level, than the proposed IR for UI forms.

@Anthropic
Copy link
Member

Anthropic commented Dec 11, 2016

@mmc41 if you run the ASF demo and in chrome dev tools search the source for var merged and watcch the line after it, you can see the merge that we currently use. Still uses schema data, but combined with the form data and we modify the key to an object path for traversal of the model.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants