ngx-schema-form icon indicating copy to clipboard operation
ngx-schema-form copied to clipboard

Support of prefixed schema extensions

Open WhileTrueEndWhile opened this issue 6 years ago • 17 comments

Hi there,

in order to better distinguish schema extensions from standardized keywords, I think it would make sense to add compliant alternative names. Thus, no old code needs to be changed and new code can clearly distinguish between schema and extension.

Looks like three key words are involved:

Name Prefixed Name
fieldsets x-field-sets
widget x-widget
visibleIf x-visible-if

(I hope the list is complete...)

The reason why I write this is that I want to create the schemas from Java POJOs (at first only out of curiosity). There it would be very unclean, in my opinion, to define such properties without a prefix there. If there is any interest in this topic, I will gladly report on it.

Moreover, I have unfortunately not found the code, which e.g. makes a { "widget": { "id": "string" } from { "widget": "string" }. I don't know if I could do an implementation without help. Alternatively I could of course run a crappy recursive algorithm over the schema ;-)

Thanks in advance!

WhileTrueEndWhile avatar Jul 09 '18 09:07 WhileTrueEndWhile

@WhileTrueEndWhile that's the method normalizeWidget here: https://github.com/makinacorpus/ngx-schema-form/blob/master/projects/schema-form/src/lib/model/schemapreprocessor.ts#L98

I would advice you to give a try to the implementation, it is usually easier than one can imagine at first. We will help you if you get lost :)

ebrehault avatar Jul 09 '18 12:07 ebrehault

Thanks for the quick answer.

Something like...

...

const props = [
    { name: "fieldsets", alias: "x-field-sets" },
    { name: "fieldsets", alias: "x-widget" }
    { name: "fieldsets", alias: "x-visible-if" }
];

props.forEach(p => {
    const x = p.name;
    const y = p.alias;

    fieldSchema[x] = JSON.parse(JSON.stringify(fieldSchema[y] || fieldSchema[x] || null)); 
});

...

... will be enough?

This means that the original values (x-*) may not be changed and both properties are in the schema at runtime.

Great!

I think that I will soon create a PR, if this really works with the few lines of code.

WhileTrueEndWhile avatar Jul 11 '18 13:07 WhileTrueEndWhile

I don't get why you use JSON.parse and JSON.stringify here. Any reason why something like that could not work?

const props = [
    { name: "fieldsets", alias: "x-field-sets" },
    { name: "fieldsets", alias: "x-widget" }
    { name: "fieldsets", alias: "x-visible-if" }
];

props
.filter(p => p.name || p.alias)
.forEach(p => {
    const id = p.alias || p.name;
    fieldSchema[id] = ...
});

ebrehault avatar Jul 11 '18 13:07 ebrehault

I have only used JSON.* to make both properties really independent of each other (deep copy). I don't know any easier way ;-)

Is there still something wrong with inserting this code? Maybe you want props to be pulled out of the code... ...because there are other 2000+ lines waiting ;-)

Otherwise I would really fork!

WhileTrueEndWhile avatar Jul 11 '18 13:07 WhileTrueEndWhile

I have only used JSON.* to make both properties really independent of each other (deep copy).

ok, fair enough

Is there still something wrong with inserting this code?

looks good to me, go ahead

ebrehault avatar Jul 11 '18 13:07 ebrehault

Thanks!

I don't have a code based on it yet. But when I insert this, it should of course be compact and constant. I have seen little official documentation dealing with this topic, so I want to be careful.

Almost the only thing I found is the following wiki entry of NJsonSchema, which I do not use: https://github.com/RSuter/NJsonSchema/wiki/Custom-Schema-Properties And this seems to have been just a developer's decision.

But if you look at other JSON schema attributes like oneOf, x-widget wouldn't fit either, but xWidget...


Possible solution to never have to touch it again, no matter what:

Name Regular Expression Examples
fieldsets /^x-?field-?sets$/i X-Field-Sets, x-fieldsets, xFieldSets
widget /^x-?widget$/i X-Widget, x-widget, xWidget
visibleIf /^x-?visible-?if$/i X-Visible-If, x-visbleif, xVisibleIf

As soon as I'm settled, I'll make a request... (done)

WhileTrueEndWhile avatar Jul 11 '18 13:07 WhileTrueEndWhile

@ebrehault To clarify my last comment: I have code based on ngx-schema-form, but the reason why I want this feature is only now reasonably ready...

This means there is no rush to accept the PR. As motivation why I suggest this, I can briefly collect the results:

This test ...

https://github.com/WhileTrueEndWhile/JJSchema/blob/master/src/test/java/com/github/reinert/jjschema/xproperties/XPropertiesTest.java

... produces this schema ...

https://github.com/WhileTrueEndWhile/JJSchema/blob/master/src/test/resources/xproperties_example.json

