Forums › Forums › OroPlatform › OroPlatform – Programming Questions › dynamic form in actions.yml operation definition
This topic contains 9 replies, has 3 voices, and was last updated by adriwan_kenoby 5 years, 9 months ago.
Starting from March 1, 2020 the forum has been switched to the read-only mode. Please head to StackOverflow for support.
- CreatorTopic
- April 13, 2018 at 3:43 am #34547
Is it possible to render a dynamic form using actions.yml ?
The first field “backupHost” rely to a ManyToMany association, The related entities are correctly display. Then I would like to have another field depend on the previous host selected.
Dynamic form field are already difficult to handle in regular way, but in this case of yaml declaration I don’t know how to proceed or even if it is possible.
YAML1234567891011121314151617181920212223242526272829303132333435363738394041restore_hosting_website:label: sinabs.hosting.operations.restore_websiteroutes:- sinabs_hosting_website_viewacl_resource: [UPDATE, entity:Sinabs\Bundle\HostingBundle\Entity\Website]button_options:icon: fa-uploadclass: btnpreconditions:@not_equal: [$backupedAt, null]frontend_options:template: SinabsHostingBundle:Action:restore.html.twigtitle: sinabs.hosting.operations.restore_websiteoptions:width: 550modal: falsestateEnabled: falseincrementalPosition: falseallowMaximize: trueallowMinimize: truedblclick: maximizemaximizedHeightDecreaseBy: minimize-barattributes:backupHost:label: 'Backup Host'type: entityoptions:class: %sinabs.host.entity.class%form_options:attribute_fields:backupHost:form_type: genemu_jqueryselect2_entityoptions:class: %sinabs.host.entity.class%choices: $backupHosts - CreatorTopic
- AuthorReplies
- April 13, 2018 at 4:35 am #34548
Hi adriwan_kenoby,
Actually the solution will be the same as with well known php forms.
First you should use single Symfony form type for entire form that contains all the dynamic fields.Now it’s just a regular form.
So a usual write all the dynamic logic at form type event listers.
https://symfony.com/doc/2.8/form/dynamic_form_modification.htmlIf you don’t have an entity or another model representing the whole form you can create DTO for that purpose and map form to it with “data_class” option.
I didn’t find the example at ORO packages with actions.yml, but here it from the workflows for editing RFQ at OroCommerce, it’s very similar:
https://github.com/oroinc/orocommerce/blob/1.6.1/src/Oro/Bundle/RFPBundle/Resources/config/oro/actions.yml#L55-L70
The only difference, you’ll probably will have a DTO for it, for example of a form mapped to DTO you can check below links:
DTO – https://github.com/oroinc/platform/blob/2.6.1/src/Oro/Bundle/ImapBundle/Form/Model/AccountTypeModel.php
FormType for it – https://github.com/oroinc/platform/blob/2.6.1/src/Oro/Bundle/ImapBundle/Form/Type/ChoiceAccountType.php#L119April 13, 2018 at 4:56 am #34549Hi Anyt,
If I “use single Symfony form type for entire form that contains all the dynamic fields.”
How then can I use it in actions.yml ?
April 13, 2018 at 5:13 am #34550By defining form_type option like in this example https://github.com/oroinc/orocommerce/blob/1.6.1/src/Oro/Bundle/RFPBundle/Resources/config/oro/actions.yml#L67
April 13, 2018 at 5:23 am #34551Thank you Anyt I will work on that. Still in need of comprehension of the Oro technologie stack chaplin + backbone + underscore…
April 13, 2018 at 5:35 am #34552You can check series of JS master class available in our youtube channel to understand it better
April 17, 2018 at 7:08 am #34553I come back to you because I try to do what I want based on the examples, but I can’t do it.
To resume, I have an operation define in actions.yml
YAML12345678910111213141516171819202122232425262728293031323334353637383940operations:restore_hosting_website:label: sinabs.hosting.operations.restore_websiteroutes:- sinabs_hosting_website_viewacl_resource: [UPDATE, entity:Sinabs\Bundle\HostingBundle\Entity\Website]button_options:icon: fa-uploadclass: btnpreconditions:@not_equal: [$backupedAt, null]frontend_options:template: SinabsHostingBundle:Action:restore.html.twigtitle: sinabs.hosting.operations.restore_websiteoptions:width: 550modal: falsestateEnabled: falseincrementalPosition: falseallowMaximize: trueallowMinimize: truedblclick: maximizemaximizedHeightDecreaseBy: minimize-barattributes:backup:label: 'Backup'type: entityoptions:class: Sinabs\Bundle\HostingBundle\Form\Model\BackupTypeModelform_options:attribute_fields:backup:form_type: sinabs_backup_typeoptions:backupHosts: $backupHoststhe template associated with
PHP123456789101112131415161718192021222324252627{% extends 'OroActionBundle:Operation:form.html.twig' %}{% block form %}{% set buttonOptions = operation.definition.buttonOptions %}{% set pageComponentOptions = {} %}<div class="widget-content"><form id="{{ form.vars.id }}" name="{{ form.vars.name }}"action="{{ app.request.uri }}" {{ form_enctype(form) }}method="{{ form.vars.method }}" data-collect="true" class="form-dialog"{% if buttonOptions.page_component_module is defined %}data-page-component-module="{{ buttonOptions.page_component_module }}"{% endif %}><div>{{ form_widget(form.backup) }}</div><div class="hidden">{{ form_rest(form) }}</div><div class="widget-actions"><button type="reset" class="btn">{{ 'sinabs.horaire.action.cancel'|trans }}</button><button type="submit"class="btn btn-success">{{ 'sinabs.horaire.action.save'|trans }}</button></div></form></div>{% endblock %}And the form type, here I don’t quite understand what I do in events…
PHP123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190<?php/*** Created by PhpStorm.* User: adrien* Date: 16/04/18* Time: 10:02*/namespace Sinabs\Bundle\HostingBundle\Form\Type;use Symfony\Component\Form\AbstractType;use Symfony\Component\Form\FormBuilderInterface;use Symfony\Component\Form\FormEvent;use Symfony\Component\Form\FormEvents;use Symfony\Component\Form\FormInterface;use Symfony\Component\Form\FormView;use Symfony\Component\OptionsResolver\OptionsResolver;use Symfony\Bridge\Doctrine\Form\Type\EntityType;use Symfony\Component\Form\Extension\Core\Type\ChoiceType;use Oro\Bundle\ConfigBundle\Config\ConfigManager;use Sinabs\Bundle\HostingBundle\Form\Model\BackupTypeModel;use Sinabs\Bundle\HostingBundle\Entity\Host;use Sinabs\Bundle\SSH2Bundle\Factory\ConnectionFactory as SSH2Factory;class BackupType extends AbstractType{const NAME = 'sinabs_backup_type';/*** @var ConfigManager*/protected $configManager;/*** @var SSH2Factory*/protected $ssh2;public function __construct(ConfigManager $configManager, SSH2Factory $ssh2){$this->configManager = $configManager;$this->ssh2 = $ssh2;}/*** {@inheritdoc}*/public function getName(){return $this->getBlockPrefix();}/*** {@inheritdoc}*/public function getBlockPrefix(){return self::NAME;}/*** {@inheritdoc}*/public function configureOptions(OptionsResolver $resolver){$resolver->setDefaults(['data_class' => BackupTypeModel::class,'page_component' => 'sinabshosting/js/app/components/backup-type-component','page_component_options' => ['route' => 'sinabs_hosting_change_backup_type',]]);$resolver->setAllowedTypes('page_component_options', 'array');$resolver->setAllowedTypes('page_component', 'string');$resolver->setRequired('backupHosts');}/*** {@inheritdoc}*/public function buildForm(FormBuilderInterface $builder, array $options){$builder->add('host', EntityType::class, ['class' => Host::class,'choices' => isset($options['backupHosts']) ? $options['backupHosts'] : []]);$this->initEvents($builder);}/*** Update form if host is changed** @param FormBuilderInterface $builder*/protected function initEvents(FormBuilderInterface $builder){$builder->addEventListener(FormEvents::PRE_SUBMIT, [$this, 'preSubmit']);$builder->addEventListener(FormEvents::PRE_SET_DATA, [$this, 'preSetData']);}/*** @param FormEvent $formEvent*/public function preSubmit(FormEvent $formEvent){$form = $formEvent->getForm();$data = $formEvent->getData();if (!isset($data['host'])) {return;}$backupTypeModel = $this->createBackupTypeModelFromData($data);if ($backupTypeModel === null) {//reset data for avoiding form extra parameters error$formEvent->setData(null);return;} elseif ($form->getData() && $form->getData()->getHost() !== $data['host']) {//set data here, for renew viewData of the form$form->setData($backupTypeModel);}if ($backupTypeModel instanceof AccountTypeModel) {$this->updateForm($form, $backupTypeModel);}}/*** @param FormEvent $formEvent*/public function preSetData(FormEvent $formEvent){$backup = $formEvent->getData();$form = $formEvent->getForm();if ($backup instanceof BackupTypeModel) {$this->updateForm($form, $backup);}}/*** @param FormInterface $form* @param BackupTypeModel $backupTypeModel*/protected function updateForm(FormInterface $form, BackupTypeModel $backupTypeModel){$host = $backupTypeModel->getHost();$conn = $this->ssh2->connect(['address' => $host->getIp(),'login' => $host->getLogin(),'port' => $host->getSshPort(),'id-rsa-pub' => $this->configManager->get('sinabs_ssh2.id_rsa_pub'),'id-rsa-pem' => $this->configManager->get('sinabs_ssh2.id_rsa_pem'),'passphrase' => $this->configManager->get('sinabs_ssh2.passphrase')]);$sftp = ssh2_sftp($conn->getConnection());$files = [];$dirHandle = opendir("ssh2.sftp://$sftp/");while (false !== ($file = readdir($dirHandle))) {if ($file != '.' && $file != '..') {$files[] = $file;}}$form->add('file', ChoiceType::class, ['choices' => $files]);}public function createBackupTypeModelFromData($data){$file = isset($data['file']) ? $data['file'] : '';if (empty($file)) {return null;}$backupTypeModel = new BackupTypeModel();$backupTypeModel->setHost($data['host']);$backupTypeModel->setFile($file);return $backupTypeModel;}/*** {@inheritdoc}*/public function finishView(FormView $view, FormInterface $form, array $options){$view->vars['page_component'] = $options['page_component'];$view->vars['page_component_options'] = $options['page_component_options'];}}The JS component and view
JavaScript12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455define(function (require) {'use strict';var backupTypeView;var _ = require('underscore');var BaseView = require('oroui/js/app/views/base/view');var $ = require('jquery');backupTypeView = BaseView.extend({html: '',events: {'change select[name="oro_action_operation[backup][host]"]': 'onChangeBackupType'},/*** @constructor** @param {Object} options*/initialize: function (options) {},render: function () {this.$el.html(this.html);this._deferredRender();this.initLayout().done(_.bind(this._resolveDeferredRender, this));},/*** handler event change AccountType* @param e*/onChangeBackupType: function (e) {this.trigger('backupChangeType', $(e.target).val());},/*** Set property html** @param html** @returns {accountTypeView}*/setHtml: function (html) {this.html = html;return this;}});return backupTypeView;});JavaScript123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384define(function (require) {'use strict';var BackupTypeComponent;var $ = require('jquery');var _ = require('underscore');var mediator = require('oroui/js/mediator');var backupTypeView = require('sinabshosting/js/app/views/backup-type-view');var BaseComponent = require('oroui/js/app/components/base/component');var routing = require('routing');BackupTypeComponent = BaseComponent.extend({ViewType: backupTypeView,/*** @constructor* @param {Object} options*/initialize: function (options) {this.route = _.result(options, 'route') || '';var viewConfig = this.prepareViewOptions(options);this.view = new this.ViewType(viewConfig);this.listenTo(this.view, 'backupChangeType', this.onChangeBackupType);},/*** Prepares options for the related view** @param {Object} options - component's options** @return {Object}*/prepareViewOptions: function (options) {return {el: options._sourceElement};},/*** Makes the request to get a form template if account type is changed* @param value - values of the form backup*/onChangeBackupType: function (value) {mediator.execute('showLoading');$.ajax({url: this.getUrl(),method: 'POST',data: {'host': value},success: _.bind(this.templateLoaded, this)});},/*** Handler response* @param {Object} response - contain`s html of new form*/templateLoaded: function (response) {mediator.execute('hideLoading');this.view.setHtml(response.html).render();},/*** Generate url for requests* @returns {string|*}*/getUrl: function () {return routing.generate(this.route, this._getUrlParams());},/*** Prepare parameters for routes* @returns {{}}* @private*/_getUrlParams: function () {return {};}});return BackupTypeComponent;});A function in a controller to render the form to be replaced
PHP12345678910111213141516171819/*** @Route("/backup/change", name="sinabs_hosting_change_backup_type", methods={"POST"})*/public function getFormAction(){$request = $this->get('request_stack')->getCurrentRequest();$host = $request->get('host');$form = $this->get('form.factory')->create('sinabs_backup_type', ['host' => $host]);$html = $this->renderView('SinabsHostingBundle:Form:backup.html.twig', ['form' => $form->createView(),]);$response = ['html' => $html];return new JsonResponse($response);}and his template
PHP123<fieldset class="form-horizontal">{{ form_widget(form) }}</fieldset>I am not sure if I need all this logic, maybe a JS view should be enough to satisfy my needs.
And the problem is that their is still nothing dynamic in my form.Could you point me what I am doing wrong ?
Thanks for your time and your help.
May 8, 2018 at 8:54 am #34554Hello, adriwan_kenoby!
Did you debug your code? Maybe you have some errors?
First, check your ajax action, when you create a form, you need to pass the model as a second parameter, not the array.
In addition, I suggest checking the correctness of loading js. Is the ajax action performed when the host changes?June 22, 2018 at 3:10 am #34555The ajax actions is not performed. No error, nothing to help me to understand what is going wrong.
- This reply was modified 5 years, 9 months ago by
adriwan_kenoby. - This reply was modified 5 years, 9 months ago by
adriwan_kenoby.
June 27, 2018 at 4:49 am #34558Finally achieved. A dynamic form displaying the content of a specific folder depending on selected server
- This reply was modified 5 years, 9 months ago by
- AuthorReplies
The forum ‘OroPlatform – Programming Questions’ is closed to new topics and replies.