根据Observable数组

时间:2017-07-28 14:32:56

标签: knockout.js

我有一个包含所有可用审批级别的选择菜单列表(self.approvalLevels observable array)。

我有一个包含3个选定审批级别的有序列表(self.userSelectedApprovalLevels可观察数组)。这些按优先级排序。

我希望self.userSelectedApprovalLevels中的项目可观察在选择菜单列表中标记为已选中。

我该怎么做?



var viewModel = function() {
  var self = this;

  self.approvalLevels = ko.observableArray([]); // observable array to store list of all available approval levels
  self.userSelectedApprovalLevels = ko.observableArray([]); // observable array to store list of approval levels selected by the user


  self.approvalLevels = ko.observableArray([{
      "ApprovalLevelID": 1,
      "ApprovalLevelName": "Analyst"
    },
    {
      "ApprovalLevelID": 2,
      "ApprovalLevelName": "Supervisor"
    },
    {
      "ApprovalLevelID": 3,
      "ApprovalLevelName": "Tester"
    },
    {
      "ApprovalLevelID": 4,
      "ApprovalLevelName": "Manager"
    }
  ]);



  self.userSelectedApprovalLevels = ko.observableArray([{
      "ApprovalLevelID": 2,
      "ApprovalLevelName": "Supervisor",
      "ApprovalLevelPriority": 0
    },
    {
      "ApprovalLevelID": 1,
      "ApprovalLevelName": "Analyst",
      "ApprovalLevelPriority": 1
    },
    {
      "ApprovalLevelID": 3,
      "ApprovalLevelName": "Tester",
      "ApprovalLevelPriority": 2
    }
  ]);


};
ko.applyBindings(new viewModel());

<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>


<div class="row">
  <div class="col-lg-6">
    <div class="form-group">
      <label for="approvalList">Select Approval Level(s)</label>
      <select multiple="multiple" class="form-control" id="approvalLevelList" data-bind="options: approvalLevels, selectedOptions: userSelectedApprovalLevels, optionsText: 'ApprovalLevelName', optionsvalue: 'ApprovalLevelName'"></select>
      <small id="approvalLevelList" class="form-text text-muted">Select Approval Levels Required for this Task.</small>
    </div>
  </div>

  <div class="col-lg-6">
    <p>Sort in Order of Approval level (Highest to Lowest):</p>
    <ol id="selectedApprovalLevelsList" data-bind="foreach: userSelectedApprovalLevels">
      <li class="list-group-item-info">
        <span data-bind="text: ApprovalLevelName"> </span>
      </li>
    </ol>
  </div>
</div>
&#13;
&#13;
&#13;

3 个答案:

答案 0 :(得分:0)

要正确链接选择,他们需要引用相同的对象,或者引用可以使用=== 进行比较的基元。< / p>

我的建议是在self.approvalLevels内创建一次的源对象。要设置初始选择,请将对此数组中项目的引用添加到selectedApprovalLevels,如下所示:

self.userSelectedApprovalLevels = ko.observableArray([
  self.approvalLevels()[0],
  self.approvalLevels()[1],
  self.approvalLevels()[2]
]);

现在,所选列表不会被排序,但会正确同步。

要对列表进行排序,我们会创建一个额外的computed图层,用于选择并重新排列它们:

self.sortedSAL = ko.pureComputed(function() {
  return self.userSelectedApprovalLevels().sort(function(a, b) {
    return a.ApprovalLevelPriority - b.ApprovalLevelPriority;
  });
});

我把它放在下面的工作示例中。

var viewModel = function() {
  var self = this;

  self.approvalLevels = ko.observableArray([]); // observable array to store list of all available approval levels
  self.userSelectedApprovalLevels = ko.observableArray([]); // observable array to store list of approval levels selected by the user


  self.approvalLevels = ko.observableArray([

    {
      "ApprovalLevelID": 1,
      "ApprovalLevelName": "Analyst",
      "ApprovalLevelPriority": 1
    },
    {
      "ApprovalLevelID": 2,
      "ApprovalLevelName": "Supervisor",
      "ApprovalLevelPriority": 0
    },
    {
      "ApprovalLevelID": 3,
      "ApprovalLevelName": "Tester",
      "ApprovalLevelPriority": 2
    },
    {
      "ApprovalLevelID": 4,
      "ApprovalLevelName": "Manager",
      "ApprovalLevelPriority": 2
    }
  ]);



  self.userSelectedApprovalLevels = ko.observableArray(
    // Initial selections:
    self.approvalLevels().slice(0, 3)
  );
  
  self.sortedSAL = ko.pureComputed(function() {
    return self.userSelectedApprovalLevels().sort(function(a, b) {
      return a.ApprovalLevelPriority - b.ApprovalLevelPriority;
    });
  });


};
ko.applyBindings(new viewModel());
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>


