2016-06-01 1 views
2

Я пытаюсь отфильтровать наблюдаемый массив, но я сталкиваюсь с проблемами из-за того, что я считаю, что метод ko.utils.arrayFilter меняет все имена полей моей модели на в нижнем регистре. Должен заметить, что в этом проекте используется TypScript.Q.Promise и KO.mapping, не преобразовывая массив объектов в правильный тип

Это моя модель:

export class MyListModel { 
    constructor(jsObject?: {}) { 
     if (jsObject) { 
      ko.mapping.fromJS(jsObject, {}, this); 
     } 
    } 
    Text = ko.observable<string>(); 
    Value = ko.observable<string>(); 
} 

В моей ViewModel у меня есть следующие поля:

inches = ko.observableArray<Models.MyListModel>([]); 

В другой части программы я называю filterInches() метод для фильтрации массива заданного некоторые критерии. Параметр value является текущим выбранным значением в раскрывающемся списке.

filterInches(value) { 

     if (value == 6) { 
      var filtered = ko.utils.arrayFilter(this.inches(), 
       function (item) { 

        if (parseInt(item.Text()) <= 8) 
         return true; 
       }); 

      this.filteredInches(filtered); 
     } else { 
      this.filteredInches(this.inches()); 
     } 
    } 

ошибок не выбрасываются во время компиляции, но при запуске приложения в браузере я получаю сообщение об ошибке сказав «item.Text не является функцией». Когда я просматриваю код в Chrome, он выглядит так, как элемент преобразуется в анонимный объект с текстом и значением. Поля теперь строчные, что, по-моему, вызывает проблемы, с которыми я сталкиваюсь. Что может вызвать такое поведение?

EDIT: Я работаю с другой, но соответствующей частью этого кода, и я думаю, что я начинаю понимать, почему это не работает. Я считаю, что это имеет какое-то отношение к библиотеке Q Promise, но я недостаточно понимаю эту библиотеку, чтобы понять, почему она не работает (да, я читал документацию). Я думаю, что разработчики, написавшие этот код, не понимали, что он не делает то, что, по их мнению, делает.

Первая вещь, которую я попробовал, чтобы убедиться, что-то не так, чтобы изменить имена свойств нашей модели:

export class MyListModel { 
constructor(jsObject?: {}) { 
    if (jsObject) { 
     ko.mapping.fromJS(jsObject, {}, this); 
    } 
} 
    Cat = ko.observable<string>(); 
    Chicken = ko.observable<string>(); 
} 

Теперь, если мы вернемся к пересмотренным filterInches() метод, который мы увидит, что item.Cat работает во время компиляции, но когда я просматриваю код в Chrome, объект item фактически не имеет свойства Cat (он не определен). Его свойства до сих пор текст и значение:

filterInches(value) { 

    if (value == 6) { 
     var filtered = ko.utils.arrayFilter(this.inches(), 
      function (item) { 

       if (parseInt(item.Cat()) <= 8) 
        return true; 
      }); 

     this.filteredInches(filtered); 
    } else { 
     this.filteredInches(this.inches()); 
    } 
} 

Это говорит о том, что объекты, мы извлечение из JSON не получают сопоставляется с MyListModel объектов. Я считаю, что сам класс MyListModel хорош.

Проблема я думаю, что возникает из кода, который получает дюйма в первую очередь:

refreshInches() { 
     this.DataService.getInches().done(entities => { 
      this.inches(entities); 
     }); 
    } 

, а затем getInches() метод следующим образом:

getInches(): Q.Promise<Array<Models.MyListModel>> { 
     return Q($.getJSON(this._baseUrl + 'GetInches')); 
    } 

Я думаю, Первоначальное намерение этого кода состояло в том, чтобы получить данные о дюймах асинхронно с конечной точки и преобразовать данные json в объекты MyListModel. Как я уже говорил ранее, я недостаточно знаком с Q.Promise, чтобы узнать, что может быть неправильным с методом getInches(). Для меня это довольно ясно, но это просто возвращает массив анонимных объектов из данных JSON.

