2012-03-30 1 views
41

Есть ли способ сопоставить объект данных JSON с наблюдаемым массивом и затем, в свою очередь, инициализировать каждый элемент наблюдаемого массива в определенную модель типа просмотра?Карта данных JSON для нокаута наблюдаемого массива с конкретным видом модели типа

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

Итак, у меня есть следующие JSON данные:

var data = { 
    state : { 
     name : 'SD', 
     cities : [{ 
      name : 'Sioux Falls', 
      streets : [{ 
       number : 1 
      }, { 
       number : 3 
      }] 
     }, { 
      name : 'Rapid City', 
      streets : [{ 
       number : 2 
      }, { 
       number : 4 
      }] 
     }] 
    } 
}; 

И у меня есть следующие модели вида:

var StateViewModel = function(){ 
    this.name = ko.observable(); 
    this.cities = ko.observableArray([new CityViewModel()]); 
} 

var CityViewModel = function(){ 
    this.name = ko.observable(); 
    this.streets = ko.observableArray([new StreetViewModel()]); 
} 

var StreetViewModel = function(){ 
    this.number = ko.observable(); 
} 

Можно ли с данной структурой данных и используя отображение плагина KNOCKOUT, в чтобы в результате StateViewModel содержал наблюдаемый массив, заполненный 2-мя CityViewModels, и каждый CityViewModel, содержащий наблюдаемый массив, заполненный 2-мя объектами StreetViewModels?

В настоящее время с использованием плагина сопоставления я могу получить его для сопоставления с StateViewModel, но коллекции «городов» и «улиц» заполняются общими объектами, а не экземплярами моих моделей просмотра City and Street.

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

ответ

68

Проверить это http://jsfiddle.net/pTEbA/268/

Object.prototype.getName = function() { 
    var funcNameRegex = /function (.{1,})\(/; 
    var results = (funcNameRegex).exec((this).constructor.toString()); 
    return (results && results.length > 1) ? results[1] : ""; 
}; 

function StateViewModel(data){ 
    this.name = ko.observable(); 
    ko.mapping.fromJS(data, mapping, this); 
} 

function CityViewModel(data) { 
    this.name = ko.observable(); 
    ko.mapping.fromJS(data, mapping, this); 
} 

function StreetViewModel(data) { 
    this.name = ko.observable(); 
    ko.mapping.fromJS(data, mapping, this); 
} 

var mapping = { 
    'cities': { 
     create: function(options) { 
      return new CityViewModel(options.data); 
     } 
    }, 
    'streets': { 
     create: function(options) { 
      return new StreetViewModel(options.data); 
     } 
    } 
} 


var data = { state: {name:'SD', cities:[{name:'Sioux Falls',streets:[{number:1},{number:3}]}, 
             {name:'Rapid City',streets:[{number:2},{number:4}]}]}}; 

var vm = new StateViewModel(data.state) 
console.log(vm); 
console.log(vm.getName()); 
console.log(vm.cities()); 
console.log(vm.cities()[0].getName()); 
console.log(vm.cities()[0].streets()); 
console.log(vm.cities()[0].streets()[0].getName()); 
​ 
+1

Я есть один следить за вопрос, хотя: при создании 'cities', это выглядит как' отображение create' фактически возвращает один объект типа 'CityViewModel', и передается объект города. Что бы вы сделали, если бы хотели иметь такой тип коллекции, как 'CitiesViewModel', с его собственными методами сбора? –

+0

Выглядит отлично? Но что делает 'this. $ Type = '..Model' в конструкторах? Я не могу найти ничего о свойстве '$ type' в онлайн-документации Knockout или Knockout.mapping. – Bart

+0

$ type на самом деле не нужен ... просто чтобы продемонстрировать, что был вызван конструктор. – Artem