Наследование и переопределение Knockout вычисляется наблюдаемым

Я работаю над тем, чтобы сделать мое приложение javascript более масштабируемым. Приложение использует Knockout.js для привязки сетки некоторого типа элемента базы данных, который пользователь может редактировать и обновлять. Теперь я иду по пути работы с наследованием и наличием BaseModel и моделей, которые наследуются от него. Проблема, с которой я сталкиваюсь, возникает, когда я хочу переопределить вычисляемый наблюдаемый объект, определенный в BaseModel классом, который наследует от него. Вот вариант использования.

Базовая сетка имеет вычисляемый наблюдаемый объект, к которому привязан пользовательский интерфейс. Вычисляемый наблюдаемый объект работает с наблюдаемым массивом с фильтром текстового поля, который пользователь может ввести для поиска. Довольно простой.

Дочерний класс, роли, представляет собой определенный тип сетки, и часть бизнес-требований заключалась в том, чтобы иметь фильтр для отдела. Это раскрывающийся список в пользовательском интерфейсе. Я хочу переопределить вычисляемый наблюдатель в базовом классе реализацией ролей в дочернем классе. Не уверен, как это сделать.

Вот JSFiddle: http://jsfiddle.net/Icestorm0141/vx6843pt/6/

Модель BaseGridViewModel:

var BaseGridViewModel = function (serverData) {
    var self = this;    

    //searchTerm is a text box that the user types to search
    self.searchTerm = ko.observable();

    //items are the data from the server
    self.items = ko.mapping.fromJS(serverData, mapping);

    //filteredItems are the filtered list of items that the grid is actually bound to
    //thus when the user searches, the UI updates automatically
    self.filteredItems = ko.computed(function () {
        if (self.searchTerm() == null || self.searchTerm() == "" || self.searchTerm().length < 3) {
            return self.items();
        } 
        else {
            return ko.utils.arrayFilter(self.items(), function (item) {
                 return item.Description().toLowerCase().indexOf(self.searchTerm().toLowerCase()) >= 0;
            });
        }
    });
}

Дочерний класс:

var RoleGridModel = function (roleData) {
    var self = this;

    //calling the base model, I think this is the proper way to do this?
    BaseGridViewModel.call(self, roleData);

    //UI has a drop down list of departments to filter by, this is the selected filter
    self.departmentFilter = ko.observable();

    //I WANT to set the filter function for the items to something completely different
    var filterOverride = function () {
        if (self.departmentFilter() == null || self.departmentFilter() == "") {
            return self.items();
        }
        else {
            return ko.utils.arrayFilter(self.items(), function (role) {
                return role.DepartmentId() == self.departmentFilter();
            });
        }
    };
}

Я провел много исследований по этому поводу в последние несколько дней, и я удивлен, что еще не нашел решения. Я не чувствую, что делаю что-то слишком экстраординарное, но, может быть, у кого-то есть какие-то предложения. Я открыт для любого!


person Icestorm0141    schedule 27.10.2014    source источник


Ответы (4)


Вам нужно переопределить filteredItems:

self.filteredItems = ko.computed(filterOverride);

См. Fiddle.

person haim770    schedule 27.10.2014
comment
Это должно хорошо работать. Получение некоторых ошибок с моей конкретной реализацией (я думаю, что сетка кендо пытается связать в неподходящее время), но я думаю, что как только я это сделаю, это будет работать хорошо. Спасибо! - person Icestorm0141; 29.10.2014

Я предполагаю, что вы намерены объединить фильтр базового класса с фильтром наследующего класса, а не просто выбросить фильтр базового класса. Если вы просто хотите игнорировать фильтр базового класса, вы можете просто переопределить self.filteredItems, как в ответе haim770.

В противном случае, есть много способов сделать это. Одним из способов было бы сделать что-то вроде этого:

//In RoleGridModel
var baseFilteredItems = self.filteredItems; //Keep a reference to the base class's implementation
self.filteredItems = ko.computed(function () {
    var baseItems = baseFilteredItems();
    return filterByDepartment(baseItems); //filtering logic goes here
});

По сути, это делает то, что вы хотите, и является ближайшим аналогом «переопределения» вычисляемого.


Другой способ сделать это, кроме «переопределения» вычисляемого, состоит в том, чтобы изменить базовый класс для поддержки произвольных фильтров. Что-то типа:

//BaseGridViewModel:

var filters = ko.observableArray([]);
self.addFilter = function(filterFunction) {
    filters.push(filterFunction);
}

self.filteredItems = ko.computed(function() {
    var items = self.items();
    filters().forEach(function (filter) {
        //filters could also be implemented to take individual items and return true or false whether that item should be filtered, but that's a side point
        items = filter(items);
    });
    return items;
});

var searchFilter = function (items) {
    var filteredItems = // filtering logic here
    return filteredItems;
};

self.addFilter(searchFilter);

а также

//RoleGridModel
var departmentFilter = function (items) {
    var filteredItems = // filtering logic here
    return filteredItems;
}

self.addFilter(departmentFilter)

Это немного более читабельно в классах-наследниках, хотя требует больше логики в базовом классе.

person Retsam    schedule 28.10.2014
comment
Это чрезвычайно полезно, я даже не подумал объединить фильтр базового класса с переопределением. Я думаю, что мне нужно то, что выложил haim770. Кажется, что происходит что-то еще, что приводит к поломке пользовательского интерфейса. Вероятно, привязка к сетке кендо мешает ему работать. Но, по крайней мере, теперь я знаю основные принципы того, как делать то, что я ищу. Спасибо! - person Icestorm0141; 29.10.2014
comment
Очень полезный ответ! Хотел бы я проголосовать за это несколько раз! - person Raj Pawan Gumdal; 06.10.2016

Для всех, кто может найти решение Retsam полезным, которое мне с тех пор пришлось использовать, возникла синтаксическая ошибка при объявлении addFilter. Исправленный синтаксис:

self.addFilter = function(filterFunction) {
    filters().push(filterFunction);
}

Кроме того, поскольку фильтры являются наблюдаемым массивом, элементы в нем не отслеживались. Таким образом, каждая из функций фильтра, которые я передал, изначально была вычисляемой наблюдаемой, потому что у них были наблюдаемые, от которых они зависели. Пример:

self.addFilter(function () {
//used to be wrapped in a computed observable
    if (self.searchTerm() == null || self.searchTerm() == "" || self.searchTerm().length < 3) {
        return self.items();
    }
    else {
        return ko.utils.arrayFilter(self.items(), function (item) {
            return item.Description().toLowerCase().indexOf(self.searchTerm().toLowerCase()) >= 0;
        });
    }
});

В этом примере первая функция фильтра зависит от searchTerm(), но поскольку она добавлена ​​в массив фильтров, который является просто наблюдаемым массивом, пользовательский интерфейс не обновлялся при обновлении searchTerm. Мне пришлось вручную инициировать обновление через подписки для каждого наблюдаемого объекта, который должен был обновить пользовательский интерфейс при фильтрации:

self.searchTerm.subscribe(function (newValue) {
    filters.valueHasMutated();
});

Возможно, есть лучший способ сделать это, но я еще не нашел его.

person Icestorm0141    schedule 18.11.2014

Я бы использовал библиотеку наследования для быстрого и надежного наследования.

https://github.com/dotnetwise/Javascript-FastClass

Например, ваш метод не поддерживает прототип

person Anders    schedule 28.10.2014