如何从另一个视图模型访问 select 选项可观察到 selected 值?

How to access a select option observable for selected value from another view model?

我有两个视图模型,一个包含一个 observable,它接收来自 select option 的 selectedValue。

如何从另一个视图模型访问所选值,在本例中为 selecteArtist()?每次更改值时,我都需要将其作为参数传递给 ViewModelB。

ViewModel A:

function ViewModelA(){

    var self = this;

    // Access this from outside
    self.selectedArtist = ko.observable();

    var data = [{
        "ArtistId": "1",
        "LastName": "Secada",
        "FirstName": "Jon"
    },

    {
       "ArtistId": "2",
       "LastName": "Dion",
       "FirstName": "Celine"

    }];


    self.artists = ko.computed(function () {

       return ko.utils.arrayMap(data,

               function (artist) {

                    return {
                        fullName: artist.FirstName + ' ' + artist.LastName,
                        value: artist.ArtistId
           }

         });
    });
}

ViewModel B:

function ViewModelB(){
    var self = this;

    // Something like this:
    var viewModelA = new ViewModelA();

    self.selArtist = ko.observable(viewModelA.selectedArtist().value);

}

我试图做这样的事情:

    self.selArtist = ko.computed(function () {

        if(self.selectedArtist()){
            return self.selectedArtist().ArtistId;
        }
    });

然后这样访问:

    var viewModelA = new ViewModelA();
    viewModelA.selArtist();

结果:undefined:

更新:

根据下面的评论,我稍微更改了代码,但我仍然无法从 viewModelA 访问选定的值。

例如这个有效:

   self.firstName = ko.observable('Bob');
   self.lastName = ko.observable('Smith');

   self.fullName = ko.computed(function() {
        return self.firstName() + " " + self.lastName();
    });

这样调用:

var viewModelB = function(vma){
    var self = this;

    // Alert for example:
    alert(vma.fullName());
};

var viewModelMain = (function(){
    var self = this;
    self.viewModelA =  new viewModelA();
    self.viewModelB = new viewModelB(self.viewModelA);
})();

如果我尝试调用 self.selectedArtist().value 它不会像上面所说的那样工作。

我问过a similar question on CodeReview。基本上,有三个选项:

  1. 将 parent 传递给 child 并在 child 视图模型中放置逻辑;
  2. 让parent处理children的一切;
  3. 创建管理服务/object 来处理这些事情。

我发现第三个选项在 Knockout 中表现不佳,但请注意,这主要是个人意见。

我发现当 child class 非常小 and/or 视图模型树中的叶节点时,第二个选项很有用。

我发现我通常最终会使用第一个选项。使用您提供给我们的代码,这对您来说也是最有意义的。例如:

function ViewModelB(parent){
    var self = this;

    self.selArtist = ko.computed(function () {
        if(!!parent.selectedArtist()){
            return parent.selectedArtist().id;
        }
        return undefined;
    });
}

function ArtistVm(data) {
    var self = this;
    self.id = data.ArtistId;
    self.firstName = ko.observable(data.FirstName);
    self.lastName = ko.observable(data.LastName);
    self.fullName = ko.pureComputed(function() {
        return self.firstName() + ' ' + self.lastName();
    });
}

function ViewModelA(){
    var self = this;

    self.selectedArtist = ko.observable(null);    

    self.artists = ko.observableArray(ko.utils.arrayMap(data,
        function (artist) {
            return new ArtistVm(artist);
        });
    });

    self.viewModelB = ko.observable(new ViewModelB(self));
}

更新:工作解决方案:

你想做什么?

我需要将视图模型分解成更小的功能部分,以便将每个主要功能划分为它自己的功能(视图模型)。

你遇到了什么问题?

该应用程序包含一个 Select Option Binding,我需要从另一个视图模型访问所选值,以便我可以使用该值作为参数来使用不同的视图模型执行其他绑定。

但是,我无法访问Selected Value,当我终于解决了这个问题时,又出现了另一个问题,这次Select Options Binding如果之前有任何依赖绑定将无法工作。

因为我使用选定值作为参数,如果使用参数的任何依赖绑定被放置在 select 选项 HTML 之上,代码将无法运行。 select 选项必须放在第一位:

解法:

HTML:

   <!-- Binding with View Model B --> 
    <div id="artist-detail" data-bind="with: ArtistBio()[0]">
    <p data-bind="text: ArtistId"></p>
    <p data-bind="text: Bio"></p>
    </div>

   <!-- Binding with View Model A -->
    <div id="artist-list">
        <select data-bind="options: Artists, 
    optionsText: 'FullName', value: SelectedArtist"></select>
    </div>

查看模型A:

 function ViewModelA(){

     var self = this;

     self.SelectedArtist = ko.observable();

     var artistList = [{
         "ArtistId": "1",
         "LastName": "Secada",
         "FirstName": "Jon"
     },

         {
             "ArtistId": "2",
             "LastName": "Dion",
             "FirstName": "Celine"

         }];


     self.Artists = ko.computed(function () {
         return ko.utils.arrayMap(artistList,

       function (artist) {

          return {

         FullName: artist.FirstName + ' ' + artist.LastName,
            value: artist.ArtistId
      }
        });
     });
 }

查看模型 B:

 function ViewModelB(vma){

     var self = this;

     var artistDetail = [{
         "ArtistId": "1",
         "Bio": "Jon Secada is a Cuban American singer and songwriter. " +
         "Secada was born in Havana, Cuba, and raised in Hialeah, Florida. " +
         "He has won two Grammy Awards and sold 20 million albums since his " +
         "English-language debut album in 1992"
     },

         {
             "ArtistId": "2",
             "Bio": "Céline Marie Claudette Dion, CC OQ ChLD is a Canadian " +
             "singer, songwriter, businesswoman and occasional actress."
         }];

     self.ArtistBio = ko.computed(function () {
         if(vma.SelectedArtist()){
             return ko.utils.arrayFilter(artistDetail, function (item) {

                 // Access the selected value here and return matching actist
                 return item.ArtistId === vma.SelectedArtist().value;
             });
         }
     });
 }

执行两个视图模型的绑定:

 var viewModelA = new ViewModelA();
 var viewModelB = new ViewModelB(viewModelA);

 ko.applyBindings(viewModelA, document.getElementById('artist-list'));
 ko.applyBindings(viewModelB, document.getElementById('artist-detail'));

使用此代码,我将 select 列表放在哪里并不重要,因为我首先调用并绑定到视图模型 A。

这是我学习的第四周KnockoutJS所以请随时改进答案。

这是一个带有工作示例的 JSFiddle