Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JQuery MVC collection name attributes

I have a table, and I've dynamically added / removed rows from it. The table contains fields that will be posted back as a collection to MVC. In this scenario updating the table with jquery means I up with collections that contain incomplete collections for example ...

function add() {
     $.ajax({
            url: "GetRow",
            type: 'post',
            dataType: 'html',
            timeout: 30000,
            data: {
                RowIndex: otherRows.length,
                "Item.Property1": $("option:selected", dropDown).text(),
                "Item.Property2": $("option:selected", dropDown).val()
            },
            success: function (result) {
                var newRow = $(result);
                tableBody.append(newRow);
            }
        });
}

This function makes an ajax call to go get an mvc action result that returns the result of a row at the given index and additionally sets some default values from a drop down on my page.

Now lets assume I have a similar function called "delete" in which using jquery i remove a row and at this point the table has 3 rows (ignoring the header row of course), and the row i'm removing is the middle row.

this means the fields in my rows end up looking something like this ...

//Row 1:
<input type="text" name="SomeCollection[0].Property1" /> 

//Row 2:
   Nothing, deleted of course

//Row 3: 
<input type="text" name="SomeCollection[2].Property1" /> 

So if i now do a postback, because of the inconsistent id range, the MVC model binder will only bind item 1 for me and item 2 (actually item 3 prior to the client side delete) is not mapped.

The idea is that i want my server logic to do a very simple "if the id for an item in my collection is not in the post data, delete it from the database", this way all the collection manipulation can be entirely client side in save on constant postbacks on this very heavy page.

So I started putting a function together in jquery to fix the problem ...

function updateNames(table) {
    var rows = $("tr", table);
    var index = 0;

    rows.each(function () {
        var inputFields = $("input");

        inputFields.each(function (){
          //replace name="bla[<any number>].Anything" with 
          //        name="bla[" + index + "].Anything"        
        });

        index++;
    });
}

So my question is ... How do i say to jquery "replace [] in the name attribute with [index]"?

I know this wont solve the problem of nested collections / other such complex scenarios but for that once i have this function solved I can always extend it later and my requirements are not that involved yet.

EDIT:

Some additional detail on my current thought pattern ... good or bad?

if i grab the name attribute value and "walk the chars" until i find the first "[" and the next "]" then replace everything in between, it should solve my problem, but this type of thing on IE probably slow as hell.

Anyone got a neat "just call this clever function" type answer? (if there is one).

EDIT 2:

Wow I really gotta look harder ... what a dumbass i feel like right now for 2 reasons (not looking and regex is an obvious solution here)

JQuery: how to replace all between certain characters?

if i can figure out the right regex i've got my solution (maybe that's the question I should have asked as i'm constantly annoyed by the crazyness of regex).

But the power of a regex cannot be under estimated :)

like image 370
War Avatar asked Feb 17 '23 15:02

War


2 Answers

This should work for you:

function updateNames(table) {

    var rows = $("tr", table);
    var index = 0;

    rows.each(function () {
        var inputFields = $(this).find("input");
        inputFields.each(function (){
            var currentName = $(this).attr("name");
            $(this).attr("name", currentName.replace(/\[(.*?)\]/, '['+index+']')); 

        });
        index++;
     });
}

If there are multiple inputs inside each row and you just want to update one then consider using the 'starts with' jQuery selector: http://api.jquery.com/attribute-starts-with-selector/.

like image 161
Joe Avatar answered Mar 07 '23 19:03

Joe


Though you've already got a resolution, I thought a working demo of how one might implement this functionality from the ground up might be useful.

Supposing you have a button with a class of delete on each row, you can achieve this with the following:

$('.delete').click(function(e) {
    // Get table body and rows
    var body = $(this).closest('tbody');

    // Remove current row
    $(this).closest('tr').remove();

    // Get new set of rows from table body
    var rows = body.find('tr')

    // Update all indeces in rows
    var re = new RegExp(/\[[0-9]+\]/);
    var index = 0;
    rows.each(function () {
        $(this).find('input').each(function (e) {
            var input = $(this).attr('name');
            if (input) {
                $(this).attr('name', input.replace(re, '['+index+']'));
            }
        });
        index ++;
    });
});

This should delete the row and update all indeces for every input in the remaining rows so that they increment properly again (and thus will be bound properly by the model binder). Additionally, it should be restricted to the current table. Here it is in action.

like image 26
Ant P Avatar answered Mar 07 '23 20:03

Ant P