У меня есть цепочка Rx.Subjects (A->B->C->A), но последний шаг не работает
Я совершенно новичок в Rx*. Я пытаюсь создать реактивную версию MVC, используя RxJS для моей диссертации. Это свободно основано на https://github.com/staltz/mvi-example
Я, вероятно, должен был изучить RxJS больше, прежде чем начинать кодировать, но я понял, что обычно учусь лучше всего, просто перепрыгивая в глубину пула. Теперь я полностью застрял, однако.
Вид имеет текстовое поле с числовым значением и кнопку. Нажатие кнопки изменяет цвет текста, нажатие на текст повторяет числовое значение. Значение сохраняется в модели, так что "цепочка" проходит через все три объекта (View->Controller->Model->View). Нажатие на кнопку не требует модели, так что цепочка это View->Controller->View.
Все остальное работает, но последняя часть цепочек (-> Вид) доставляет мне неприятности. Щелчок по тексту (в представлении) распространяет изменения в контроллере и модели, но представление не уведомляется об изменении в модели. Я не понимаю почему, так как, как я понимаю, все это реализовано одинаково.
Я добавил комментарии к примеру кода ниже, чтобы обозначить функции, которые не вызываются.
Код доступен здесь. Обратите внимание, что, как я уже сказал, код предназначен для моей диссертации, поэтому некоторые дизайнерские решения и само приложение могут показаться странными. Ниже я также попытался включить части, которые, по моему мнению, наиболее актуальны. Я знаю, что должен привести полный пример, но в этом случае это невозможно.
Параметр "obs", который получают объекты, является внутренней структурой данных, которую я использую для хранения наблюдаемых. Соответствующая часть
var OBSERVABLES = {
observe: function(source, observer, observer_name) {
this.request(source, function(s) {
console.log("\t[" + source + " is being observed by " + observer_name + " (" + s.constructor.name + " -> " + observer.constructor.name + ")]")
return s.subscribe(
function myOnNext(x) {
console.log("\t(" + source + ") " + s.constructor.name + " -> OnNext -> (" + observer_name + ") " + observer.constructor.name)
observer.onNext(x)
},
function myOnError(err) {
console.log(error)
})
})
},
View.js
var modelAmount = new Rx.Subject()
var textClicks = new Rx.Subject()
var buttonClicks = new Rx.Subject()
var changeColors = new Rx.Subject()
function View(obs, div) {
console.log("View :: New View")
obs = obs
div = div
var self = this
obs.observe("clickAmount", modelAmount, "modelAmount")
obs.observe("changeColor", changeColors, "changeColors")
obs.add("textClicks", textClicks)
obs.add("buttonClicks", buttonClicks)
//Draw HTML elements
render(div);
}
...
function onClick() {
console.log("View :: onClick() emits 'textClicks'");
textClicks.onNext()
}
function onButtonClick() {
console.log("View :: onButtonClick() emits 'buttonClicks'");
buttonClicks.onNext()
}
//Listen to controllers instruction to change the color
// DOES NOT WORK
var changeNumberColor = changeColors.map(function(c) {
console.log("View :: Listened to 'changeColor'")
if (c === undefined) {
c = getColor()
}
CONTENT.css('color', c)
});
//Listen to model's instruction to change the value
// DOES NOT WORK
var setValue = modelAmount.last(function(latestValue) {
console.log("View :: Listened to 'clickAmount'")
console.log("View :: setValue(" + latestValue + ")")
CONTENT.text(latestValue)
});
Controller.js
var inputTextClicks = new Rx.Subject();
var inputButtonClicks = new Rx.Subject();
var obs = null;
function Controller(obs){
console.log("Controller :: New Controller")
obs = obs
obs.observe("textClicks", inputTextClicks, "inputTextClicks")
obs.observe("buttonClicks", inputButtonClicks, "inputButtonClicks")
obs.add("addAmount", addAmount);
obs.add("changeColor", changeColor);
}
//Listen to input events and give instructions to model
var addAmount = inputTextClicks.map(function(){
console.log("Controller :: addAmount() listened to 'textClicks' and emits 'addAmount'")
return 1;
});
//Listen to button presses and give instructions to the view
var changeColor = inputButtonClicks.map(function(){
console.log("Controller :: ChangeColor() listened to 'buttonClicks' and emits 'changeColor'")
return 1;
});
model.js
var controllerAddAmount = new Rx.Subject();
var obs = null;
//Stores the texts value. Starts at 0
var VALUE = 0;
function Model(obs){
console.log("Model :: New Model")
obs = obs
obs.observe("addAmount", controllerAddAmount, "controllerAddAmount")
obs.add("clickAmount", amountChanged)
}
//Listen to the controller about changing the value, notify the view about the new value
var amountChanged = controllerAddAmount.map(function(val){
console.log("Model :: amountChanged() listened to 'addAmount' and emits 'clickAmount'")
VALUE += val
console.log("Model :: Amount Changed to " + VALUE)
return VALUE;
})
Таким образом, проблема заключается в View.setValue() и View.setNumberColor().
1 ответ
У вас есть много кода, который по сути выглядит так:
// Listen to x to ...
var a = x.map(function (val) { ... });
Но тогда ты никогда не покажешь, что ты делаешь с a
, map
берет наблюдаемую и создает новую наблюдаемую, которая будет запускать предоставленную операцию отображения. Тем не менее, наблюдаемое, которое он возвращает, является инертным и фактически ничего не делает, если вы в конечном итоге не подпишетесь на a
(или некоторая производная от a
).
Похоже, что для вашего контроллера и модели вы в конечном итоге подписываетесь на наблюдаемые ими производимые (через обходной путь добавления их в ваш obs
а потом подписывайтесь на них по вашему мнению).
Тем не менее, не ясно, вы когда-либо подписываться на changeNumberColor
или же modelAmount
, На основании императивного кода в этих двух map
методы, я думаю, вы должны использовать subscribe
вместо map
в этих 2 случаях.
Также для modelAmount
вы используете last
оператор неправильно. Скорее всего, вы думаете last
делает что-то другое, чем делает.