KnockoutJS Mapping插件(observableArray)

时间:2015-04-25 09:41:59

标签: javascript json knockout.js knockout-mapping-plugin

我是淘汰赛的新手,我在使用映射插件时遇到问题,因为我不明白它是如何映射我的JSON数据的。
这是一个类似于我程序中的示例json数据:

contact: {
        name : 'John',
        email : 'address@domain.com',
        phones : [{
            phoneType : 'Home Phone',
            phoneNumber: '999-888-777'},
            {
            phoneType : 'Business Phone',
            phoneNumber: '444-888-777'},
            }]
        }

如您所见,此json数据包含一系列手机。
我使用了淘汰映射插件,我可以绑定'名称','电子邮件'并在'foreach:phones'中循环电话号码,没有麻烦,直到我尝试在phoneNumber上创建一个ko.compute,这是一个对象。阵列电话。

@section scripts
{
    <script src="~/ViewModels/ContactModel.js"></script>
    <script type="text/javascript">
        var viewModel = new ContactModel(@Html.Raw(Model.ToJson()));
        $(document).ready(function () {
            ko.applyBindings(viewModel);
        });
</script>

<label>Name</label><input data-bind="value: name" />
<label>Email</label><input data-bind="value: email" />
<label>Phones</label>
<table>
  <tbody data-bind="foreach: phones">
     <tr>
      <td><strong data-bind='text: phoneType'></strong></td>
      <td><input data-bind='value: phoneNumber' /></td>
     </tr>
   /tbody>
 </table>

这是ContactModel.js

    var ContactModel = function (data) {
    var self = this;
    ko.mapping.fromJS(data, {}, self);

    self.reformatPhoneNumber = ko.computed(function(){
    var newnumber;
    newnumber = '+(1)' + self.phones().phoneNumber;
    return newnumber;
    });

    };

对于直观表示,这就是现在的看法:

Name: John
Email: address@domain.com
Phones:
<--foreach: phones -->
Home Phone: 999-888-777
Business Phone: 444-888-777

我想要做的是重新格式化phoneNumber以这种方式显示它:

Name: John
Email: address@domain.com
Phones:
<--foreach: phones -->
Home Phone: (+1)999-888-777
Business Phone: (+1)444-888-777

我试着通过在我的绑定中使用reformatPhoneNumber代替phoneNumber来做到这一点:

<table>
      <tbody data-bind="foreach: phones">
         <tr>
          <td><strong data-bind='text: phoneType'></strong></td>
          <td><input data-bind='value: $root.reformatPhoneNumber' /></td>
         </tr>
       /tbody>
     </table>

但是当我这样做时,reformatPhoneNumber的值不会出现。 我在这里读到的地方,我必须使我的observableArray中的对象也是可观察的,因为默认情况下ko.mapping不会这样做。但我无法想象如何做到这一点,因为我期待ko.mapping插件自动完成所有工作,因为我是这个jslibrary的新手。
任何帮助将不胜感激。非常感谢!!

1 个答案:

答案 0 :(得分:4)

您对命名的使用(计算名为reformatPhoneNumber)表明您将计算机视为函数。虽然它们在技术上是功能,但它们代表值。将它们视为价值观,就像对待可观察者一样。在您的情况下,这意味着它应该更像formattedPhoneNumber,并且应该作为电话号码的属性,而不是联系人的财产。

将您的模型分成可以从原始数据引导自己的单个单元。

模型层次结构中最小的信息单位是电话号码:

function PhoneNumber(data) {
    var self = this;

    self.phoneType = ko.observable();
    self.phoneNumber = ko.observable();
    self.formattedPhoneNumber = ko.pureComputed(function () {
        return '+(1) ' + ko.unwrap(self.phoneNumber);
    });

    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);
        }
    }
};

现在,您可以通过实例化PhoneBook对象来创建整个对象图:

var phoneBookData = {
    contacts: [{
        name: 'John',
        email: 'address@domain.com',
        phones: [{
            phoneType: 'Home Phone',
            phoneNumber: '999-888-777'
        }, {
            phoneType: 'Business Phone',
            phoneNumber: '444-888-777'
        }]
    }]
};
var phoneBook = new PhoneBook(phoneBookData);

通读documentation of the mapping plugin

展开以下代码段以查看其是否有效。

&#13;
&#13;
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);
        }
    }
};
// ------------------------------------------------------------------

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 PhoneNumber(data) {
    var self = this;
    
    self.phoneType = ko.observable();
    self.phoneNumber = ko.observable();
    self.formattedPhoneNumber = ko.pureComputed(function () {
        return '+(1) ' + ko.unwrap(self.phoneNumber);
    });
    
    ko.mapping.fromJS(data, PhoneNumber.mapping, self);
}
PhoneNumber.mapping = {};
// ------------------------------------------------------------------

var phoneBook = new PhoneBook({
    contacts: [{
        name: 'John',
        email: 'address@domain.com',
        phones: [{
            phoneType: 'Home Phone',
            phoneNumber: '999-888-777'
        }, {
            phoneType: 'Business Phone',
            phoneNumber: '444-888-777'
        }]
    }]
});

ko.applyBindings(phoneBook);
&#13;
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.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: formattedPhoneNumber"></span>
            </li>
        </ul>
    </li>
</ul>

<hr />
Model data:
<pre data-bind="text: ko.toJSON(ko.mapping.toJS($root), null, 2)"></pre>

Viewmodel data:
<pre data-bind="text: ko.toJSON($root, null, 2)"></pre>
&#13;
&#13;
&#13;