Mithril - 在子组件修改父组件数据后重新排序子组件数组

Mithril - Re-sort array of child components after child component modifies parent component data

本文关键字:组件 新排序 数组 排序 数据 修改 Mithril      更新时间:2023-09-26

如果有人能为这个问题想到更好的标题,请告诉我。

经过疯狂的时间,我才想出了这个问题的答案。我相信其他人也会犯同样的错误,所以我会在下面发布答案。

一旦我提供了一些背景信息,这个问题就会更有意义。我有一个父组件,它有一个具有匹配键的对象数组(例如 ctrl.arrayOfObjects)。我按每个对象共享的键之一的值对这个数组进行排序(例如,ctrl.arrayOfObjects[0].keyToSortBy )。然后,我将现在排序的数组中的每个元素传递给另一个组件。

这个子组件有一个onclick方法,它改变父组件和子组件之间共享的数据,即父组件ctrl.arrayOfObjects中的一个对象。它更改用于对父组件的ctrl.arrayOfObjects进行排序的键的值(例如,sharedObject.keyToSortBy )。

我的问题是,在触发此onclick事件后,父组件不会对对象数组进行重新排序。onclick 事件确实修改了它应该修改的数据(sharedObject.keyToSortBy),我知道这是因为页面确实重新呈现了 sharedObject.keyToSortBy 的新值。但它不会重新排序父组件的ctrl.arrayOfObjects

奇怪的是,如果我做一个console.log,排序方法被调用,但页面没有显示任何更改。

代码如下所示(ctrl.arrayOfObjects ctrl.jokes):

var JokeWidgetSeries = {
  controller: function() {
    var ctrl = this;
    ctrl.jokes = [{
      text: "I'm a joke",
      votes: 3
    }, {
      text: "This is another joke",
      votes: 1
    }, {
      text: "A third joke",
      votes: 1
    }]
  },
  view: function(ctrl) {
    return m('#jokes-container',
      m('#jokes',
        ctrl.jokes.sort(function(a, b) {
          // Sort by number of votes.
          return (a.votes < b.votes) ? 1 : (a.votes > b.votes) ? -1 : 0;
        }).map(function(joke) {
          // Create a 'JokeWidget' for each object in 'ctrl.jokes'.
          // Share the 'joke' object with the child component.
          return m.component(JokeWidget, joke);
        })
      )
    );
  }
};
var JokeWidget = {
  controller: function(inherited) {
    var ctrl = this;
    ctrl.joke = inherited;
  },
  view: function(ctrl) {
    return m('.joke', [
      m('.joke-text', ctrl.joke.text),
      m('.joke-vote-count', ctrl.joke.votes),
      m('.thumb-up', {
        onclick: function(e) {
          // Here, the page shows that the vote count has changed,
          //   but the array of 'JokeWidget' DOM elements do not re-sort.
          ctrl.joke.votes += 1;
        }
      })
    ]);
  }
};

触发 JokeWidget 组件的 onclick 方法后,显示ctrl.joke.votes.joke-vote-count DOM 元素确实增加了 1。但它并没有像它应该的那样在页面上的小部件列表/数组中向上移动。

但是,如果我将这两个组件合并为一个,数组确实会像我想要的那样重新排序。喜欢这个:

var JokeWidgetSeries = {
  controller: function() {
    var ctrl = this;
    ctrl.jokes = [{
      text: "I'm a joke",
      votes: 3
    }, {
      text: "This is another joke",
      votes: 1
    }, {
      text: "A third joke",
      votes: 1
    }]
  },
  view: function(ctrl) {
    return m('#jokes-container',
      m('#jokes',
        ctrl.jokes.sort(function(a, b) {
          // Sort by number of votes.
          return (a.votes < b.votes) ? 1 : (a.votes > b.votes) ? -1 : 0;
        }).map(function(joke) {
          // Create a 'JokeWidget' component instance for each object in 'ctrl.jokes'.
          // Share the 'joke' object with the child component.
          return m('.joke', [
            m('.joke-text', joke.text),
            m('.joke-vote-count', joke.votes),
            m('.thumb-up', {
              onclick: function(e) {
                // Here, the array of 'JokeWidget' DOM elements
                //   DO re-sort. Why?
                joke.votes += 1;
              }
            })
          ]);
        })
      )
    );
  }
};

如何分离这些组件并仍然使这种重新排序方法起作用?

解决方案很简单。在父组件和子组件之间共享对象时,请将对象传递给子组件的view方法,而不是controller方法。

在 Mithril 中创建组件时,可以将数据传递给组件的controllerview方法。此数据将是controller方法的第一个参数和view方法的第二个参数。

与其将共享对象设置为ctrl.joke JokeWidgetcontroller方法,然后在其view方法中引用它,只需将共享对象直接传递给view方法即可。喜欢这个:

var JokeWidget = {
  // Do not pass the object this way.
  // controller: function(joke) {
  //   var ctrl = this;
  //   ctrl.joke = joke;
  // },
  // Pass the shared object as a second parameter to the 'view' method.
  view: function(ctrl, joke) {
    return m('.joke', [
      m('.joke-text', joke.text),
      m('.joke-vote-count', joke.votes),
      m('.thumb-up', {
        onclick: function(e) {
          // Now, the array of 'JokeWidget' DOM elements will re-sort.
          joke.votes += 1;
        }
      })
    ]);
  }
};

这也消除了额外的抽象层,使其更易于阅读和理解。