扩展实体或添加ko.computed以返回不在数据库中的实体中的其他属性

时间:2013-01-20 11:55:44

标签: knockout.js breeze

我无法理解如何做到这一点。我开始使用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) },

};

位于viewmodel的顶部,这将返回数组中的每个游戏。一切正常。 “游戏”有相关数据,返回一个名为“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
    };
}

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

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

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


@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中做到这一点,但似乎微风或淘汰赛应该能够做到这一点更容易吗?

3 个答案:

答案 0 :(得分:0)

一些事情:

  • 我很确定你会希望在视图模型定义中声明你的结果计算函数。只需将其置于重置功能下即可。

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

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

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

  • 最后一件不应该影响它是否工作太多的事情是你应该只在计算结果中调用getResult()并将返回值赋给变量。否则你计算它两次,这是浪费资源,技术上可能会在不同的通话之间改变。

答案 1 :(得分:0)

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

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

让我们假设你想要它在实体中(因为从Breeze的角度来看,这更有趣)。下一个问题:你需要它是可观察的吗?

你写了“它是一个简单的独立计算结果,仅用于显示目的。页面上没有任何内容可以改变,影响结果,已经发生了。”这表明它不需要观察,所以你不需要计算KO。您最近的解决方案甚至没有将其作为财产呈现;我猜你的VM正在调用getResult()

这让我觉得您可能更喜欢注册自定义构造函数并将getResult放在该构造函数的原型上:

var Game = function () { }

Game.prototype.getResult = function () {
  var ours = 0;
  var theirs = 0;
  this.sets().forEach(function (s) {
    (s.ourScore() > s.theirScore()) ? ours += 1 : theirs += 1;
  });
  return (ours || theirs) ? 
      (ours > theirs ? "Won " : "Lost ") + ours + "-" + theirs :
      "No result";
}

store.registerEntityTypeCtor("Game", Game); // ctor but no initializer

另一方面,如果您认为分数可能会发生变化并且您希望屏幕更新,那么您可能希望将逻辑移动到results KO计算中。 ourScoretheirScore可观察量应使results计算得到最新。我在这里大声思考,没有尝试过。

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

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

function gameInitializer(game) {
   game.results = ko.computed(function() { return game.getResults();});
}

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

现在是严肃的一点: 这个逻辑应该在dataservice 吗?

我不会在我的 dataservice 中找到它,因为你说的就是这个原因。这种逻辑表达了模型固有的问题,与管理数据访问无关。可以在演示中的 dataservice 中将它们全部放在一起。在“真实”代码中,可以将其分解为 model.js 组件(我将Game视为模型组件,而不是ViewModel组件)并在dataservice之间加载该脚本。 js和viewmodel.js脚本标记。

答案 2 :(得分:0)

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

您将使用映射配置添加创建处理程序以附加您想要的计算结果observable。

之类的东西
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 );
      }
    }

这更干净,它使你的游戏虚拟机更容易测试,因为它是一个较小的单元来测试。

签出映射文档,真正了解它如何通过自定义更新回调来帮助您重置()调用。