ninsuo / symfony-collection

[NOT MAINTAINED] A jQuery plugin that manages adding, deleting and moving elements from a Symfony form collection
https://symfony-collection.fuz.org/
MIT License
444 stars 88 forks source link

Duplicate textarea ckeditor in collection #11

Open informatico-madrid opened 9 years ago

informatico-madrid commented 9 years ago

Hi. i try, to do work this with ckeditor. But when click add button, textarea field in collection begin duplicate.

I find that jquery.collection.js do 2 ajax request to ckeditor.js

The initializator for first request is: send @jquery.min.js:4 n.extend.ajax @jquery.min.js:4 n._evalUrl @jquery.min.js:4 n.fn.extend.domManip @jquery.min.js:3 n.fn.extend.append @jquery.min.js:3 (anonymous function) @jquery.min.js:3 n.access @jquery.min.js:3 n.fn.extend.html @jquery.min.js:3 (anonymous function) @backend_jquery.collection_5.js:383 n.event.dispatch @jquery.min.js:3 r.handle @jquery.min.js:3

And initializator for second request:

send @jquery.min.js:4 n.extend.ajax @jquery.min.js:4 n._evalUrl @jquery.min.js:4 n.fn.extend.domManip @jquery.min.js:3 n.fn.extend.append @jquery.min.js:3 (anonymous function) @jquery.min.js:3 n.access @jquery.min.js:3 n.fn.extend.html @jquery.min.js:3 (anonymous function) @backend_jquery.collection_5.js:394 n.event.dispatch @jquery.min.js:3 r.handle @jquery.min.js:3

Meaby i do something wrong.

I have several form with this issue, all with collection and ckeditor inside. I put my code of one for example:

class BonoType extends AbstractType { protected $user; /* * @var \DateTime / private $date;

public function __construct($user = null){
    $this->user = $user;
    $this->date = new \DateTime("now");
}

/**
 * @param FormBuilderInterface $builder
 * @param array $options
 */
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        .......
    ;
    if ($this->user != null) {
        $builder
           ...............
        ->add('peticion' , 'collection',array(
                'type' => new peticionType($this->user),
                'allow_add' => true,
                'allow_delete' => true,
                'prototype' => true,
                'by_reference' => false,
                'attr' => array(
                    'class' => 'peticion-collection panel panel-primary',
                ),
            )
        )  
        ;
    } else {
        ...........
    }
    if (isset($this->user) AND $this->user->getRoles() == 'ROLE_SUPER_ADMIN') {
       $builder
            ->remove('peticion')
            ->add('peticion' , 'collection',array(
                    'type' => new peticionType($this->user),
                    'allow_add' => true,
                    'allow_delete' => false,
                    'prototype' => true,
                    'by_reference' => false,
                    'prototype_name' => '__peticion_collection',
                    'attr' => array(
                        'class' => 'peticion-collection panel panel-primary',
                    ),
                )
            )
           ...................
            )
        ;    
    }
}

}

My js: ..... $('.peticion-collection').collection({ up: '', down: '', add: '[ + ]', delete: '[ - ]', duplicate: '[ # ]', max: 100, allow_duplicate:true, name_prefix: 'peticion', after_add: function(collection, element) { joaoim.initLocale(); joaoim.initDatePicker(); .........
return true; } }); .......

If anyone know how and why this. its a awesome help.

Note: i found similar cuestion, with sonata admin bundle , i dont use sonata admin bundle, but issue its very similar: https://github.com/egeloen/IvoryCKEditorBundle/issues/140 and yes, i use IvoryCKEditorBundle.

Tankx

ninsuo commented 9 years ago

There are no ajax calls inside symfony-collection, maybe CKEditor has.

CKEditor is very complex; it generates lots and lots of DOM and I actually never thought about using this plugin to manage a collection of CKEditors.

Maybe you can use events to disable your CKEditors before applying an action to the collection, and reenable it just after.

egeloen commented 9 years ago

Can you copy paste the generated data-prototype?

informatico-madrid commented 9 years ago

Yes Thank you.