<div class="row">
  <div class="col-lg-6">
    <div class="form-group">
      <label for="approvalList">Select Approval Level(s)</label>
      <select multiple="multiple" class="form-control" id="approvalLevelList" data-bind="options: approvalLevels, selectedOptions: userSelectedApprovalLevels, optionsText: 'ApprovalLevelName', optionsvalue: 'ApprovalLevelName'"></select>
      <small id="approvalLevelList" class="form-text text-muted">Select Approval Levels Required for this Task.</small>
    </div>
  </div>

  <div class="col-lg-6">
    <p>Sort in Order of Approval level (Highest to Lowest):</p>
    <ol id="selectedApprovalLevelsList" data-bind="foreach: sortedSAL">
      <li class="list-group-item-info">
        <span data-bind="text: ApprovalLevelName"> </span>
      </li>
    </ol>
  </div>
</div>

编辑有关两个单独的普通对象列表的要求

如果您要从服务器检索两个列表,最好创建一个客户端 viewmodel ,它将您需要的数据组合在一起。您可以使用<select>的列表作为主要来源,并根据第二个列表添加computed sortOrder属性:

sortOrder: ko.pureComputed(function() {
  var match = apSortData().find(function(item) {
    return item.ApprovalLevelID === data.ApprovalLevelID;
  });

  return match ? match.ApprovalLevelPriority : 0;
})

在一个工作示例中,排序顺序数据在源信息后1秒内出现:

var viewModel = function() {
  var self = this;

  var apSortData = ko.observableArray([]);
  setTimeout(function() {
    // To show they are linked automatically
    apSortData(getSortOrderData());
  }, 1000)

  var ApprovalLevelVM = function(data) {
    return Object.assign(
      {}, 
      data, 
      {
        sortOrder: ko.pureComputed(function() {
          var match = apSortData().find(function(item) {
            return item.ApprovalLevelID === data.ApprovalLevelID;
          });
        
          return match ? match.ApprovalLevelPriority : 0;
        })
      }
    );
  };

  self.approvalLevels = ko.observableArray(
    getApprovalLevelData().map(ApprovalLevelVM)
  );

  self.userSelectedApprovalLevels = ko.observableArray(
    // Initial selections:
    self.approvalLevels().slice(0, 3)
  );

  self.sortedSAL = ko.pureComputed(function() {
    return self.userSelectedApprovalLevels()
      .sort(function(a, b) {
        return a.sortOrder() - b.sortOrder();
      });
  }).extend({"deferred": true});


};

ko.applyBindings(new viewModel());

function getApprovalLevelData() {
  return [{
      "ApprovalLevelID": 1,
      "ApprovalLevelName": "Analyst"
    },
    {
      "ApprovalLevelID": 2,
      "ApprovalLevelName": "Supervisor"
    },
    {
      "ApprovalLevelID": 3,
      "ApprovalLevelName": "Tester"
    },
    {
      "ApprovalLevelID": 4,
      "ApprovalLevelName": "Manager"
    }
  ];
};

