json-schema-form / angular-schema-form

Generate forms from a JSON schema, with AngularJS!
https://json-schema-form.github.io/angular-schema-form
MIT License
2.47k stars 654 forks source link

Does this support $ref? #69

Closed dkengaroo closed 7 years ago

dkengaroo commented 10 years ago

Hiya,

I have a schema with "$schema": "http://json-schema.org/draft-04/schema#", "title": "title", "type": "object", "item": { "type": "object", "properties": { "attribute": { "$ref": "#/definitions/schema2" } }, "required": [ "attribute_info" ] } .... something like that.

I kept the form simple with the "*" and a submit button. However the form doesn't render properly where the $ref is.

am I doing something wrong?

fiddur commented 10 years ago

No. References are planned to be implemented, but it isn't yet.

dkengaroo commented 10 years ago

Cool! Great to know :) :+1:

dkengaroo commented 10 years ago

Would you happen to know when the implementation is planned to happen?

fiddur commented 10 years ago

Hard to say right now. We might need it in the project we're working on at Textalk, in that case it will be implemented before november. Otherwise it is more uncertain.

If someone else wants to have a go at it, go ahead :) I guess it would be best to follow the reference while recursing into that level when expanding the form, keeping in mind that references could be cyclic. Probably finding a good JSON-Pointer parser...

dkengaroo commented 10 years ago

darn. I hope that the project will need it :)

I tried the generator with a schema that has a nested object. didn't work either :[ I'm guessing this only works with flattened schemas?

torstenrudolf commented 10 years ago

@dkengaroo do you mean something like this? (That works fine for me)

The Schema:

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "type": "object",
    "required": ["name"],
    "properties": {
        "name": {
            "$schema": "http://json-schema.org/draft-04/schema#",
            "title": "Person Name",
            "description": "A person's name",
            "type": "object",
            "required": ["familyName", "givenName"],
            "properties": {
                "familyName": {
                    "type": "string", "maxLength": 30, "minLength": 1,
                    "title": "Surname"
                },
                "givenName": {
                    "type": "string", "maxLength": 20, "minLength": 1,
                    "title": "First Name"
                },
                "middleName": {
                    "type": "string", "maxLength": 20, "minLength": 1,
                    "title": "Middle Name"
                }
            }
        }
    }
}
dkengaroo commented 10 years ago

Hiya,

the code you provided works for me too! However, when I do this:

 "$schema": "http://json-schema.org/draft-04/schema#",
    "type": "object",
    "required": ["name"],
    "properties": {
        "name": {
            "$schema": "http://json-schema.org/draft-04/schema#",
            "title": "Person Name",
            "description": "A person's name",
            "type": "object",
            "properties": {
                "groups":{
                    "type": "object",
                    "required": ["familyName", "givenName"],
                    "properties": {
                        "familyName": {
                            "type": "string", "maxLength": 30, "minLength": 1,
                            "title": "Surname"
                        },
                        "givenName": {
                            "type": "string", "maxLength": 20, "minLength": 1,
                            "title": "First Name"
                        },
                        "middleName": {
                            "type": "string", "maxLength": 20, "minLength": 1,
                            "title": "Middle Name"
                        }

                    }
                }
            }
        }
    }

It works as well. However the word "groups" only shows up as "g" in the form

Does it have issues because of something when it is traversing?

torstenrudolf commented 10 years ago

When I put your example in http://textalk.github.io/angular-schema-form/examples/bootstrap-example.html it works just fine, doesn't it? Are you using the most recent release v0.7.2? I think there was a problem with default title #67

dkengaroo commented 10 years ago

Hey! it works when I get v0.7.2! :) should have done an update earlier...

eskatos commented 10 years ago

$ref support would be a real :+1:

dkengaroo commented 10 years ago

whoa! Nice! :+1:

pretty spiffy :)

kuldeepcode commented 10 years ago

Could this issue be re-opened, and re-labeled as an enhancement / improvement request? If the maintainers would be receptive to including it, we may be willing to code the improvement and/or have it done.

nicklasb commented 9 years ago

Hi, with regards to the implementation of that, it is quite complicated, if not impossible to solve in a completely general matter, as schemas aren't always exposed as files.

