【问题标题】:I have a chain of Rx.Subjects (A->B->C->A), but the final step is not working我有一个 Rx.Subjects 链(A->B->C->A),但最后一步不起作用
【发布时间】:2015-02-23 16:17:58
【问题描述】:

Live example.

我对 Rx* 完全陌生。我正在尝试使用 RxJS 为我的论文创建 MVC 的反应式版本。它大致基于https://github.com/staltz/mvi-example

在开始编写代码之前,我可能应该更多地研究 RxJS,但我意识到我通常通过跳到池子的最深处才能学到最好的东西。但是现在我完全被困住了。

视图有一个带有数值的文本字段和一个按钮。按下按钮会更改文本的颜色,单击文本会迭代数值。该值存储在模型中,因此“链”通过所有三个对象(视图->控制器->模型->视图)。按下按钮不需要模型,所以链是View->Controller->View。

一切正常,但链的最后部分(->视图)给我带来了麻烦。单击文本(在视图中)会将更改传播到控制器和模型,但视图不会通知模型中的更改。我不明白为什么,因为我理解这一切的方式都是以相同的方式实现的。

我在下面的代码示例中添加了 cmets 来指定不被调用的函数。

代码可用here。请注意,就像我说的,代码是针对我的论文的,所以一些设计决策和应用程序本身可能看起来很奇怪。下面我还尝试包括我认为最相关的部分。我知道我应该提供一个完整的例子,但在这种情况下这是不可能的。

对象获取的“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;
  });

模型.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()。

【问题讨论】:

标签: javascript system.reactive reactive-programming rxjs reactive-extensions-js


【解决方案1】:

你有很多基本上看起来像这样的代码:

// Listen to x to ...
var a = x.map(function (val) { ... });

但是你永远不会展示你用a做什么。 map 接受一个 observable 并生成一个新的 observable,它将运行提供的映射操作。然而,它返回的 observable 是 inert 并且实际上并没有做任何事情,除非你最终订阅 a(或 a 的一些派生词)。

看起来对于您的控制器和模型,您最终确实订阅了它们正在生成的 observables(通过将它们添加到您的 obs 然后在您的视图中订阅它们的迂回路线)。

但是,不清楚您是否订阅过changeNumberColormodelAmount。根据这两个 map 方法中的命令式代码,我猜你应该在这两种情况下使用 subscribe 而不是 map

另外,对于modelAmount,您错误地使用了last 运算符。您很可能认为last 做了一些与它不同的事情。

【讨论】:

  • 感谢您的回答!就像我说的我是一个 Rx* 新手,所以如果我对我正在尝试做的一些事情完全不感兴趣,我不会感到惊讶。如果我正确理解了您的建议,我应该将代码更改为“var changeNumberColor = changeColors.subscribe(..)”之类的东西吗?这段代码的循环性质可能使像我这样的新手更难理解。明天我会尝试解决这个问题,也许会编辑此评论,或添加一条新评论。
  • var changeNumberSubscription = changeColors.subscribe(...)。当您订阅时,您是在说您想观察可观察对象并在值到达时做一些事情。当你想停止观察时,它会返回一个你可以使用的一次性物品。
  • 成功了!太感谢了。我在想我这样做的方式可能是错误的,因为现在我有点订阅了两次同一个流。或者其他的东西。也许我会尝试重新创建这个例子,而不用那个“obs”结构,这似乎只会让这更复杂。
  • 是的,您的obs 构造最好通过附加.do() 运算符来记录可观察链的不同阶段的消息。
猜你喜欢
  • 2016-02-19
  • 2018-08-11
  • 2015-11-28
  • 2019-10-27
  • 2011-03-24
  • 1970-01-01
  • 1970-01-01
  • 2018-03-25
  • 2011-06-13
相关资源
最近更新 更多