knockout.js 在嵌套数组上计算Vue.js

o7jaxewo  于 2022-11-10  发布在  Vue.js
关注(0)|答案(1)|浏览(186)

我有Knockout.js的背景,我一直在考虑在我未来的项目中试用Vue.js。有一件事让我困惑,那就是在嵌套的数据数组上使用计算函数。
假设我们从一个 AJAX 调用“/api/getProject/"中获得这个数据结构。

{
    projectID: 23,
    phases: [
        {
            phaseID: 23,
            activities: [
                {activityID : 23, start: "date", end : "date"},
                {activityID : 23, start: "date", end : "date"}
            ]
        },
        {
            phaseID: 24,
            activities: [
                {activityID : 23, start: "date", end : "date"},
                {activityID : 23, start: "date", end : "date"}
            ]
        },
    ]
}

为此,我需要几个计算变量。首先,是每项活动的持续时间。然后,我需要计算每个阶段的总持续时间。最后,计算项目的总持续时间。
对于KO.js,我这样做的方法是为每个数据点创建类,并将计算的函数放在每个类中。

var Project = function(obj){
    var self = this;

    self.projectID = ko.observable(obj.projectID);

    var mapped = mapArray(obj.phases, i => new Phase(i));
    self.phases = ko.observableArray(mapped);

    self.computed = {
        total : ko.computed(() => {
            var total = 0;
            for(var phase of self.phases())
                total += phase.computed.total();
            return total;
        }),
    }
}

var Phase = function(obj){
    var self = this;

    self.phaseID = ko.observable(obj.phaseID);

    var mapped = mapArray(obj.activities, i => new Activity(i));
    self.activities = ko.observableArray(mapped);

    self.computed = {
        total : ko.computed(() => {
            var total = 0;
            for(var act of self.activities())
                total += act.computed.duration();
            return total;
        }),
    }
}

var Activity = function(obj){
    var self = this;

    self.activityID = ko.observable(obj.activityID);
    self.start = ko.observable(obj.start);
    self.end = ko.observable(obj.end);

    self.computed= {
        duration: ko.computed(() => {
        return end - start;
        }),
    }
}

然后做。

$.getJSON("/api/getProject", function (data, status, xhr) {
        VM.project = new Project(data);
    }),

以下是我读过和考虑过的一些事情,以及为什么我不能让它们发挥作用。
“使用组件Project、Phase、Activity,每个组件都有自己的计算属性”-据我所知,组件不能从其他组件读取数据,我的数据都在一个调用中出现,那么我如何设置每个组件中的数据呢?大多数答案只是用硬编码值填充数据,而大多数情况下不会是这样。
任何帮助都是感激的。我知道我可能是错误的看待这个问题的方式,所以如果我可以改变我的数据是如何交付的或者我对Vue.js的看法;我愿意这样做。

vngu2lb8

vngu2lb81#

您需要全局状态管理,因此需要使用Vuex。
https://vuex.vuejs.org/en/intro.html
安装Vuex后,您将创建一个存储(表示应用程序的当前状态),然后创建一个action,它发送一个异步API请求( AJAX 请求)并相应地更新存储。每个依赖于此API调用中接收到的数据的组件都将对此更新做出React。您可以选择使用Vuex getter、它的行为与常规的组件getter完全相同,但可用于所有组件。
其思想是每个活动的日期都是可编辑的。
编辑嵌套属性没有什么秘诀,你只需要更新整个对象,以便保持React性。
下面是一个完整的示例:

const store = new Vuex.Store({
  state: {
    phases: [
      {
        phaseId: 1,
        activities: [
          { activityId: 1, start: 1, end: 6 },
          { activityId: 2, start: 5, end: 9 },
        ],
      },
      {
        phaseId: 2,
        activities: [
          { activityId: 3, start: 10, end: 16 },
          { activityId: 4, start: 14, end: 19 },
        ],
      },
    ],
  },
  mutations: {
    setDuration (state, {phaseId, activityId, start, end}) {
      const phase = state.phases.find(x => x.phaseId === phaseId);
      state.phases = [
        ...state.phases.filter(x => x !== phase),
        {
          ...phase,
          activities: [
            ...phase.activities.filter(x => x.activityId !== activityId),
            { activityId, start, end }
          ],
        },
      ];
    },
  },
});

const example = {
  data() {
    return {
      phaseId: null,
      activityId: null,
      start: null,
      end: null
    };
  },
  computed: {
    ...Vuex.mapState(['phases']),
    phasesFormated() {
      return JSON.stringify([...this.phases].sort((a, b) => a.phaseId - b.phaseId), null, 2);
    },
  },
  methods: {
    ...Vuex.mapMutations(['setDuration']),
    updatePhase() {
      this.setDuration({
        phaseId: parseInt(this.phaseId),
        activityId: parseInt(this.activityId),
        start: parseInt(this.start),
        end: parseInt(this.end),
      });
    },
  },
  template: `
    <div>
      <div>
        Phase ID:
        <input type="text" v-model="phaseId">
        <br />
        Activity ID:
        <input type="text" v-model="activityId">
        <br />
        Start:
        <input type="text" v-model="start">
        <br />
        End:
        <input type="text" v-model="end">
      </div>
      <button type="button" v-on:click="updatePhase">update</button>
      <pre style="margin-right: 1rem">{{ phasesFormated }}</pre>    
    </div>
  `,
};

const app = new Vue({
  store,
  el: '#app',
  components: { example },
  template: `
    <div v-cloak>
      <h1>app</h1>
      <example></example>
    </div>
  `
});

重建整个对象可能会有点混乱,这就是为什么使用状态管理库(Vuex,Redux等)的人经常规范化数据的原因。
Redux库的文档有a nice example of what data normalization is,Vue的创建者也有an interesting post on the subject

相关问题