However, a midway is to not overthink it, but to leave some of that to the user. I think the python jsonschema library solves this quite nicely by letting the user implement a uri handler for each namespace. So when the validator encounters a $ref with an unknown namespace, it checks the if the resolver handles it, which in turn tries its handlers. And if any does, it returns the schema(a dict in python). In the case below, it is against a file, but it could of course be against an array, object properties or whatever.

As you see, the handler is very easy to implement, and the same should work in JavaScript.

def mbe_uri_handler(self, uri):
    """ Handle the mbe:// references """
    # Use urlparse to parse the file location from the URI
    _file_location = self.json_schema_folder + urlparse(uri).netloc
    return json.loads(open(_file_location, "r", encoding="utf-8").read())

def __init__(self)
    .....
    ..... 
    _resolver = RefResolver(base_uri="",
                           handlers={"mbe": self.mbe_uri_handler}, referrer=None, cache_remote=True)
    self.d4validator = Draft4Validator({}, resolver=_resolver)

So, for example, if I have schemas referencing other scemas, I use a specific namespace. The code below, for example, references another file, and is resolved by the uri handler:

    "schemaId": {
        "description": "The schema id of the node schema, a uuid.",
        "$ref" :"mbe://custom.json#properties/uuid"
    },

I could the declare another handler for another namespace, like "obj:", that references to schemas stored in an objects' properties(like a wannabe dictionary). And I can then, in a schema, use "$ref" :"obj://customer#properties/address_postal" and let the handler figure out that "customer" means the schema stored in $scope.object.customer or whatever.

Docs: https://python-jsonschema.readthedocs.org/en/latest/references/ Resolver code: https://github.com/Julian/jsonschema/blob/master/jsonschema/validators.py#L219

nicklasb commented 9 years ago

The same pattern could obviously then apply to the forms as $ref could be used there as well. It would be great to have inheritance there as well.

Anthropic commented 9 years ago

This is really important for me on a project I am using angular-schema-form for, I wish I had time to work on it myself. I just haven't found a good json resolver that can output a full schema with predefined base URI yet.

That said json-refs is working minus a base URI.

TV4 has JavaScript based code for handling it with a base URI defined, key code is in the function ValidatorContext.prototype.resolveRefs and just below it ValidatorContext.prototype.getSchema

But just does validation and wont output a compiled schema for use with angular-schema-form

Hope that helps someone :)

nicklasb commented 9 years ago

I would think that as long as angular-schema-form is provided a way to get its hands on the referenced JSON structure, finding something in said structure should be fairly trivial. The same method and $ref-syntax could be used to extend forms, I presume.

It is important for me as well to get this working, I do have some time to work on it and make a pull request, but would first like to have some kind of discussion on what would be an acceptable approach. I am also not completely familiar with the direction of the project; what would be a angular-schema-form-istic solution?

nicklasb commented 9 years ago

If there aren't any major objections I could write something that uses the TV4-solution that Anthropic suggested. I suppose that there could be some value in having at least similar interfaces?

davidlgj commented 9 years ago

@nicklasb check out #118 there seems to be a solution using json-refs, or at least people that have got it working. It feels like "resolving" all the refs in a schema before rendering it with angular schema form is the more flexible solution, but I'm open to changing my mind. Would that work for you?

fiddur commented 9 years ago

I think this must be implemented inside schema form, and it must be done with infinite recursion in mind.

Consider this schema:

{
  "$ref": "#/definitions/sub",
  "definitions": {
    "sub": {
      "type": "object",
      "properties": { "sub": {"$ref": "#/definitions/sub" } },
      "additionalProperties": false
    }
  }
}

This will work in tv4, but it can't be expanded before usage. You can try it in http://json-schema-validator.herokuapp.com/ with data: { "sub": { "sub": { "sub": {} } } }

With this in mind, a form definition can't be generated from the schema without also considering the model; only if the sub-object has the key "sub", THEN the reference must be followed and generate a sub-form.

davidlgj commented 9 years ago

@fiddur yes that is certainly a show stopper for resolving all refs outside of angular schema form, but do we need to support this? Is there a real world example where infinitively recursive forms is useful?

Not generating a form for it though unless its in the model? what does that even mean? How should that be represented in the actual DOM/HTML? Currently an object as in your example is just a fieldset or a section (just a div) or not part of it at all if you point out the specific properties in your form definition.

eskatos commented 9 years ago

Any tree-like structure could be some real world example candidate. First thing that comes to my mind : directories/folders

fiddur commented 9 years ago

