How to add custom field type plugin with dynamic text fields

How to add custom field type plugin with dynamic text fields

kanweikanwei Posts: 14Questions: 4Answers: 0

Here is the relevant comment from 6 years ago:

https://datatables.net/forums/discussion/comment/105430/#Comment_105430

I'm wondering if anyone has done this yet? I have a use case where I need to show and edit 0-n pairs of text fields, and I want a '+' to add a new field, and a '-' to remove an existing one, and then save them all as an array. I'm able to show a text field with a '+' sign, but I'm struggling with the internals of the plugin to generate additional fields after the first one when the user clicks the '+' sign.

This question has an accepted answers - jump to answer

Answers

  • allanallan Posts: 63,676Questions: 1Answers: 10,497 Site admin

    I'm not aware of anyone having implemented specifically this I'm afraid. How are you storing your data - is it an array of values, or a comma delimited string perhaps?

    If you could post what you've got so far I can take a look.

    Allan

  • kanweikanwei Posts: 14Questions: 4Answers: 0
    edited November 2023

    I haven't committed to a data model yet, so I'm flexible on that.

       _fieldTypes.dynamicTextFields = {
    
            create: function(conf) {
                var that = this;
    
                conf._safeId = DataTable.Editor.safeId(conf.id);
                conf._enabled = true;
                conf._fieldNum = 1;
                conf._input = $('<div id="' + conf._safeId + '">' +
                                '<input type="text" name="notes" />' +
                                '<span class="fa fa-plus add-field-plus"></span>' +
                                '</div>');
    
                $('span.add-field-plus', conf._input).click(function() {
                    if (conf._enabled) {
                        //that.set(conf.name, $(this).attr('value'));
                        console.log('PLUS');
    
                        //let input = document.createElement('input');
                        //input.setAttribute('type', 'text');
                        let input = $('<div>' +
                                      '<input type="text" />' +
                                      '<span class="fa fa-plus add-field-plus"></span>' +
                                      '</div>');
                        //conf._input[++conf._fieldNum].append(input);
                        conf._input.add(input);
                        console.log('added?');
                    }
    
                    return false;
                })
    
                return conf._input;
            },
    
            get: function(conf) {
                console.log('GET');
            },
    
            set: function(conf, val) {
                console.log('SET');
            },
    
            destroy: function(conf) {
                // cleanup event handlers
            },
    
        };
    
  • allanallan Posts: 63,676Questions: 1Answers: 10,497 Site admin

    It rather of depends on your database schema. If you are doing a one-to-many join to another table then you might be best taking an approach like this (and just have a text field for each entry in the inner table). That's good for reverential integrity as well, but again, it depends upon your data and the structure you want to store it in.

    If however, you are storing it as an array or some separable string value, then the approach of using a list of fields is absolutely valid.

    I'd have conf._input as an array of div elements which contain the input and another div which could be used for the subtraction. That then makes it easier to write the get and set methods. Also introduce a conf._wrapper which would be the container div that has all of the elements from the conf._input array in it.

    Clicking the add button (which would also be in conf._wrapper) would add an item to the _input array, and re-append all items to the _wrapper (a function for that would be a good idea). Equally, a subtract button would remove the item from the array and call the re-append function.

    There are a few moving parts, but it is totally possible :)

    Allan

  • kanweikanwei Posts: 14Questions: 4Answers: 0

    Ok, I'm going to scrap all that and go with the parent/child approach, which I was unaware of. I'll have to dive into the docs and blog articles, but I think it will work, and most importantly, there's existing code to start with!

  • allanallan Posts: 63,676Questions: 1Answers: 10,497 Site admin
    Answer ✓

    Let me know how you get on. I do want to write a plug-in like you originally described at some point. I've done it in the past (closed source unfortunately), so hopefully I will have learned a few things from that.

    Allan

  • kanweikanwei Posts: 14Questions: 4Answers: 0

    Ok, I'm back to the original approach, so I'll start with your suggestions, and keep you posted. It's the best fit for my data model, which is a simple jsonb structure stored with each row that needs +/- operations but no referential integrity needs.

  • kanweikanwei Posts: 14Questions: 4Answers: 0

    Last question for now: what do you mean in your comment 'that then makes it easier to write the get and set methods'?

  • allanallan Posts: 63,676Questions: 1Answers: 10,497 Site admin

    At the moment you have:

         get: function(conf) {
             console.log('GET');
         },
     
         set: function(conf, val) {
             console.log('SET');
         },
    

    For the getter you could just use:

    get: function (conf) {
      return $(conf._inputs).find('input').map(el => el.value);
    }
    

    i.e. get the input from each entry in the conf._inputs array, get its value and return as an array.

    The set function would probably be best erasing the current entries in the array, then adding one for each entry and setting its value (similar to what the add event handler would be doing).

    Allan

  • kanweikanwei Posts: 14Questions: 4Answers: 0

    Ok, here's what I have so far, still not sure how to proceed on a couple of things:

            rebuild_wrapper: function(conf) {
                console.log('Rebuild', conf._input.length);
                $('div.wrapper-contents').empty();
    
                for (var i = 0; i < conf._input.length; i++) {
                    console.log('Appending content', i);
                    $('div.wrapper-contents').append($(conf._input[i].content));
                };
    
                $('span.delete-field-minus', conf._wrapper).click(function() {
                    if (conf._enabled) {
                        console.log('MINUS');
                        // not sure how to do this yet, via index, this, or something else
                        console.log('deleted?');
                        _fieldTypes.dynamicTextFields.rebuild_wrapper(conf);
                    }
    
                    return false;
                });
            },
    
            add_field: function(conf) {
                conf._input.push({index: conf._input.length,
                                  content: '<div>' +
                                  '<input type="text" />' +
                                  '<span class="fa fa-minus delete-field-minus"></span>' +
                                  '</div>'});
            },
    
            create: function(conf) {
                var that = this;
    
                console.log('Create');
                conf._safeId = DataTable.Editor.safeId(conf.id);
                conf._enabled = true;
                conf._input = [];
                conf._wrapper = $('<div>' +
                                  '<span class="fa fa-plus add-field-plus"></span>' +
                                  '<div class="wrapper-contents" ></>' +
                                  '</div>');
    
                // initial empty field
                _fieldTypes.dynamicTextFields.add_field(conf);
                _fieldTypes.dynamicTextFields.rebuild_wrapper(conf);
    
                $('span.add-field-plus', conf._wrapper).click(function() {
                    if (conf._enabled) {
                        console.log('PLUS');
                        _fieldTypes.dynamicTextFields.add_field(conf);
                        console.log('added?');
                        _fieldTypes.dynamicTextFields.rebuild_wrapper(conf);
                    }
    
                    return false;
                });
    
                return conf._wrapper;
            },
    
            get: function(conf) {
                console.log('GET');
                return $(conf._inputs).find('input').map(el => el.value);
            },
    
            set: function(conf, val) {
                console.log('SET', val);
                // TODO: Figure out how to build these
            },
    
    
  • kanweikanwei Posts: 14Questions: 4Answers: 0

    A couple of weird things happening:
    1. I see GET being called, and I don't understand why
    2. The first field is invisible, until clicking the '+' then I see 2
    3. Still not sure how to delete an element
    4. Still not sure I'm doing wrapper and input correctly.

Sign In or Register to comment.