I see a lot of event objects when I use the alert after the ajax post success in my jquery below.
How I can access the details for each event and change them accordingly?
eventSources: [
{
url: 'json-events.php',
type: 'POST',
error: function (data) {
alert('there was an error while fetching events!' + data.msge);
},
success: function (data) {
alert(data);
// how do i loop through the event objects and filter which ones are approved == 1?
if(data.approved == "1") {
// How do I then change the properties of each event that is approved?
event.title = title + "approved";
event.Color = 'green';
var event.className = "approved";
} else{
var event.title = title + "awaiting approval";
event.Color = 'red';
var event.className = "unapproved";
}
}
}]
Update: change color of single event once approved
// approve function
$('.approve').click(function (calEvent, jsEvent, view, getid) {
// Get id of event
var getid = $('.approve').attr('id');
if ($(this).html() == "yes") {
// AJAX post to insert into DB
$.post("ajax.php", {
action: 'approve',
color: 'green'
},
function (data) {
var fancyContent = ('<div class="header"><p>' + data.msge + '</p></div>');
$.fancybox({
content: fancyContent
});
}, "json");
// get event
var eventObject = $('#calendar').fullCalendar( 'clientEvents', getid );
// set event colors
eventObject.backgroundColor = 'green';
eventObject.eventBorderColor = 'green';
eventObject.title = event.title + " approved";
// update event somehow?
$('#calendar').fullCalendar('updateEvent', eventObject);
} else {
// close fancybox
$.fancybox.close();
} // end of if
}); // end of approve click
You can loop through you JSON response like this:
success : function( responseData ) {
$.each( function( index, responseObj ) {
if( responseObj.approved === "1" ) {
responseObj.title += " approved";
responseObj.className = "approved";
}
else {
responseObj.title += " waiting approval";
responseObj.className = "unapproved";
}
} );
}
You will not be able to set the color of each event type using this style of filtering.
You could break up each type into their own event sources, something like:
eventSources : [
{
url : 'json-events.php?approved=y',
color : 'green'
// ... success() and other attributes go here
},
{
url : 'json-events.php?approved=n',
color : 'red'
// ... success() and other attributes go here
}
]
Related
I'm working on an app where I get a json via an ajax call. This json contains objects where you get a certain status code per extension (1 = online, 2, is ringing, 3 = busy)
How can I ensure that the value that I get back is converted to the text (preferably with a different color of the )
So when I get a 1 back I want it to show Online, and with a 2 Ring etc
$.ajax({
type:'GET',
url: url,
dataType: 'json',
error: function(jqXHR, exception) {ajax_error_handler(jqXHR, exception);},
success: function(data){
// console.log(JSON.parse(data.responseText));
// console.log(JSON.parse(data.responseJSON));
console.log(data['entry']);
var event_data = '';
$.each(data.entry, function(index, value){
/* console.log(data['entry']);*/
event_data += '<tr>';
event_data += '<td>'+value.extension+'</td>';
event_data += '<td>'+value.status+'</td>';
<!--event_data += '<td>'+value.registration+'</td>';-->
event_data += '</tr>';
});
$("#list_table_json").append(event_data);
},
error: function(d){
/*console.log("error");*/
alert("404. Please wait until the File is Loaded.");
}
});
Thanks in advance!
I have change the code
function get_blf() {
$.ajax({
type:'GET',
url: url,
dataType: 'json',
error: function(jqXHR, exception) {ajax_error_handler(jqXHR, exception);},
success: function(data){
$.each(data.entry, (index, value) => {
const tableRow = document.createElement('tr');
const tdExtension = document.createElement('td');
extension.textContent = value.status;
const tdStatus = document.createElement('td');
if (value.status == 3) status.textContent = 'Busy';
if (value.status == 2) status.textContent = 'Ringing';
if (value.status == 1) status.textContent = 'Online';
tdStatus.classList.add(`status-${value.status}`);
tableRow.appendChild(tdExtension);
tableRow.appendChild(tdStatus);
$('#list_table_json').append(tableRow);
}
});
}
}
and add the css, but now i can't get any values back. but now i can't get any values back. (sorry I'm fairly new to javascript)
Please use the DOM API
One way of getting colors would be to use CSS classes for the status:
// js
...
$.each(data.entry, (index, value) => {
const tableRow = document.createElement('tr');
const tdExtension = document.createElement('td');
extension.textContent = value.extension;
const tdStatus = document.createElement('td');
if (value.status == 3) status.textContent = 'Busy';
if (value.status == 2) status.textContent = 'Ringing';
if (value.status == 1) status.textContent = 'Online';
tdStatus.classList.add(`status-${value.status}`);
tableRow.appendChild(tdExtension);
tableRow.appendChild(tdStatus);
$('#list_table_json').append(tableRow);
});
...
// css
.status-1 {
color: green;
}
.status-2 {
color: red;
}
.status-3 {
color: orange;
}
I finally got the script working. I am now trying to build in a polling, however I see that the ajax call is executed again and the array is fetched. However, the table is not refreshed but a new table is added, does anyone know a solution for this?
code I'm using now for the repoll is
function repoll(poll_request, poll_interval, param=null) {
if (poll_interval != 0) {
if (window.timeoutPool) {
window.timeoutPool.push(setTimeout(function() { poll_request(param); }, poll_interval));
}
else {
setTimeout(function() { poll_request(param); }, poll_interval);
}
}
else {
log_msg('Poll cancelled.');
}
}
tableRow.appendChild(tdExtension);
tableRow.appendChild(tdNr);
tableRow.appendChild(tdStatus);
$('#list_table_json').append(tableRow);
});
repoll(get_blf, poll_interval_blf);
I am implementing a custom solution that will initialize kendoGrid (and its multiselect columns) by applying filters to the grid using query string parameters. I am using Kendo UI v2014.2.903 and multiselectbox user extension.
To achieve this, I have a custom JavaScript function that parses query string parameters into filter object and applies it to kendoGrid's dataSource property using its filter method. Code snippet below:
var preFilterQueryString = getPreFilterQuery(queryString);
if (preFilterQueryString != '') {
kendoGrid.dataSource.filter(parseFilterString(preFilterQueryString));
}
I am initializing multiselectbox as follows:
args.element.kendoMultiSelectBox({
dataSource: getAutoCompleteDataSource(column.field),
valuePrimitive: true,
selectionChanged: function (e) {
//ignored for brevity
}
});
My problem is if I set filters to multiselectbox using above approach, they are not applied correctly to the data set.
For example, if I pass single filter as "Vancouver", correct result set is displayed. Choices in the multiselectbox are "All" and "Vancouver". However, Vancouver choice is not checked in the multiselectbox. Is that by design? Please see image below.
single filter image
If I pass in two filters Vancouver and Warsaw, only the last filter Warsaw is applied to the grid and data set containing only Warsaw is displayed. Again, none of the choices are checked in the multiselectbox.
two filters image
Below is the filter object that is applied to dataSource.filter() method.
kendoGrid's filter object image
Troubleshooting
Below is condensed version (for brevity) of selectionchanged event handler of a multiselectbox column.
if (e.newValue && e.newValue.length) {
var newValue = e.newValue;
console.log('e.newValue: ' + e.newValue);
filter.filters = [];
for (var i = 0, l = newValue.length; i < l; i++) {
filter.filters.push({field: field,operator: 'contains',value: newValue[i]});
}
kendoGrid.dataSource.filter(allFilters);
}
I noticed that when two filters are passed, e.newValue is "Warsaw" instead of an array - "[Vancouver, Warsaw]"
I spent lot of time troubleshooting why only Warsaw is applied to the data set.
Here's what I found out:
_raiseSelectionChanged function is what raises selection changed event. In that function, I noticed that it sets newValue and oldValue event arguments. To set newValue it uses "Value" function. Value function uses the below code to retrieve all the selected list items and return them.
else {
var selecteditems = this._getSelectedListItems();
return $.map(selecteditems, function (item) {
var obj = $(item).children("span").first();
return obj.attr("data-value");
}).join();
}
_getSelectedListItems function uses jQuery filter to fetch all list items that have css class ".selected"
_getSelectedListItems: function () {
return this._getAllValueListItems().filter("." + SELECTED);
},
There's a _select event which seems to be adding ".selected" class to list items. When I put a breakpoint on line 286, it is hitting it only once and that is for Warsaw. I am unable to understand why it is not getting called for Vancouver. I am out of clues and was wondering if anyone has pointers.
debug capture of _select function
Below is kendoMultiSelectBox user extension for your reference:
//MultiSelect - A user extension of KendoUI DropDownList widget.
(function ($) {
// shorten references to variables
var kendo = window.kendo,
ui = kendo.ui,
DropDownList = ui.DropDownList,
keys = kendo.keys,
SELECT = "select",
SELECTIONCHANGED = "selectionChanged",
SELECTED = "k-state-selected",
HIGHLIGHTED = "k-state-active",
CHECKBOX = "custom-multiselect-check-item",
SELECTALLITEM = "custom-multiselect-selectAll-item",
MULTISELECTPOPUP = "custom-multiselect-popup",
EMPTYSELECTION = "custom-multiselect-summary-empty";
var lineTemplate = '<input type="checkbox" name="#= {1} #" value="#= {0} #" class="' + CHECKBOX + '" />' +
'<span data-value="#= {0} #">#= {1} #</span>';
var MultiSelectBox = DropDownList.extend({
init: function (element, options) {
options.template = kendo.template(kendo.format(lineTemplate, options.dataValueField || 'data', options.dataTextField || 'data'));
// base call to widget initialization
DropDownList.fn.init.call(this, element, options);
var button = $('<input type="button" value="OK" style="float: right; padding: 3px; margin: 5px; cursor: pointer;" />');
button.on('click', $.proxy(this.close, this));
var popup = $(this.popup.element);
popup.append(button);
},
options: {
name: "MultiSelectBox",
index: -1,
showSelectAll: true,
preSummaryCount: 1, // number of items to show before summarising
emptySelectionLabel: '', // what to show when no items are selected
selectionChanged: null // provide callback to invoke when selection has changed
},
events: [
SELECTIONCHANGED
],
refresh: function () {
// base call
DropDownList.fn.refresh.call(this);
this._updateSummary();
$(this.popup.element).addClass(MULTISELECTPOPUP);
},
current: function (candidate) {
return this._current;
},
open: function () {
var self = this;
this._removeSelectAllItem();
this._addSelectAllItem();
if ($(this.ul).find('li').length > 6) {
$(this.popup.element).css({ 'padding-bottom': '30px' });
}
else {
$(this.popup.element).css({ 'padding-bottom': '0' });
}
DropDownList.fn.open.call(this);
//hook on to popup event because dropdown close does not
//fire consistently when user clicks on some other elements
//like a dataviz chart graphic
this.popup.one('close', $.proxy(this._onPopupClosed, this));
},
_onPopupClosed: function () {
this._removeSelectAllItem();
this._current = null;
this._raiseSelectionChanged();
},
_raiseSelectionChanged: function () {
var currentValue = this.value();
var currentValues = $.map((currentValue.length > 0 ? currentValue.split(",") : []).sort(), function (item) { return item.toString(); });
var oldValues = $.map((this._oldValue || []).sort(), function (item) { return item ? item.toString() : ''; });
// store for next pass
this._oldValue = $.map(currentValues, function (item) { return item.toString(); });
var changedArgs = { newValue: currentValues, oldValue: oldValues };
if (oldValues) {
var hasChanged = ($(oldValues).not(currentValues).length == 0 && $(currentValues).not(oldValues).length == 0) !== true;
if (hasChanged) {
//if (this.options.selectionChanged)
// this.options.selectionChanged(changedArgs);
this.trigger(SELECTIONCHANGED, changedArgs);
}
}
else if (currentValue.length > 0) {
//if (this.options.selectionChanged)
// this.options.selectionChanged(changedArgs);
this.trigger(SELECTIONCHANGED, changedArgs);
}
},
_addSelectAllItem: function () {
if (!this.options.showSelectAll) return;
var firstListItem = this.ul.children('li:first');
if (firstListItem.length > 0) {
this.selectAllListItem = $('<li tabindex="-1" role="option" unselectable="on" class="k-item ' + SELECTALLITEM + '"></li>').insertBefore(firstListItem);
// fake a data object to use for the template binding below
var selectAllData = {};
selectAllData[this.options.dataValueField || 'data'] = '*';
selectAllData[this.options.dataTextField || 'data'] = 'All';
this.selectAllListItem.html(this.options.template(selectAllData));
this._updateSelectAllItem();
this._makeUnselectable(); // required for IE8
}
},
_removeSelectAllItem: function () {
if (this.selectAllListItem) {
this.selectAllListItem.remove();
}
this.selectAllListItem = null;
},
_focus: function (li) {
if (this.popup.visible() && li && this.trigger(SELECT, { item: li })) {
this.close();
return;
}
this.select(li);
},
_keydown: function (e) {
// currently ignore Home and End keys
// can be added later
if (e.keyCode == kendo.keys.HOME ||
e.keyCode == kendo.keys.END) {
e.preventDefault();
return;
}
DropDownList.fn._keydown.call(this, e);
},
_keypress: function (e) {
// disable existing function
},
_move: function (e) {
var that = this,
key = e.keyCode,
ul = that.ul[0],
down = key === keys.DOWN,
pressed;
if (key === keys.UP || down) {
if (down) {
if (!that.popup.visible()) {
that.toggle(down);
}
if (!that._current) {
that._current = ul.firstChild;
} else {
that._current = ($(that._current)[0].nextSibling || that._current);
}
} else {
//up
// only if anything is highlighted
if (that._current) {
that._current = ($(that._current)[0].previousSibling || ul.firstChild);
}
}
if (that._current) {
that._scroll(that._current);
}
that._highlightCurrent();
e.preventDefault();
pressed = true;
} else {
pressed = DropDownList.fn._move.call(this, e);
}
return pressed;
},
selectAll: function () {
var unselectedItems = this._getUnselectedListItems();
this._selectItems(unselectedItems);
// todo: raise custom event
},
unselectAll: function () {
var selectedItems = this._getSelectedListItems();
this._selectItems(selectedItems); // will invert the selection
// todo: raise custom event
},
_selectItems: function (listItems) {
var that = this;
$.each(listItems, function (i, item) {
var idx = ui.List.inArray(item, that.ul[0]);
that.select(idx); // select OR unselect
});
},
_selectItem: function () {
// method override to prevent default selection of first item, done by normal dropdown
var that = this,
options = that.options,
useOptionIndex,
value;
useOptionIndex = that._isSelect && !that._initial && !options.value && options.index && !that._bound;
if (!useOptionIndex) {
value = that._selectedValue || options.value || that._accessor();
}
if (value) {
that.value(value);
} else if (that._bound === undefined) {
that.select(options.index);
}
},
_select: function (li) {
var that = this,
value,
text,
idx;
li = that._get(li);
if (li && li[0]) {
idx = ui.List.inArray(li[0], that.ul[0]);
if (idx > -1) {
if (li.hasClass(SELECTED)) {
li.removeClass(SELECTED);
that._uncheckItem(li);
if (this.selectAllListItem && li[0] === this.selectAllListItem[0]) {
this.unselectAll();
}
} else {
li.addClass(SELECTED);
that._checkItem(li);
if (this.selectAllListItem && li[0] === this.selectAllListItem[0]) {
this.selectAll();
}
}
if (this._open) {
that._current(li);
that._highlightCurrent();
}
var selecteditems = this._getSelectedListItems();
value = [];
text = [];
$.each(selecteditems, function (indx, item) {
var obj = $(item).children("span").first();
value.push(obj.attr("data-value"));
text.push(obj.text());
});
that._updateSummary(text);
that._updateSelectAllItem();
that._accessor(value, idx);
// todo: raise change event (add support for selectedIndex) if required
that._raiseSelectionChanged();
}
}
},
_getAllValueListItems: function () {
if (this.selectAllListItem) {
return this.ul.children("li").not(this.selectAllListItem[0]);
} else {
return this.ul.children("li");
}
},
_getSelectedListItems: function () {
return this._getAllValueListItems().filter("." + SELECTED);
},
_getUnselectedListItems: function () {
return this._getAllValueListItems().filter(":not(." + SELECTED + ")");
},
_getSelectedItemsText: function () {
var text = [];
var selecteditems = this._getSelectedListItems();
$.each(selecteditems, function (indx, item) {
var obj = $(item).children("span").first();
text.push(obj.text());
});
return text;
},
_updateSelectAllItem: function () {
if (!this.selectAllListItem) return;
// are all items selected?
if (this._getAllValueListItems().length == this._getSelectedListItems().length) {
this._checkItem(this.selectAllListItem);
this.selectAllListItem.addClass(SELECTED);
}
else {
this._uncheckItem(this.selectAllListItem);
this.selectAllListItem.removeClass(SELECTED);
}
},
_updateSummary: function (itemsText) {
if (!itemsText) {
itemsText = this._getSelectedItemsText();
}
if (itemsText.length == 0) {
this._inputWrapper.addClass(EMPTYSELECTION);
this.text(this.options.emptySelectionLabel);
return;
} else {
this._inputWrapper.removeClass(EMPTYSELECTION);
}
if (itemsText.length <= this.options.preSummaryCount) {
this._textAccessor(itemsText.join(", "));
}
else {
this._textAccessor(itemsText.length + ' selected');
}
},
_checkItem: function (itemContainer) {
if (!itemContainer) return;
itemContainer.children("input").prop("checked", true);
},
_uncheckItem: function (itemContainer) {
if (!itemContainer) return;
itemContainer.children("input").removeAttr("checked");
},
_isItemChecked: function (itemContainer) {
return itemContainer.children("input:checked").length > 0;
},
value: function (value) {
if(value != undefined)
console.log("value", value);
var that = this,
idx,
valuesList = [];
if (value !== undefined) {
if (!$.isArray(value)) {
valuesList.push(value);
this._oldValue = valuesList; // to allow for selectionChanged event
}
else {
valuesList = value;
this._oldValue = value; // to allow for selectionChanged event
}
// clear all selections first
$(that.ul[0]).children("li").removeClass(SELECTED);
$("input", that.ul[0]).removeAttr("checked");
$.each(valuesList, function (indx, item) {
var hasValue;
if (item !== null) {
item = item.toString();
}
that._selectedValue = item;
hasValue = value || (that.options.optionLabel && !that.element[0].disabled && value === "");
if (hasValue && that._fetchItems(value)) {
return;
}
idx = that._index(item);
if (idx > -1) {
that.select(
that.options.showSelectAll ? idx + 1 : idx
);
}
});
that._updateSummary();
}
else {
var selecteditems = this._getSelectedListItems();
return $.map(selecteditems, function (item) {
var obj = $(item).children("span").first();
return obj.attr("data-value");
}).join();
}
},
});
ui.plugin(MultiSelectBox);
})(jQuery);
UPDATE
I came across documentation for multiselectbox's select event that clearly states that the select event is not fired when an item is selected programmatically. Is it relevant to what I am trying to do? But why is it firing for one of the filters even though I am setting them programmatically?
enter link description here
I was wondering if you could help.
I have the below code which sets a timeout delay before making a http request. The watch is bound to a input box. This is currently in my controller and it works.
$scope.$watch('query.keyword',function($http){
var searchInput = document.getElementById('searchInput').value;
var minLength = 3;
var req;
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(function(){
var newValue = searchInput;
if(newValue !== null && newValue.length > minLength) {
window.alert(newValue);
req = {
method: 'SET',
url: ''
};
}
}, 3000);
return $http(req);
});
Now I want this as a factory/service to call upon rather than listing it in my controller.
I then made this...
app.factory('sendSearchData', function($http) {
var searchInput = document.getElementById('searchInput').value;
var minLength = 3;
var req = null;
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(function(){
var newValue = searchInput;
if(newValue !== null && newValue.length > minLength) {
window.alert(newValue);
req = {
method: 'SET',
url: 'haha.php'
};
}
}, 3000);
return function() {
if($http !== null) {
return $http(req);
} else { return 0; }
};
});
Not sure if the return is right, as the previous return was flagging up a http null error.
So to use it I've done a few variations of the below code in my controller.
$scope.$watch('query.keyword', sendSearchData.success());
But I am having no luck and it's refusing to render.
Can anyone help?
I'm new at fumbling with Swagger, so I might be asking a silly question. Is it in any way possible to prevent the site from crashing whenever it is "unable to read from api"?
My site is working most of the time, but if there for some reason is an api that is unreadable (or just unreachable) swagger just stop working. It still displays the api's it managed to reach, but all functionality is completely gone its not even able to expand a row.
To summarize:
How do I prevent swagger from crashing, when one or more API's is unreadable and returns something like this:
Unable to read api 'XXXX' from path
http://example.com/swagger/api-docs/XXXX (server
returned undefined)
Below is my initialization of Swagger:
function loadSwagger() {
window.swaggerUi = new SwaggerUi({
url: "/frameworks/swagger/v1/api.json",
dom_id: "swagger-ui-container",
supportedSubmitMethods: ['get', 'post', 'put', 'delete'],
onComplete: function (swaggerApi, swaggerUi) {
log("Loaded SwaggerUI");
if (typeof initOAuth == "function") {
initOAuth({
clientId: "your-client-id",
realm: "your-realms",
appName: "your-app-name"
});
}
$('pre code').each(function (i, e) {
hljs.highlightBlock(e);
});
},
onFailure: function (data) {
log("Unable to Load SwaggerUI");
},
docExpansion: "none",
sorter: "alpha"
});
$('#input_apiKey').change(function () {
var key = $('#input_apiKey')[0].value;
log("key: " + key);
if (key && key.trim() != "") {
log("added key " + key);
window.authorizations.add("api_key", new ApiKeyAuthorization('api_key', key, 'header'));
}
});
$('#apiVersionSelectID').change(function () {
var sel = $('#apiVersionSelectID').val();
window.swaggerUi.url = sel;
$('#input_baseUrl').val(sel);
$('#explore').click();
});
window.swaggerUi.load();
};
I was searching for a solution to this problem too but could not find one. Here is a quick hack i did to solve the problem. Hope it can be of help to someone who is having the same trouble.
In swagger-client.js Find the function error: function (response) {
I replaced the return api_fail with addApiDeclaration to make it draw the api with some limited information even when it fails. I send in a dummy api json object with the path set to "/unable to load ' + _this.url. I send in an extra parameter that can be true or false, where true indicates that this is a failed api.
Old code:
enter cerror: function (response) {
_this.api.resourceCount += 1;
return _this.api.fail('Unable to read api \'' +
_this.name + '\' from path ' + _this.url + ' (server returned ' +response.statusText + ')');
}
New code
error: function (response) {
_this.api.resourceCount += 1;
return _this.addApiDeclaration(JSON.parse('{"apis":[{"path":"/unable to load ' + _this.url + '","operations":[{"nickname":"A","method":" "}]}],"models":{}}'), true);
}
I modified the addApiDeclaration function in the same file to display a different message for a failed api by first adding a secondary parameter to it called failed and then an if statement that check if failed is true and then change the name of the api to "FAILED TO LOAD RESOURCE " + this.name. This adds the FAILED TO LOAD RESOURCE text before the failed api.
Old code
SwaggerResource.prototype.addApiDeclaration = function (response) {
if (typeof response.produces === 'string')
this.produces = response.produces;
if (typeof response.consumes === 'string')
this.consumes = response.consumes;
if ((typeof response.basePath === 'string') && response.basePath.replace(/\s/g, '').length > 0)
this.basePath = response.basePath.indexOf('http') === -1 ? this.getAbsoluteBasePath(response.basePath) : response.basePath;
this.resourcePath = response.resourcePath;
this.addModels(response.models);
if (response.apis) {
for (var i = 0 ; i < response.apis.length; i++) {
var endpoint = response.apis[i];
this.addOperations(endpoint.path, endpoint.operations, response.consumes, response.produces);
}
}
this.api[this.name] = this;
this.ready = true;
if(this.api.resourceCount === this.api.expectedResourceCount)
this.api.finish();
return this;
};
New code
SwaggerResource.prototype.addApiDeclaration = function (response, failed) {
if (typeof response.produces === 'string')
this.produces = response.produces;
if (typeof response.consumes === 'string')
this.consumes = response.consumes;
if ((typeof response.basePath === 'string') && response.basePath.replace(/\s/g, '').length > 0)
this.basePath = response.basePath.indexOf('http') === -1 ? this.getAbsoluteBasePath(response.basePath) : response.basePath;
this.resourcePath = response.resourcePath;
this.addModels(response.models);
if (response.apis) {
for (var i = 0 ; i < response.apis.length; i++) {
var endpoint = response.apis[i];
this.addOperations(endpoint.path, endpoint.operations, response.consumes, response.produces);
}
}
if (failed == true) {
this.name = "FAILED TO LOAD RESOURCE - " + this.name;
}
this.api[this.name] = this;
this.ready = true;
if(this.api.resourceCount === this.api.expectedResourceCount)
this.api.finish();
return this;
};
Thanks in advance for any help. I have spent a couple of weeks scouring the web for some insight. I have been developing code for over 50 years but I am a fairly new JavaScript, HTML, knockout. From what I see, this will be great if I can figure out how to make it work. The example given below is only one of the many things I have tried. Please advise.
I have defined two variables as observables, one computed observable, and one observableArray in my view model. Within the document.ready function, I make an Ajax call which returns a json in object notation. ( I checked it in the debugger). When my HTML page displays the observables and computed observables show up properly. The
observable array generates an error (see below) and then displays data obtained from only the first row returned from Ajax. (two were returned).
How do I adjust my code so that all the rows in the Ajax data are shown in the displayed HTML?
Here is the error message that I get:
Uncaught ReferenceError: Unable to process binding "foreach: function (){return cartItems }"
Message: Unable to process binding "text: function (){return itemSubTotal() }"
Message: itemSubTotal is not defined (19:41:39:172 | error, javascript)
Here is my global data for the view model:
var cartDB = "";
var totalPrice = "100";
var cartItems = new Array;
Here is the view model:
var ViewModel =function (){
// direct items ==
this.cartDB = ko.observable(cartDB);
// array itesm
// this.cartItems = ko.observableArray(cartItems);
this.cartItems = ko.mapping.fromJS(cartItems);
//for (var n = 1;n++;n<cartItems.length){ this.cartItems.push(cartItem[n]);}
// computed items
this.totalPriceSv = ko.computed(function(){
return "Total Price*=" + centsToDollars(totalPrice);// todo fix
} ,this);//end totalSvPrice
};// end ViewModel
The data is obtained from the following routine which calls on Ajax.This routine is called once from within document.ready and obtains the expected data on the success callback.
function databaseCart(commandInput, cartDBInput, cartPidInput,logPhpInput) {
var postData = new Array();
postData[0] = commandInput;
postData[1] = cartDBInput;
postData[2] = cartPidInput;
postData[3] = logPhpInput; //debug log on php side
data = null; //for return values
$.ajax({
type: "GET",
url: 'ww_CartProcess.php', //the script to call to get data
data: {data: postData},
dataType: 'json',
success: function(data) {
cartItems = data;
debug = 0;
},
complete: function(data) {
ko.applyBindings(new ViewModel);
return TRUE;
},
error: function(x, e) {//this is the error function for ajax
var xErr = x;
var eErr = e;
ajaxErrorProcessing(xErr, eErr, "addToCart", postData);
}
});
}// end databasecart
Here is the HTML 5 snippet.
<div>
<h1 id="cartTitle">Cart Number: <span data-bind="text: cartDB"> </h1>
<div class ="boxCartItem" data-bind="foreach:cartItems" >
<div class ="boxItemTitle">
<h2 data-bind="text: title()"></h2>
</div><!--boxItemTitle-->
<div class ="cartItemBottom"></div>
</div ><!--class ="boxCartItem"-->
My thanks to the commenters. I still do not know how to add an element to all item rows in an observable array, but this problem was caused by not having the item listed defined. Clue> When multiple errors are presented it is sometimes ( and maybe always) good to work from the bottom up.
The problem can be better stated as : Given a 2 x 17 array (2 rows and 17 columns of independent vars)create an observableArray that contains 2 rows and 17 plus columns consisting of the 17 independent variables (can only be changed in the database or by limited user input) augmented with a large number of computed functions .
1.0 I created an orderModel which contained the ko.computed(functions() for each dependent variable.
function rowOrder(data) {
var self = this;
var model = ko.mapping.fromJS(data, {}, self);
model.imagePathSv = ko.computed(function() {
var category = self.category();
var imageName = self.imageName();
var sv ="";
var sv = "products\\" +category+"\\" + imageName;
return sv;
});//end model.imagePathSv
//original offer
model.origNumValidate = ko.computed(function() {
ans = self.origNum();
if (ans < 0 | ans > 1) {
alert("\
Only onw Original can be pruchased");
ans = 1;
}
self.origNum(ans);
return ans;
});//originalNumValidate
model.origNumLabel = ko.computed(function() {
var sv = "Original..." + centsToDollars(self.origCost());
return sv;
});//end model.origNumLabel
model.origMattedShow = ko.computed(function() {
if (self.origMattedCost() > 0) {
return true;
}
else {
return false;
}
});
model.origMattedLabel = ko.computed(function() {
var sv = "Matting...." + centsToDollars(self.origMattedCost());
return sv;
});
model.origFramedShow = ko.computed(function() {
if (self.origFramedCost() > 0) {
return true;
}
else {
return false;
}
});
model.origFramedLabel = ko.computed(function() {
var sv = "Framing...." + centsToDollars(self.origFramedCost());
return sv;
});
//reproductons offer
model.reproNumValidate = ko.computed(function() {
ans = self.reproNum();
self.reproNum(ans);
return ans;
});
model.reproNumLabel = ko.computed(function() {
var sv = "Reproductions." + centsToDollars(self.reproCost()) + " each";
return sv;
});//end model.reproNumLabel
model.reproMattedShow = ko.computed(function() {
if (self.reproMattedCost() > 0) {
return true;
}
else {
return false;
}
});
model.reproMatted = ko.observable(true);
model.reproMattedLabel = ko.computed(function() {
var sv ="Matting...." +centsToDollars(self.reproMattedCost());
return sv;
});
model.reproFramedShow = ko.computed(function(){
if(self.reproFramedCost()>0){return true;}
else {return false;}
});
model.reproFramed = ko.observable(true);
model.reproFramedLabel = ko.computed(function() {
var sv ="Framing...." +centsToDollars(self.reproFramedCost());
return sv;
});
//pricing subTotals
model.productsSubTotal = ko.computed(function() {
var ans =self.origNum() * self.origCost() + self.reproNum() * self.reproCost();
return ans;
});//end model.productsSubTotal
model.productsSubTotalSv = ko.computed(function() {
return "Products.." +centsToDollars(model.productsSubTotal());
return ans;
});//end model.productsSubTotal
model.mattingSubTotal = ko.computed(function() {
return self.origNum() * self.origMattedCost() + self.reproNum() * self.reproMattedCost();
});//end model.mattingSubTotal
model.mattingSubTotalSv = ko.computed(function() {
return "Matting...." +centsToDollars(model.mattingSubTotal());
});//end model.mattingSubTotal
model.framingSubTotal = ko.computed(function() {
return self.origNum() * self.origFramedCost() + self.reproNum() * self.reproFramedCost();
});//end model.framingSubTotal
model.framingSubTotalSv = ko.computed(function() {
return "Framing...." +centsToDollars(model.framingSubTotal());
});//end model.productsSubTotal
model.rowSubTotal = ko.computed(function() {
return model.productsSubTotal() +model.mattingSubTotal() + model.framingSubTotal();
});//end model.rowsSubTotal
model.rowSubTotalSv = ko.computed(function() {
return "Item Subtotal.." +centsToDollars(model.rowSubTotal());
});//end model.productsSubTotal
};// END rowOrder
2.0 I created a mapping variable as follows:
var mapping = {
create: function(options) {
return new rowOrder(options.data);
}
};
3,0 I created a View Model as follows:
function ViewModel() {
// direct items for whole page
var self = this;
this.cartId = ko.observable(cartDB);
this.cartIdSv = ko.computed(function() {
var sv = "Cart Number: "+ self.cartId();
return sv;
},this);//
this.totalPrice = ko.computed(function() {//to DO COMPUTE
var int = 10000;
return int;
},this);
this.totalPriceSv = ko.computed(function(){
return "Total Price*: " + centsToDollars(this.totalPrice());
},this);
// by row items
this.cartItems = ko.mapping.fromJS(cartItems, mapping);
}// end ViewModel
4.0 In the success call back from ajax :
success: function(data) {
cartItems = data;
ViewModel();
5.0 I put the ko.apply.bindings(new ViewModel) in the ajax complete callback.
The result was that my rather involved page came up as expected with the computed values initially set.
I am still working on how to update this page. I have not been able to get my code to recompute the computed variables when the user clicks or unclicks a checkbox.
I could not have done this without suggestions from the stackOverflow group. Thanks to you all for the posts that I found all over the google.