Handle ajax calls with knockoutJS? [duplicate] - javascript

i am using the knockout js, i am finding diffcult to bind the data while in ajax get method, i have created model, viewModel, and ajax function, i have the ajax method in the same js file where i have created viewModel i am calling the ajax on page load and trying to bind my html with konckout js, i am getting the error userModel is undefined if i give this.name = ko.observale(result[0].name) before the ajax call, after the ajax called it give name is undefined need help
<html>
<head>
<script src="js/jquery1.9.js"></script>
<script src="js/knockout-3.3.0.js"></script>
<script src="js/knockout.mapping.js"></script>
<script src="model/usermodel.js"></script>
</head>
<body>
<div>
<h1><span data-bind="text:user().name"></span></h1>
<h1><span data-bind="text:user().userName"></span></h1>
</div>
<script src="ViewModel/userDetailsViewModel.js"></script>
</body>
</html>
////Model////
function userModel(result) {
var self = this;
this.name = ko.observable(result[0].name); /// give me error undefined before the ajax call and after ajax call i get the value in result
this.userName = ko.observable();
}
/////View Model////
var result
var userDetailsViewModel = function(result) {
self = this;
self.user = ko.observable(new userModel(result));
};
var mainUserDetailsViewModel = new userDetailsViewModel(result);
ko.applyBindings(mainUserDetailsViewModel);
////ajax called on the page load ////
$.ajax({
type: "POST",
dataType: "json",
url: baseUrl + 'api/xx/xxx',
data: jason.strigfy(),
success: function(data) {
result = data;
////I am getting in result json data object 0=["name":"nnnn","Username":"mmmmmm"],
//// i am passing this result to ViewModel and to Usermodel Constructor//
mainUserDetailsViewModel.user(new userModel(result));
},
error: function(error) {
jsonValue = jQuery.parseJSON(error.responseText);
//jError('An error has occurred while saving the new part source: ' + jsonValue, { TimeShown: 3000 });
}
});

Here is my suggestion to have a clean nested view model.
Example : https://jsfiddle.net/kyr6w2x3/28/
function UserViewModel() {
var self = this;
self.UsersList = ko.observableArray([]);
self.GetUsers = function() {
$.ajax({
type: "POST",
dataType: "json",
url: baseUrl + 'api/xx/xxx',
data: jason.strigfy(),
success: function (data) {
//Here you map and create a new instance of userDetailVM
self.UsersList($.map(data, function (user) {
return new UserDetailViewModel(user);
}));
}
});
}
//call to get users list when the VM is loading or you can call it on any event on your model
self.GetUsers();
}
function UserDetailViewModel(data){
var self = this;
self.Name = ko.observable(data.name);
self.UserName = ko.observable(data.username);
}
ko.applyBindings(new UserViewModel());
View :
<h1 data-bind="foreach: UsersList">
<div data-bind="text: Name"></div>
<div data-bind="text: UserName"></div>
</h1>

Related

ajax call occurs after ko binding is applied

