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

CollectionType just render first row #33

Open
hugoalexmartins opened this issue Sep 6, 2018 · 16 comments
Open

CollectionType just render first row #33

hugoalexmartins opened this issue Sep 6, 2018 · 16 comments

Comments

@hugoalexmartins
Copy link

Hi, I am trying to implement a collection type field, where I can have multiple rows and each one has fields.

But, unfortunatly the collection type just give one row, not all on related rows.

This is what i get from output:

{
    "schema": {
        "title": "visaexpress_wizardbundle_wizard",
        "type": "object",
        "properties": {
            "wizard_questions": {
                "type": "array",
                "title": "wizard_questions",
                "items": {
                    "title": "0",
                    "type": "object",
                    "properties": {
                        "question": {
                            "type": "string",
                            "title": "question",
                            "propertyOrder": 1
                        },
                        "helper": {
                            "type": "string",
                            "title": "helper",
                            "widget": "textarea",
                            "propertyOrder": 2
                        }
                    },
                    "required": [
                        "question"
                    ]
                },
                "propertyOrder": 1
            }
        },
        "required": [
            "wizard_questions"
        ]
    }
}

It should render 5 rows, but I just get one.
Anyone knows what might be ?

Thanks in advance.

@antonyoneill
Copy link

antonyoneill commented Sep 7, 2018

Hey @hugoalexmartins,

Thanks for reaching out to me via email! We actually stopped using liforms very early on, we hit a couple of stumbling blocks and so abandoned the project.

I do intend to revisit the project at a later date as a pet project, and I'd be more than happy to help you out, if I can!

Please be aware that my PR #22 for serialising CollectionTypes as an array wasn't ever merged in, so unless the owners have changed anything it probably is still serialising as a StdObject.

It'd be useful if you could do a var_dump of the Symfony $form variable and post that here.

@hugoalexmartins
Copy link
Author

hugoalexmartins commented Sep 7, 2018

Hey @antonyoneill ,

Thanks you very much for your reply.

Here is the dump($form)

form_dump.txt

Please rename it to .html and you can see all.

@antonyoneill
Copy link

Thanks for sending that over @hugoalexmartins - I've had a look at that form_dump and it looks like you've got a form that has this kind of structure:

  • visaexpress_wizardbundle_wizard type: WizardQuizzType3:
    • wizard_questions, type: CollectionType:
      • 0, type: WizardQuestionType:
        • A sub type (not shown in the dump)
        • A sub type (not shown in the dump)
      • 1, type: WizardQuestionType:
        • A sub type (not shown in the dump)
        • A sub type (not shown in the dump)
      • 2, type: WizardQuestionType:
        • A sub type (not shown in the dump)
        • A sub type (not shown in the dump)

Those subtypes of WizardQuestionType I assume are the types that are coming out for 0 in the schema above question, and helper.

I think you may mean that you expect 3 here:

It should render 5 rows, but I just get one.

