0

我无法完全理解如何做到这一点。我从 Julie Lerman 的“BreezyDevices”解决方案开始学习,并以她的 javascript 视图模型为基础。

我有:

//properties and methods to expose via this class
var vm = {
    game: ko.observableArray([]),
    save: function () {
        dataservice.saveChanges();
    },
    reset: function () { dataservice.reset(getAllGames) },

};

在视图模型的顶部,这将在一个数组中返回我的每个游戏。一切正常。“游戏”具有相关数据,该数据返回一个名为“Sets”的数组,其中包含“ourscore”和“theirscore”作为属性。

在我的 html 页面上,我想绑定作为“游戏”实体的一部分返回的具体数据库属性,但我还想为每个游戏创建一个“结果”属性,该属性是基于循环每个设置分数的 javascript 函数计算的,并且相应地返回值。

我尝试在微风“Todo”解决方案中使用布局,并立即在上面的代码下进行设置:

initVm();

function initVm() {
    addComputeds();
}

function addComputeds() {
    vm.result = ko.computed(function () {
        var ourSets = getResult().ourSets;
        var theirSets = getResult().theirSets;

        if (ourSets == 0 && theirSets == 0) {
            return "No Result";
        }
        return (ourSets > theirSets ? "Won " : "Lost ") + "<b>" + ourSets.toString + "</b>-" + theirSets.ToString;
    });
}


function getResult() {
    var ourSets = 0;
    var theirSets = 0;

    vm.game().forEach(function (game) {
        for (var gs in game.Sets) {
            if (gs.ourScore > gs.theirScore) {
                ourSets +=1;
            }
            else {
                theirSets +=1;
            }               
        }
    });

    return {
        ourSets: ourSets,
        theirSets: theirSets
    };
}

但在我看来,好像它会为视图模型(vm)而不是每个游戏实体添加一个“结果”?此外,当我运行代码时,它不会出错,但它不会在我能看到的任何地方创建“结果”属性,而且似乎无法正常工作。

当我在这里添加它时再次查看它,我可以看到它是错误的,因为它需要处理每个特定的游戏实体来计算每个结果而不是游戏数组(所以我需要 vm.games.result 中的东西而不是vm.result),但我对此太陌生,无法理解如何处理每个单独的游戏实体。我的 .net 编码大脑会让我将循环中的每个游戏实体传递给一个函数以返回该游戏的结果,但我不知道它是否也适用于微风/淘汰赛。

我到处搜索,但我似乎无法找到符合我要求的相关示例,所以非常感谢一些指点!


@BeaverProj

我有一个 main.js 文件,其中发生了这种情况:

(function (root) {
    var app = root.app;

    app.logger.info('Please wait... data loading');

    ko.applyBindings(app.gameViewModel, $("content").get(0));

    $(".view").css({ display: 'block' });
}(window));

现在已将顶部部分编辑为:

var vm = {
    game: ko.observableArray([]),
    save: function () {
        dataservice.saveChanges();
    },
    reset: function () { dataservice.reset(getAllGames) },
    result: ko.computed(function () {
        var gameRes = getResult();
        var ourSets = gameRes.ourSets;
        var theirSets = gameRes.theirSets;

        if (ourSets == 0 && theirSets == 0) {
            return "No Result";
        }
        return (ourSets > theirSets ? "Won " : "Lost ") + "<b>" + ourSets + "</b>-" + theirSets;
        })
};

"getResult" 现在引用 "app.gameViewModel.game().forEach(function (Game) {" 而不是 "vm..."

和以前一样 - 没有错误,但也没有结果。我仍然得到“游戏”的数组,但没有别的。上面的视图模型对我来说仍然是错误的......“结果”应该附加到游戏实体(vm.game)而不是 vm - 目前这将给出 vm.result 并且每个游戏都有一个结果(所以 vm. game.result),而不是每个游戏数组。这就是为什么我想知道是否需要通过微风扩展实体。我可以在普通的 javascript 中做到这一点,但似乎微风或淘汰赛应该能够更容易做到这一点?

4

4 回答 4

0

不要出汗。继续探索。我们都在学习。

我不知道将它放在 ViewModel (VM) 还是实体中是否更好。没有内在的正确答案。

假设您希望在实体中使用它(因为从 Breeze 的角度来看这更有趣)。下一个问题:你需要它是可观察的吗?

您写道“这是一个简单的独立计算结果,仅用于显示目的。页面上不会发生任何会影响结果的更改,它已经发生了。 ”这表明它不需要是可观察的,因此您不需要一个 KO 计算。您最近的解决方案甚至没有将其作为属性呈现;我猜你的虚拟机中的某些东西正在调用getResult().

这使我认为您可能更喜欢注册自定义构造函数并放置getResult该构造函数的原型:

var 游戏 = 函数 () { }

Game.prototype.getResult = function () {
  var 我们的 = 0;
  var 他们的 = 0;
  this.sets().forEach(function (s) {
    (s.ourScore() > s.theirScore()) ?我们的 += 1 : 他们的 += 1;
  });
  返回(我们的||他们的)?
      (我们的>他们的?“赢了”:“输了”)+我们的+“-”+他们的:
      “没有结果”;
}

store.registerEntityTypeCtor("游戏", 游戏); // ctor 但没有初始化器