Yes, there are relevant real world examples. A bit too long to put here though… But imagine for example a json schema representing a tree structure. On each level, there might be a name, some data, and possibly another container for the next level, something like:

Schema:
{
  "$ref": "#/definitions/child",
  "definitions": {
    "child": {
      "type": "object",
      "properties": {
        "name": { "type": "string" },
        "children": {
          "type": "array",
          "items": { "$ref": "#/definitions/child" }
        }
      },
      "additionalProperties": false
    }
  }
}

Model:
{
  "name": "Grandfather",
  "children": [
    {
      "name": "My Father",
      "children": [
        { "name": "Me" },
        { "name": "My Sister" }
      ]
    },
    {
      "name": "My Uncle"
    }
  ]
} 

It is not an uncommon structure.

In this case I would want the form to display the above data with name as simple fields, just that the leaf would have an empty array. Adding an item in that array would give the empty form for name and another empty array for children.

davidlgj commented 9 years ago

@eskatos and @fiddur yes a tree structure is a relevant example but IMHO not best solved this way. By rendering it with with schema form it would just be a russion doll of forms withing fieldsets within forms within fieldsets. Each child getting smaller and smaller.

The only way to get around that is to make a new form type (an add-on), that renders a tree properly (which also could add features ). This is possible to do today without $ref support because it's only the add-on that needs to handle the $refs in that case. And it can still use the standard way to render each node in the tree.

That said if you then had such a recursive structure + some other refs that you do want to have resolved you wouldn't be able to resolve them before hand. It might still be a good idea to actually support $ref in the schema, probably in the same fashion as tv4 does it.

fiddur commented 9 years ago

Many trees have normally few levels, but even with 2 levels a $ref enables you to not repeat yourself there, and there might be possible to add more levels, so a working automatic form would be very useful in that case as well.

Anthropic commented 9 years ago

I'm wondering if there is a way to do it that could benefit tv4 and angular-schema-form and reduce duplication of tv4 code in angular-schema-form...

davidlgj commented 9 years ago

@Anthropic that would be interesting, I got to check it out more to actually get a feel for it. Basically I think supporting $ref but stopping on recursion is a good start.

nicklasb commented 9 years ago

Two levels would be sufficient for me, at least currently. To add an early version but yet a real life structure to the discussion, this is the schema for my entity tree "node" type(MongoDB):

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "MBE Node Schema",
    "type": "object",
    "version": "0.1",
    "properties": {
        "_id": {
            "$ref" :"mbe://custom.json#properties/objectId",
            "description": "The objectId of the node.",
            "field_uuid": "e9487ec8-dfce-440a-a5d5-e65e89bc84aa"
        },
        "parent_id": {
            "$ref" :"mbe://custom.json#properties/objectId",
            "description": "The parent objectId of the node. Empty if top node.",
            "refSchemaId": "a9ca8030-6ea4-11e4-9803-0800200c9a66",
            "field_uuid": "aaf26d75-480a-4176-b4c6-ee544a325ac2"
        },
        "nodeName": {
            "description": "The friendly short name of the node.",
            "type": "string",
            "field_uuid": "9ce4a360-53fe-4c02-b894-c5b1787cf835"
        },
        "description": {
            "description": "The descriptive text of the node.",
            "type": "string",
            "field_uuid": "674286ab-55b8-4e3f-918f-8d3fd9593602"
        },
        "createdWhen": {
            "description": "The time the session was created",
            "type": "string",
            "format": "date-time",
            "field_id": "293137d2-a97f-482d-8e29-2917b32f5e67"
        },
        "schemaId": {
            "description": "The schema id of the node schema, a uuid.",
            "$ref" :"mbe://custom.json#properties/uuid",
            "field_id": "e7b166f5-4bf0-4b8c-9a60-601030daa04b"
        },
        "canRead": {
            "description": "A list of the groups that can read the value of the node.",
            "type": "array",
            "items": { "$ref" :"mbe://custom.json#properties/objectId",
                "refSchemaId": "5d3b8e21-adf3-4005-b73c-1fb63a46f399",
                "field_uuid": "5ede11e9-ccbe-425f-98ac-66d5136ed58b"
            }
        },
        "canWrite": {
            "description": "A list of the groups that can write to the node(and create/delete subnodes).",
            "type": "array",
            "items": { "$ref" :"mbe://custom.json#properties/objectId",
                "refSchemaId": "5d3b8e21-adf3-4005-b73c-1fb63a46f399",
                "field_uuid": "98286b6b-8cba-4086-8a71-2d754ac023b2"
            }
        }
    },
    "required": ["nodeName", "createdWhen", "schemaId", "canRead", "canWrite"],
    "collection": "node",
    "schemaId": "a9ca8030-6ea4-11e4-9803-0800200c9a66"
}