I do think that the CollectionType has actually been serialised into the schema correctly, since it wouldn't make a lot of sense if you were to make a schema for each of the entries in the collection type, since they would all be the same - adding more rows (as would naturally be possible) wouldn't have a schema. (#22 is a different issue that I don't think you're experiencing here - and in hindsight I think it may have been a Symfony issue, not liform)

So! I'm not convinced that the issue is with the serialisation of the Form itself!

Can you give me a bit more detail about how you normalise the data (which is what is powering your wizard), and how you are attempting to render the content? I assume you're using the LiformBundle and Symfony Normalizer to normalise the data? And liform-react to render?

Sorry it took so long to get back to you!

@antonyoneill
Copy link

Depending on the answers to how you normalise and render, I might suggest you move this issue into one of the other projects (though I will continue to help you)

@hugoalexmartins
Copy link
Author

hugoalexmartins commented Sep 27, 2018

Hey @antonyoneill

About the 5 rows to render I told you by mistake, in this case it has 3 rows.

I am using LifromBundle and normalize with:
$this->get('liform')->transform($form)

For this case my json response is like this:

{
    "schema": {
        "title": "visaexpress_wizardbundle_wizard",
        "type": "object",
        "properties": {
            "wizard_questions": {
                "type": "array",
                "title": "wizard_questions",
                "items": {
                    "title": "0",
                    "type": "object",
                    "properties": {
                        "wizard_answers": {
                            "type": "array",
                            "title": "wizard_answers",
                            "items": {
                                "title": "0",
                                "type": "object",
                                "properties": {
                                    "value": {
                                        "type": "string",
                                        "title": "Introduza por favor a sua residência igual à que consta no seu passaporte",
                                        "attr": {
                                            "ng-disabled": "false"
                                        },
                                        "propertyOrder": 1
                                    }
                                },
                                "required": [
                                    "value"
                                ]
                            },
                            "propertyOrder": 1
                        }
                    },
                    "required": [
                        "wizard_answers"
                    ]
                },
                "propertyOrder": 1
            }
        },
        "required": [
            "wizard_questions"
        ]
    }
}

It should output 3 rows of wizard_questions.

If I just render the form using Twig (symfony way) all the rows are rendered well, that's why I think it could be something about Liform.

Also, I am using angular-schema-form to render it.

Many thanks for your help.

@hugoalexmartins
Copy link
Author

hugoalexmartins commented Sep 27, 2018

Also, if I remove the Liform transformer and let FOS User transform the response I have is this:

{
    "children": {
        "wizard_questions": {
            "children": [
                {
                    "children": {
                        "wizard_answers": {
                            "children": [
                                {
                                    "children": {
                                        "value": {}
                                    }
                                }
                            ]
                        }
                    }
                },
                {
                    "children": {
                        "wizard_answers": {
                            "children": [
                                {
                                    "children": {
                                        "value": {}
                                    }
                                }
                            ]
                        }
                    }
                },
                {
                    "children": {
                        "wizard_answers": {
                            "children": [
                                {
                                    "children": {
                                        "value": {}
                                    }
                                }
                            ]
                        }
                    }
                },
                {
                    "children": {
                        "wizard_answers": {
                            "children": [
                                {
                                    "children": {
                                        "value": {}
                                    }
                                }
                            ]
                        }
                    }
                },
                {
                    "children": {
                        "wizard_answers": {
                            "children": [
                                {
                                    "children": {
                                        "value": {}
                                    }
                                }
                            ]
                        }
                    }
                },
                {
                    "children": {
                        "wizard_answers": {
                            "children": [
                                {
                                    "children": {
                                        "value": {}
                                    }
                                }
                            ]
                        }
                    }
                },
                {
                    "children": {
                        "wizard_answers": {
                            "children": [
                                {
                                    "children": {
                                        "value": {}
                                    }
                                }
                            ]
                        }
                    }
                }
            ]
        }
    }
}

@antonyoneill
Copy link

antonyoneill commented Sep 27, 2018

Hey @hugoalexmartins
So remember that the Liform::transform() only produces the 'schema' (what the form should look like) and I wouldn't expect it to contain any data..
You'll need to serialise the initial data for the form as well by using the Symfony Normalizer..

In my example, I use a $pageState that I then dump (using twig) into a JavaScript part of the HTML that I then use to render the form.

        /** @var Liform $liform */
        $liform = $this->get('liform');
        $symfonyNormalizer = $this->get('serializer');
        // ... 
        $pageState->formSchema = $liform->transform($form);
        $pageState->formInitial = $symfonyNormalizer->normalize($form);


        return $this->render(
            'MyBundle:Liform/form.html.twig',
            [
                'initialState' => $this->serialiseObjectToJson($pageState),
                // ...
            ]
        );

Then in twig I have

    var container = document.querySelector('#liformEntry')

    var properties = {
        page: '{{ page }}',
        initialState: {{ initialState | raw }},
        additionalState: {}
    }

    MyLiformEntry.init(container, properties)

Which in my case I then feed initialState into the redux store, then get it out when I render the form using liform-react - so you'll use a slightly different way of doing it.

export const MyLiformWrapper = (props: Props) => (
    <Liform
        schema={props.schema} // this comes from initialState.formSchema
        initialValues={props.initial} // this comes from initialState.formInitial
        onSubmit={props.onSubmit}
        baseForm={BaseForm}
        syncValidation={buildSyncValidation(props.schema)}
    />
)

I'm not familiar with the angular-schema-form library..

You'll need to find a way to set the data on the form using your formInitial equivalent. It might be as simple as "updating sf-form"? then calling a redraw see (Updating Form)[https://github.com/json-schema-form/angular-schema-form/blob/development/docs/index.md#updating-form]. I don't know enough about Angular.. 😬

tl;dr: It's important to remember that \Limenius\Liform\Liform::transform only produces the schema. You'll need to use \Symfony\Component\Serializer\Serializer::normalize to get the data into json form.

Remember what I said above about the collection type only storing a single schema, a (prototype)[https://symfony.com/doc/current/reference/forms/types/collection.html#prototype] if you like, for the entities that it represents.

@antonyoneill
Copy link

Here you can see how the ArrayTransformer selects only the first child to represent the rest of them

@antonyoneill
Copy link

Also take a read of this section, it looks like the schema output by liform matches the schema expected by angular-schema-form!
https://github.com/json-schema-form/angular-schema-form/blob/master/docs/index.md#array

So I think your missing piece of the puzzle is setting that initial data (somehow) on the form.

This example might be helpful! https://azimi.me/angular-json-schema-form/demo/demo.html

@hugoalexmartins
Copy link
Author

Hey @antonyoneill
Many thanks for your help.

Now I got a better idea of how Liform works.

At first, in my case I need to use a CollectionType, which in the background is dynamic Form for a Wizard, in which I have Questions and I will have User Answers, how do I need to render multiple Questions, I thought they would come from an array to the Schema.
I think what you told me was that Schema is powered by the initialValues in your case, which then tries to be rendered by the React.

In my case, my initialValues will be rendered by Angular Schema Forms, similar to React.

In the background the Symfony serializer will normalize the Object to the Form, am I correct in this statement?

In the meantime I have made my code for Symfony handle the Object for the Form, but it returns empty, is anything more to escape me?

@hugoalexmartins
Copy link
Author

Also, can you tell how do you deal with "select boxes"?
I have a json response with two key's one enum and another enum_titles, liform-react handles both arrays ?

@antonyoneill
Copy link

Hey @hugoalexmartins - I was on holiday last week and sick yesterday. Let me get back to you in a couple of days as I have a lot to catch up with at work.

I think your questions come from entities, right? You use the Form to describe how to render the entities/questions on screen – Think of the schema as a template or scaffolding for your form. It only has knowledge to render. It's the normalised entity data that you set in the angular two-way binding that is then drawn onto the screen. For more help with this you should probably get in contact with the angular-schema-form maintainers.

Provided you've set the entity in your form (which you have, judging by the dump you shared) the normaliser should serialise the form data for you automatically. You may then need to do some manipulation to make it the right 'shape' for angular-schema-form.

This liform-react code might help you understand how React renders the select boxes (named ChoiceType here) https://github.com/Limenius/liform-react/blob/master/src/themes/bootstrap3/ChoiceWidget.js

I'll try to get back to you in a few days if you're still struggling.

@hugoalexmartins
Copy link
Author

hugoalexmartins commented Oct 12, 2018

Hey @antonyoneill, I think I've join all the pieces of my puzzle.
But, in the end, I've facing some issue with DEV and PROD environment, which I don't know whats happening, and this is weird.

I am using a form, which will attach the dump, the same one in DEV and PROD, but in PROD it triggers and error:
"Uncaught PHP Exception Limenius\Liform\Exception\TransformerException: "Liform cannot infer the json-schema representation of a an empty Collection or array-like type without the option "allow_add" (to check the proptotype). Evaluating "wizard_questions"" at /Users/hugomartins/projectos/visaexpress/visaExpress/vendor/limenius/liform/src/Limenius/Liform/Transformer/ArrayTransformer.php line 65 {"exception":"[object] (Limenius\Liform\Exception\TransformerException(code: 0): Liform cannot infer the json-schema representation of a an empty Collection or array-like type without the option "allow_add" (to check the proptotype). Evaluating "wizard_questions" at"

I am using exactly the same form, and this is not making any sense to me...

What issue could be this one ?
dumpPROD.txt
dumpDEV.txt

@antonyoneill
Copy link

Hey @hugoalexmartins sorry for the delay, super busy at work at the moment. Will try to get back to you this week. I suspect that you have different package versions installed on dev/prod or there's some obscure setting somewhere. I don't think it'll be a liform issue but some other difference between your environments. Unfortunately as I'm not familiar with your environments I cannot suggest a place to start

@hugoalexmartins
Copy link
Author

Hey @antonyoneill its seems be something with embed data into ColletionType, I use PRE_SET_DATA event, to find out which data should be in CollectionType and add "data" options to CollectionType.
This works perfect in DEV, but in PROD CollectionType has no data (and I don't know why), so Liform throw's the exception correctly, because the CollectionType has no data and therefore has no way to generate Array of Questions.
End the end, I added this workaround to ensure data is added to CollectionType in DEV and PROD.

$form->add('wizard_questions', CollectionType::class, array(
          'entry_type' => WizardQuestionType::class,
          'data' => $choices
 ));
      
      
$form->get('wizard_questions')->setData($choices);

Looking to this, it looks like data is added twice, but this only way I can make this work.

@nacmartin
Copy link
Member

nacmartin commented Oct 31, 2018

Hi, sorry for not paying attention so often :(. I am terribly busy and the section that is using Liform in our project has been stable for some time so I have to find time from my spare time to help with these issues and it is being hard. Thanks a lot @antonyoneill for caring ❤️

Basically one thing is the schema and another are the values. The schema is a json-schema representation https://json-schema.org/ and as such for a collection it describes the type of the item in a collection (an array in json-schema), but not each item itself. The schema describes the "shape" of the data, not the actual values. In a collection, the shape is defined by defined how items should be shaped, but the actual items.

The information about the actual values that populate the form, that should contain the values for each item in the collection, is produced independently by this normalizer https://github.com/Limenius/Liform/blob/master/src/Limenius/Liform/Serializer/Normalizer/InitialValuesNormalizer.php

Using something like this: https://github.com/Limenius/symfony-react-sandbox/blob/master/src/Controller/AdminController.php#L45

The distinction between schema and values can be seen clearly here https://mozilla-services.github.io/react-jsonschema-form/ (the extra options they use in their schema are different than in liform but the general idea applies).

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

3 participants