@Html.Hidd" /> @Html.Hidd" /> @Html.Hidd"/>

MVC 5 - Registering JavaScript events from partial view which uses BeginCollectionItem

449 views Asked by At

I have a partial view which looks like this:

@model Whatever.Models.Jobs
@using (Html.BeginCollectionItem("JobRecords"))
{
    <tr id="jobRow">
        @Html.HiddenFor(m => m.Id)

        <th width="40%">
            @Html.EditorFor(m => m.Project, new { htmlAttributes = new { @class = "form-control", id = "project", list = "projectResults" } })
            <datalist id="projectResults"></datalist>
            @Html.ValidationMessageFor(m => m.Project, "", new { @class = "text-danger" })
        </th>
    </tr>
}

This is embedded in the parent view like:

@if (Model.JobRecords != null)
{
    foreach (var item in Model.JobRecords)
    {
        Html.RenderPartial("_JobEditorRow", item);
    }
}

It all works, but as you can see from the partial view I want to add a searchable box using data-list, so I add a method in my controller:

[HttpGet]
public JsonResult GetProjects(string query)
{
    var list = unitOfWork.ProjectRepository.Get(c => c.Name.contains(query));


    var serialized = list.Select(p => new { p.ProjectCode, p.Name});

    return Json(serialized, JsonRequestBehavior.AllowGet);
}

And then add some javascript which uses AJAX to populate this data-list:

$(document).ready(function () {
    var delay = 1000;
    var globalTimeout = null;
    function contractsCall() {
        console.log("called");
        $("#projectResults").empty();
        $.ajax({
            cache: false,
            type: "GET",
            contentType: "application/json;charset=utf-8",
            url: "/MyController/GetProjects?query=" + $("#project").val()
        })
            .done(function (data) {
                if (data[0] !== undefined) {
                    for (var i = 0; i < data.length; i++) {
                        $("#projectResults").append(
                            "<option value=" + data[i]["ProjectCode"] + ">" + data[i]["Name"] + " - " + data[i]["ProjectCode"] + "</option>"
                        );
                    }
                } else {
                    $("#projectResults").empty();
                }
            });
    }

    $("#project").keyup(function (event) {
        console.log("keyup");
        if (globalTimeout !== null) {
            clearTimeout(globalTimeout);
        }
        globalTimeout = setTimeout(function () {
            globalTimeout = null;
            contractsCall();
        }, delay);
    });

});

This javascript is included in the parent view which is also embedding the partial view. But I add a job record and type something in the input box, it doesn't do anything despite it having the id of project.

My question is, how can I register events that occur in partial views from a parent view; or if I could embed by javascript file in the partialview to fix this? And the more difficult question is, how can I prevent the duplication of ids when adding a job with BeginCollectionItem? Because adding multiple rows would add multiple project id input boxes.

Update

@Stephen helped me greatly to fix this issue, I removed the <datalist> tag from the partial view and placed it in the main view, and used class instead of id. Then I changed my JS to something like this:

$("body").on("keyup", ".projectField", function () {
    var self = $(this);
    if (globalTimeout !== null) {
        clearTimeout(globalTimeout);
    }
    globalTimeout = setTimeout(function () {
        globalTimeout = null;
        contractsCall(self.val());
    }, delay);
});

This way I can search using my AJAX call for $(this) input box. The other issue I had was seeing which input box was clicked so I can update some other things, for this I used:

document.querySelectorAll('input[list="projectResults"]').forEach(function (element) {
    element.addEventListener('input', onInput);
});

Which calls:

function onInput(e) {
    var input = e.target,
    val = input.value;
    list = input.getAttribute('list'),
    options = document.getElementById(list).childNodes;
    var row = $(this).closest("tr");
    var p = row.find(".projDescription");

    for (var i = 0; i < options.length; i++) {
        if (options[i].value === val) {
            $.ajax({
                cache: false,
                type: "GET",
                contentType: "application/json;charset=utf-8",
                url: "/ContractProject/GetProjects?query=" + val;
            })
            .done(function (data) {
                if (data[0] !== undefined) {
                    console.log(data[0]["WorksDescription"]);
                    p.text(data[0]["WorksDescription"]);
                }
            });
            break;
        }
    }
}

It's a little bit counter intuitive because I am making 2 AJAX calls but the second one isn't going to be heavy as it's asking for a specific element.

0

There are 0 answers