2016-12-27 33 views
1

这个问题会稍微具体一点,所以请随便以更宽泛的方式回答,或者指向正确的方向。动态生命线 - 动态观察对象和依赖关系

我拿起Knockout.js前几天,因为它解决了我在我的应用程序事先有一个问题,虽然这样说,这么新的图书馆往往使更多的问题......

我正在尝试为GM(Game Master)应用程序制作一个简单的PC(可玩角色)管理器。我从一个json对象数组中加载所有的PC信息(稍后将来自一个数据库),并将它们放在引导标签面板中以便于维护。

我想要创建的第一件事是为每个玩家提供一个健康栏,用于增加和减少hp的控件以及一个X/Y简单文本视图。这些组件中的每一个都应该独立于另一个玩家的组件,并且应该在提交输入框中的值时动态更新。

我遇到的问题是在淘汰赛方面而不是简单的JavaScript方面。我将在稍后解释。

这里是我的代码:

〜JSON阵列数据:

var initialData = [ 
    { 
     id: 0, 
     pcName: "Player 1", 
     hp: 12, 
     curHp: 12, 
     name: "Dudebro One", 
     playerClass: "Ranger", 
     level: 1, 
     background: "", 
     race: "Elf - Wood", 
     Alignment: "", 
     exp: 700, 
     inspiration: 0, 
     proficiencyBonus: 0 
    }, 
    { 
     id: 1, 
     pcName: "Player 2", 
     hp: 10, 
     curHp: 10, 
     name: "Brodude Two", 
     playerClass: "Fighter", 
     level: 1, 
     background: "Soldier", 
     race: "Gnome", 
     Alignment: "", 
     exp: 700, 
     inspiration: 0, 
     proficiencyBonus: 0 
    } 
]; 

(注:数据是不完整的可读性也承担n的玩家数量,而不是2)

〜淘汰赛代码(FAR from complete):

var PCModel = function (pcs) { 
    var self = this; 

    var currentHp = ko.observable(10); 
    var maximumHp = ko.observable(10); 

    self.pcsList = ko.observableArray(ko.utils.arrayMap(pcs, function (pc) { 
     return { 
      id: pc.id, pcName: pc.pcName, hp: pc.hp, curHp: pc.curHp, name: pc.name, playerClass: pc.playerClass, level: pc.level, background: pc.background, race: pc.race, Alignment: pc.Alignment, exp: pc.exp, 
      inspiration: pc.inspiration, proficiencyBonus: pc.proficiencyBonus 
     }; 
    })); 

    //TODO: REMOVE (Note: Here just for testing). 
    self.myFunction= function(pc){ 
     currentHp--; 
    }; 

    self.getHpPercentage = function (pc) { 
     var hpRound = Math.round((pc.curHp/pc.hp) * 100); 
     return hpRound + "%" 
    } 

    self.hpClass = function (pc) { 
     var hp = currentHp()/maximumHp() * 100; 
     if (hp >= 70) { 
      return 'progress-bar-success'; 
     } else if (hp < 70 && hp >= 30) { 
      return 'progress-bar-warning'; 
     } else if (hp < 30) { 
      return 'progress-bar-danger'; 
     } 
    }; 
}; 
ko.applyBindings(new PCModel(initialData)); 

否让我解释一下自己。我知道,要动态更新的东西,我需要定义observables或手动订阅的东西。我决定走可观察的路线。例如,我的问题是var maximumHp = ko.observable(10);对我来说没有意义,因为我拥有json数组中所需的全部数据。此外,对于正在查看/处理的CURRENT对象,maximumHp必须如此。我不知道如何定义?

〜我用结合的HTML代码(同样,无处接近完成对):

<div style="float:right; margin-top:25px; width: 65vw;"> 
<div class="container"> 
    <ul class="nav nav-tabs" id="sortable" data-bind="foreach: pcsList"> 

     <li data-bind="css: {active: $index() == 0 }" > 
      <a data-bind="attr: {href: '#tab' + id}, text: pcName" data-toggle="tab"></a> 
     </li> 
    </ul> 
    <div class="container-border-cup"> 
     <div class="tab-content " data-bind="foreach: pcsList"> 

      <div class="tab-pane tabbed-content-style" data-bind="attr: {id: 'tab' + id}, css: {active: $index() == 0 }"> 
       <h3 data-bind="text: name"></h3> 

       <div style="float:left; font-size:15pt; font-weight:500; line-height:35px; padding-right:20px; min-width:100px;"> 
        <span data-bind="text: curHp"></span>/<span data-bind="text: hp"></span> 
       </div> 

       <button data-bind="click: $root.myFunction"> 
        Click me 
       </button> 

       <div class="progress" style="width: 50%; height: 35px; float:left"> 
        <div class="progress-bar" style="float:left;" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" data-bind="text: $root.getHpPercentage($data), attr:{class: 'progress-bar ' + $root.hpClass($data)}, style:{width: $root.getHpPercentage($data)}"></div> 
       </div> 


       <div class="input-group" style="padding-left:20px"> 
        <span class="input-group-btn"> 
         <input type="button" class="btn btn-danger" id="btnToggleHP" value="-" style="width: 35px; font-weight:900;" /> 
        </span> 
        <input id="inModHP" style="width:70px" type="text" class="form-control" value="0"> 
        <span class="input-group-btn" style="float:left"> 
         <input type="button" class="btn btn-default" id="btnGoHP" value="Go" /> 
        </span> 
       </div> 
      </div> 
     </div> 
    </div> 
