Как я могу вложить цикл из одного массива в knockoutjs?

1

Для простоты у меня есть простой массив. То, что я хочу сделать, - это цикл вопросов массива для первой категории, а затем цикл через вопросы массива для второй категории и т.д.

"Ответ" будет текстовыми полями ввода или переключателями или любым другим.

Как бы я "вложил" две циклы в knockout.js? Я не могу разделить его на два массива, потому что тогда я теряю связь между двумя циклами.

//knockout.js viewModel
function SurveyViewModel() {
    var self = this;
    self.EvaluationElement = ko.observableArray([
        {category: "Category1", question: "Question #1"},
        {category: "Category1", question: "Question #2"},
        {category: "Category2", question: "Question #3"},
        {category: "Category2", question: "Question #4"},
        {category: "Category3", question: "Question #5"},
        {category: "Category3", question: "Question #6"},
        {category: "Category4", question: "Question #7"},

И я хочу построить представление (HTML), которое по существу представляет собой таблицу, вложенную следующим образом:

<table>
  <tr>
    <th colspan="3">Category 1</th>
  </tr>
  <tr>
    <td>Question #1</td>
    <td>Answer</td>
    <td></td>
  </tr>
  <tr>
    <td>Question #2</td>
    <td>Answer</td>
    <td></td>
  </tr>
  <tr>
    <td colspan="3">Category 2</td>
  </tr>
  <tr>
    <td>Question #3</td>
    <td>Answer</td>
    <td></td>
  </tr>
  <tr>
    <td>Question #4</td>
    <td>Answer</td>
    <td></td>
  </tr>
  <tr>
    <td>Question #5</td>
    <td>Answer</td>
    <td></td>
  </tr>
</table>

Я пытаюсь сделать что-то вроде этого... есть очевидные ошибки, но, надеюсь, это передает мое мышление.

<br>
<table class="table table-hover">
   <tbody data-bind="foreach: EvaluationElement/Category">
      <tr>
        <td colspan="2" data-bind="text: category></td>
      </tr>
      <data-bind="foreach: EvaluationElement">
           <tr>
            <td data-bind="text: question"></td>
            <td>Answer</td>
            <td></td>
          </tr>
       <// close loop>
   </tbody>
</table>
<br>
<button data-bind="click: submitSurvey">Submit</button>
Теги:
arrays
knockout.js

1 ответ

1
Лучший ответ

Вы должны реструктурировать свои данные в своей модели просмотра, чтобы ваш взгляд был приятным и простым. Если в вашем представлении требуются тела таблиц для каждой категории, то то, что должны отражать данные в viewmodel.

Чтобы перестроить список вопросов в списке категорий, вы можете использовать Knockout pureComputed.

Мы создаем функцию, которая преобразует данные из [ { category, question } ] в [ { category, questions: [ { category, question ] } ].

Это очень похоже на groupBy. Существует много способов написать функцию группировки; найдите "group by property in javascript", если вы хотите узнать больше о разных подходах.

С новой структурой вид очень похож на ваш желаемый формат:

function SurveyViewModel() {
  var self = this;
  self.EvaluationElement = ko.observableArray([{
      category: "Category1",
      question: "Question #1"
    },
    {
      category: "Category1",
      question: "Question #2"
    },
    {
      category: "Category2",
      question: "Question #3"
    },
    {
      category: "Category2",
      question: "Question #4"
    },
    {
      category: "Category3",
      question: "Question #5"
    },
    {
      category: "Category3",
      question: "Question #6"
    },
    {
      category: "Category4",
      question: "Question #7"
    }
  ]);

  self.categories = ko.pureComputed(function() {
    // Assumes questions sorted by category
    return self.EvaluationElement().reduce(
      function(cats, q, i, qs) {
        const prev = qs[i - 1];
        
        if (!prev || prev.category !== q.category) {
          cats.push({ 
            category: q.category,
            questions: [ q ]
          });
        } else {
          cats[cats.length - 1].questions.push(q);
        };
        
        return cats;
      }, []);
  });
};

ko.applyBindings(new SurveyViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<table>
  <tbody data-bind="foreach: categories">
    <tr>
      <td colspan="2" data-bind="text: category"></td>
    </tr>
    <!-- ko foreach: questions -->
    <tr>
      <td data-bind="text: question"></td>
      <td>Answer</td>
      <td></td>
    </tr>
    <!-- /ko -->
  </tbody>
</table>
  • 1
    Спасибо, это очень полезно.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню