knockout.js with binding - data-binding

This is probably a really basic question but in the following view model I am populating self.userData from an ajax call. I want to display this information in the UI and I figured the 'with' binding is the way to go.. however since self.userData is empty until the ajax function is called I get an error from the with binding
HTML
<div data-bind="with: userData">
<div data-bind="text: userId"></div>
...
</div>
Model
var viewModel = function () {
var self = this;
self.userData = {};
self.login = function(data) {
var postData = { ..trimmed out.. };
$.ajax({
type: "POST",
url: "myService",
data: postData
}).done(function (data, status) {
self.userData = ko.mapping.fromJS(data, userData);
console.log(self.userData);
}).fail(function (data, status) {
alert('Could Not Login');
});
}
};
ko.applyBindings(new viewModel());

Initialize userData with an empty observable, an then set it with the object created by the the mapping plugin once the call return. I.e. change
self.userData = {};
with
self.userDara = ko.observable();
and change
self.userData = ko.mapping.fromJS(data, userData);
with
self.userData(ko.mapping.fromJS(data,userData));

You need to initialize userData as an empty observable (single object from ajax call) or observable array (multiple objects) first:
self.userData = ko.observableArray([]);
or
self.userData = ko.observable();

Related

asp.net MVC - AJAX model to controller

Is it possible to pass my view Model to the controller, using ajax, without 'rebuilding' the object?
I have my view:
#using Project.Models
#model InfoFormulaireEnqueteModele
#section Style{
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" />}
#section Scripts{
#Scripts.Render("~/bundles/autocomplete")
#Scripts.Render("~/bundles/timeentry")
<script type="text/javascript">
var $status = $('#status'),
$commentBox = $('#commentBox'),
timeoutId;
var model = #Model; //<- something's wrong here
$commentBox.keypress(function () {
$status.attr('class', 'pending').text('changes pending');
// If a timer was already started, clear it.
if (timeoutId) clearTimeout(timeoutId);
var r = '';
// Set timer that will save comment when it fires.
timeoutId = setTimeout(function () {
var test = $('#commentBox').val();
// Make ajax call to save data.
$.ajax({
type: "POST",
data: JSON.stringify(model),
url: '/Enquete/IndexPartial/',
contentType: "application/json"
}).done(function (res) {
$("#SomeDivToShowTheResult").html(res);
});
$status.attr('class', 'saved').text('changes saved');
}, 1000);
return r;
});
</script>
Controller:
[HttpPost]
public PartialViewResult IndexPartial(InfoFormulaireEnqueteModele m)
{
return PartialView("_IndexPartial", m);
}
I am able to reach my controller, but my model (m) has only null values once in the controller.. The values were set in the controller before being sent to the view.
In your action method speciy [FromBody] as below
[HttpPost]
public PartialViewResult IndexPartial([FromBody] InfoFormulaireEnqueteModele m)
{
return PartialView("_IndexPartial", m);
}
You should do it like this:
var model = #Html.Raw(Json.Encode(#Model));
That's how you convert your model to json and pass it to js

KnockoutJS and async ajax call

