2014-01-07 4 views
3

Мои AppModel состоит из наблюдаемого множества комментариевпересматривают нокаут вычислен, который зависит только от наблюдаемого массива

self.comments=ko.observableArray([]); // Default Value is an empty array 
/* 
    Here comes some code to initially fill the observable array 
    with items from an JSON Response 
*/ 

Кроме того у меня есть два computeds, которые должны представлять самый первый комментарий и последний комментарий

self.firstComment = ko.computed(function() { 
    var sorted = self.comments.sort(function (left, right) { 
     return left.Id() - right.Id(); 
    }); 
    return sorted[0]; 
}); 

self.lastComment = ko.computed(function() { 
    var sorted = self.comments.sort(function (left, right) { 
     return left.Id() - right.Id(); 
    }); 
    return sorted[sorted.length - 1]; 
}); 

Это отлично работает при инициализации приложения (загрузка JSON с сервера, создание модели приложения ...), но когда я добавляю комментарий к массиву, вычисленные не распознают, что количество элементов массива изменилось (как я понял, наблюдаемый массив просто наблюдаемое, где наблюдаются свойства массива). Так что, когда я делаю:

self.comments.push(aNewCommentObject); 

self.lastComment по-прежнему связан с элементом массива, что это было, когда приложение изначально загружен.

Я нашел this blog post, как заставить вычислить, вводя фиктивный наблюдаемый, но мне не нравится подход. С какой целью наблюдаемый массив используется тогда и как?

Дополнительная проблема: Я бы хотел, чтобы наблюдаемые элементы были отсортированы при любых обстоятельствах (потому что это комментарий, который нужно просто отсортировать в хронологическом порядке). Я попытался сделать это с вычисленным commentsSorted, но также имеет проблемы, которые это не обновляет, когда у наблюдаемогоArray есть новые элементы, поэтому такая же проблема здесь. Это причина, почему я сортирую каждый раз в firstComment и lastComment.

ответ

4

Try разворачивание комментариев, чтобы вызвать отслеживание зависимостей KNOCKOUT в:

self.firstComment = ko.computed(function() { 
    var sorted = self.comments().sort(function (left, right) { 
     // -------------------^^ ! 
     return left.Id() - right.Id(); 
    }); 
    return sorted[0]; 
}); 

или (то же самое):

self.firstComment = ko.computed(function() { 
    var sorted = ko.unwrap(self.comments).sort(function (left, right) { 
     return left.Id() - right.Id(); 
    }); 
    return sorted[0]; 
}); 

Конечно, вы можете абстрактное это в отдельном вычисляемый.

self.commentsSorted = ko.computed(function() { 
    return self.comments().sort(function (left, right) { 
     return left.Id() - right.Id(); 
    }); 
}); 

self.firstComment = ko.computed(function() { 
    return ko.unwrap(self.commentsSorted)[0]; 
}); 

Поскольку computeds кэшировать их возвращаемые значения (так же как и любой другой наблюдаемой делает) вам не нужно беспокоиться о вызове self.commentsSorted несколько раз. Он будет пересчитываться только тогда, когда вероятнее всего будет наблюдаться основной наблюдаемый массив.

+0

О, да, это было так. Работая над одним проектом в качестве первого таймера нокаута в течение примерно 2 месяцев, и я действительно боюсь 95% времени, если объект, с которым я сталкиваюсь в данный момент, является наблюдаемым и его необходимо развернуть или нет. Из-за этого код становится намного сложнее, но я надеюсь, что Knockout и я скоро приеду;) Большое спасибо, я думаю, что следующая проблема нокаута не за горами –

+1

Просто используйте 'ko.unwrap' на все. Он будет иметь дело с наблюдаемыми и равными значениями, вам не нужно беспокоиться о том, что (и ваш код становится более гибким таким образом) – Tomalak

+0

@ Jürgen Также: Есть такие вспомогательные функции, которые пригодится, когда 'ko. unwrap() 'недостаточно:' ko.isObservable() ',' ko.isSubscribable() ',' ko.isWriteableObservable() '. – Tomalak

1

Альтернативное решение, используя subscribe:

self.comments = ko.observableArray([]); 

self.comments.subscribe(function(comments) { 
    var sorted = comments.sort(function (left, right) { 
     return left.Id() - right.Id(); 
    }); 

    self.firstComment(sorted[0]); 
    self.lastComment(sorted[sorted.length - 1]); 

}); 
+0

Как я понял, подписка должна использоваться только в том случае, если ничего не возможно, это означает, что если вы не можете использовать вычисляемые или если у вас есть особые вещи в пользовательском интерфейсе, которые вы не можете решить с привязками.У меня уже была зависимость от круга (функция подписки изменила другую наблюдаемую, которая сама изменила ту, которую я подписал снова и т. Д.). Ужасный сценарий отладки ... –

2

Как можно "держать observableArray элементы сортируются при любых обстоятельствах"? Мой совет не пытаться отсортировать исходный массив (я опишу подводные камни более подробно ниже), но использовать вычисленный наблюдаемым, который возвращает новый массив, отсортированный:

self.commentsSorted = ko.computed(function() { 
    return self.comments.slice(0).sort(function (left, right) { 
     return left.Id() - right.Id(); 
    }); 
}); 

Важная вещь, чтобы понять, о JavaScript Array sort() метод заключается в том, что он изменяет исходный массив. Knockout's observableArray обертывает многие функции массива, поэтому их можно использовать непосредственно на объекте observableArray. Вот почему вы можете использовать myObservableArray.sort() вместо того, чтобы делать myObservableArray().sort().Но два не эквивалентны, поскольку Нокаут оборачивает методы массива мутаторный иначе, примерно так:

sort: function (sortFunction) { 
    this.valueWillMutate(); 
    var result = this.peek().sort(sortFunction); 
    this.valueHasMutated(); 
    return result; 
} 

Так что же случилось с автоматическим изменением исходной observableArray?

  1. Трудно понять, что происходит. Легко либо не получить зависимость от массива, и, таким образом, никогда не обновлять прошлое после инициализации или не уведомлять об изменении и тем самым нарушать другие элементы от получения правильного представления массива.

  2. Даже если вы правильно настроили его, теперь вы создали круговую зависимость, потому что у вас есть что-то, что обновляется всякий раз, когда изменяется массив, а затем снова обновляет массив. Хотя Knockout в настоящее время отключает это для синхронных обновлений вычисленного наблюдаемого (но это может измениться в будущем), это приведет к неограниченной рекурсии, если вы используете ручную подписку или вычислено с помощью опции throttle.

+0

Это важно знать о наблюдаемых массивах и отличный ответ. Я буду использовать slice() для копирования массива в вычисляемом, но у меня есть больше вопросов об этом. Как указано выше, во время инициализации загружается объемная загрузка в мой массив (около 500 выпусков, каждая из которых содержит от 5 до 10 комментариев). Я использую функцию push() массива для их загрузки. Разве не каждый push() вызывает повторную рассылку 'commentsSorted'? Разве не каждый slice() создает совершенно новый массив? Разве это невероятно медленно, а память неэффективна? Я думаю, что я должен отключить отслеживание зависимостей здесь временно? –

+0

При загрузке нескольких элементов в наблюдаемый массив вы можете использовать 'myObsArray.push.apply (myObsArray, items)'. –

Смежные вопросы