1
votes

I think one of my problems is similar to another Stack Overflow question, but not exactly, and this is fairly complicated to explain, so I have a lot of detail, but I think might be straight-forward to answer. And because there might be other ways to do this than my approach, I'm, of course, open to suggestions. Say I have this in a Kendo grid, in the HTML markup as seen after it renders:

<table>
    <tr id=activeRow_1>
         <td>
            <input type=textbox data-bind=value:Status jQuery123456789="136" data-val="true" kendoBindingObject="[object Object]" />
        </td>
        <td>
        ....... // other columns in my grid, here
        </td>
        <td>
            <a class="k-button k-button-icontext k-grid-update" id=btnUpdate_1 href=#><span class="k-icon k-update"/></a>
        </td>
    </tr>
</table>

And I need to find out what "jQuery123456789" really is. It can be ANY number, because this textbox is auto-generated by a Kendo UI grid control that has inline editing. I'll explain in a bit why I think I might need this attribute name.

This is how I'm getting to the textbox - a function that runs from an onclick event on an anchor tag (link button, essentially) that was in that first column before the Kendo control re-wrote the column dynamically to insert its textbox. I pass in the ID of that row and get the textbox, and assign an ID to the Update button for later:

function btnClick(id) {
    $('id="btnEdit_'+id+'"]').click(); // programmatic click to Edit button I'll hide, later, that exposes the textbox
    var activeRow = $('[id="activeRow_"'+id']'); // gridrow "TR" element
    var activeElem = activeRow.children(0).children(0); // <-- textbox node

    activeRow.childNodes[4].childNodes[0].setAttribute("id", "btnUpdate_" + id); // sets ID on Update button for later jQuery clicking
    ....
}

In that ... area, above, I then also add a dropdown to the 1st column, dynamically, as the TD's 3rd node (2nd node is the field-validator for the textbox I didn't show). I have it in this format below just for display purposes, but in the code it is all on one line, inside activeRow.children(0).append() :

'<select id=ddlStatus_'+id+' onchange=setVal(this.value)>
    <option value=1>Active</option>
    <option value=0>Inactive</option>
    <option value=2>Cancelled</option>
</select>'

I then have it use the textbox's value and set the selected value on the dropdown with .val(). The onchange setVal() will programmatically update the textbox with the dropdown's new value, then I have it programmatically click the Update button with $('id="btnUpdate_'+id+'"]').click(); in the setVal() function.

The problem is that the update to the database does not happen. If I comment out the programmatic update click, click into the textbox, hit the spacebar, click off of it, then click the Update button, then it will work fine. I tried adding a .blur() on the textbox in setVal(), but no dice, and adding something like SendKeys is probably not an option as they scrutinize add-ons unless typed up by hand, directly. Code resides on a non-internet connected network.

So I was thinking that in the ... area is where I need to update the dropdown with the textbox's attributes - that maybe if I copied the textbox's attributes and apply them to the dropdown, maybe then it will update the database, because of the user interaction with that control that it wants. I can't leave it as a textbox because users won't know the value to input for these 3 possible values and if they enter a string, it will fail since it is expecting a number. I really don't want to have to tack on validation for something that is already user-unfriendly.

So, to use the dropdown like I think I need to, I need the attributes of the auto-generated textbox. But to get that "jQuery" attribute name, I need to do a partial match on the NAME, not its value, so I can set both on the dropdown.

I tried this:

