每次使用单独的选择框时,我都需要更新observableArray。问题在于酵母......如果用户选择“丹星”作为酵母的类别,“酵母品种”现在应该只显示
Danstar
Danstar 1
Danstar 2
其中“Danstar”是optgroup。我的想法是抓住
{category: 'Danstar', yeasts: ['Danstar 1', 'Danstar 2']}
仅并将其设置为yeast_groups_individual
。我需要每一对“Yeast Brand Filter”和“Yeast Variety”独立运作。我到目前为止:
ko.observableArray.fn.countVisible = function(){
return ko.computed(function(){
var items = this();
if (items === undefined || items.length === undefined){
return 0;
}
var visibleCount = 0;
for (var index = 0; index < items.length; index++){
if (items[index]._destroy != true){
visibleCount++;
}
}
return visibleCount;
}, this)();
};
function Hop(data) {
this.name = ko.observable(data.name || "");
this.amount = ko.observable(data.amount || "");
this.time = ko.observable(data.time || "");
this.use = ko.observable(data.use || "Boil");
}
function Fermentable(data) {
this.name = ko.observable(data.name || "");
this.pounds = ko.observable(data.pounds || "");
this.ounces = ko.observable(data.ounces || "");
this.weight_unit = ko.observable(data.weight_unit || "oz");
this.milling_preference = ko.observable(data.milling_preference || "Milled");
}
function Yeast(data){
var self = this;
self.name = ko.observable(data.name || "White Wine");
self.current_filter = ko.observable("-Any-");
var yeast_options = [
{category: 'Danstar', yeasts: ['Danstar 1', 'Danstar 2']},
{category: 'Fermentis', yeasts: ['West Coast', 'American Saison', 'White Wine']},
{category: 'White Labs', yeasts: ['White 1', 'White Saison']},
];
self.yeast_groups_individual = ko.observableArray();
ko.computed(function(){
if (self.current_filter() !== "-Any-"){
var options = _.filter(yeast_options, function(option){
return option.category === self.current_filter();
})
} else{
self.yeast_groups_individual(yeast_options);
}
}
);
self.yeast_categories = ko.observableArray();
ko.computed(function(){
var starter_list = ['-Any-'];
var categories = _.pluck(self.yeast_groups_individual(), 'category');
var final = starter_list.concat(categories);
self.yeast_categories(final);
})
}
function TaskListViewModel() {
var self = this;
self.recipe_name = ko.observable("");
self.hops = ko.observableArray([new Hop({}), new Hop({})]);
self.fermentables = ko.observableArray([new Fermentable({name: 'test'}), new Fermentable({})]);
self.yeasts = ko.observableArray([new Yeast({})]);
self.hops_uses = ko.observableArray(['Boil', 'Dry Hop']);
self.weight_units = ko.observableArray(['oz', 'lb']);
self.milling_preferences = ko.observableArray(['Milled', 'Unmilled']);
self.addFermentable = function(){
self.fermentables.push(new Fermentable({}))
}
self.addYeast = function(){
self.yeasts.push(new Yeast({}));
}
self.addHop = function(){
self.hops.push(new Hop({}));
}
self.removeFermentable = function(fermentable){
self.fermentables.destroy(fermentable);
}
self.removeYeast = function(yeast){
self.yeasts.destroy(yeast);
}
self.removeHop = function(hop){
self.hops.destroy(hop);
}
self.removeItem = function(item, name){
name.remove(function(hop){
return hop.name === item.name;
});
console.log(name());
console.log(self.hops());
}
self.prepareJSON = function(){
object = {
fermentables: self.fermentables(),
hops: self.hops(),
yeasts: self.yeasts(),
}
return object
}
self.saveRecipeData = function(){
var data_to_send = ko.toJSON(self);
var recipe_data = ko.toJSON(self.prepareJSON());
alert("This is the data you're sending (universal Javascript object notation):\n\n" + recipe_data)
$.ajax({
url: "http://127.0.0.1:5000/receive-recipe",
headers: {
"Content-Type": "application/json"
},
method: "POST",
dataType: "json",
data: data_to_send,
success: function(data){
console.log("Success! Saved the recipe");
}
});
}
}
ko.applyBindings(new TaskListViewModel());
<style>
.setuprow label {
margin: 8px 0 0;
display: block;
clear: both;
}
</style>
<script type='text/javascript' src='https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.1/knockout-min.js'></script>
<script type='text/javascript' src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js'></script>
<script type='text/javascript' src='https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js'></script>
<div class="setuprow">
<label>Recipe Name:</label>
<input type="text" data-bind="value: recipe_name" maxlength="250" class="recipeSetupText" />
</div>
<div class="setuprow">
<label>Brew Method:</label>
<select class="recipeSetupSelect">
<option value="extract" selected>Extract</option>
<option value="partialmash">Mini-Mash</option>
<option value="allgrain">All Grain</option>
<option value="biab">Brew-in-a-bag</option>
</select>
</div>
<div class="setuprow">
<label>Batch Size:</label>
<input type="text" value="5" maxlength="6" style="width: 60px" /> <span class="unit">(gallons)</span>
</div>
<div class="setuprow">
<label>Style:</label>
<select class="recipeSetupSelect">
<option value="--">--</option>
<option value="Standard American Beer">1. Standard American Beer</option>
<option value="International Lager">2. International Lager</option>
<option value="Czech Lager">3. Czech Lager</option>
<option value="Pale Malty European Lager">4. Pale Malty European Lager</option>
<option value="Pale Bitter European Beer">5. Pale Bitter European Beer</option>
<option value="Amber Malty European Lager">6. Amber Malty European Lager</option>
<option value="Amber Bitter European Lager">7. Amber Bitter European Lager</option>
<option value="Dark European Lager">8. Dark European Lager</option>
</select>
</div>
<div>
<label>Boil Time:</label>
<input type="text" value="60" maxlength="3" style="width: 60px" /> <span class="unit">(minutes)</span>
</div>
<div class="row">
<h2>Fermentables</h2>
<div data-bind="foreach: fermentables">
<label>Name:</label>
<input type="text" data-bind="value: name"/>
<label>Amount:</label>
<input style="width: 45px;" type="number" min="0" data-bind="value: pounds"/> lb
<input style="width: 55px;" type="number" min="0" data-bind="value: ounces"/> oz
<label>Milling preference: </label>
<select data-bind="options: $root.milling_preferences, value: weight_unit">
</select>
<a href="#" data-bind="click: $root.removeFermentable, visible: $root.fermentables.countVisible() > 1">
Delete
</a>
<br><br>
</div>
<input data-bind="click: addFermentable" type="button" value="Add Fermentable" />
</div>
<div class="row">
<h2 class="">Yeast</h2>
<div data-bind="foreach: yeasts">
<span>Yeast Brand Filter:</span>
<select data-bind="options: yeast_categories, value: current_filter" id="yeast-brand-select">
</select>
<br/>
<span>Yeast Variety:</span>
<select id="yeast-variety-select" style="width:325px" data-bind="value: name">
<option value="-"> - </option>
<!-- ko foreach: yeast_groups_individual -->
<optgroup data-bind="attr: {label: category}">
<!-- ko foreach: yeasts -->
<option data-bind="value: $data, text: $data"></option>
<!-- /ko -->
</optgroup>
<!-- /ko -->
</select>
<a href="#" data-bind="click: $root.removeYeast, visible: $root.yeasts.countVisible() > 1">Delete</a>
<br><br>
</div>
<br>
<input data-bind="click: addYeast" type="button" value="Add Yeast"/>
</div>
<div class="row">
<h2 class="">Hops</h2>
<div data-bind='foreach: hops'>
<label>Hop:</label> <input type="text" data-bind="value: name" >
<label>Amount:</label>
<input type="number" data-bind="value: amount" maxlength="6"> oz
Time: <input type="text" data-bind="value: time" >
Min.
Use: <select data-bind="options: $root.hops_uses, value: use"></select>
<a href="#" data-bind="click: function() { $root.removeItem($data, $root.hops) }, visible: $root.hops.countVisible() > 1">Delete</a>
<br><br>
</div>
<br>
<input data-bind="click: addHop" type="button" value="Add Hop" />
</div>
<p>
<button data-bind="click: saveRecipeData">Save recipe</button>
</p>
如果更改yeast_groups_individual
,如何更新current_filter
可观察数组?谢谢
答案 0 :(得分:0)
我到目前为止找到的最佳答案是将主阵列永久地连接到ViewModel,并将其副本(yeast_options
)传递给每个酵母,现在它可以在其中播放阵列不会破坏其他酵母的行为:
ko.observableArray.fn.countVisible = function(){
return ko.computed(function(){
var items = this();
if (items === undefined || items.length === undefined){
return 0;
}
var visibleCount = 0;
for (var index = 0; index < items.length; index++){
if (items[index]._destroy != true){
visibleCount++;
}
}
return visibleCount;
}, this)();
};
function Hop(data) {
this.name = ko.observable(data.name || "");
this.amount = ko.observable(data.amount || "");
this.time = ko.observable(data.time || "");
this.use = ko.observable(data.use || "Boil");
}
function Fermentable(data) {
this.name = ko.observable(data.name || "");
this.pounds = ko.observable(data.pounds || "");
this.ounces = ko.observable(data.ounces || "");
this.weight_unit = ko.observable(data.weight_unit || "oz");
this.milling_preference = ko.observable(data.milling_preference || "Milled");
}
function Yeast(data){
var self = this;
var permanent_yeast_options = data.yeast_options;
self.name = ko.observable(data.name || "-");
self.current_filter = ko.observable("-Any-");
self.yeast_groups_individual = ko.computed(function(){
if (self.current_filter() !== "-Any-"){
var options = _.filter(data.yeast_options, function(option){
return option.category === self.current_filter();
});
return options;
} else{
return permanent_yeast_options;
}
}
);
self.yeast_categories = ko.observableArray();
ko.computed(function(){
var starter_list = ['-Any-'];
var categories = _.pluck(permanent_yeast_options, 'category');
var final = starter_list.concat(categories);
self.yeast_categories(final);
})
}
function TaskListViewModel() {
var self = this;
self.hops_uses = ko.observableArray(['Boil', 'Dry Hop']);
self.weight_units = ko.observableArray(['oz', 'lb']);
self.milling_preferences = ko.observableArray(['Milled', 'Unmilled']);
self.recipe_name = ko.observable("");
self.hops = ko.observableArray([new Hop({}), new Hop({})]);
self.fermentables = ko.observableArray([new Fermentable({name: 'test'}), new Fermentable({})]);
self.yeast_options = [
{category: 'Danstar', yeasts: ['Danstar 1', 'Danstar 2']},
{category: 'Fermentis', yeasts: ['West Coast', 'American Saison', 'White Wine']},
{category: 'White Labs', yeasts: ['White 1', 'White Saison']},
];
self.yeasts = ko.observableArray([new Yeast({yeast_options: self.yeast_options})]);
self.addFermentable = function(){
self.fermentables.push(new Fermentable({}))
}
self.addYeast = function(){
self.yeasts.push(new Yeast({yeast_options: self.yeast_options}));
}
self.addHop = function(){
self.hops.push(new Hop({}));
}
self.removeFermentable = function(fermentable){
self.fermentables.destroy(fermentable);
}
self.removeYeast = function(yeast){
self.yeasts.destroy(yeast);
}
self.removeHop = function(hop){
self.hops.destroy(hop);
}
self.removeItem = function(item, name){
name.remove(function(hop){
return hop.name === item.name;
});
console.log(name());
console.log(self.hops());
}
self.ingredient_filter = function(the_array){
var options = _.filter(the_array, function(item){
return item.name != "" && item.name != "-";
});
return options;
}
self.prepareJSON = function(){
object = {
fermentables: self.fermentables(),
hops: self.hops(),
yeasts: self.yeasts(),
}
return object
}
self.saveRecipeData = function(){
var data_to_send = ko.toJSON(self);
var recipe_data = ko.toJSON(self.prepareJSON());
alert("This is the data you're sending (universal Javascript object notation):\n\n" + recipe_data)
$.ajax({
url: "http://127.0.0.1:5000/receive-recipe",
headers: {
"Content-Type": "application/json"
},
method: "POST",
dataType: "json",
data: recipe_data,
success: function(data){
console.log("Success! Saved the recipe");
}
});
}
}
ko.applyBindings(new TaskListViewModel());
&#13;
<head>
<style>
body {
margin: 30px;
padding: 30px;
}
.setuprow label {
margin: 8px 0 0;
display: block;
clear: both;
}
</style>
<script type='text/javascript' src='https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.1/knockout-min.js'></script>
<script type='text/javascript' src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js'></script>
<script type='text/javascript' src='https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js'></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>
<body>
<div class="setuprow">
<label>Recipe Name:</label>
<input type="text" data-bind="value: recipe_name" maxlength="250" class="recipeSetupText" />
</div>
<div class="setuprow">
<label>Brew Method:</label>
<select class="recipeSetupSelect">
<option value="extract" selected>Extract</option>
<option value="partialmash">Mini-Mash</option>
<option value="allgrain">All Grain</option>
<option value="biab">Brew-in-a-bag</option>
</select>
</div>
<div class="setuprow">
<label>Batch Size:</label>
<input type="text" value="5" maxlength="6" style="width: 60px" /> <span class="unit">(gallons)</span>
</div>
<div class="setuprow">
<label>Style:</label>
<select class="recipeSetupSelect">
<option value="--">--</option>
<option value="Standard American Beer">1. Standard American Beer</option>
<option value="International Lager">2. International Lager</option>
<option value="Czech Lager">3. Czech Lager</option>
<option value="Pale Malty European Lager">4. Pale Malty European Lager</option>
<option value="Pale Bitter European Beer">5. Pale Bitter European Beer</option>
<option value="Amber Malty European Lager">6. Amber Malty European Lager</option>
<option value="Amber Bitter European Lager">7. Amber Bitter European Lager</option>
<option value="Dark European Lager">8. Dark European Lager</option>
</select>
</div>
<div>
<label>Boil Time:</label>
<input type="text" value="60" maxlength="3" style="width: 60px" /> <span class="unit">(minutes)</span>
</div>
<div class="row">
<h2>Fermentables</h2>
<div data-bind="foreach: fermentables">
<label>Name:</label>
<input type="text" data-bind="value: name"/>
<label>Amount:</label>
<input style="width: 45px;" type="number" min="0" data-bind="value: pounds"/> lb
<input style="width: 55px;" type="number" min="0" data-bind="value: ounces"/> oz
<label>Milling preference: </label>
<select data-bind="options: $root.milling_preferences, value: weight_unit">
</select>
<a href="#" data-bind="click: $root.removeFermentable, visible: $root.fermentables.countVisible() > 1">
Delete
</a>
<br><br>
</div>
<input data-bind="click: addFermentable" type="button" value="Add Fermentable" />
</div>
<div class="row">
<h2 class="">Yeast</h2>
<div data-bind="foreach: yeasts">
<span>Yeast Brand Filter:</span>
<select data-bind="options: yeast_categories, value: current_filter" id="yeast-brand-select">
</select>
<br/>
<span>Yeast Variety:</span>
<select id="yeast-variety-select" style="width:325px" data-bind="value: name">
<option value="-"> - </option>
<!-- ko foreach: yeast_groups_individual -->
<optgroup data-bind="attr: {label: category}">
<!-- ko foreach: yeasts -->
<option data-bind="value: $data, text: $data"></option>
<!-- /ko -->
</optgroup>
<!-- /ko -->
</select>
<a href="#" data-bind="click: $root.removeYeast, visible: $root.yeasts.countVisible() > 1">Delete</a>
<br><br>
</div>
<br>
<input data-bind="click: addYeast" type="button" value="Add Yeast"/>
</div>
<div class="row">
<h2 class="">Hops</h2>
<div data-bind='foreach: hops'>
<label>Hop:</label> <input type="text" data-bind="value: name" >
<label>Amount:</label>
<input type="number" data-bind="value: amount" maxlength="6"> oz
Time: <input type="text" data-bind="value: time" >
Min.
Use: <select data-bind="options: $root.hops_uses, value: use"></select>
<a href="#" data-bind="click: function() { $root.removeItem($data, $root.hops) }, visible: $root.hops.countVisible() > 1">Delete</a>
<br><br>
</div>
<br>
<input data-bind="click: addHop" type="button" value="Add Hop" />
</div>
<p>
<button data-bind="click: saveRecipeData">Save recipe</button>
</p>
</body>
&#13;