How do I dynamically pass a property reference as a method arguments ?
This is what ajax success function response data look like:
{
users: {
data: {}
},
countries: {
data: {}
},
states: {
data: {}
}
}
This is example how i store the data previously:
var users = ko.observable();
var countries = ko.observable();
var states = ko.observable();
var store = function(data, observable)
{
observable(data);
}
$.ajax({
//... ajax options...
success: function(response)
{
// This is how i store the data previously
store(response.users.data, users);
store(response.countries.data, countries);
store(response.states.data, states);
}
});
And this is example what I have try so far:
$.ajax({
//... ajax options...
success: function(response)
{
// This is how i want to achieve
ko.utils.objectForEach(response, function(key, data)
{
store(data.data, key);
});
}
});
But unfortunately I just only pass the text string to the 2nd arguments of store method.
Any help and suggestions would be appreciated!
Thank you.
Make them properties of an object, then use strings:
var obj = {
users: ko.observable(),
countries: ko.observable(),
states: ko.observable()
};
var store = function(data, observable)
{
var prop = obj[observable];
if (prop) { // Just being defensive
prop(data);
}
};
$.ajax({
//... ajax options...
success: function(response)
{
var key;
for (key in response) {
store(response[key].data, key);
}
}
});
Related
I'am using Knockout.js. I have a function like this:
function deviceGroupItem(item) {
this.DeviceGroupName = item.DeviceGroupName;
this.groupDevicesVisible = ko.observable(false)
this.groupDevicesArray = ko.observableArray();
this.deviceGroupClick = function () {
if (this.groupDevicesVisible() == false) {
this.groupDevicesVisible(true)
$.ajax({
url: returnServer() + '/api/Mobile/getRoomDevices?accessToken=' + localStorage.getItem('Token'),
type: "GET",
dataType: "json",
success: function (data) {
this.groupDevicesArray()($.map(data, function (item) {
return new roomDeviceItem(item);
}))
},
error: function () {
}
})
} else {
this.groupDevicesVisible(false)
}
}
return this;
}
Problem is, when I'am trying bind:
this.groupDevicesArray = ko.observableArray();
Using:
this.groupDevicesArray()($.map(data, function (item) {
return new roomDeviceItem(item);
}))
I'am receiving error "this.groupDevicesArray is not a function". Honestly, I dont know how to do this in correct way. Do You know how can I achieve that?
The issue is because of you referring observable Array with this inside the function deviceGroupClick which does not exist because this refers to current context .
This technique depends on current closure which is a pseudo variable
that may differ from scope to scope dynamically .
viewModel:
function roomDeviceItem(data) {
this.room = ko.observable(data.room)
}
function deviceGroupItem() {
var self=this; //Assign this to self & use self everywhere
self.groupDevicesArray = ko.observableArray();
self.deviceGroupClick = function () {
$.ajax({
url: '/echo/json/',
type: "GET",
dataType: "json",
success: function (data) {
data = [{
'room': 'One'
}, {
'room': 'Two'
}]
self.groupDevicesArray($.map(data, function (item) {
return new roomDeviceItem(item);
}))
}
});
};
};
ko.applyBindings(new deviceGroupItem());
working sample here
Just in-case if you are looking for solution with this you need to use bind(this) to get reference of outer closure check here
Try
this.groupDevicesArray($.map(data, function (item) {
return new roomDeviceItem(item);
}));
groupDevicesArray is observableArray and $.map returns an array.
{model : {"firstName":"David","lastName":"Bawa","state":"AL"}}
{"allStates":[{"id":1,"value":"AL","text":"ALABAMA"},{"id":2,"value":"AK","text":"ALASKA"}}
<select name="state" data-bind="options: allStates,optionsValue : 'value', optionsText: 'text' ,value: model.state,optionsCaption: 'Choose...'" id="ddlState"></select>
var registrationModel = {
staticData: function () {
var self = this;
self.allStates = ko.observableArray();
self.hearUsAll = ko.observableArray();
registrationService.getAllStates().done(function (result) {
if (result != null) {
$.each(result.List, function (i, v) {
self.allStates.push(v);
});
}
});
}
}
and my registration service is
var registrationService = {
getAllStates: function () {
return service.staticService.get('GetAllStates');
}}
and my static service is
var service={
staticService: {
get: function (method) {
var results = store.fetch(method);
if (results) {
var dfd = new $.Deferred();
dfd.resolve(results);
return dfd.promise();
}
return $.ajax({
type: 'GET',
url: "http://xxxx/Service1.svc/" + method,
dataType: "json",
success: function (result) { if (result) { store.save(method,result) } }
}).promise();
}
states are populating from server through ajax, it works fine if I wait for allstates to be fetched and then apply bindings.
as you can see I am getting a pre-selected value of state in model. But when I do not wait for states to be populated and call apply bindings it will not work. it changes my state variable to null. I am getting my view model from mapping plugin.
Please help.
I am trying to use JQPlot within a VB.NET application under .NET 3.5. On a button click, using jquery, I am trying to populate the JQPlot Chart with JSON derived data using a ASP.NET Webservices Source file (which is part of the solution).
The JSON data is sent by the web service but when it is presented to JQPlot I get the javascript error 'No Data Specified' which is generated by JQPlot code.
My code listing is as follows:
Code to listen for the button to be clicked:
$(document).ready(function () {
$('#<%=btnASMX1.ClientID%>').click(function () {
getElectricDataJSON();
return false;
});
});
Javascript code outside the 'document.ready' function:
function ajaxDataRenderer() {
var ret = null;
$.ajax({
// have to use synchronous here, else the function
// will return before the data is fetched
async: false,
//url: url,
type: 'POST',
contentType: 'application/json; charset=utf-8',
url: "AccountsService.asmx/GetJSONData",
data: "{AccountID: " + $('#<%= hiddenAccountID.ClientID%>').val() + " }",
dataType: "json",
success: function (response) {
var ret = response.d;
// The following two lines just display the JSON data for testing purposes
$('#<%=outputASMX.ClientID%>').empty();
$('#<%=outputASMX.ClientID%>').html("<div>" + ret + "</div>");
return ret;
},
error: function (request) {
$('#<%=outputASMX.ClientID%>').html("<div style='color:red;'>WEBSERVICE UNREACHABLE</div>");
}
});
return ret;
};
var jsonurl = "./jsondata.txt";
function getElectricDataJSON() {
var ret = ajaxDataRenderer();
var plot1 = $.jqplot('chart2', jsonurl, {
title: "AJAX JSON Data Renderer",
dataRenderer: ret, //$.jqplot.ciParser
dataRendererOptions: {
unusedOptionalUrl: jsonurl
}
});
}
The JSON data format is as follows:
[ { "todate": "2013-09-23T00:00:00", "Bill": 7095.65 }, { "todate": "2013-08-22T00:00:00", "Bill": 1137.96 }, { "todate": "2013-07-24T00:00:00", "Bill": 220429.41 }, ... ]
Any help or advice will be appreciated.
Thanks to #Fresh for their quick response. Here is the complete solution to my problem:
Code to listen for the button to be clicked:
$(document).ready(function () {
$('#<%=btnASMX1.ClientID%>').click(function () {
getElectricDataJSON();
return false;
});
});
JS function to get the data from a web service:
function ajaxDataRenderer() {
var ret = null;
$.ajax({
// have to use synchronous here, else the function
// will return before the data is fetched
async: false,
type: 'POST',
contentType: 'application/json; charset=utf-8',
url: "AccountsService.asmx/GetJSONData",
data: "{AccountID: " + $('#<%= hiddenAccountID.ClientID%>').val() + " }",
dataType: "json",
success: function (response) {
ret = response.d; // return response string object
},
error: function (request) {
$('#<%=outputASMX.ClientID%>').html("<div style='color:red;'>WEBSERVICE UNREACHABLE</div>");
}
});
return ret;
};
Data structure outputted by the web service is:
[ { "todate": "2013-09-23T00:00:00", "Bill": 7,095.65 }, { "todate": "2013-08-22T00:00:00", "Bill": 1,137.96 }, { "todate": "2013-07-24T00:00:00", "Bill": 220,429.41 }, ... ]
Data structure that is expected by JQPlot:
[ [ "2013-09-23T00:00:00", 7095.65 ] , [ "2013-08-22T00:00:00", 1137.96 ], [ "2013-07-24T00:00:00", 220429.41 ], ... ]
Note the removal of the comma's in the 'expected data' Bill field.
And finally, the function getElectricDataJSON() that is being called by btnASMX1 where 'chart2' is the ID of the div tags where the chart will be drawn.
function getElectricDataJSON() {
// Get JSON 'string' object
var ret = ajaxDataRenderer();
// If JSON string object is null, stop processing with friendly message
if (ret == null) {
$('#<%=outputASMX.ClientID%>').html("<div style='color:red;'>CHARTS ARE NOT AVAILABLE AT THIS TIME</div>");
return false;
}
// Now push required data into a JSON array object
var sampleData = [], item;
$.each(ret, function (key, value) {
sampleData.push([value.todate, parseFloat(value.Bill.replace(/,/g, ""))]);
});
var plot = $.jqplot('chart2', [sampleData], {
title: 'AJAX JSON Data Renderer',
dataRenderer: sampleData,
...
});
}
The method signature for your datarender (i.e. ajaxDataRender) is wrong. The signature should look like this:
function(userData, plotObject, options) { ... return data; }
(See the documentation here)
In your example you are passing the datarenderer "ret" which is not a function with the correct datarender signature. Also the jsonurl you are passing to getElectricDataJSON() is redundant as at no point in your code is the data from "AccountsService.asmx/GetJSONData" persisted to "./jsondata.txt".
Hence you should change your code to this:
$(document).ready(function(){
function ajaxDataRenderer(url, plot, options) {
var ret = null;
$.ajax({
// have to use synchronous here, else the function
// will return before the data is fetched
async: false,
url: url,
dataType: "json",
success: function (response) {
var ret = response;
// The following two lines just display the JSON data for testing purposes
$('#<%=outputASMX.ClientID%>').empty();
$('#<%=outputASMX.ClientID%>').html("<div>" + ret + "</div>");
},
error: function (request) {
$('#<%=outputASMX.ClientID%>').html("<div style='color:red;'>WEBSERVICE UNREACHABLE</div>");
}
});
return ret;
};
var url = "AccountsService.asmx/GetJSONData";
function getElectricDataJSON() {
var plot1 = $.jqplot('chart2', url, {
title: "AJAX JSON Data Renderer",
dataRenderer: ajaxDataRenderer,
});
}
I am trying to bind an array of values from a check box list to a model that has a property type of byte. So I am returned {"1", "2" , "3"} from check box selections I am needing this in byte format before it reaches my model so it can be passed properly to my model.
Property byte { get; set; }
My Knockout Viewmodel where self.DaysList = ko.observableArray(); is my check box list property.
function ScheduledCommand() {
//data
var self = this;
// An observable is a “JavaScript object that can notify subscribers about changes.”
// When the contents of an observable change, the view is automatically updated to match.
self.ScheduledCommandId = ko.observable();
self.NextOccurence = ko.observable();
self.CommandType = ko.observable();
self.CommandAssembly = ko.observable();
self.IntervalAmount = ko.observable();
self.IntervalType = ko.observable();
self.Catchup = ko.observable();
self.Retry = ko.observable();
self.SendToQueue = ko.observable();
self.Enabled = ko.observable();
self.SelectedCommand = ko.observable();
self.DaysList = ko.observableArray();
var Command = {
ScheduledCommandId: self.ScheduledCommandId,
NextOccurence: self.NextOccurence,
CommandType: self.CommandType,
CommandAssembly: self.CommandAssembly,
IntervalAmount: self.IntervalAmount,
IntervalType: self.IntervalType,
Catchup: self.Catchup,
Retry: self.Retry,
SendToQueue: self.SendToQueue,
Enabled: self.Enabled,
SelectedCommand: ko.observable(),
DaysList: ko.observableArray(),
};
self.Command = ko.observable();
self.Commands = ko.observableArray();
self.get = function () {
$.ajax({
url: '/api/Values',
cache: false,
type: 'GET',
contentType: 'application/json; charset=utf-8',
data: {},
success: function (data) {
self.Commands(data); //Put the response in ObservableArray
$("#btnGetCommands").hide();
$("#commandlist").show();
$("#btnHideCommands").show();
}
});
}
self.hidecommands = function ()
{
$("#btnGetCommands").show();
$("#btnHideCommands").hide();
$("#commandlist").hide();
}
//ko.bindingHandlers.bootstrapPopover = {
// init: function (element, valueAccessor, allBindingsAccessor, Command) {
// var options = valueAccessor();
// var defaultOptions = {};
// options = $.extend(true, {}, defaultOptions, options);
// $(element).popover(options);
// }
//};
self.create = function (formElement) {
if (Command.NextOccurence() != "" && Command.CommandType() != "" && Command.CommandAssembly() != "") {
$.ajax({
url: '/api/Values',
cache: false,
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: ko.toJSON(Command),
success: function (data) {
// alert('added');
self.Commands.push(data);
self.DaysList("");
self.NextOccurence("");
self.CommandType("");
self.CommandAssembly("");
self.IntervalAmount("");
self.IntervalType("");
self.Catchup("");
self.Retry("");
self.SendToQueue("");
self.Enabled("");
alert("Command " + self.CommandType() + " successfully created.")
}
}).fail(
function (xhr, textStatus, err) {
alert(err);
});
}
else {
alert('Please Enter All the Values !!');
}
},
self.selectCommand = function (command) {
self.SelectedCommand(command.ScheduledCommandId)
alert(command.ScheduledCommandId);
},
document.getElementById("btnGetCommands").onclick
}
$(document).ready(function () {
// When the DOM is fulled loaded, call the ko.applyBindings function and pass
// in a new instance of the commandViewModel:
$("#btnHideCommands").hide();
$("#commandlist").hide();
ko.applyBindings(new ScheduledCommand());
// The ko.applyBindings method activates Knockout and wires up the view-model to the view.
});
My API Controller Method this has no value for DaysList
public HttpResponseMessage Post(ScheduledCommand model)
{
repo.Add(model);
var response = Request.CreateResponse<ScheduledCommand>(HttpStatusCode.Created, model);
string uri = Url.Link("DefaultApi", new { id = model.ScheduledCommandId });
response.Headers.Location = new Uri(uri);
return response;
}
Finally my check box list.
#foreach (var item in ViewData["checklist"] as IEnumerable<SelectListItem>)
{
<div class="checkbox">
<label>
<input type="checkbox" data-bind="attr: { value: #item.Value}, checked: $root.DaysOfWeek">
#item.Text
</label>
</div>
}
The Knockout side of it is good it returns what I want but I can not convert it to a byte.
Thank you for your time!
What I wanted to do was try to convert a knockout observable array to a byte before it was passed to server in order to satisfy the parameter type(small int). Instead, what I did was create a "placeholder"(string array) property and convert it to a byte to satisfy my data property.
I was just hoping to remove the additional step using javascript.
I have been using knockout.js for a while now, and haven't encountered this problem before. Usually, when I try to push a new js object to an observableArray, it works without an issue, but for some reason, this time around I'm getting this error:
TypeError: self.Students.push is not a function
Here is a snippet of my code:
window.ApiClient = {
ServiceUrl: "/api/students",
Start: function () {
var viewModel = ApiClient.ViewModel(ngon.ClientViewModel);
ko.applyBindings(viewModel);
viewModel.get();
}
};
ApiClient.ViewModel = function(data) {
var self = this;
ko.mapping.fromJS(data, {}, this);
this.get = function (id) {
if (id == undefined) {
return ApiClient.Service.get(self.PageSize(), self.PageNumber(), function (data) {
self.Students(data);
});
}
}
this.post = function () {
return ApiClient.Service.post(self.DetailedStudent, function (data) {
self.Students.push(data);
});
}
return this;
}
ApiClient.Service = function () {
var _get = function (pageSize, pageNumber, callback) {
sv.shouldShowLoading = false;
var queryParams = String.format("?pageSize={0}&pageNumber={1}", pageSize, pageNumber);
$.ajax(ApiClient.ServiceUrl + queryParams, {
dataType: "json",
type: "get",
success: callback
});
}
var _post = function (student, callback) {
$.ajax(ApiClient.ServiceUrl, {
data: ko.mapping.toJSON(student),
type: "post",
contentType: "application/json; charset-utf-8",
statusCode: {
201 /*Created*/: callback,
400 /*BadRequest*/: function (jqxhr) {
var validationResult = $.parseJSON(jqxhr.responseText);
alert(jqxhr.responseText);
}
}
});
}
return {
get: _get,
post: _post
};
}();
$(document).ready(function () {
ApiClient.Start();
});
My student object is a very simple C# object that has Id, FirstName, LastName. The get() function works without any issues, it's just the callback function from the post() that cannot push the resulting data. Also, the data being returned back from the server looks correct:
{"Id":"rea","FirstName":"asdf","MiddleName":null,"LastName":"rrr"}
I solved this! It's because the initial viewModel, when being instantiated by the page's view model object had 'null' for its Students property.
knockout.js requires non-null values for all fields that are to be auto mapped.