... which produces this form ...

https://makinacorpus.github.io/ngx-schema-form/dist/ngx-schema-form/?https%3A%2F%2Fraw.githubusercontent.com%2FWhileTrueEndWhile%2FJJSchema%2Fmaster%2Fsrc%2Ftest%2Fresources%2Fxproperties_example.json

If my code is good enough, you can use the following POJO:

public static class XPropertiesExample {
    @JsonProperty(value = "FirstName", required = true, defaultValue = "John")
    @Attributes(title = "First Name")
    @XProperties({ "X-Widget.id = \"string\"" })
    private String firstName;

    @JsonProperty(value = "LastName", required = true, defaultValue = "Doe")
    @Attributes(title = "Last Name")
    @XProperties({ "X-Widget.id = \"string\"" })
    private String lastName;

    @JsonProperty(value = "Age", required = true, defaultValue = "1")
    @Attributes(title = "Age in years", minimum = 0)
    @XProperties({ "X-Widget.id = \"number\"", "maximum = 100" })
    private int age;

    @JsonProperty(value = "EnumString", required = true, defaultValue = "foo")
    @Attributes(title = "Enum String")
    @XProperties(files = { "/xproperties_example.properties" })
    private String enumString;

    // ...
}

I think it looks subtly better. So no hurry, I want to use the "new approach" as soon as it is good enough...

WhileTrueEndWhile avatar Jul 13 '18 16:07 WhileTrueEndWhile

ok, fine :)

ebrehault avatar Jul 13 '18 18:07 ebrehault

My 50 cent:

We keep ngx-schema-form json schema extension in a separate file.

We call this file schemaform.json.

At compile time the standard json schema and the schemaform.json get merged together. (There are plenty deep merge libs out there)

This is driven by the fact that mostly the standard json schema cames from the backend.

daniele-pecora avatar Aug 07 '18 06:08 daniele-pecora

Thank you for accepting this PR.

Is there a difference between...

https://github.com/makinacorpus/ngx-schema-form#conditional-fields

...
        "visibleIf": {
          "comment": ['$ANY$']
        }
...

... and ...

https://json-schema.org/understanding-json-schema/reference/object.html#property-dependencies

...
  "dependencies": {
    "credit_card": ["billing_address"]
  }
...

???

If not, you could transform "dependencies" to "visibleIf" as well.

WhileTrueEndWhile avatar Sep 17 '18 09:09 WhileTrueEndWhile

@WhileTrueEndWhile I might be wrong but as far as I remember, visibleIf is just about visibility, so if the field value exists already, it will be processed, validated, stored, etc.

Whereas dependencies will discard the field from the model.

ebrehault avatar Sep 17 '18 09:09 ebrehault

Thank you for the quick feedback. I have created a GIST for this. It looks like the model always matches the view after all. The only thing the standard doesn't support is "$ANY$", which should mean "not null".

http://makinacorpus.github.io/ngx-schema-form/dist/ngx-schema-form/?https%3A%2F%2Fgist.githubusercontent.com%2FWhileTrueEndWhile%2F158b63ce219a2bff0173d4b59a01d215%2Fraw%2Fb3a3e0f4dbe7ef07514e73f51d6ee1226d7a495b%2Fvisibleif.schema.json

WhileTrueEndWhile avatar Sep 17 '18 10:09 WhileTrueEndWhile

ok well then maybe we could transform "dependencies" to "visibleIf" as you proposed

ebrehault avatar Sep 17 '18 11:09 ebrehault

To distinguish between standard and nxg-schema-form related properties ngx-schema-form does now provide a json schema (came with #241).

Bit apart from that: what about moving all non standard properties into the widget property?

daniele-pecora avatar Sep 25 '18 06:09 daniele-pecora

Even if, semantically, all our non standard properties are not all about the widget, I guess it could be nice.

ebrehault avatar Sep 25 '18 06:09 ebrehault

Thank you very much for the great work!

I have two more additions: https://github.com/guillotinaweb/ngx-schema-form/compare/master...WhileTrueEndWhile:master

formelement.component.ts: With your own widget registry, setting the validation state CSS classes at this level is more than superfluous. In order not to break legacy code, it would be useful to define an optional property that deactivates it. Is there a way to access the global schema at this point?

schemapreprocessor.ts: The stupid copying of the "dependencies" property as "visibleIf" has already been discussed and is probably not priority 1 ;-)

WhileTrueEndWhile avatar Feb 05 '19 21:02 WhileTrueEndWhile

Support of prefixed schema extension came with commit d7c3d8f3dadf9a8bcbaa0ba004452efc28372b72 See also : https://stackblitz.com/edit/ngx-schema-form-customs-widgets-223?file=src/app/myform/schema-form.json

daniele-pecora avatar Aug 30 '20 06:08 daniele-pecora