2016-12-27 70 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