I am trying to pull data from a database using an ajax call and then put that data in a view model which will then be bound to a table using knockout. Here is my code:
<script>
$(document).ready(function () {
var LoadFiles = '#Url.Action("Files", "Home")';
var HomeModel = function () {
debugger
var self = this;
self.rows = ko.observableArray([]);
$.ajax({
method: "POST",
url: LoadFiles,
success: function (data) {
alert('inside ajax call');
self.rows = JSON.parse(data);
},
error: function (data) {
alert('error');
}
});
}
alert('outside ajax call');
var model = new HomeModel();
debugger
ko.applyBindings(model);
});
</script>
<pre data-bind="text: ko.toJSON($data, null, 2)"></pre>
However, the problem is that my view model.rows is empty because as I discovered through some alert("") calls, the `alert('outside ajax call'); is called first, then the page loads and binding is applied, then the alert('inside ajax call'); is called. I don't understand how this is possible especially since I call var model = new HomeModel() before the bindings are applied. How can I ensure that the self.rows of my HomeModel are populated before the page loads to ensure my .rows are not empty?
UPDATE:
Thanks to #RoyJ, this now works as expected:
<script>
$(document).ready(function () {
var LoadFiles = '#Url.Action("Files", "Home")';
var HomeModel = function () {
debugger
alert('above');
var self = this;
self.rows = ko.observableArray([]);
$.ajax({
method: "POST",
url: LoadFiles,
success: function (data) {
alert('inside ajax call');
self.rows(JSON.parse(data))
alert('below');
},
error: function (data) {
alert('error');
}
});
}
alert('outside ajax call');
var model = new HomeModel();
debugger
ko.applyBindings(model);
});
</script>
<pre data-bind="text: ko.toJSON($data, null, 2)"></pre>

Two ViewModels Binding Using KnockOut JS in a MVC4 View

I am using knockoutJs 3.3.0. In My application I have common javascript file which is reffered all over the application. apart from that I'll be having individual Js files for each page.
So I have two view Models, one is in common and another is page-level viewModel page level one view model. and all the functions of my both Js files are ajax.
I need to bind them in view.
This is my Common ViewModel
var App = function () {
var self = {};
self.FP = ko.observable($("#fpTag").val());
self.UserName = ko.observable($("#StoreValues").val());
self.Enterprise = ko.observable($("#fpIdentifier").val());
self.UpdateFP = function () {
$.ajax({
url: "/Home/createDeal",
type: "POST",
data: { tag: self.FP() },
success: function (data) {
self.FpData($.parseJSON(data));
//return result;
},
error: function (data) {
//return result;
}
});
}
return self;
}
ko.applyBindings(new App());
and this is my PageLevel Js
var Discovery = function() {
var self = {};
var application = new App();
self.KeyWords = ko.observable();
self.GetSearchKeywords = ko.computed(function () {
var data = application.FpData();
if (data != null) {
$.ajax({
url: "/Home/GetSearchKeywords",
type: "POST",
data: { tag: application.UserName(), EnterpriseId: application.Enterprise(), StoreType: "SINGLE", offset: 1 },
success: function (res) {
self.KeyWords($.parseJSON(res));
},
error: function (res) {
}
});
}
});
return self;};ko.applyBindings(new Discovery());
I My view How can I refer the Value as I need all values from both ViewModels.
In My view:
<tbody data-bind="foreach: $root.KeyWords()">
<tr>
<td data-bind="text: keyword"></td>
<td data-bind="text: App().FormatDate(createdOn)"></td>
<td data-bind="text: ip"></td>
</tr>
</tbody>
<input data-bind="value: App().FP()"/>
How can I achieve this..?
UPDATE
Here is the link which I found Helpful. multiple viewmodels communication
This is (IMHO) not possible, one can bind only one model to a specific element.
I would try to make one model object a property of the other one.
You should 'composite' the two viewModels together:
JS:
var App = function (pageVm) {
var self = {};
if (typeof pageVm !== 'undefined') {
self.pageVm = pageVm;
}
self.FP = ko.observable($("#fpTag").val());
self.UserName = ko.observable($("#StoreValues").val());
self.Enterprise = ko.observable($("#fpIdentifier").val());
self.UpdateFP = function () {
$.ajax({
url: "/Home/createDeal",
type: "POST",
data: { tag: self.FP() },
success: function (data) {
self.FpData($.parseJSON(data));
//return result;
},
error: function (data) {
//return result;
}
});
}
return self;
}
var pageVm = new Discovery();
ko.applyBindings(new App(pageVm));
Now your App bindings work without modification, and you can access your page-specific VMs through the App.pageVm attribute.
I prefer this, typically, rather than binding each VM to a different DOM node (also an option) because often one node may be nested inside the other which can make that difficult.

Knockout ObserableArray dropdown binding issue

I am trying to fetch data from restful wcf service in page load and bind to drop down which is not working.
function CreateItem(name, value) {
var self = this;
self.itemName = ko.observable(name);
self.itemValue = ko.observable(value);
};
function AppViewModel() {
var self = this;
self.titleList = ko.observableArray();
self.load = function () {
alert("click fired");
$.ajax({
url: "https://mydomain/RestfulService/Service1.svc/CreateData?name=venkat",
type: "POST",
cahce: false,
async: false,
data:'',
dataType: "jsonp",
success: function (data) {
for (var i = 0; i < data.length; i++) {
self.titleList().push(new CreateItem(data[i].Description, data[i].TitleID));
}
alert("success " + data);
},
error: function (error) {
alert("failed " + error);
}
});
};
};
<div>
<select data-bind="options: titleList(), optionsText: 'itemName', optionsValue: 'itemValue', value: selectedTitleValue, optionsCaption: 'Please choose'"></select>
</div>
<script type="text/javascript">
$(document).ready(function() {
var model = new AppViewModel();
model.load();
ko.applyBindings(model);
});
</script>
The issue is, knockout array is populating in load function correctly but drop down is not refreshing updated data. I am not understanding where is the problem. Please give inputs.
Replace:
self.titleList().push(new CreateItem(data[i].Description, data[i].TitleID));
with
self.titleList.push(new CreateItem(data[i].Description, data[i].TitleID));
The reason is self.titleList() returns the underlying array, when you push data to it, Knockout is unaware of the changes and does not notify the views.

Knockout.js posting a form

What is the simplest method of posting a form to the server when clicking the submit button using knockout.js?
This is what I have currently but it is not posting. What is broken with my saveForm function?
// Here's my data model with save option
var self = this;
var viewModel;
$.getJSON('#Url.Content("~/api/myData")', function (data) {
viewModel = ko.mapping.fromJS(data);
self.save = function (form) {
alert("Could now transmit to server");
};
viewModel.saveForm = function () {
var jsonData = ko.mapping.toJSON(viewModel);
$.ajax({
type: "POST",
url: '#Url.Content("~/api/myData")',
data: jsonData
});
};
ko.applyBindings(viewModel);
});
<button type="submit">Save</button>
probably worth putting the line
debugger;
before
viewModel = ko.mapping.fromJS(data);
and checking what happens to viewModel in firebug. "viewModel = ko.mapping.fromJS(data);" will replace everything in viewModel with the json you are loading. This includeds your function saveForm

Function Doesn't Work When Called Within Object

I have the following code:
// auto_suggest.js
function AutoSuggest(textboxElem, resultElem, url) {
this.textboxElem = textboxElem;
this.resultElem = resultElem;
this.url = url;
this.registerEvent();
}
AutoSuggest.prototype = {
registerEvent: function() {
this.textboxElem.onkeyup = this.getSuggestions;
},
getSuggestions: function() {
// This doesn't work either: this.loadResponse("some data");
$.get(this.url, { text: this.textboxElem.value }, this.loadResponse);
},
loadResponse: function(data) {
// Not called
this.resultElem.innerHTML = data;
}
};
// suggest.html
<script src="jquery-1.6.1.js" type="text/javascript"></script>
<script src="auto_suggest.js" type="text/javascript"></script>
<script>
$(function() {
var suggest = new AutoSuggest(document.getElementById("suggest"),
document.getElementById("result"),
"result.txt");
// This DOES work: suggest.loadResponse("123");
});
</script>
<input type="text" id="suggest" /><br>
<div id="result"></div>
The function loadResponse refuses to be called from within the object, but from the outside it is fine. What could be the problem?
The AJAX callback (AutoSuggest.loadResponse) is, by default, passed the jqXHR object as its context (this value). You need to override this by passing the context option to $.ajax. Replace your $.get function with this:
$.ajax({
url: this.url,
type: 'GET',
data: { text: this.textboxElem.value },
success: this.loadResponse,
context: this
});
This makes jQuery set this to the correct value.
The code
this.textboxElem.onkeyup = this.getSuggestions;
should be
var t = this;
this.textboxElem.onkeyup = function() {
t.getSuggestions();
}

Categories