data-prototype="<div class="form-group"><label class="control-label required">__name__label__</label>:

            <div id="joaoim_bundle_bonosbundle_bono_peticion___name__" class=" subform-control"><div class="form-group"><label class="control-label required" for="joaoim_bundle_bonosbundle_bono_peticion___name___titulo">Titulo</label>:

            <input type="text" id="joaoim_bundle_bonosbundle_bono_peticion___name___titulo" name="joaoim_bundle_bonosbundle_bono[peticion][__name__][titulo]" required="required" class="subform-control form-control" /></div><div class="form-group"><label class="control-label required" for="joaoim_bundle_bonosbundle_bono_peticion___name___tipo">Tipo</label>:

            <select id="joaoim_bundle_bonosbundle_bono_peticion___name___tipo" name="joaoim_bundle_bonosbundle_bono[peticion][__name__][tipo]" class="subform-control form-control"><option value="error">Error</option><option value="nueva_funcionalidad">Nueva funcionalidad</option><option value="ajustes">Ajustes</option><option value="Consulta">Consulta</option><option value="otra">otra</option></select></div><div class="form-group"><label class="control-label required">Fecha</label>:

            <input type="text" id="joaoim_bundle_bonosbundle_bono_peticion___name___fecha_date" name="joaoim_bundle_bonosbundle_bono[peticion][__name__][fecha][date]" required="required" /><input type="text" id="joaoim_bundle_bonosbundle_bono_peticion___name___fecha_date_datepicker" name="datepicker_joaoim_bundle_bonosbundle_bono[peticion][__name__][fecha][date]" required="required" class="joaoimDatePicker"/>
        &nbsp;<select id="joaoim_bundle_bonosbundle_bono_peticion___name___fecha_time_hour" name="joaoim_bundle_bonosbundle_bono[peticion][__name__][fecha][time][hour]" size="1" style="width: 80px; display: inline;" class="form-control"><option value="0">00</option><option value="1">01</option><option value="2">02</option><option value="3">03</option><option value="4">04</option><option value="5">05</option><option value="6">06</option><option value="7">07</option><option value="8">08</option><option value="9">09</option><option value="10">10</option><option value="11">11</option><option value="12">12</option><option value="13">13</option><option value="14">14</option><option value="15">15</option><option value="16">16</option><option value="17">17</option><option value="18">18</option><option value="19">19</option><option value="20">20</option><option value="21">21</option><option value="22">22</option><option value="23">23</option></select>:<select id="joaoim_bundle_bonosbundle_bono_peticion___name___fecha_time_minute" name="joaoim_bundle_bonosbundle_bono[peticion][__name__][fecha][time][minute]" size="1" style="width: 80px; display: inline;" class="form-control"><option value="0">00</option><option value="1">01</option><option value="2">02</option><option value="3">03</option><option value="4">04</option><option value="5">05</option><option value="6">06</option><option value="7">07</option><option value="8">08</option><option value="9">09</option><option value="10">10</option><option value="11">11</option><option value="12">12</option><option value="13">13</option><option value="14">14</option><option value="15">15</option><option value="16">16</option><option value="17">17</option><option value="18">18</option><option value="19">19</option><option value="20">20</option><option value="21">21</option><option value="22">22</option><option value="23">23</option><option value="24">24</option><option value="25">25</option><option value="26">26</option><option value="27">27</option><option value="28">28</option><option value="29">29</option><option value="30">30</option><option value="31">31</option><option value="32">32</option><option value="33">33</option><option value="34">34</option><option value="35">35</option><option value="36">36</option><option value="37">37</option><option value="38">38</option><option value="39">39</option><option value="40">40</option><option value="41">41</option><option value="42">42</option><option value="43">43</option><option value="44">44</option><option value="45">45</option><option value="46">46</option><option value="47">47</option><option value="48">48</option><option value="49">49</option><option value="50">50</option><option value="51">51</option><option value="52">52</option><option value="53">53</option><option value="54">54</option><option value="55">55</option><option value="56">56</option><option value="57">57</option><option value="58">58</option><option value="59">59</option></select></div><div class="form-group"><label class="control-label required" for="joaoim_bundle_bonosbundle_bono_peticion___name___descripcion">Descripcion</label>:

                <textarea id="joaoim_bundle_bonosbundle_bono_peticion___name___descripcion" name="joaoim_bundle_bonosbundle_bono[peticion][__name__][descripcion]" required="required" class=" subform-control"></textarea><script type="text/javascript">
                var CKEDITOR_BASEPATH = "/bundles/ivoryckeditor/";
            </script><script type="text/javascript" src="/bundles/ivoryckeditor/ckeditor.js"></script><script type="text/javascript">
            if (CKEDITOR.instances["joaoim_bundle_bonosbundle_bono_peticion___name___descripcion"]) { delete CKEDITOR.instances["joaoim_bundle_bonosbundle_bono_peticion___name___descripcion"]; }

                        CKEDITOR.replace("joaoim_bundle_bonosbundle_bono_peticion___name___descripcion", {"toolbar":[["JustifyLeft","JustifyCenter","JustifyRight","JustifyBlock","-","BidiLtr","BidiRtl","-","Blockquote","CreateDiv"],["NumberedList","BulletedList","-","Outdent","Indent"],["Link","Unlink","Anchor"],["Maximize","ShowBlocks","-","About"],["Source"],"\/",["Image","Smiley"],["Bold","Italic","Underline","Strike","Subscript","Superscript","-","RemoveFormat"],["Styles","Format","Font","FontSize"],["TextColor","BGColor"]],"filebrowserImageBrowseParameters":{"mode":"image"},"allowedContent":true,"extraAllowedContent":"img(*){*}[*]","uiColor":"#000000","config_name":"default","autoload":"false","filebrowserImageBrowseUrl":"\/app_dev.php\/blog\/elfinder"});
        </script></div><div class="form-group"><label class="control-label required" for="joaoim_bundle_bonosbundle_bono_peticion___name___bono">Bono</label>:

            <select id="joaoim_bundle_bonosbundle_bono_peticion___name___bono" name="joaoim_bundle_bonosbundle_bono[peticion][__name__][bono]" class="subform-control form-control"><option value="15">Bono 10 Horas : 280 € + I.V.A. : adminuser</option><option value="19">Bono 10 Horas : 280 € + I.V.A. : adminuser</option></select></div><div class="form-group"><label class="control-label required" for="joaoim_bundle_bonosbundle_bono_peticion___name___peticionPadre">Peticion padre</label>:

            <select id="joaoim_bundle_bonosbundle_bono_peticion___name___peticionPadre" name="joaoim_bundle_bonosbundle_bono[peticion][__name__][peticionPadre]" required="required" class="subform-control form-control"><option value="" selected="selected">huérfano</option><option value="22">Petición de admin</option><option value="26">asdfa asdf</option><option value="27">añadiendo peticion</option><option value="28">fantasma</option></select></div></div></div>"