</div> 
</div> 

前面的所有代码的散发出以下效果: enter image description here

(不要担心ulgy按钮,这是只用于测试的按钮)。理想情况下,用户最终可以点击红色的减号按钮,并在绿色加号和绿色加号之间切换,以确定从健康池中增加或减少。单击Go按钮应该级联更新所有内容。

尽管我可能会将最终有用的东西拼凑在一起,但我想在此学习一些良好的做法。

感谢您阅读我小说中的一个问题,如果您愿意放弃一个答案,更要感谢您!

回答

1

如果您正在实施的行为与每个玩家相关,则应该将该逻辑放在PlayerViewModel中,并为每个玩家设置一个模型。您可以跟踪另一个视图模型中的模型集合。我们称之为App

App,我们只做一个件事:

  1. 以JSON数组并为在它的每个对象创建一个新的PlayerViewModel

当我们创建一个new Player,我们做几件事情:

  1. 首先,我们复制所有的静态JSON性能给我们新的播放器实例。您可以手动执行此操作,就像您在代码中已经完成的操作一样,也可以自动执行此操作,例如我使用Object.assign

  2. 然后,我们覆盖一些属性使用ko.observable s。 您希望能够在UI中更改的每个属性都必须是可观察的。所以,如果你想要改变一个名字,你也必须添加它作为一个可观察对象。

  3. 现在我们可以添加我们自己的可观察值和计算值。例如:如果玩家可以更改curHp,我们可以自动计算HP百分比。

  4. 最后,每Player通过Player.prototype获得一组功能。例如:有一个函数将input字段中的值添加到curHp。现在

,因为所有的信息和逻辑是Player模型里面,你可以轻松地将数据绑定,而不必跟着玩家参考通过,而无需使用$parent$root

var Player = function(playerData) { 
 
    // Make a copy of all properties to the new player 
 
    // These are static properties, suitably for one-way 
 
    // data-binding. 
 
    Object.assign(this, playerData); 
 
    
 
    // Create observables for two-way properties 
 
    // (properties you want to change via the UI) 
 
    this.curHp = ko.observable(playerData.curHp); 
 
    this.subtractValue = ko.observable(0); 
 
    
 
    // Create computed values for the UI 
 
    this.hpPercentage = ko.pureComputed(function() { 
 
    return (this.curHp()/playerData.hp * 100).toFixed(1); 
 
    }, this); 
 
    
 
    this.healthWarning = ko.pureComputed(function() { 
 
    var hpPerc = this.hpPercentage(); 
 
    if (hpPerc === 0) return "Dead"; 
 
    if (hpPerc < 30) return "Watch out"; 
 
    if (hpPerc < 70) return "Steady"; 
 
    return "OK"; 
 
    }, this); 
 
}; 
 

 
// Add functions 
 
Player.prototype.addHp = function() { 
 
    var val = parseFloat(this.subtractValue()); 
 
    var newHP = Math.min(
 
    Math.max(this.curHp() + val, 0), this.hp); 
 
    
 
    this.curHp(newHP); 
 
}; 
 

 
// A helper constructor 
 
Player.create = function(playerJson) { 
 
    return new Player(playerJson); 
 
}; 
 

 
var App = function(jsonData) { 
 
    this.players = jsonData.map(Player.create); 
 
}; 
 

 

 
var initialData=[{id:0,pcName:"Player 1",hp:12,curHp:4,name:"Dudebro One",playerClass:"Ranger",level:1,background:"",race:"Elf - Wood",Alignment:"",exp:700,inspiration:0,proficiencyBonus:0},{id:1,pcName:"Player 2",hp:10,curHp:7,name:"Brodude Two",playerClass:"Fighter",level:1,background:"Soldier",race:"Gnome",Alignment:"",exp:700,inspiration:0,proficiencyBonus:0}]; 
 

 
ko.applyBindings(new App(initialData));
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script> 
 

 
<h2>Players</h2> 
 
<ul data-bind="foreach: players"> 
 
    <li> 
 
    <h3 data-bind="text: name"></h3> 
 
    <h4 data-bind="text: healthWarning"></h4> 
 
    <input disabled type="range" min="0" data-bind="value: curHp, attr: { max: hp }"> 
 
    <input type="number" min="-10" max="10" data-bind="value: subtractValue"> 
 
    <button data-bind="click: addHp">update HP</button> 
 
    <span data-bind="text: hpPercentage() + '%'"></span> 
 
    </li> 
 
</ul>

+0

我会给你接受的答案,因为,它做什么,我问,但是我来到了一个不同的解决方案。我最终决定做的是,当手动复制json属性时,我使用'pc.curHp'和'pc.hp'来创建maxhp和curhp observables,如下所示:'obCurHp:ko.observable( pc.curHp)'。然后,我制作了使用所述可观察值计算百分比和进度条宽度(bootstrap)的函数。这样,如果可观察性发生变化,依赖关系也会发生变化,从而一切都会被级联更新。感谢您的建议,但! –

+0

我觉得这基本上是我试图解释的。例如,在我的代码示例中,我写道:'this.curHp = ko.observable(playerData.curHp);'。无论如何,很高兴你知道了! – user3297291