rjsf-team/react-jsonschema-form

Expose a field template API

n1k0 opened this issue · 3 comments

n1k0 commented

Context: #275, #268, #205, #196

As suggested in #275 (comment), we should expose an API for developers to provide a template for field rendering:

// dumb example, more props would be exposed
function MyFieldTemplate({id, label, required, errors, children}) {
  return (
    <div className="my-field">
      <label htmlFor={id}>{label}{required ? "*" : ""}</label>
      {children}
      {errors}
    </div>
  );
}

render(<Form fieldTemplate={MyFieldTemplate} />);

How would this interact with something like overriding the default TitleField implementation?

edit: Not sure if this is right place for this, but I was wondering why the getLabel function exists on SchemaField when TitleField already exists. That seems like duplicate logic that would circumvent the user overriding the default TitleField implementation. Never mind, those are different things.

n1k0 commented

How would this interact with something like overriding the default TitleField implementation?

The registry (which holds TitleField) would be passed as a prop to the template function, so you could use it however you want.

What about a fieldset template? (mentioned on #268) Then I'd be able to have the fieldset template component be a react-bootstrap Tab (page), or an Accordion, or Col or some other library or my own custom component.

Defined by uiSchema, it could look something like:

json data

{
  user: { first_name: "Dan", last_name: "Richfield"},
  company: { name: "Acme", address: {street: "123 Main Street", city: "Boston"}
}

ui schema

"user" {
  "ui:fieldset": {
     "ui:widget": "tab",
     "ui:props": { "title": "Personal", eventKey: 1 },
     "ui:include": ["first_name","last_name"]
   }
},
"company": {
  "ui:fieldset": {
    "ui:widget": "tab",
    "ui:props": { "title": "Company", eventKey: 2 },
    "ui:include": ["name", "address"]
   }
}

Where you'd define a fieldSet object (similar to 'fields') connecting up the name referenced in the uiSchema and the component. So just a { tab: Tab} would do it for this example.

The limits of that structure is that the wrapping fieldset components are coupled to the layout of the json data, in my uses, this doesn't matter but could be quite limiting for some. Maybe another way (but probably more complex) would be to have one area of the uiSchema have definitions for named fieldsets then be able to indicate as part of the regular field's uiSchema options which named fieldset they are in (if different from their parent). That pattern breaks from how uiSchema has been used in the past, as well. I don't know that the example of the uiSchema given is a good path to go down.

What I'm trying to think through is being able to have a number of different fieldsets used, where a Tab could contain multiple Cols which could contain an Accordion where some fields are in the Accordion and some are "free floating" in the Col.

Side note: This component is one of my favorites ever and I use it all over my site. I store a lot of schema/uiSchema on the server. I have a wrapping component around this one that pulls/caches the schemas, will pulls initial data "/edit" endpoint if from an endpoint, knows how to post/put to REST endpoints and render errors from the server. Sometimes I have it do a direct map to my Rails model, but other times I store the entire JSON in a text field in the database that does server-side schema validation before it allows saving... with some models, every row in the table can be set to use a different schema and I get great forms that I can redesign without requiring a recompile of the client. I have an "Experience" schema where there's an admin schema form that builds the data into a single (highly-nested) JSON object that (when viewed by visitors) eventually gets passed into a react component built specifically to handle data from that schema. We can create new schemas and the admin page will know exactly how to edit every different version without having had to write tons and tons of components to do all the editing. It has saved me a bunch of time and given us a lot of freedom to try new things without tons of db migrations and queries just to render a page.