As an entity tree, I have a large number of node types. I have the following refs:

  1. All references the "node" schema except itself.
  2. All references the objectId and uuid definitions where needed.
  3. mbe:// tells Python JSON schema library it should ask the a designated resolver for a resolution for the mbe:// "scheme". In other words, I will lazy fetch those schemas from the database, memory or files when. TV4 handles that with the tv4.addSchema(uri, schema) function, but then one have to know what to load prior to validation. Anyway, both approaches work.

There are three extra properties I have added to make it useful for a MongoDB Back End:

  1. "field_uuid": Makes it a) possible to follow a certain value even if it has moved in the structure and b) to a degree automate upgrading documents to match new schemas.
  2. "collection": Tells MBE what collection the data belongs in (i have a "session" collection as well, for example.
  3. "schemaId": A unique identifier identifying a certain instance and version of a schema. All documents in the database contains a reference to that through node.schemaId. 4: "refSchemaId" is an attempt to add really cheap lookup and foreign key functionality. That's on the "seems to work"-level currently.
rhelgeby commented 9 years ago

I would consider splitting reference resolving and rendering into two isolated steps.

As someone mentioned, it could preprocess the schema and resolve references before rendering. This could even be delegated to another library. The difference would be that angular-schema-form would be responsible for doing the preprocessing itself, instead of the users. I'm not sure about how to handle live schema updates, though.

nicklasb commented 9 years ago

rhelgeby: IMO, it doesn't matter if the resolving and rendering is separate, as long as it is done by angular-schema-form. I agree on that it is important that angular-schema-form is responsible for the preprocessing, as it probably is a very common problem. One starts using refs very early.

To me, it is a huge benefit not having to know what schemas need to be loaded before parsing and then provide a generalized way to find them. However, I have just realized that I can make an extra pass when loading the schemas and use the "missing"-array of TV4 to use as base to load the relevant schemas and be able to pass them into angulars own processing.

Couldn't just angular-schema-form take a "schemas" parameter where references could be registered and just passed to tv4?

Or perhaps I'll just get it together and make a pull request to add lazy loading of refs to tv4, what do I know. :-) (Edit: I just realized that that would have to be a synchrounous call, which would not work well)

Perhaps live schema updates could be a matter for later considerations(using $watch and friends?).

nicklasb commented 9 years ago

By the way, with regards to infinite loops, tv4 has a checkRecursive parameter.

ulion commented 9 years ago

my limited implement for jsonform to resolve local $ref:

  var resolvedSchemaRefNodes = [];
  function resolveRefs(obj, defs)
  {
    Object.keys(obj).forEach(function(prop, index, array){
      var def = obj[prop];
      if (def !== null && typeof def === 'object') {
        if (def.$ref) {
          if (def.$ref.slice(0, 14) === '#/definitions/') {
            var ref = def.$ref.replace(/^#\/definitions\//, '');
            obj[prop] = defs[ref];
          }
          else {
            console.log('Unresolved $ref: ' + def.$ref);
          }
        }
        else if (resolvedSchemaRefNodes.indexOf(def) < 0) {
          resolveRefs(def, defs);
          resolvedSchemaRefNodes.push(def);
        }
      }
    })
  }

  if (this.formDesc.schema.definitions) {
    resolveRefs(this.formDesc.schema, this.formDesc.schema.definitions);
  }
  console.log(this.formDesc.schema);

the result schema may has some inner link, can be used to generate form, but maybe not work for the validation (but can use original schema to do the validation).

seriousme commented 9 years ago

I have been looking for $ref support as well. My take (but I'm just an amateur programmer toying around) would be to look at TV4 getSchema(uri) which accepts fragments as well.

To avoid too many loops I'd try to maximize the auto generation of the form by default to one level below the model. I'm trying to get a Swagger spec editor working, but anything I looked sofar has issues with the refs.

Examples of swagger doc instances to test against can be found at https://github.com/swagger-api/swagger-spec/blob/master/examples/v2.0/json

I'm more than happy to help where possible.

seriousme commented 9 years ago

Ok, I did some testing and came up with the following http://jsfiddle.net/jklu/6e4bujwy/ So if I can have a tv4 instance containing the current schema while walking the schema its easy to automatically follow refs. As tv4 allows multiple schemas using tv4.addSchema its also possible to preload external schema to be used in external refs. tv4 can take care of cyclic checks etc.

Hope this helps..

urpro-bas commented 9 years ago

Ok from seriousme's idea I have created a service that can fetch all dependencies in a tv4 instance. It can then also replace the refs in the schema with actual object references that can be followed. It's still just a quick implementation but let me know if you have any comments. Check it out here: http://jsfiddle.net/9sjaqka7/

The functions: fetchSchemaWithDependencies takes an uri to a schema and returns a promise for a schemaSetobject. schemaSet.schema is the schema on the uri and schemaSet.tv4 contains the tv4 instance with all the dependencies. refToObjRef takes a schemaSet and converts the refs to object references.

As far as I figured out it supports all kinds of references, including external and cyclic. Keep in mind though angular-schema-form does not support cylic references.

seriousme commented 9 years ago

Nice, the core schema (http://json-schema.org/draft-04/schema") does not offer CORS headers (and you probably don't want to be depending on some other site either) so it would be nice if you could hand the app a list with aliases e.g. { "http://json-schema.org/draft-04/schema": "myMetaSchema.json" } or { "http://json-schema.org/draft-04/schema": myMetaSchema } to solve that one ;-).

urpro-bas commented 9 years ago

I am not sure, it feels kind of hacky, isn't this what the $schema and id properties are for? xml should have similar problems do you know how javascript xml/xsd handlers solve this problem?

eskatos commented 9 years ago

@urpro-bas XML/XSD handlers solve this using "resolvers" which are pretty much like what @seriousme suggested.

seriousme commented 9 years ago

@urpro-bas that is also why validators like tv4 have the addSchema(uri,url) method ;-)

mike-marcacci commented 9 years ago

Hey all, just to make sure I'm clear on this, is this feature request to support $ref in the traversal/form-generation logic, or support fetching external schemas? The way I see it these are two different features.

The former (support for $ref) would be extremely useful even without the latter. I often define many sub-schemas within one file which can then be referenced after the hash.

The latter (support for external schemas) could be accomplished by pre-registering schemas, much the way jjv does. All schemas could be registered with the schema-form directive with an optional sf-schemas attribute. The current sf-schema attribute could continue to work as it currently does, and also accept a string reference to any registered schema.

This would offload the dozens of inevitable edge-cases that will surface when fetching schemas cross-origin, with special authentication methods, etc.


$scope.schemas = {
    'http://example.com/schemas/one.json': { ... },
    'http://example.com/schemas/two.json': { ... }
}

$scope.mySchema = { ... };
<!-- direct reference -->
<form sf-schema="mySchema" sf-form="form" sf-model="model"></form>

<!-- direct reference w/ external schemas -->
<form sf-schemas="schemas" sf-schema="mySchema" sf-form="form" sf-model="model"></form>

<!-- ID reference w/ external schemas -->
<form sf-schemas="schemas" sf-schema="'http://example.com/schemas/one.json'" sf-form="form" sf-model="model"></form>
urpro-bas commented 9 years ago

@mike-marcacci in some sense my code example supported both, The fetchSchemaWithDependencies method fetched the nessary external schemas, while refToObjRef replaced the ref objects with the object it was referencing, thus easing the traversal of the schema and also implicitly enabling form generation from schema's containing refs.

I agree that those are separate features, but I think are both are necessary features. Finding dependended schemas and fetching prevents you from having to maintain a list of dependencies. I am not sure if a solution without the edge cases is better than no solution at all.

But I also think all those feature start to drift away from the core of angular-schema-form. Maybe a separate schema manager project would be an idea. This could then be used by validators and other users of json schemas.

I am not sure about all this, I am not really experienced.

seriousme commented 9 years ago

I'm no angular guru either, but I'd think that one could hand the whole schema management to tv4. For backwards compatibility and ease of use, the user could still inject a schema, but now also a tv4 object. Experienced users can then fillup the tv4 object outside angular-schema-form and inject it into the scope.

My 2cts ;-)

itozapata commented 9 years ago

Is there any sort of update on this one as far as when proper $ref support is coming?

AleksueiR commented 9 years ago

:+1:

davidlgj commented 9 years ago

@itozapata @AleksueiR We need it soonish at Textalk, so I will be implementing it. But I don't know exactly when yet.

mlegenhausen commented 8 years ago

+1

nicklasb commented 8 years ago

I just realized i had spent several hundred hours building this huge solution around believing local refs where supported, thought this was about remote ones. :-/ I also realized that I could work around that, but damn i got scared there for a while.. :-)

WRT rendering, only the "next" level should be rendered. There is no point in rendering all possibilities.

What I also realized would be very useful, when doing this anyway, would be to be able to specify a sub-schema path for a key that defines where in a schema the.

For example, "#/definitions/myCar" would specify that I am editing a myCar, not the entire schema, and #/definitions/myTypeWheels that are referenced within myType should be resolved in the context of the parent schema.

This because many JSON schemas doesn't specify a single structure, but are just holders of definitions. I.e. they have no "properties", just a "definitions" and are resources to other schemas.

AndreNel7 commented 8 years ago

Example of "$ref" workaround, with "allOf" workaround:

paste following in test.html (based on: http://schemaform.io/examples/custom-validators.html)

<!DOCTYPE html>
<html>
<head>
<title>Custom validators, async validators etc</title>
<link rel="stylesheet" href="./dist/bootstrap/3.3.4/css/bootstrap.min.css">
<link rel="stylesheet" href="./dist/bootstrap/3.3.4/css/bootstrap-theme.min.css">
</head>
<body ng-app="test" class="container" ng-controller="TestCtrl">

<h3>ASF (Angular Schema Form)</h3>
<form name="theForm">
<div sf-schema="schema" sf-form="form" sf-model="model"></div>
<div>
The form is <em ng-show="theForm.$pristine">pristine</em><em ng-show="theForm.$dirty">dirty</em>
and <em ng-show="theForm.$valid">valid</em><em ng-show="!theForm.$valid">invalid</em>.
</div>
<div>{{prettyModel}}</div>
</form>

<script type="text/javascript" src="./bower_components/tv4/tv4.js"></script> <!-- Tiny Validator for Schema v4 - https://github.com/geraintluff/tv4 -->
<script type="text/javascript" src="./bower_components/angular/angular.min.js"></script>
<script type="text/javascript" src="./bower_components/angular-sanitize/angular-sanitize.min.js">
</script>

<script type="text/javascript" src="./bower_components/objectpath/lib/ObjectPath.js"></script>

<script type="text/javascript" src="./dist/schema-form.js"></script>
<script type="text/javascript" src="./dist/bootstrap-decorator.min.js"></script>

<script type="text/javascript" src="./json-refs-standalone.js"></script> <!-- https://github.com/whitlockjc/json-refs -->

<script type="text/javascript" src="./jquery-2.1.3.js"></script> <!-- for json extend / merge -->
<script type="text/javascript" src="./jQuery.extendext.min.js"></script> <!-- for json extend / merge - with array extend (instead of array overwrite) - https://github.com/mistic100/jQuery.extendext -->

<script>
    function getObject(in_obj, in_prop, in_val, in_path) // http://stackoverflow.com/questions/15523514/find-by-key-deep-in-nested-json-object // http://jsfiddle.net/FM3qu/7/
    {
        if (!in_path) in_path = '';
        var result = null;
        if (in_obj instanceof Array)
        {
            for (var i = 0; i <in_obj.length; i++)
            {
                result = getObject(in_obj[i], in_prop, in_val);
                if (result)
                {
                    break;
                }
            }
        }
        else
        {
            for (var prop in in_obj)
            {
                //console.log(prop + ': ' + in_obj[prop] + ' -> ' + in_prop);
                if (prop == in_prop)
                {
                    //console.log(prop);
                    if (in_val)
                    {
                        if (in_obj[prop] == in_val)
                        {
                            return in_obj;
                        }
                    }
                    else
                    {
                        return in_path += '.' + prop; // return path rather than object
                        //console.log(in_path);
                        //return in_obj;
                    }
                }
                if (in_obj[prop] instanceof Object || in_obj[prop] instanceof Array)
                {
                    //console.log(in_path + '.' + prop);
                    result = getObject(in_obj[prop], in_prop, in_val, in_path + '.' + prop);
                    if (result)
                    {
                        break;
                    }
                }
            }
        }
        return result;
    }

    var defRefJsonSchema =
    {
        "$schema": "http://json-schema.org/draft-04/schema#",

        "definitions": {
            "address": {
                "type": "object",
                "properties": {
                    "street_address": { "type": "string" },
                    "city": { "type": "string" },
                    "state": { "type": "string" }
                },
                "required": ["street_address", "city", "state"]
            },
            "country": {
                "type": "object",
                "properties": {
                    "country": { "type": "string" },
                    "country-dial-code": { "type": "integer" },
                    "country-short": { "type": "string" }
                },
                "required": ["country", "country-dial-code", "country-short"]
            }
        },
        "type": "object",
        "properties": {
            "billing_address": { "allOf": [{ "type": "object", "properties": { "billing_id": { "type": "number" } } }, { "$ref": "#/definitions/address" }, { "$ref": "#/definitions/country" }] },
        "shipping_address": { "allOf": [ {"type": "object", "properties": { "shipping_id": { "type": "number" } } }, { "$ref": "#/definitions/address" } ] },
        }
    };
    // ___________________
    //| "$ref" workaround |
    //|___________________|
    JsonRefs
    .resolveRefs(defRefJsonSchema, {
        "filter": ['relative', 'local']
    })
    .then(function (res) // https://github.com/whitlockjc/json-     refs/blob/master/docs/API.md#module_JsonRefs.resolveRefs
    {
        var sjson = JSON.stringify(res.resolved);

        // ____________________
        //| "allOf" workaround |
        //|____________________|
        while (sjson.indexOf('allOf') !== -1) // loop until all 'allOf' have been replaced
        {
            // Replace allOf with combined JSON object
            //console.log('---------------------allOf-------------------------------');
            //console.log(res.resolved);
            //var aAllOf = getObject(res.resolved, 'allOf').allOf;
            var aAllOfPath = getObject(res.resolved, 'allOf'); // hacked getObject() to return path to sub-object (from inside the main-object) rather than the sub-object itself
            //console.log(aAllOfPath);
            var aAllOf = eval('res.resolved' + aAllOfPath); // get sub-object from path in main-object
            //console.log(aAllOf);

            //var result = $.extend(true, {}, aAllOf[0], aAllOf[1]); // resultant object from combining allOf-path's objects
            var result = {}; // resultant object from combining allOf-path's objects
            for (var i = 0, length = aAllOf.length; i <length; i++)
            {
                //console.log(aAllOf[i]);
                //$.extend(true, result, aAllOf[i]);
                $.extendext(true, 'extend', result, aAllOf[i]); // https://github.com/mistic100/jQuery.extendext
            }
            console.log(JSON.stringify(result));

            eval('delete res.resolved' + aAllOfPath); // delete allOf
            //console.log('res.resolved' + aAllOfPath.split('.allOf')[0]);
            eval('res.resolved' + aAllOfPath.split('.allOf')[0] + ' = ' + JSON.stringify(result)); // put combined obj in place of deleted allOf
            //console.log(res.resolved);
            sjson = JSON.stringify(res.resolved);
        }
        //console.log(sjson)

        buildASF(res.resolved);
    }, function (err)
    {
        console.log(err.stack);
    });

    function buildASF(jsonSchema)
    {
        angular.module('test', ['schemaForm']).controller('TestCtrl', function ($scope, $q, $timeout)
        {
            $scope.schema = jsonSchema;

            $scope.form = [
                "*",
                {
                    "type": "submit",
                    "title": "OK"
                }
            ];

            $scope.model =
            {
                "billing_address": { "billing_id": 111, "street_address": "123 Home", "city":"centurion", "state":"Gauteng", "country":"South Africa", "country-short":"ZA", "country-dial-code":27 },
                "shipping_address": { "shipping_id": 222, "street_address": "7 Ship" }
            };

            $scope.$watch('model', function (value)
            {
                if (value)
                {
                    $scope.prettyModel = JSON.stringify(value, undefined, 2);
                }
            }, true);
        });
    }
</script>

</body>
</html>
mariusheil commented 7 years ago

Any updates on this issue?

Anthropic commented 7 years ago

@mariusheil I have one more failing test to fix before I start taking a look at it, Although anything implemented would not be much beyond what @AndreNel7 posted above initially, just internally. It could be a while before we can fully support recursive form generation.

Anthropic commented 7 years ago

@mariusheil the second alpha has $ref pre-processing