note: both ckeditor have same id. note2: Meaby i do something wrong and i need refactoring my code to see it.

egeloen commented 9 years ago

AFAIR, the prototype is fine, you only have one textarea with a unique ID. Where do you see a duplicated id?

informatico-madrid commented 9 years ago

Duplicated attribute id are after prototipe add/replace seleccion_019

id duplicated are: #cke_joaoim_bonosbundle_bono_peticion_0_descripcion, both, just below textarea, and in the same div.form-group.

When i load a form of entity with some populated collection, all are fine, if i click then in duplicate button, are fine too. But when i click in add button, the new collection become with duplicate ckeditor.

i will try today to refactoring i see if i can se if i am doing somthing wrong. Thnkx

egeloen commented 9 years ago

IMO, there is something wrong with the replacement of the placeholder which is part of the prototype. A collection should never have the same index used multiple times. Does this library rely on the length of the collection in order to determine the new index to use? If yes, this is a buggy solution which can be rely on.

informatico-madrid commented 9 years ago

i am doing a little debug, and i find a confused behavior:

i modify egeloen/ckeditor-bundle/Templating/CKEditorHelper.php - in renderWidget() function - Line 89 to 94.

i change this

 $replace = sprintf(
            'CKEDITOR.%s("%s", %s);',
            $inline ? 'inline' : 'replace',
            $id,
            $this->fixConfigConstants($this->jsonBuilder->build())
        );

for this:

$replace = sprintf(
            'if(remplazado == 0){ CKEDITOR.%s("%s", %s); remplazado = 1 }',
            $inline ? 'inline' : 'replace',
            $id,
            $this->fixConfigConstants($this->jsonBuilder->build())
        );

And in my base.html.twig, head tag, in my webBundle, this is my base template for all site.

...
 <script>
       ...
          var remplazado = 0;
       ...
</script>
...

and now work fine.

Its this elegant solution? its this way correct?, i need extend CKEditorHelper class in my own bundle?

UPDATE: Only fix it in first click to add button, When you click again to add other collection ckeditor dont load textarea.

UPDATE2: With this improve i can do it work with multiple add collection:

CKEditorHelper.php

$replace = sprintf(
            'if(remplazado["'.$id.'"] != 1){ CKEDITOR.%s("%s", %s); remplazado["'.$id.'"] = 1 }',
            $inline ? 'inline' : 'replace',
            $id,
            $this->fixConfigConstants($this->jsonBuilder->build())
        );
...

my base.html.twig

        <script>
          ...
          var remplazado = {};
        </script>
informatico-madrid commented 9 years ago

sure this its not a good solution, but meaby It puts us in the right direction

cgraefe commented 8 years ago

I just encountered exactly the same issue. Any new insights on this? Any ideas or further hints for a cleaner solution?

cgraefe commented 8 years ago

Ok, I dug into this a little. The problems seems to be that the JS code contained in the prototype is inadvertently executed when doAdd is trying to find an unused item id about here:

var code = $(prototype.replace(regexp, freeIndex));
var tmp = collection.find('> .' + settings.prefix + '-tmp');
var id = tmp.html(code).find('[id]').first().attr('id');

I'll try to fix this an to provide a pull request.

mzekukule87 commented 3 years ago

Hello, In your APP.JS file: window.editors = function editors(id){ if(id){ ClassicEditor .create(document.querySelector('#'+id), { language: { ui: 'fr', content: 'fr' }, toolbar: [ 'heading', '|', 'bold', 'italic', // 'link', 'bulletedList', 'numberedList', //'blockQuote', //'insertTable', //'mediaEmbed', 'undo', 'redo' ] }) } }

In your twig template file: ` {% block javascripts %} {{ parent() }}

<script>
    $('.service-collection').collection({
            add: '<a href="#" class="btn-collection btn btn-default mb-2" title="Add"><i class="fas fa-plus"></i></a>',
            remove: '<a href="#" class="btn-collection btn btn-default mb-2" title="Delete"><i class="fas fa-trash-alt"></i></a>',
            allow_duplicate: false,
            init_with_n_elements: 1,
            min: 1,
            add_at_the_end: true,
            allow_down: false,
            allow_up: false,
            after_add: function(collection, element) {
                editors(element[0].elements[0].id);
            }
    });
</script>

{% endblock %} `