var jQAttrName = activeElem.filter(function() {
    for (var property in $(this).data()) {
        alert(property);
        if (property.indexOf('jQuery') != -1) {
            return property;
    }
});

The only property it finds is "bind", not "data-bind", and definitely nothing with "jQuery" in it. Has anyone done a programmatic update like this before, or matched on a partial name of an attribute, and gotten the full name, and then successfully gotten a programmatic update to work on a Kendo grid?

JSFiddle for getting the textbox attributes: http://jsfiddle.net/vAk52/1

UPDATE: This might be a simpler question, how can I get something like this to work to update the datasource, and then use remote data/AJAX and MVC? New JSFiddle: http://jsfiddle.net/4VMJE/

1

1 Answers

1
votes

It turns out that I didn't have to get the "jQuery####" attribute on the textbox, or even use a textbox, at all, and could replace it using an EditorTemplate that would have the dropdown I needed. So under "Views", in a "Shared" folder, I have an "EditorTemplates" folder and created a View inside called "StatusComboBox.cs" with a Kendo DropDown in it:

@(Html.Kendo().ComboBox()
    .Name("Status")
    .DataValueField("Value")
    .DataTextField("Text")
    .BindTo((System.Collections.Ienumerable)ViewData["status"]))

Next, we have a model called SimpleListItem:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace OurProject.Models
{
    [Serializable]
    public class SimpleListItem<T>
    {
        public T Value { get; set; }
        public string Text { get; set; }
    }
}

Then, in the controller, we put in these:

private void populateData()
{
    using (var db = new OurDatabase(false))
    {
        var statusList = new List<SimpleListItem<int>>();
        statusList.Add(new SimpleListItem<int> { Value = 0, Text = "0 - Inactive" });
        statusList.Add(new SimpleListItem<int> { Value = 1, Text = "1 - Active" });
        statusList.Add(new SimpleListItem<int> { Value = 2, Text = "2 - Cancelled" });

        ViewData["status"] = statusList;
        ViewBag.jsonStatus = JsonSerializer.Serialize(statusList);
    }
}
public class JsonSerializer
{
    public static T Deserialize<T>(string s);
    public static string Serialize<T>(T o);
}

... and put populateData() in here, in the Controller...

public ActionResult Index()
{
    populateData();
    return View();
}

And in the view, I had this for the column:

columns.Bound(p => p.Status).Title("Active").Width(65).ClientTemplate("# if (Status == 1) {# <a href=javascript:void(0) id=btnActive_${Id} onclick=changeImg(this, '1', ${Id}) class='k-button k-button-icontext k-grid-update'><img id=imgActive src=../../Images/active_1.png /></a> #} else if (Status == 0) {# <a href=javascript:void(0) id=btnActive_${Id} onclick=changeImg(this, '0', ${Id}) class='k-button k-button-icontext k-grid-update'><img id=imgActive src=../../Images/active_0.png /></a> #} else if (Status == 2) {# <a href=javascript:void(0) id=btnActive_${Id} onclick=changeImg(this, '1', ${Id}) class='k-button k-button-icontext k-grid-update'><img id=imgActive src=../../Images/active_1.png /></a> #}#").EditorTemplateName("StatusComboBox");

You'll notice that this column effectively has a linkbutton with the <a></a> and <img> inside, which is set based on the value coming back from the database -- either a checkmark image for "1", or "x" for "0" or "2". Clicking the "button" exposes the dropdown by programmatically clicking the Edit button. I do this by setting an ID on the hidden Edit button:

columns.Command(command => {command.Edit().HtmlAttributes(new { id = "btnEdit_" + "${Id}" }); }).Width(100).Hidden(true);

then calling .click() in the checkmark's/X's onclick function, changeImg().

Requirements were to 1) set the value when the dropdown was changed, and 2) ensure no other fields were editable

This is the onclick function for the linkbutton in the first column:

function changeImg(obj, status, id) {
    var parentTr = obj.parentNode.parentNode;
    $('[id="btnEdit_'+id+'"]').click(); // Edit button clicked to expose dropdown 
    parentTr.childNodes[4].childNodes[0].setAttribute("id", "btnUpdate_"+id); // this represents 5th column, first node in the cell - and sets an ID on the button for jQuery

    // Get values and make them read-only - remember, columns start at 0 where linkbutton is
    parentTr.childNodes[1].childNodes[0].setAttribute('id', 'titleCtrl'+id);
    parentTr.childNodes[2].childNodes[0].setAttribute('id', 'startDateCtrl'+id);
    parentTr.childNodes[3].childNodes[0].setAttribute('id', 'endDateCtrl'+id);
    var titleCtrl = $('[id="titleCtrl"+id]');
    var titleVal = titleCtrl.val();
    titleCtrl.css('display', 'none').parent().html(titleVal);
    var startDateCtrl = $('[id="startDateCtrl"+id]');
    var startDateVal = startDateCtrl.val();
    startDateCtrl.css('display', 'none').parent().html(startDateVal);
    var endDateCtrl = $('[id="endDateCtrl"+id]');
    var endDateVal = endDateCtrl.val();
    endDateCtrl.css('display', 'none').parent().html(endDateVal);

    // Click the button after update
    var statusField = $('input[name="Status_input"]');
    statusField.blur(function() {
        $('[id="btnUpdate_'+id+'"]').click();  // performs update here
    }
}

Note that "changeImg()" is sort of a misnomer -- the code doesn't flip the image from a checkmark to an X directly - updating the database and the grid refreshing does that.