Greg Mason
Greg Mason

Reputation: 803

Deleting from breeze/knockout table produces "Cannot read property 'BoardName' of null"

I have been using simple and clean access to removing items in a table populated from a breeze data service. Everything worked well until I started including navigation properties in the displayed table. Now whenever I use the delete functionality I get an error of Uncaught TypeError: Cannot read property 'BoardName' of null.

My database entities are set as:

public class SpecificAdapter
{
    public int Id { get; set; }
    public string PbaNumber { get; set; }
    public int NumberOfPorts { get; set; }

    public int AdapterId { get; set; }
    public virtual Adapter Adapter { get; set; }
}

public class Adapter
{
    public int Id { get; set; }
    public string BoardName { get; set; }
    public string DeviceId { get; set; }

    public virtual ICollection<SpecificAdapter> SpecificAdapters { get; set; }
}

I am using a data service base in Breezejs this way:

var loadSpecificAdaptersTable = function () {
    return em.executeQuery(breeze.EntityQuery.from('SpecificAdapters').expand('Adapter'));
};

Loaded in the view model like this:

adaptersDataService.loadSpecificAdaptersTable()
    .then(function (data) { specificAdapters(data.results); })
    .fail(function (error) { logger.error(error.message, "loadSpecificAdaptersTable failed during initialization"); });

I map it in the html like this:

<table class="table tablesorter">
    <thead>
        <tr>
            <th data-bind="sort: {arr: specificAdapters, prop: 'Adapter().BoardName'}">Board Name</th>
            <th data-bind="sort: {arr: specificAdapters, prop: 'PbaNumber'}">Pba Number</th>
            <th data-bind="sort: {arr: specificAdapters, prop: 'NumberOfPorts'}"># Ports</th>
            <th data-bind="sort: {arr: specificAdapters, prop: 'FirmwareVersion'}">Firmware Version</th>
            <th></th>
            <th></th>
        </tr>
    </thead>
    <tbody data-bind="foreach: specificAdapters">
        <tr>
            <td data-bind="text: Adapter().BoardName"></td>
            <td data-bind="text: PbaNumber"></td>
            <td data-bind="text: NumberOfPorts"></td>
            <td data-bind="text: FirmwareVersion"></td>
            <td>
                <button data-bind="click: $parent.editSpecificAdapter" class="btn">Edit</button>
            </td>
            <td>
                <button data-bind="click: $parent.deleteSpecificAdapter" class="btn">Delete</button>
            </td>
        </tr>
    </tbody>
</table>

Previous to Adapter().BoardName being added as a reference in the table, I could click the delete button and everything worked. Now I get an error. The delete logic is as:

var deleteSpecificAdapter = function (item) {
    item.entityAspect.setDeleted();
    specificAdapters.remove(item);
};

The error is thrown upon item.entityAspect.setDeleted(); being run. Does adding the data-binding to Adapter().BoardName change the item variable in a way that doesn't map back well enough to use? Do I need to have different logic for determining the actual item or do I need to bind the click event differently in order to get the specific, non-remapped item from the foreach?

Upvotes: 0

Views: 297

Answers (1)

PW Kad
PW Kad

Reputation: 14995

By binding to the BoardName the way you are it is creating a timing issue. Since the value is being cleared before the parent context updates fully it throws an error. There are a few ways to get around this -

<td data-bind="with: Adapter"><span data-bind="text: BoardName"></span></td>

This will only bind to the BoardName property when adapter is populated

<td data-bind="if: Adapter"><span data-bind="text: BoardName"></span></td>

This will only evaluate the inner-span's data-binding when Adapter has a value.

Upvotes: 2

Related Questions