另一方面,如果您认为分数可能会改变,并且您希望屏幕按照他们所做的那样更新,您可能希望将逻辑移动到results计算的 KO 中。ourScoretheirScoreobservables 应该保持计算的results最新。我在这里大声思考,没有尝试过。

您将在初始化程序而不是 ctor中定义results计算。为什么?因为 KO observables 必须附加到实例,而不是原型。如果您在 ctor 中定义它,Breeze 可能会尝试序列化并更改跟踪它。最好将它附加到初始化程序中的实体上;Breeze 忽略初始化器添加的实体成员。

进一步考虑,也许我getResult会将方法保留在原型中的位置并编写一个添加了 ko 计算的初始化程序......就像这样(警告:未测试):

功能游戏初始化器(游戏){
   game.results = ko.computed(function() { return game.getResults();});
}

store.registerEntityTypeCtor("游戏", Game, gameInitializer);

现在是严肃的一点:这个逻辑应该在数据服务中吗?

出于您自己陈述的原因,我不会在我的数据服务中使用它。这种逻辑表达了模型固有的关注点,与管理数据访问无关。可以在演示的数据服务中将所有内容混合在一起。在“真实”代码中,将其分解为model.js组件(我认为Game是 Model 组件,而不是 ViewModel 组件)并在 dataservice.js 和 viewmodel.js 脚本标签之间加载该脚本。

于 2013-01-22T05:41:34.190 回答
0

您的构造后初始化程序与此问题的答案非常相似,即使用 ko 映射插件 doc'd @ http://knockoutjs.com/documentation/plugins-mapping.html

您将使用映射配置添加一个创建处理程序来附加您想要的计算结果。就像是

var mapping = {
  game : {
    create : function(options) {
            var game = options.data;
            game.results = ko.computed( function(){ 
              //your result sets calc here
            }  );

            return game;
        }
  }
}

更好的是定义您自己的 GameVM 类型并将其放在那里。

function GameVM( GameData )
{
    var self = this;
    this.Sets = ko.mapping.fromJS( GameData.Sets );
    this.Results = ko.computed( function(){
         self.Sets()...
    } );

}

和一个映射

 var mapping = {
      game : {
        create : function(options) {
               return new GameVM( options.data );
      }
    }

这更干净,并且它使您的游戏 VM 更易于测试,因为它是一个较小的测试单元。

查看映射文档以真正了解它如何通过自定义更新回调帮助您的 reset() 调用。

于 2013-01-22T05:42:55.323 回答
0

好的,经过一番折腾,我取得了一些进展。对不起,沃德(如果你读到了这篇文章)——你的文档可能对有经验的编码员很好,但对新手不好!)

dataservice.js 的顶部现在是这样的:

var manager = new breeze.EntityManager(serviceName);
var store = manager.metadataStore;

var Game = function () {
    //this.result = "Won 3-2";

};

var gameInitializer = function (game) {
    //game.result = "Won 3-2";
    game.result = function () {
        return getResult(game);
    }();
 };

store.registerEntityTypeCtor("Game", Game, gameInitializer);

和 getResult 函数:

function getResult(game) {
    var ourSets = 0;
    var theirSets = 0;

    game.Sets().forEach(function (gs) {
        if (gs.ourScore() > gs.theirScore()) {
            ourSets += 1;
        }
        else {
            theirSets += 1;
        }
    }
    );
    if (ourSets == 0 && theirSets == 0) {
                return "No Result";
    }
    else {
            return (ourSets > theirSets ? "Won " : "Lost ") + ourSets + "-" + theirSets;
    }
}

显然,这在微风中被称为“构造后初始化程序”,并且在这种情况下似乎是满足我需求的一个很好的解决方案,因为我不需要对结果做任何事情,它是一个简单的独立计算结果和仅用于显示目的。页面上没有任何改变会影响结果,它已经发生了。

我仍然不知道这是否是最(甚至唯一)这样做的方式,或者我是否可以通过淘汰赛甚至标准 javascript 来获得类似的结果。在微风中执行它似乎可以处理游戏实体的传递,然后公开相关的“集合”实体。只是必须小心引用带有括号的属性,否则一切都会失败。

但是,我不喜欢让数据服务充满特定于模型的构造函数、初始化程序和函数的想法。在我看来,我所做的大部分工作都属于“vm.game.js”文件,而不是目前的通用“dataservice.js”文件。

我现在将尝试将代码转移过来。我可能需要一些时间来制定参考资料!

于 2013-01-21T23:07:36.887 回答
0

一些东西:

  • 我相当确定您会希望在视图模型定义中声明您的结果计算函数。把它放在reset功能下就行了。

  • 在您共享的代码中,您尚未初始化视图模型或与 knockout.js 共享它。你需要在这样的地方打电话:

    var gameViewModel = new vm(); ko.applyBindings(gameViewModel);

  • 更改后,在您的 getResult() 方法中,您将引用 gameViewModel(实例)而不是 vm 变量(类定义)。

  • 最后一件事不应该影响它是否工作太多是你应该只在你的计算结果中调用一次 getResult() 并将返回值分配给一个变量。否则,您将计算两次,这是一种资源浪费,并且在技术上可能会在调用之间发生变化。

于 2013-01-20T15:22:14.743 回答