function getSortOrderData() {
  return [{
      "ApprovalLevelID": 2,
      "ApprovalLevelName": "Supervisor",
      "ApprovalLevelPriority": 0
    },
    {
      "ApprovalLevelID": 1,
      "ApprovalLevelName": "Analyst",
      "ApprovalLevelPriority": 1
    },
    {
      "ApprovalLevelID": 3,
      "ApprovalLevelName": "Tester",
      "ApprovalLevelPriority": 2
    }
  ];
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>


<div class="row">
  <div class="col-lg-6">
    <div class="form-group">
      <label for="approvalList">Select Approval Level(s)</label>
      <select multiple="multiple" class="form-control" id="approvalLevelList" data-bind="options: approvalLevels, selectedOptions: userSelectedApprovalLevels, optionsText: 'ApprovalLevelName', optionsvalue: 'ApprovalLevelName'"></select>
      <small id="approvalLevelList" class="form-text text-muted">Select Approval Levels Required for this Task.</small>
    </div>
  </div>

  <div class="col-lg-6">
    <p>Sort in Order of Approval level (Highest to Lowest):</p>
    <ol id="selectedApprovalLevelsList" data-bind="foreach: sortedSAL">
      <li class="list-group-item-info">
        <span data-bind="text: ApprovalLevelName + ' - '+ sortOrder()"> </span>
      </li>
    </ol>
  </div>
</div>

答案 1 :(得分:0)

所以我认为这个问题的关键是多选择不喜欢为selectedOptions bindng创建一个对象数组。我试图通过使用可写的observable来解决这个限制。写作部分可能比我使用的if语句做得更好,但你可以理解。

这是工作小提琴。https://jsfiddle.net/j3zk5uw9/3/

这是可写的可观察的。

  this.userSelectedApprovalLevelWritable = ko.pureComputed({
    read: function() {
      var ApprovalLevelIDs = ko.utils.arrayMap(self.userSelectedApprovalLevels(), function(item) {
        return item.ApprovalLevelID;
      });
      return ApprovalLevelIDs.sort();

    },
    write: function(value) {
      self.userSelectedApprovalLevels.removeAll();
      for (var i = 0; i < value.length; i++) {
        if (value[i] === 2) {
          self.userSelectedApprovalLevels.push({
            "ApprovalLevelID": 2, "ApprovalLevelName": "Supervisor",
            "ApprovalLevelPriority": 0
          })
        }
        if (value[i] === 1) {
          self.userSelectedApprovalLevels.push({
            "ApprovalLevelID": 1, "ApprovalLevelName": "Analyst",
            "ApprovalLevelPriority": 1
          })
        }
        if (value[i] === 3) {
          self.userSelectedApprovalLevels.push({
            "ApprovalLevelID": 3, "ApprovalLevelName": "Tester",
            "ApprovalLevelPriority": 2
          })
        }
        if (value[i] === 4) {
          self.userSelectedApprovalLevels.push({
            "ApprovalLevelID": 4, "ApprovalLevelName": "Manager",
            "ApprovalLevelPriority": 3
          })
        }
      }
    },
    owner: this
  });

答案 2 :(得分:0)

正如@ user3297291所说,选择时拥有相同的对象是件好事。如果没有ApprovalLevelPriority,您可以在推入阵列之前将其添加到对象,但是您需要有一个逻辑来分配优先级编号

这是我的方法: 您可以在选择中添加更改事件,并添加一个observable,每次选择项目时都会保存selectedOptions。

示例:https://jsfiddle.net/21dcxh2c/4/

<强> HTML:

<div class="row">
   <div class="col-lg-6">
      <div class="form-group">
         <label for="approvalList">Select Approval Level(s)</label>
         <select multiple="multiple" class="form-control" id="approvalLevelList" data-bind="options: approvalLevels, selectedOptions: selectedApproval, optionsText: 'ApprovalLevelName', optionsvalue: 'ApprovalLevelName',event: {change: addApproval}"></select>
         <small id="approvalLevelList" class="form-text text-muted">Select Approval Levels Required for this Task.</small>
      </div>
   </div>

   <div class="col-lg-6">
      <p>Sort in Order of Approval level (Highest to Lowest):</p>
      <ol id="selectedApprovalLevelsList" data-bind="foreach: userSelectedApprovalLevels">
         <li class="list-group-item-info">
            <span data-bind="text: ApprovalLevelName"> </span>
         </li>
      </ol>
   </div>
</div>

<强> JS

var viewModel = function() {
   var self = this;

   self.approvalLevels = ko.observableArray([]); // observable array to store list of all available approval levels
   self.userSelectedApprovalLevels = ko.observableArray([]); // observable array to store list of approval levels selected by the user
    self.selectedApproval = ko.observable();
   self.userSelectedApprovalLevels = ko.observableArray([]);

   self.approvalLevels = ko.observableArray([{
      "ApprovalLevelID": 1,
      "ApprovalLevelName": "Analyst"
   }, {
      "ApprovalLevelID": 2,
      "ApprovalLevelName": "Supervisor"
   }, {
      "ApprovalLevelID": 3,
      "ApprovalLevelName": "Tester"
   }, {
      "ApprovalLevelID": 4,
      "ApprovalLevelName": "Manager"
   }]);

   self.addApproval = function() {
      var duplicate = false;
      var obj = self.selectedApproval()[0]
      ko.utils.arrayForEach(self.userSelectedApprovalLevels(), function(element) {
         if (obj.ApprovalLevelID == element.ApprovalLevelID) {
            return duplicate = true
         }
      });
      if (!duplicate) {
          // here you can add your priority to the object 
          // right now I am adding the ApprovalLevelID as ApprovalLevelPriority you probably will have your logic 
         obj.ApprovalLevelPriority = obj.ApprovalLevelID
         self.userSelectedApprovalLevels.push(obj)
      } //else you can remove if it already exists
   }

   self.sortSelection = ko.computed(function() {
      return self.userSelectedApprovalLevels().sort(function(a, b) {
         return a.ApprovalLevelPriority > b.ApprovalLevelPriority;
      });
   });

};
ko.applyBindings(new viewModel());