Reputation: 12933
So consider the following:
(function($, ko){
var ViewModel = function() {
var self = this;
self.errorMessages = ko.observableArray([]);
self.desigItems = ko.observableArray([]);
self.frequencies = ko.observableArray([]);
}
var viewModel = new ViewModel();
var loadData = function() {
$.ddcApiGet('/widget/desigs.json', [], function(data) {
_processLoadedData(data);
}, viewModel.errorMessages);
}
var loadFrequencies = function() {
$.ddcApiGet('/widget/freqs.json', [], function(data) {
_processFrequenciesData(data);
}, viewModel.errorMessages);
}
var _processLoadedData = function(data) {
for (var i = 0; i < data.desig_options.length; i++) {
viewModel.desigItems.push(data.desig_options[i]);
}
}
var _processFrequenciesData = function(data) {
var frequencies = [];
// Get the list of frequencies.
for (var i = 0; i < data.frequency.length; i++) {
frequencies.push(data.frequency[i]);
}
// Push frequencies on to each of the design items in the list.
for (var i = 0; i < viewModel.desigItems.length; i++) {
viewModel.desigItems[i]['frequencies'] = frequencies
}
console.log(viewModel.desigItems());
}
var preBind = function() {
loadData();
loadFrequencies();
}
preBind();
ko.applyBindings(viewModel, $('#designnation-list-container')[0]);
})(jQuery, ko);
Which is then associated with the following view:
<h1>Hello World</h1>
<div id="designnation-list-container" data-bind="foreach: desigItems">
<div class="donor-js-wid-desig-opt donor-style-box donor-style-2col">
<h2 class="donor-js-desig-desc" data-bind="text: desc"> </h2>
<img class="donor-js-desig-logo donor-style-donation-image" alt="Web Desc 10000 - adding this" src="https://d36vh9gkg2fzwi.cloudfront.net/imageserver/be1b7b8e195587ff422c40750a6caa9f/10000_IPHONE_v1.JPG">
<form method="post" enctype="multipart/form-data" action="javascript:">
<input class="donor-js-wid-desig" type="hidden" name="gift_desig" value="10000">
<p class="donor-js-desig-shorttext" data-bind="html: shortdesc"> </p>
<p id="donor-js-donation-amount-label">
<label class="donor-style-label">Donation Amount</label>
</p>
<p>
<div data-bind="foreach: amount_options">
<label class="donor-js-amount-choice">
<input type="radio" class="donor-js-webware" name="gift_amt" data-bind="attr: {value: desc}, checked: selected"><span class="donor-js-currency-symbol">$</span> <span class="donor-js-amount" data-bind="text: desc"></span> <span class="donor-js-input-currency"></span><br>
</label>
</div>
<label>
<input type="radio" class="donor-js-other-amount-opt" name="gift_amt" value="other">
<span id="donor-js-other-label">Amount:</span>
<span class="donor-js-currency-symbol">$</span></label>
<input class="donor-js-other-amount-input" type="text" name="other_amt" onclick="$(this).closest('form').find('input[class=donor-js-other-amount-opt]').attr('checked', true);"> <span class="donor-js-input-currency"></span>
</p>
<p>
<select class="donor-js-wid-frequency" name="gift_freq" data-bind="options: frequencies, optionsText: desc, value: desc"></select>
</p>
<p id="donor-js-gift-notes" style="display:none">
<label class="donor-js-gift-note-label">Gift Notes
<textarea name="gift_note" class="donor-style-select" rows="2" cols="40"></textarea>
</label>
</p>
<p><input type="submit" class="donor-js-dona-submit button" value="Add to Cart"><input type="button" class="donor-js-desig-info donor-style-buttonlink" value="show more details">
</p></form>
<div class="donor-style-clearb"></div>
</div>
</div>
Its a lot to take in I know but I am having issues with one specific function: _processFrequenciesData
See the issue is that I have a select element:
<p>
<select class="donor-js-wid-frequency" name="gift_freq" data-bind="options: frequencies, optionsText: desc, value: desc"></select>
</p>
As you may see from the initial view I posted, this select element is inside a loop that loops over the self.desigItems
There is a console.log
in this function (_processFrequenciesData
) but it returns: []
Which you would think, ok maybe the data isn't fetched, accept it is at this point, because 7 items get rendered to the screen.
What I am trying to do is say, ok: add this list of frequencies
to each of the desigItems
So why is it when I console.log(viewModel.desigItems)
I get an empty array back, yet when the view walks over the desigItems, I get seven things back?
Upvotes: 0
Views: 33
Reputation: 338406
An observableArray
is not an array. It is a function (!) that wraps an array (just as an observable
is not a value, but a function that wraps a value). To retrieve the actual value an observable must be called:
// Push frequencies on to each of the design items in the list.
var desigItems = viewModel.desigItems(); // access the underlying array
for (var i = 0; i < desigItems.length; i++) {
desigItems[i].frequency = frequencies[i];
}
viewModel.desigItems.notifySubscribers();
console.log(viewModel.desigItems());
What you did was adding properties to the observable itself, but that does not affect the value the observable contains.
Now, since we have changed the underlying value of viewModel.desigItems
without giving knockout a chance to notice, we must call notifySubscribers()
to inform everyone that the observable has changed.
That being said, I would rewrite your entire code like so:
(function($, ko){
function ViewModel() {
var self = this;
self.errorMessages = ko.observableArray();
self.desigItems = ko.observableArray();
self.frequencies = ko.observableArray();
}
var viewModel = new ViewModel();
$(function () {
ko.applyBindings(viewModel, $('#designnation-list-container')[0]);
});
var desigsReq = $.ddcApiGet('/widget/desigs.json').done(viewModel.desigItems);
var freqsReq = $.ddcApiGet('/widget/freqs.json').done(viewModel.frequencies);
$.when(desigsReq, freqsReq).done(function (desigs, freqs) {
desigs.forEach(function (desig, i) {
desig.frequency = freqs[i];
});
}).fail(viewModel.errorMessages);
})(jQuery, ko);
This fixes two timing issues in your code:
Whether it's actually useful to combine the two arrays in this manner is a different matter. I'd say it's not a very clean solution to begin with.
P.S.: You really could have made this question shorter, virtually none of the code you have posted has anything to do with the issue at hand.
Upvotes: 1
Reputation: 11344
So why is it when I
console.log(viewModel.desigItems())
I get an empty array back, yet when the view walks over the desigItems, I get seven things back?
You have the console.log(viewModel.desigItems)
call inside the _processFrequenciesData
callback function.
However, you do not populate viewModel.desigItems
there - it's populated in the _processLoadedData
callback function instead.
That might be a simple error. You might just want to move that console.log(...)
into the other callback.
The way it is now, you will have an empty console output (as you are seeing) when the response for loadFrequencies
is processed before the response for loadData
.
Upvotes: 1