значение beforeChange не определено в Knockout при использовании подключаемого модуля отображения - PullRequest
1 голос
/ 09 мая 2019

Исходя из ответа на этот вопрос , я пытаюсь получить значение перед изменением в наблюдаемой с помощью следующего кода.

var phoneBook;

function debug(s) {
  $("#log").append('<br>' + s);
}

function PhoneNumber(data) {
  var self = this;
  self.phoneType = ko.observable();
  self.phoneNumber = ko.observable();


  self.phoneNumber.subscribe(function(newValue) {
    debug('newvalue: ' + newValue);
  });

  self.phoneNumber.subscribe(function(previousValue) {
    debug(previousValue);

  }, self, "beforeChange");

  ko.mapping.fromJS(data, PhoneNumber.mapping, self);
}
PhoneNumber.mapping = {};

function Contact(data) {
  var self = this;

  self.name = ko.observable();
  self.email = ko.observable();
  self.phones = ko.observableArray();

  ko.mapping.fromJS(data, Contact.mapping, self);
}
Contact.mapping = {
  phones: {
    create: function(options) {
      return new PhoneNumber(options.data);
    }
  }
};

function PhoneBook(data) {
  var self = this;

  self.contacts = ko.observableArray();

  ko.mapping.fromJS(data, PhoneBook.mapping, self);
}
PhoneBook.mapping = {
  contacts: {
    create: function(options) {
      return new Contact(options.data);
    }
  }
};

var phoneBookData = {
  contacts: [{
      name: 'John',
      email: 'address@domain.com',
      phones: [{
        phoneType: 'Home Phone',
        phoneNumber: '999-888-777'
      }, {
        phoneType: 'Business Phone',
        phoneNumber: '444-888-777'
      }]
    },
    {
      name: 'John2',
      email: '222address@domain.com',
      phones: [{
        phoneType: '22Home Phone',
        phoneNumber: '22999-888-777'
      }, {
        phoneType: '22Business Phone',
        phoneNumber: '444-888-777'
      }]
    }
  ]
};

var phoneBookDataOther = {
  contacts: [{
      name: 'peter',
      email: 'address@domain.com',
      phones: [{
        phoneType: 'Home Phone',
        phoneNumber: '999-888-777'
      }, {
        phoneType: 'Business Phone',
        phoneNumber: '444-888-777'
      }]
    },
    {
      name: 'almond',
      email: '222address@domain.com',
      phones: [{
        phoneType: '22Home Phone',
        phoneNumber: '22999-888-777'
      }, {
        phoneType: '22Business Phone',
        phoneNumber: '444-888-777'
      }]
    }
  ]
};

function dofunc() {
  ko.mapping.fromJS(phoneBookDataOther, phoneBook);
}

