-
-
Notifications
You must be signed in to change notification settings - Fork 328
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
[LiveComponent] Adds MultiStep Form Feature #2433
base: 2.x
Are you sure you want to change the base?
Changes from all commits
0d280f9
20ef568
6fb25c2
59f13dc
c886e65
06df934
3c43ca5
a3a3514
d2e0660
b2ea6ed
a67443f
4c6a065
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,312 @@ | ||||||||||||||
<?php | ||||||||||||||
|
||||||||||||||
|
||||||||||||||
/* | ||||||||||||||
* This file is part of the Symfony package. | ||||||||||||||
* | ||||||||||||||
* (c) Fabien Potencier <[email protected]> | ||||||||||||||
* | ||||||||||||||
* For the full copyright and license information, please view the LICENSE | ||||||||||||||
* file that was distributed with this source code. | ||||||||||||||
*/ | ||||||||||||||
|
||||||||||||||
namespace Symfony\UX\LiveComponent; | ||||||||||||||
|
||||||||||||||
use Symfony\Component\Form\FormFactoryInterface; | ||||||||||||||
use Symfony\Component\Form\FormInterface; | ||||||||||||||
use Symfony\UX\LiveComponent\Attribute\LiveAction; | ||||||||||||||
use Symfony\UX\LiveComponent\Attribute\LiveProp; | ||||||||||||||
use Symfony\UX\LiveComponent\Storage\StorageInterface; | ||||||||||||||
use Symfony\UX\TwigComponent\Attribute\ExposeInTemplate; | ||||||||||||||
use Symfony\UX\TwigComponent\Attribute\PostMount; | ||||||||||||||
|
||||||||||||||
use function Symfony\Component\String\u; | ||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* Trait for managing multi-step forms in LiveComponent. | ||||||||||||||
* | ||||||||||||||
* This trait simplifies the implementation of multi-step forms by handling | ||||||||||||||
* step transitions, form validation, data persistence, and state management. | ||||||||||||||
* | ||||||||||||||
* @author Silas Joisten <[email protected]> | ||||||||||||||
* @author Patrick Reimers <[email protected]> | ||||||||||||||
* @author Jules Pietri <[email protected]> | ||||||||||||||
*/ | ||||||||||||||
trait ComponentWithMultiStepFormTrait | ||||||||||||||
silasjoisten marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
{ | ||||||||||||||
use ComponentWithFormTrait; | ||||||||||||||
use DefaultActionTrait; | ||||||||||||||
|
||||||||||||||
#[LiveProp] | ||||||||||||||
public ?string $currentStepName = null; | ||||||||||||||
Comment on lines
+40
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we make this non nullable? I don't see when a multi-step could have "no current step" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are in a trait and we dont have a constructor or something here. which means it depends on the #[PostMount] hook. if thats not executed it is null by default. i think thats something by design we should not change. |
||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* @var string[] | ||||||||||||||
*/ | ||||||||||||||
#[LiveProp] | ||||||||||||||
public array $stepNames = []; | ||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* Checks if the current form has validation errors. | ||||||||||||||
*/ | ||||||||||||||
public function hasValidationErrors(): bool | ||||||||||||||
{ | ||||||||||||||
return $this->form->isSubmitted() && !$this->form->isValid(); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* @internal | ||||||||||||||
* | ||||||||||||||
* Initializes the form and restores the state from storage. | ||||||||||||||
* | ||||||||||||||
* This method must be executed after `ComponentWithFormTrait::initializeForm()`. | ||||||||||||||
*/ | ||||||||||||||
#[PostMount(priority: -250)] | ||||||||||||||
public function initialize(): void | ||||||||||||||
{ | ||||||||||||||
$this->currentStepName = $this->getStorage()->get(\sprintf('%s_current_step_name', self::prefix()), $this->formView->vars['current_step_name']); | ||||||||||||||
|
||||||||||||||
$this->form = $this->instantiateForm(); | ||||||||||||||
|
||||||||||||||
$formData = $this->getStorage()->get(\sprintf('%s_form_values_%s', self::prefix(), $this->currentStepName)); | ||||||||||||||
|
||||||||||||||
$this->form->setData($formData); | ||||||||||||||
|
||||||||||||||
$this->formValues = [] === $formData | ||||||||||||||
? $this->extractFormValues($this->getFormView()) | ||||||||||||||
: $formData; | ||||||||||||||
|
||||||||||||||
$this->stepNames = $this->formView->vars['steps_names']; | ||||||||||||||
|
||||||||||||||
// Do not move this. The order is important. | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why? 😅 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good question :D i cann add a better comment |
||||||||||||||
$this->formView = null; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* Advances to the next step in the form. | ||||||||||||||
* | ||||||||||||||
* Validates the current step, saves its data, and moves to the next step. | ||||||||||||||
* Throws a RuntimeException if no next step is available. | ||||||||||||||
*/ | ||||||||||||||
#[LiveAction] | ||||||||||||||
public function next(): void | ||||||||||||||
{ | ||||||||||||||
$this->submitForm(); | ||||||||||||||
|
||||||||||||||
if ($this->hasValidationErrors()) { | ||||||||||||||
return; | ||||||||||||||
} | ||||||||||||||
Comment on lines
+94
to
+98
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure to be honest. i can test it. |
||||||||||||||
|
||||||||||||||
$this->getStorage()->persist(\sprintf('%s_form_values_%s', self::prefix(), $this->currentStepName), $this->form->getData()); | ||||||||||||||
|
||||||||||||||
$found = false; | ||||||||||||||
$next = null; | ||||||||||||||
|
||||||||||||||
foreach ($this->stepNames as $stepName) { | ||||||||||||||
if ($this->currentStepName === $stepName) { | ||||||||||||||
$found = true; | ||||||||||||||
|
||||||||||||||
continue; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
if ($found) { | ||||||||||||||
$next = $stepName; | ||||||||||||||
|
||||||||||||||
break; | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
if (null === $next) { | ||||||||||||||
throw new \RuntimeException('No next forms available.'); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
$this->currentStepName = $next; | ||||||||||||||
$this->getStorage()->persist(\sprintf('%s_current_step_name', self::prefix()), $this->currentStepName); | ||||||||||||||
|
||||||||||||||
// If we have a next step, we need to resinstantiate the form and reset the form view and values. | ||||||||||||||
$this->form = $this->instantiateForm(); | ||||||||||||||
$this->formView = null; | ||||||||||||||
|
||||||||||||||
$formData = $this->getStorage()->get(\sprintf('%s_form_values_%s', self::prefix(), $this->currentStepName)); | ||||||||||||||
|
||||||||||||||
$this->formValues = [] === $formData | ||||||||||||||
? $this->extractFormValues($this->getFormView()) | ||||||||||||||
: $formData; | ||||||||||||||
|
||||||||||||||
$this->form->setData($formData); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* Moves to the previous step in the form. | ||||||||||||||
* | ||||||||||||||
* Retrieves the previous step's data and updates the form state. | ||||||||||||||
* Throws a RuntimeException if no previous step is available. | ||||||||||||||
*/ | ||||||||||||||
#[LiveAction] | ||||||||||||||
public function previous(): void | ||||||||||||||
{ | ||||||||||||||
$found = false; | ||||||||||||||
$previous = null; | ||||||||||||||
|
||||||||||||||
foreach (array_reverse($this->stepNames) as $stepName) { | ||||||||||||||
if ($this->currentStepName === $stepName) { | ||||||||||||||
$found = true; | ||||||||||||||
|
||||||||||||||
continue; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
if ($found) { | ||||||||||||||
$previous = $stepName; | ||||||||||||||
|
||||||||||||||
break; | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
if (null === $previous) { | ||||||||||||||
throw new \RuntimeException('No previous forms available.'); | ||||||||||||||
} | ||||||||||||||
Comment on lines
+148
to
+167
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. $current = array_search($currentStep, $steps, true);
if (!$current) {
throw new \RuntimeException('No previous steps available.');
}
$previous = $steps[$current - 1]; (or similar) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. with you example the exception is thrown if no current step is available. |
||||||||||||||
|
||||||||||||||
$this->currentStepName = $previous; | ||||||||||||||
$this->getStorage()->persist(\sprintf('%s_current_step_name', self::prefix()), $this->currentStepName); | ||||||||||||||
|
||||||||||||||
$this->form = $this->instantiateForm(); | ||||||||||||||
$this->formView = null; | ||||||||||||||
|
||||||||||||||
$formData = $this->getStorage()->get(\sprintf( | ||||||||||||||
'%s_form_values_%s', | ||||||||||||||
self::prefix(), | ||||||||||||||
$this->currentStepName, | ||||||||||||||
)); | ||||||||||||||
|
||||||||||||||
$this->formValues = $formData; | ||||||||||||||
$this->form->setData($formData); | ||||||||||||||
Comment on lines
+170
to
+182
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Duplicate with "next" method... should this be the main "getCurrentForm" or "getStepForm" method or something like that ? |
||||||||||||||
} | ||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* Checks if the current step is the first step. | ||||||||||||||
* | ||||||||||||||
* @return bool true if the current step is the first; false otherwise | ||||||||||||||
*/ | ||||||||||||||
#[ExposeInTemplate] | ||||||||||||||
public function isFirst(): bool | ||||||||||||||
{ | ||||||||||||||
return $this->currentStepName === $this->stepNames[array_key_first($this->stepNames)]; | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
wdyt ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. reset can return false which makes it not typesafe enough for me. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then currentStep === .. would return false no ? |
||||||||||||||
} | ||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* Checks if the current step is the last step. | ||||||||||||||
* | ||||||||||||||
* @return bool true if the current step is the last; false otherwise | ||||||||||||||
*/ | ||||||||||||||
#[ExposeInTemplate] | ||||||||||||||
public function isLast(): bool | ||||||||||||||
{ | ||||||||||||||
return $this->currentStepName === $this->stepNames[array_key_last($this->stepNames)]; | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i would like to use the new php functions array_key_first and array_key_last. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. haha the "new" :) The "problem" (a detail, to be honest) here is that you first look for a key, then get the value indexed by that key, then compare currentStepName is identical. But we can update this in time if necessary :) |
||||||||||||||
} | ||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* Submits the form and triggers the `onSubmit` callback if valid. | ||||||||||||||
*/ | ||||||||||||||
#[LiveAction] | ||||||||||||||
public function submit(): void | ||||||||||||||
{ | ||||||||||||||
$this->submitForm(); | ||||||||||||||
|
||||||||||||||
if ($this->hasValidationErrors()) { | ||||||||||||||
return; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
$this->getStorage()->persist(\sprintf('%s_form_values_%s', self::prefix(), $this->currentStepName), $this->form->getData()); | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
This feels very implementation specific to me... more on that later :) |
||||||||||||||
|
||||||||||||||
$this->onSubmit(); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* Abstract method to be implemented by the component for custom submission logic. | ||||||||||||||
*/ | ||||||||||||||
abstract public function onSubmit(); | ||||||||||||||
Comment on lines
+224
to
+227
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure what this mean, but onSubmit feels uncommon name.. :/ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe doSubmit ? |
||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* Retrieves all data from all steps. | ||||||||||||||
* | ||||||||||||||
* @return array<string, mixed> an associative array of step names and their data | ||||||||||||||
*/ | ||||||||||||||
public function getAllData(): array | ||||||||||||||
{ | ||||||||||||||
$data = []; | ||||||||||||||
|
||||||||||||||
foreach ($this->stepNames as $stepName) { | ||||||||||||||
$data[$stepName] = $this->getStorage()->get(\sprintf('%s_form_values_%s', self::prefix(), $stepName)); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
return $data; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* Resets the form, clearing all stored data and returning to the first step. | ||||||||||||||
*/ | ||||||||||||||
public function resetForm(): void | ||||||||||||||
{ | ||||||||||||||
foreach ($this->stepNames as $stepName) { | ||||||||||||||
$this->getStorage()->remove(\sprintf('%s_form_values_%s', self::prefix(), $stepName)); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
$this->getStorage()->remove(\sprintf('%s_current_step_name', self::prefix())); | ||||||||||||||
|
||||||||||||||
$this->currentStepName = $this->stepNames[array_key_first($this->stepNames)]; | ||||||||||||||
$this->form = $this->instantiateForm(); | ||||||||||||||
$this->formView = null; | ||||||||||||||
$this->formValues = $this->extractFormValues($this->getFormView()); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* Abstract method to retrieve the storage implementation. | ||||||||||||||
* | ||||||||||||||
* @return StorageInterface the storage instance | ||||||||||||||
*/ | ||||||||||||||
abstract protected function getStorage(): StorageInterface; | ||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* Abstract method to specify the form class for the component. | ||||||||||||||
* | ||||||||||||||
* @return class-string<FormInterface> the form class name | ||||||||||||||
*/ | ||||||||||||||
abstract protected static function formClass(): string; | ||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* Abstract method to retrieve the form factory instance. | ||||||||||||||
* | ||||||||||||||
* @return FormFactoryInterface the form factory | ||||||||||||||
*/ | ||||||||||||||
abstract protected function getFormFactory(): FormFactoryInterface; | ||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* @internal | ||||||||||||||
* | ||||||||||||||
* Instantiates the form for the current step | ||||||||||||||
* | ||||||||||||||
* @return FormInterface the form instance | ||||||||||||||
*/ | ||||||||||||||
protected function instantiateForm(): FormInterface | ||||||||||||||
{ | ||||||||||||||
$options = []; | ||||||||||||||
|
||||||||||||||
if (null !== $this->currentStepName) { | ||||||||||||||
$options['current_step_name'] = $this->currentStepName; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
return $this->getFormFactory()->create(static::formClass(), null, $options); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* @internal | ||||||||||||||
* | ||||||||||||||
* Generates a unique prefix based on the component's class name | ||||||||||||||
* | ||||||||||||||
* @return string the generated prefix in snake case | ||||||||||||||
*/ | ||||||||||||||
private static function prefix(): string | ||||||||||||||
{ | ||||||||||||||
return u(static::class)->afterLast('\\')->snake()->toString(); | ||||||||||||||
} | ||||||||||||||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,48 @@ | ||||||||||||||||||||||||
<?php | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
|
||||||||||||||||||||||||
/* | ||||||||||||||||||||||||
* This file is part of the Symfony package. | ||||||||||||||||||||||||
* | ||||||||||||||||||||||||
* (c) Fabien Potencier <[email protected]> | ||||||||||||||||||||||||
* | ||||||||||||||||||||||||
* For the full copyright and license information, please view the LICENSE | ||||||||||||||||||||||||
* file that was distributed with this source code. | ||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
namespace Symfony\UX\LiveComponent\Form\Type; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
use Symfony\Component\Form\AbstractType; | ||||||||||||||||||||||||
use Symfony\Component\Form\FormBuilderInterface; | ||||||||||||||||||||||||
use Symfony\Component\Form\FormInterface; | ||||||||||||||||||||||||
use Symfony\Component\Form\FormView; | ||||||||||||||||||||||||
use Symfony\Component\OptionsResolver\Options; | ||||||||||||||||||||||||
use Symfony\Component\OptionsResolver\OptionsResolver; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
/** | ||||||||||||||||||||||||
* @author Silas Joisten <[email protected]> | ||||||||||||||||||||||||
* @author Patrick Reimers <[email protected]> | ||||||||||||||||||||||||
* @author Jules Pietri <[email protected]> | ||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||
final class MultiStepType extends AbstractType | ||||||||||||||||||||||||
silasjoisten marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is where we should describe/document what is a "step". |
||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
public function configureOptions(OptionsResolver $resolver): void | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
$resolver | ||||||||||||||||||||||||
->setDefault('current_step_name', static function (Options $options): string { | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's discuss this more globally, but i think "current_step" or even "step" could be enough, and improve readability. Maybe just me, but i feel the implementation choice (e.g., passing the steps as a map) should not impose constraints on the overall naming convention. wdyt ? |
||||||||||||||||||||||||
return array_key_first($options['steps']); | ||||||||||||||||||||||||
}) | ||||||||||||||||||||||||
->setRequired('steps'); | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will require more checks There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What checks you got in mind? |
||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
public function buildForm(FormBuilderInterface $builder, array $options): void | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
$options['steps'][$options['current_step_name']]($builder); | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be checked first i guess ? 😅 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I dont think so, because configure options is already ensuring that. (see the test) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I may miss-read this, but to me at this point there is no certitude that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Allowing to use Maybe with something like that (haven't tested it):
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes i was thinking about the same. so callback or class string of FormTypeInterface. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
IMO, passing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure we can do that but my question is. Why would it make sense to pass the options of the "parent" form StepType to the children? you got an example or use case where it might be useful? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is more to be consistent with how work the In your PR, you cannot set your form options yet, so there is no direct example. You may want to create step form that is configurable, either from:
Is it clearer that way? |
||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
public function buildView(FormView $view, FormInterface $form, array $options): void | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
$view->vars['current_step_name'] = $options['current_step_name']; | ||||||||||||||||||||||||
$view->vars['steps_names'] = array_keys($options['steps']); | ||||||||||||||||||||||||
Comment on lines
+45
to
+46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about "step" and "steps" (or "current_step" and "steps" ? To illustrate my point here: if you look at ChoiceType, it does not use "prefered_choices**_values**" or "prefered_choices**_names**" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure we can rename this. |
||||||||||||||||||||||||
} | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we remove it ( if only used for the string/camel stuff ?)