andrey.shedko
andrey.shedko

Reputation: 3238

Knockout.js attr binding broken when use mapping

I'm trying to update ViewModel via mapping plugin with data sent by WebApi Controller on click. On first load everything is working fine, but when data updated, binding is broken. I did tried to change attr binding to text binding and it's working nornally, so I suppose that problem is in attr binding. So here is JS:

<script>
    function viewModel() {
        var self = this;
        self.currentPage = ko.observable();
        self.pageSize = ko.observable(10);
        self.currentPageIndex = ko.observable(0);
        self.jobs = ko.observableArray();
        self.currentPage = ko.computed(function () {
            var pagesize = parseInt(self.pageSize(), 10),
            startIndex = pagesize * self.currentPageIndex(),
            endIndex = startIndex + pagesize;
            return self.jobs.slice(startIndex, endIndex);
        });
        self.nextPage = function () {
            if (((self.currentPageIndex() + 1) * self.pageSize()) < self.jobs().length) {
                self.currentPageIndex(self.currentPageIndex() + 1);
            }
            else {
                self.currentPageIndex(0);
            }
        }
        self.previousPage = function () {
            if (self.currentPageIndex() > 0) {
                self.currentPageIndex(self.currentPageIndex() - 1);
            }
            else {
                self.currentPageIndex((Math.ceil(self.jobs().length / self.pageSize())) - 1);
            }
        }
        self.sortType = "ascending";
        self.sortTable = function (viewModel, e) {
            var orderProp = $(e.target).attr("data-column")
            self.jobs.sort(function (left, right) {
                leftVal = left[orderProp];
                rightVal = right[orderProp];
                if (self.sortType == "ascending") {
                    return leftVal < rightVal ? 1 : -1;
                }
                else {
                    return leftVal > rightVal ? 1 : -1;
                }
            });
            self.sortType = (self.sortType == "ascending") ? "descending" : "ascending";
        }
        // Here is function that must update ViewModel
        self.getDays = function (days) {
            var uri = "/api/job?days=" + days;
            $.getJSON(uri, function (data) {
                ko.mapping.fromJS(data.$values, {}, self.jobs);
            })
            .error(function (xhr, status, error) {
                var err = eval("(" + xhr.responseText + ")");
                alert(err.Message);
            });
        }
        $.getJSON("/api/job", function (data) {
            self.jobs(data.$values);
        });
    }
    $(document).ready(function () {
        ko.applyBindings(new viewModel());
    });
</script>

This is how I call update.

<div class="btn-group">
            <button type="button" class="btn btn-default" id="7" value="7" data-bind="click: getDays.bind($data, '7')">7 дней</button>
            <button type="button" class="btn btn-default" id="10" value="10" data-bind="click: getDays.bind($data, '10')">10 дней</button>
            <button type="button" class="btn btn-default" id="14" value="14" data-bind="click: getDays.bind($data, '14')">14 дней</button>
            <button type="button" class="btn btn-default" id="30" value="30" data-bind="click: getDays.bind($data, '30')">30 дней</button>
            <button type="button" class="btn btn-default" id="60" value="60" data-bind="click: getDays.bind($data, '60')">60 дней</button>
            <button type="button" class="btn btn-default" id="180" value="180" data-bind="click: getDays.bind($data, '180')">180 дней</button>
        </div>

And this is the table that present data.

<table id="arrival" class="table table-condensed table-striped">
        <thead>
            <tr data-bind="click: sortTable">
                <th></th>
                <th data-column="excursion">Название</th>
                <th data-column="excursiondate">Дата</th>
            </tr>
        </thead>
        <tbody data-bind="foreach: currentPage">
            <tr>
                <td><a data-bind="attr: { href: '/list/' + $data.kodg }" target="_parent">Список туристов</a></td>
                <%--<td data-bind="text: $data.kodg""></td>--%>
                <td data-bind="text: $data.excursion"></td>
                <td data-bind="text: $data.excursiondate"></td>
            </tr>
        </tbody>
        <tfoot>
            <tr>
                <td colspan="3" class="pager">
                    <button data-bind="click: previousPage" class="btn previous"><i class="glyphicon glyphicon-backward"></i></button>
                    Страница
                            <label data-bind="text: currentPageIndex() + 1" class="badge"></label>
                    <button data-bind="click: nextPage" class="btn next"><i class="glyphicon glyphicon-forward"></i></button>
                </td>
            </tr>
        </tfoot>
    </table>

Any ideas?

Upvotes: 0

Views: 244

Answers (2)

Muhammad Raheel
Muhammad Raheel

Reputation: 19882

Try this

<a 
    data-bind="
        attr: { href: $parent.Link($data.kodg) }" 
    target="_parent">
    Список туристов
</a>

And in your viewmodel

self.Link = function(data){
    return '/list/' + data
   // Or if you have observable return '/list/' + data()
}

Upvotes: 0

Maku
Maku

Reputation: 1558

Knockout mapping causes all properties to be converted into observables. So in your attr binding the kodg property is a function (ko.observable instance) and you should make a following change:

attr: { href: '/list/' + ko.utils.unwrapObservable($data.kodg) }

or simply

attr: { href: '/list/' + $data.kodg() }

if the kodg is always observable.

Upvotes: 2

Related Questions