$(document).ready(function() {
  phoneBook = new PhoneBook(phoneBookData);
  ko.applyBindings(phoneBook);

  setTimeout(dofunc, 5000)
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>

<ul data-bind="foreach: contacts">
  <li>
    <div data-bind="text: name"></div>
    <div data-bind="text: email"></div>
    <ul data-bind="foreach: phones">
      <li>
        <span data-bind="text: phoneType"></span>:
        <span data-bind="text: phoneNumber"></span>
      </li>
    </ul>
  </li>
</ul>


<div>
  <p id="log"></p>
</div>

Событие изменения происходит, когда подключаемый модуль сопоставления снова вызывается (здесь через 5 секунд), но previousValue всегда получается какundefined.

Что я делаю не так?

Вот и jsfiddle: https://jsfiddle.net/icinema/ungbz27s/1/

1 Ответ

2 голосов
/ 09 мая 2019

Проблема здесь в том, что вы используете плагин сопоставления неправильно, и что ваши тестовые данные не имеют смысла.

Всегда будет «предыдущее» значение, когда вы записываете новое значение в точно такая же наблюдаемая .Но плагин сопоставления отбросит все ваши видовые модели и создаст новые, когда вы отобразите совершенно другой набор данных.

Как узнать, что объект с именем "Джон" в первом раундеПредполагается, что это тот же человек, который имеет имя «Питер» во втором туре?Не можетТаким образом, он выбрасывает все контакты, включая все их номера телефонов, и создает новые.В этом сценарии никогда не бывает «предыдущего» значения.

Вам нужно:

  • Дайте контактам и телефонным номерам ключ, чтобы их можно было идентифицировать как один и тот же объект по всемувызывает ko.mapping.fromJS.
  • Сообщите подключаемому плагину, какое из свойств объекта должно быть ключевым, добавив функцию key в конфигурацию отображения.

Чтение документация по подключаемому модулю - прочитайте полностью, для начала это не так уж и много.

В следующем примере я использовал name в качестве ключа для контактов и phoneTypeв качестве ключа для телефонов, и я изменил тестовые данные, чтобы они имели одинаковые имена и типы телефонов в обоих наборах.Возможно, вы захотите использовать идентификационный номер контакта в качестве ключа вместо имени.

Преимущество использования функции key состоит в том, что нокаут только обновляет текст телефонного номера в DOM, а не выбрасываети воссоздание всего <li> и всего в нем, потому что он может распознавать существующие экземпляры модели представления и сохранять их.Это сократит время рендеринга.

/* global ko, $ */

function debug(s) {
  $("#log").append('<br>' + s);
}

function PhoneNumber(data) {
  var self = this;
  self.phoneType = ko.observable();
  self.phoneNumber = ko.observable();

  self.phoneNumber.subscribe(function(newValue) {
    debug('new value: ' + newValue);
  });

  self.phoneNumber.subscribe(function(previousValue) {
    debug('previous value: ' + previousValue);

  }, self, "beforeChange");

  ko.mapping.fromJS(data, PhoneNumber.mapping, self);
}
PhoneNumber.mapping = {};

function Contact(data) {
  var self = this;

  self.name = ko.observable();
  self.email = ko.observable();
  self.phones = ko.observableArray();

  ko.mapping.fromJS(data, Contact.mapping, self);
}
Contact.mapping = {
  phones: {
    create: function(options) {
      return new PhoneNumber(options.data);
    },
    key: function (data) {
      return ko.unwrap(data.phoneType);
    }
  }
};

function PhoneBook(data) {
  var self = this;

  self.contacts = ko.observableArray();

  ko.mapping.fromJS(data, PhoneBook.mapping, self);
}
PhoneBook.mapping = {
  contacts: {
    create: function(options) {
      return new Contact(options.data);
    },
    key: function (data) {
      return ko.unwrap(data.name);
    }
  }
};


var phoneBookData = {
  contacts: [{
      name: 'John',
      email: 'john@domain.com',
      phones: [{
        phoneType: 'Home Phone',
        phoneNumber: '999-888-777-old'
      }, {
        phoneType: 'Business Phone',
        phoneNumber: '444-888-777-old'
      }]
    },
    {
      name: 'Peter',
      email: 'peter@domain.com',
      phones: [{
        phoneType: 'Home Phone',
        phoneNumber: '22999-888-777-old'
      }, {
        phoneType: 'Business Phone',
        phoneNumber: '444-888-777-old'
      }]
    }
  ]
};

var phoneBookDataOther = {
  contacts: [{
      name: 'John',
      email: 'john@domain.com',
      phones: [{
        phoneType: 'Home Phone',
        phoneNumber: '999-888-777-new'
      }, {
        phoneType: 'Business Phone',
        phoneNumber: '444-888-777-new'
      }]
    },
    {
      name: 'Peter',
      email: 'peter@domain.com',
      phones: [{
        phoneType: 'Home Phone',
        phoneNumber: '22999-888-777-new'
      }, {
        phoneType: 'Business Phone',
        phoneNumber: '444-888-777-new'
      }]
    }
  ]
};

$(document).ready(function() {
  var phoneBook = new PhoneBook(phoneBookData);

  ko.applyBindings(phoneBook);

  debug('<hr>');
  setTimeout(function dofunc() {
    ko.mapping.fromJS(phoneBookDataOther, phoneBook);
  }, 3000);
});
#log { font-family: monospace; font-size: small; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>

<ul data-bind="foreach: contacts">
  <li>
    <div data-bind="text: name"></div>
    <div data-bind="text: email"></div>
    <ul data-bind="foreach: phones">
      <li>
        <span data-bind="text: phoneType"></span>:
        <span data-bind="text: phoneNumber"></span>
      </li>
    </ul>
  </li>
</ul>

<hr>
<div id="log"></div>
...