Для справки объектов JSON, возвращаемых из конечной точки выглядеть следующим образом:

[{"text":"0","value":"0"},{"text":"1","value":"1"},...] 

Каждый знает, как getInches метод() может быть улучшена, чтобы делать то, что он должен делать?

+3

KnockoutJS - с открытым исходным кодом, я проверил [исходный код 'arrayFilter' (https://github.com/knockout/knockout/blob/master/src/utils.js#L149), но обнаружил, что он не работает Не делай никаких нижних конечностей. Во всяком случае, нам понадобится [mcve], чтобы иметь возможность помочь, любой шанс, который вы могли бы попытаться сделать одним (например, включив полный код, если это было полезно/возможно скомпилировано javascript или не удалось получить полный код TS)? – Jeroen

+1

"parseInt (item.Text)" возвращает NaN, если "item.Text" определяется как "Text = ko.observable ();" – TSV

+2

Эта строка, вероятно, должна быть 'parseInt (item.Text(), 10)'. – Jeroen

ответ

2

В соответствии с вашим кодом, getInches должен возвращать Q.Promise<Array<Models.MyListModel>>, то есть обещание для массива объектов MyListModel.

Однако в своем нынешнем виде:

getInches(): Q.Promise<Array<Models.MyListModel>> { 
    return Q($.getJSON(this._baseUrl + 'GetInches')); 
} 

он не делает этого. Приведенное выше дает обещание для <whatever the server gives you>, в данном случае, видимо, массив простых объектов.

Чтобы изменить его так, что он соответствует предполагаемому типу, мы могли бы сделать

getInches(): Q.Promise<Array<Models.MyListModel>> { 
    var jqXhr = $.getJSON(this._baseUrl + 'GetInches'); 

    return Q(jqXhr).then(data => { 
     return data.map(item => { 
      return new Models.MyListModel(item); 
     }); 
    }); 

    // or, if you must 
    return Q(jqXhr).then(data => data.map(item => new Models.MyListModel(item))); 
} 

Так что теперь мы можем хранить свежесобранный созданные MyListModel экземпляров в наблюдаемом:

refreshInches() { 
    this.DataService.getInches().done(this.inches); 
} 

Обратите внимание, что this.inches является наблюдаемые и наблюдаемые - это функции, которые позволяют нам непосредственно использовать его в качестве обратного вызова.

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


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

this.filteredInches = ko.pureComputed(() => { 
    var value = this.value(); 

    return ko.utils.arrayFilter(this.inches(), item => { 
     value != 6 || +item.Text() <= 8; 
    }); 
}); 

Глядя на вашем творении ViewModel, вы, вероятно, хотите иметь числовой наблюдаемый (или вычисленный) где-то так, что вам не нужно, не забудьте ввести, преобразовать Text свойства в расчетах.

+0

Эй @ Томалак благодарит вас за очень подробный ответ. Это было очень полезно. Я думаю, что я на 99%! Я применил ваши изменения к getInches() и refreshInches(), а json - это сопоставление с моей моделью. Массив объектов MyModel заполняется следующими реквизитами: Cat: observable(), Chicken: observable(), text: observable(), value: observable(). 'This.inches() [0] .Cat()' возвращает «undefined», но 'this.inches() [0] .text()' возвращает «0», что является значением, которое мне нужно. – Bruno

+1

Да, так работает плагин сопоставления. Без дополнительных опций (как в этом случае) он отображает все свойства входного объекта в наблюдаемые в целевой модели. Входной объект '{text:" bla "}' будет превращен в объект '{text: ko.наблюдаемый ("ли")} '. Наблюдаемые объекты вашей целевой модели просмотра уже («Кошка») будут заполнены (если их имя совпадает) или оставлено в покое. Поскольку во входном объекте нет 'Cat',' Cat' в viewmodel останется неопределенным. По-видимому, хотя на входе есть «текст», который будет отображаться в вашей модели просмотра, так это то, что вы видите. – Tomalak

+0

Ты лучший, спасибо! – Bruno