Populate a "new row" form as is done with an "edit row" form

Populate a "new row" form as is done with an "edit row" form

burncharburnchar Posts: 118Questions: 12Answers: 0
edited August 2013 in Editor
I'd like to implement a "copy row" hyperlink, next to the existing "edit" and "delete" hyperlink.

My users want to make one or two small changes to the original data before copying. I'm using "editor.create()" because a new row is being created (actually many new rows in different tables, but that's abstracted server-side).

How can I pre-populate the editor.create form with the clicked row's data, as is done when I use "editor.edit()"?

I thought of trying to use editor.edit and editor.create in the same function and of sending a special hidden form field while just using editor.edit (to tell the server's REST method for HTTP PUT to use special handling), but both seem really clunky.

Replies

  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin
    This is very defiantly going to be a topic for a tutorial!

    Until I can write it though, the approach I'd take is to use the `onCreateInit` event when you call `create()` (either via the TableTools button or the API) and use the `set()` method to set the values you want.

    So:

    1. Use clicks on row they want to use as a template
    2. They then click on a 'duplicate' / 'template' button (which could attach a `one( 'onCreateInit' ... )` event handler if you want to still have a plain create
    3. The event handler would read the data from the table for the selected row
    4. Then it would use `set()` to populate the form.

    Sorry for the brevity of this answer - but does it make sense?

    Allan
  • burncharburnchar Posts: 118Questions: 12Answers: 0
    Thank you for the response. I wanted to wait to reply until I started working with this feature.

    #4, reading the data, is really where I'm having difficulty.

    If this were an edit using onInitEdit, I could use [code]this.s.editRow[/code], but it's null.

    The fnGetData documentation seems to make assumptions about [code]this[/code] which aren't the case in the onInitCreate handler.
    At least, [code] var data = oTable.fnGetData( this );[/code] yields the error:
    "Uncaught TypeError: Cannot call method 'toLowerCase' of undefined"

    Can you give me a hint as to how to get the row data to the onInitCreate handler?
  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin
    To complete step 4 a little more, you'd use the TableTools fnGetSelected method ( http://datatables.net/extras/tabletools/api#fnGetSelected ) to get the row. Actually, there is also fnGetSelectedData which just makes the call to fnGetData for you, returning the data for the row.

    Regards,
    Allan
  • burncharburnchar Posts: 118Questions: 12Answers: 0
    Those are TableTools APIs. I am using edit/delete/copy links like in the following documentation:
    http://editor.datatables.net/release/DataTables/extras/Editor/examples/inlineControls.html

    Is it possible to get the row data when not using the TableTools plugin with Editor?
  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin
    Exactly the same still applies - use fnGetData to get the data for the row in question and the populate the create form as needed:

    [code]
    // Use a record as a template
    $('#example').on('click', 'a.editor_template', function (e) {
    e.preventDefault();

    var data = table.fnGetData( $(this).parents('tr')[0] );
    editor.create( 'Template' );

    // Loop over the fields in the editor instance and use the `set` method or just statically assign them
    editor.set( 'myField', data.myField );
    ...
    } );

    [/code]

    Allan
  • burncharburnchar Posts: 118Questions: 12Answers: 0
    edited September 2013
    Thank you for the help, Allan. I keep coming back to this after a week of other work and forgetting a lot of what I'd figured out.
    Many Javascript state objects are still mysterious to me, so I wouldn't have thought to look in $(this).parents, nor would I think that "this", in the context of a called function would know anything about the table contents of the row that happens to have the text of the URI that I clicked.

    In any case, your example works perfectly. I don't want to manually set each and every field; Its kind of high-maintenance and my software needs to handle any table configuration without alteration.

    I found that this kind of works for filling in all fields:
    [code]for (var key in data)
    editor.set(key, data[key]);[/code]
    ...But a field in the clicked row isn't necessarily in the editor form, so I added a check. Unfortunately this turns into about O(N^2), but it's unlikely there will be tons of fields in any form, so not too big a deal:
    [code]
    var fieldlist = editor.fields();
    for (var key in data) {
    if ($.inArray(key, fieldlist) >= 0)
    editor.set(key, data[key]);
    }[/code]

    This, however, doesn't work with joined tables at all (http://editor.datatables.net/release/DataTables/extras/Editor/examples/join.html), and the `editor.set()` documentation doesn't show how to work with joined fields, so I experimentated.

    My final result is a 'copy' function that auto-detects each field, including 1:1 and 1:N joined fields (each of which have to be handled differently). Appending the `[].id` to an `editor.set()` parameter seems smelly to me (Is there a better way to do this?), but it works. Hopefully this helps another DT user or helps start the tutorial:

    [code]
    //
    // "Copy" is clicked
    //
    $(jqDomTableID).on('click', 'a.editor_copy', function(e) {
    e.preventDefault();
    editor.create('You must change at least one field in order to copy this row.',
    {
    "label": "Create new row with this data",
    "fn": function () { this.submit(); }
    });
    var fieldlist = editor.fields();
    var data = dataTableObject.fnGetData($(this).parents('tr')[0]);
    // Can't use Object.keys() because IE8 doesn't support it :(
    for (var key in data) {
    // Ensure this field from the clicked row is part of the Editor form.
    // This does not work with joined tables fields, which are handled later.
    if ($.inArray(key, fieldlist) >= 0) {
    editor.set(key, data[key]);
    }

    // 1:1 fields have an 'id' property and are named "FIELDNAME.id", e.g. "manufacturer.id"
    // The ".id" part is why there was no match in the field list.
    else if (data[key].hasOwnProperty('id') && $.inArray(key + '.id', fieldlist) >= 0)
    editor.set(key + '.id', data[key].id); // key is the field name, data[key] is the numeric id.

    // 1:N fields have a numeric key for each entry and are named "FIELDNAME[].id", e.g. "brandnames[].id".
    // Those entries look like, e.g. [{"id": "4","name": "TurboMax"},{"id": "27","name": "OtherBrand"}]
    else if ($.inArray(key + '[].id', fieldlist) >= 0) {
    var fieldIds = [];
    for (var subKey in data[key]) {
    if(data[key][subKey].hasOwnProperty('id'))
    fieldIds.push(data[key][subKey]['id']);
    }
    // When setting a 1:N value in the editor, it wants only an array of id values. No names.
    // Using the above example data, `editor.set` wants: [4, 27]
    editor.set(key + '[].id', fieldIds);
    }
    }
    });
    [/code]
  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin
    Excellent to hear that you managed to resolve this! Thanks for sharing your code with us!

    I completely agree about the setting of the fields individually being a real pain - you've got a nice solution for the problem there. Editor 1.3's new `val()` method is going to include an option for getting the data set for the whole form (i.e. just calling `val()` with no parameters will return all fields in key / value pairs).

    > I wouldn't have thought to look in $(this).parents, nor would I think that "this", in the context of a called function would know anything about the table contents of the row that happens to have the text of the URI that I clicked.

    It sort of does and sort of doesn't :-). `this` in a jQuery event handler callback is the element in question - so in this case the `A` tag. We can then use jQuery to find its parent `TR` row, and from there use DataTables' API to tell us the data for that row.

    Regards,
    Allan
This discussion has been closed.