I am using SPServices and KnockoutJS to load some data on a page, specifically a list of projects and the number of tasks for each project.
I have simplified the example a bit but what I am having issues with is to get the value of the ajax call async. I solved it cheaply by using jQuery to bind the result instead of returning anything:
function ProjectModel(title, id) {
var self = this;
self.id = id;
self.title = title;
self.tasks = ko.computed(function() {
$().SPServices({
operation: "GetListItems",
async: true,
webURL: "/projects/" + self.id,
listName: "Tasks",
CAMLQuery: // query..,
CAMLViewFields: "<ViewFields><FieldRef Name='ID' /></ViewFields>",
completefunc: function (xData, Status) {
$(".tasks-" + id).text($(xData.responseXML).SPFilterNode("z:row").length);
}
});
});
}
I had a look at these sites
Async dependantobservables
async computed observables
Is this the way to go?
Edit:
OK so some more code (have stripped out some stuff here):
function onGetProjectsCompleted(projects) {
var projectViewModel = new ProjectViewModel();
projectViewModel.init(projects);
ko.applyBindings(projectViewModel);
}
function beforeLoadingProjects() {
$(".loadingMessage").show();
}
function initProjectsView() {
ProjectsRepository.getOpenProjects(beforeLoadingProjects, onGetProjectsCompleted);
}
function ProjectViewModel() {
var self = this;
self.openProjects = ko.observableArray();
self.init = function initProjectViewModel(projects) {
$.each(projects, function() {
self.openProjects.push(this);
});
});
};
}
var ProjectsRepository = {
getOpenProjects: function (beforeComplete, onComplete) {
var options = {
operation: "GetListItems",
completefunc: function(xData, status) {
var projects = new Array();
$(xData.responseXML).SPFilterNode("z:row").each(function() {
var item = $(this);
projects.push(new ProjectModel(
item.attr("ows_Title"),
item.attr("ows_ProjectID")
));
});
onComplete(projects);
}
};
beforeComplete();
$().SPServices(options);
}
};
Please note, I don't want to list the tasks for each project, I just want to have a property with the Number of tasks for each project, i.e. no array.
Thanks again.
I can't tell how you are trying to use the class (Model) above, but #anders is correct: your model should not manipulate DOM element directly... That's what Knockout's view binding is for...
Try this (disclosure: I did not test this in a live environment):
Model:
function ProjectModel(title, id) {
var self = this;
self.id = id;
self.title = title;
self.tasks = ko.observable(0);
$().SPServices({
operation: "GetListItems",
async: true,
webURL: "/projects/" + self.id,
listName: "Tasks",
CAMLQuery: // query..,
CAMLViewFields: "<ViewFields><FieldRef Name='ID' /></ViewFields>",
completefunc: function (xData, Status) {
if (Status !== "success") {
alert("failed to get tasks! Ajax call error.");
return;
}
self.tasks(
$(xData.responseXML).SPFilterNode("rs:data").attr('ItemCount')
);
}
});
}
View:
<div id="mytemplate">
<h2>
<span data-bind="text: title"></span> Project has
<span data-bind="text: tasks"></span> Tasks
</h2>
</div>
Controller/Binding:
ko.applyBindings(
new ProjectModel("your project title", "your project ID"),
document.getElementById("mytemplate")
);
Try to use the above and see if you get output... The number of tasks will be initially zero, but if your tasks list is valid and tasks are there, then it will update itself once the query completes.
I haven't used SPServices before, but I would start by separating the model from the ajax calls by creating a service object. then pass in the observable to be filled once the
Something like
var ajaxServices = (function(){
return {
getProjectTasks: getProjectTasks
};
function getProjectTasks(projectId, tasksObservable){
$().SPServices({
operation: "GetListItems",
async: true,
webURL: "/projects/" + projectId,
listName: "Tasks",
CAMLQuery: // query..,
CAMLViewFields: "<ViewFields><FieldRef Name='ID' /></ViewFields>",
completefunc: function (xData, Status) {
tasksObservable(xData);
});
}
})();
function ProjectModel(title, id) {
var self = this;
self.id = id;
self.title = title;
self.tasks = ko.observableArray([]);
self.getTasks = function (){
ajaxServices.getProjectsTasks(sif.id, self.tasks);
};
}

How can I call a vb.net function from angularJS code?

I'm trying to pass data stored in a dynamically created table to the server, and while I can access the data in my angularJS controller, I am having difficulty learning how to pass this data to the server to process.
Here is my angularjs function that is able to access my table data, its just passing the data and calling my vb.net function that I am having trouble with.
$scope.requestThatCertificatesBeEmailed = function () {
for (index = 0; index < $scope.requests.length; ++index) {
alert('For loop entered')
var submittedEmailAddressString = $scope.requests[index].emailAddress;
var submittedCertificateTypeString = $scope.requests[index].certificateType;
var submittedSearchTypeString = $scope.requests[index].searchType;
var submittedSearchString = $scope.requests[index].submittedNumbers;
alert(submittedSearchTypeString);
$http.post("/Home/NewTextFile", { submittedEmailAddress: submittedEmailAddressString, submittedCertificateType: submittedCertificateTypeString, submittedSearchType: submittedSearchTypeString, submittedSearch: submittedSearchString }).error(function () {
$scope.requests = [];
});
You will need to post/put the data back to the server. If you are working in a ASP.NET WebForms application, you will likely need to pass the value as JSON to the server in a hidden input field. If you are working in an ASP.NET MVC application, you should be able to invoke a controller action sending in the JSON table data from javascript.
Your action method in your MVC Controller should look like this:
<HttpPost> _
Public Function NewTextFile(submittedEmailAddress As String, submittedCertificateType As String, submittedSearchType As String, submittedSearch As String) As ActionResult
'do some work
End Function
Using jQuery, you could invoke the controller action like so:
$.ajax({
url: '/Home/NewTextFile',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
submittedEmailAddress: submittedEmailAddressString,
submittedCertificateType: submittedCertificateTypeString,
submittedSearchType: submittedSearchTypeString,
submittedSearch: submittedSearchString
}),
processData: false,
dataType: 'json'
});
Below is an AngularJS example I whipped together:
On/referenced from the View:
<script type="text/javascript">
angular.module('httpExample', [])
.controller('ContactController', ['$scope', '$http',
function ($scope, $http, $templateCache) {
$scope.contact = { userName: '', firstName: '', lastName: '' };
$scope.get = function () {
$scope.code = null;
$scope.response = null;
$http.get('/Home/Contact').
success(function (data, status) {
$scope.status = status;
$scope.data = data;
$scope.contact = data;
}).
error(function (data, status) {
$scope.data = data || "Request failed";
$scope.contact = { userName: '', firstName: '', lastName: '' }
$scope.status = status;
});
};
$scope.post = function () {
$scope.code = null;
$scope.response = null;
$http.post('/Home/Contact', $scope.contact).
success(function (data, status) {
$scope.status = status;
$scope.data = data;
}).
error(function (data, status) {
$scope.data = data || "Request failed";
$scope.status = status;
});
};
}]);
</script>
Somewhere in the body:
<div>
<div ng-app="httpExample">
<div ng-controller="ContactController">
<fieldset>
<legend>Contact</legend>
Username: <input type="text" ng-model="contact.userName"/><br/>
Last Name: <input type="text" ng-model="contact.lastName"/><br/>
First Name: <input type="text" ng-model="contact.firstName"/><br/>
</fieldset>
<br />
<button id="getbtn" ng-click="get()">get</button>
<button id="postbtn" ng-click="post()">post</button><br/><br/>
<pre>http status code: {{status}}</pre>
<pre>http response data: {{data}}</pre>
</div>
</div>
</div>
And your MVC server side Home controller has methods that look like this:
<HttpGet> _
Public Function Contact() As JsonResult
Dim contact = New With { .userName = "smithjk", .firstName = "John", .lastName = "Smith" }
Return Json(contact, JsonRequestBehavior.AllowGet)
End Function
<HttpPost> _
Public Function Contact(userName As String, firstName As String, lastName As String) As ActionResult
'do some work
System.Diagnostics.Debug.Print(userName)
Return New EmptyResult()
End Function

Knockoutjs not binding to element

I am kind of new to KnockoutJS and trying to use it.
I am having trouble binding the element using knockoutjs. Please see the fiddle below and help in resolving and correcting me.
It is basically not binding the value to the span element.
http://jsfiddle.net/EpyRA/
HTML:
<div id="taxyear">
<table style="width: 100%;" cellpadding="4" cellspacing="4">
<tr>
<td style="width: 25%">
<span>Name:</span><span data-bind="value: ReturnData.Name"></span>
</td>
</tr>
</table>
</div>
Javascript:
var myWeb = myWeb || {};
$(function () {
(function (myWeb) {
"use strict";
var serviceBase = '../Services/Data.asmx/',
getSvcUrl = function (method) { return serviceBase + method; };
myWeb.ajaxService = (function () {
var ajaxGetJson = function (method, jsonIn, callback) {
$.ajax({
url: getSvcUrl(method),
type: "GET",
data: jsonIn,
dataType: "json",
contentType: "application/json",
success: function (json) {
callback(json);
}
});
},
ajaxPostJson = function (method, jsonIn, callback) {
var test = { "Name": "testRaju", "SourceID": "ABCD" };
//just return data instead of calling for testing
callback(test);
};
return {
ajaxGetJson: ajaxGetJson,
ajaxPostJson: ajaxPostJson
};
})();
} (myWeb));
(function (myWeb) {
"use strict";
myWeb.DataService = {
getReturnData: function (jsonIn, callback) {
myWeb.ajaxService.ajaxPostJson("GetReturnData", jsonIn, callback);
}
};
} (myWeb));
//Constructor for a ReturnData object
myWeb.ReturnData = function () {
this.Name = ko.observable();
this.SourceID = ko.observable();
},
//View Model
myWeb.prdviewmodel = (function () {
var prd = ko.observable();
loadReturnDataCallback = function (jsonReturnData) {
alert(jsonReturnData.Name);
prd = new myWeb.ReturnData()
.Name(jsonReturnData.Name)
.SourceID(jsonReturnData.SourceID);
},
loadReturnData = function () {
myWeb.DataService.getReturnData("{'YearID':'" + 22 + "'}", myWeb.prdviewmodel.loadReturnDataCallback);
};
//public
return {
loadReturnData: loadReturnData,
loadReturnDataCallback: loadReturnDataCallback,
ReturnData: prd
}
})();
//hookup knockout to our viewmodel
myWeb.prdviewmodel.loadReturnData();
ko.applyBindings(myWeb.prdviewmodel);
});
Thanks in Advance,
Sam
I see a couple of minor issues:
When binding against a span, you would want to use the text binding rather than the value binding.
In the AJAX callback, you would want to set the prd observable's value by calling it as a function, rather than setting it as a new value.
In the UI, you can make use of the with binding to ensure that it does not try to bind to a property of the observable, before it has been loaded.
Here is an updated fiddle: http://jsfiddle.net/rniemeyer/Bdz5a/

how do i auto update UI where there is a change in an observableArray?

Hello I have the following code which uses knockout.js,
var allJobberDetailsArray = [];
getAllJobberDetailsArray();
function getAllJobberDetailsArray() {
$(function() {
$.ajax({
url: 'http://example.com',
type: 'GET',
data: {
request: "yes",
getAllJobberDetailsArray: "yes"
},
success: function(data) {
allJobberDetailsArray = data;
}
});
})
}
// ViewModel
function JobberCheckBoxListUserControlViewModel() {
var self = this;
self.allJobberDetailsArray = ko.observableArray(allJobberDetailsArray);
}
ko.applyBindings(new JobberCheckBoxListUserControlViewModel());
any change to allJobberDetailsArray I would like to update the UI automatically say an array item is added updated or deleted i would like the UI to reflect it.
How do i achieve it?
EDIT
var allJobberDetailsArray = ko.observableArray();
getAllJobberDetailsArray();
function getAllJobberDetailsArray() {
$(function() {
$.ajax({
url: 'http://example.com',
type: 'GET',
data: {
request: "yes",
getAllJobberDetailsArray: "yes"
},
success: function(data) {
allJobberDetailsArray.removeAll();
allJobberDetailsArray.push(data);
},
});
})
}
// ViewModel
function JobberCheckBoxListUserControlViewModel() {
var self = this;
self.allJobberDetailsArray = allJobberDetailsArray;// allJobberDetailsArray is now observable but any change to this doesn't reflect in the UI
}
var viewModel = new JobberCheckBoxListUserControlViewModel();
ko.applyBindings(viewModel);
in the above code i made the array itself observable and trying to auto update the UI if there are any changes to the array how do i achieve it?
Firstly the array you are updating isn't an observable array. Secondly your success callback actually overwrites the array. Finally you do not keep a reference to your viewModel so you are unable to properly update the observable array. ObservableArray wrap real arrays to make them observable. All add's removes etc need to go through the observableArray so the subscribers can be notified.
Try this instead.
// ViewModel
function JobberCheckBoxListUserControlViewModel(initialArray) {
var self = this;
self.allJobberDetailsArray = ko.observableArray(initialArray || []);
}
var viewModel = new JobberCheckBoxListUserControlViewModel();
function getAllJobberDetailsArray() {
$(function() {
$.ajax({
url: 'http://example.com',
type: 'GET',
data: {
request: "yes",
getAllJobberDetailsArray: "yes"
},
success: function(data) {
viewModel.allJobberDetailsArray(data);
}
});
})
}
ko.applyBindings(viewModel);
I would also recommend refactoring to put the getAllJobberDetailsArray method inside your viewModel so it doesn't polute the global namespace and maintains encapsulation.
Hope this helps.

Resources