Suman Kundu Suman Kundu - 6 months ago 220
JSON Question

Schema form using Aurelia

I am using aurelia to build a dynamic form based on a json. The form is generating from a json like the following:

Schema = [{
'key': 'Name',
'display': 'Name',
'type': 'text',
'placeholder': 'Name',
'required': true
},
{
'key': 'IsSubscribed',
'display': 'Subscribed to newsletter?',
'type': 'checkbox',
'placeholder': null,
'required': false
}];


The model to fill the form is available via a Web API service. So, far I succeed by the following template.

<template>

<section class="au-animate">
<h2>Edit Form</h2>
<form class="form-group">
<div repeat.for="item of Schema" class="form-group">
<label if.bind="item.type === 'text' || item.type === 'checkbox'" class="control-label" for.bind="item.key">${item.display}
<input class="form-control" id.bind="item.key" placeholder.bind="item.placeholder" type.bind="item.type" value.bind="Model[item.key]" />
</label>
<label if.bind="item.type === 'textarea'">${item.display}
<textarea placeholder.bind="item.placeholder" value.bind="Model[item.key]></textarea>
</label>
...
</div>
</form>
</section>

</template>


Now I am facing difficulties, when the Model contains another object as property. E.g., for the property Address I would like a input box for City. Hence, item.key = "Address.City".

I can bind with (1) Model.Address.City or (2) Model['Address']['City'] which are not possible as the form is generating at runtime. I would like to use something like (3) Model['Address.City'], so that I can use Model[item.key] for the binding. Is their any easy syntex to achive this.

Example of similar application in Angular Js is Angular Schema Form

Thanks in advance.

Answer

This can be accomplished with a binding behavior that understands what to do with the keys. The end-result is the binding will function like any other binding expression.

Here's an example: https://gist.run?id=720d20b2db5adba92f62f7e665cf3b96

app.html

<template>
  <require from="./dynamic-expression-binding-behavior"></require>

  <label>
    Address 1:
    <input value.bind="model & dynamicExpression:'address.address1'">
  </label>
  <label>
    Address 2:
    <input value.bind="model & dynamicExpression:'address.address2'">
  </label>
  <label>
    City:
    <input value.bind="model & dynamicExpression:key">
  </label>
  <label>
    State:
    <input value.bind="model & dynamicExpression:'address.state'">
  </label>
  <label>
    Zip:
    <input value.bind="model & dynamicExpression:'address.zip'">
  </label>
</template>

app.js

export class App {
  model = {
    address: {
      address1: '1 Main Street',
      address2: '',
      city: 'Burlington',
      state: 'VT',
      zip: '05401'
    }
  };

  key = 'address.city';
}

dynamic-expression-binding-behavior.js

import {inject} from 'aurelia-dependency-injection';
import {Parser} from 'aurelia-binding';
import {rebaseExpression} from './expression-rebaser';

@inject(Parser)
export class DynamicExpressionBindingBehavior {
  constructor(parser) {
    this.parser = parser;
  }

  bind(binding, source, rawExpression) {
    // Parse the expression that was passed as a string argument to
    // the binding behavior.
    let expression = this.parser.parse(rawExpression);

    // Rebase the expression
    expression = rebaseExpression(expression, binding.sourceExpression);

    // Squirrel away the binding's original expression so we can restore
    // the binding to it's initial state later.
    binding.originalSourceExpression = binding.sourceExpression;

    // Replace the binding's expression.
    binding.sourceExpression = expression;
  }

  unbind(binding, source) {
    // Restore the binding to it's initial state.
    binding.sourceExpression = binding.originalSourceExpression;
    binding.originalSourceExpression = null;
  }
}