loading a knockout.js observableArray() from .ajax() call - javascript

This puzzles me. It must be something small I'm not seeing. I'm trying to load a very simple observableArray in knockout with an ajax call.
javascript
// we bind the array to the view model property with an empty array.
var data = [];
var viewModel = {
vendors: ko.observableArray(data)
};
ko.applyBindings(viewModel);
$(function () {
// on this click event, we popular the observable array
$('#load').click(function () {
// WORKS. Html is updated appropriately.
viewModel.vendors([{ "Id": "01" },{ "Id": "02" },{ "Id": "03" }]);
// DOES NOT WORK. Fiddler2 shows the same exact json string come back
// as in the example above, and the success function is being called.
$.ajax({
url: '/vendors/10',
dataType: 'json',
success: function (data) {
viewModel.vendors(data);
}
});
});
});
html
<button id="load">Load</button>
<ul data-bind="template: { foreach: vendors }">
<li><span data-bind="text: Id"></span></li>
</ul>
Question: Why does the successful ajax call, who's data variable value matches byte-for-byte the hard typed value, not trigger the html refresh?

There is no reason this would not work fine. As this demonstrates.
http://jsfiddle.net/madcapnmckay/EYueU/
I would check that the ajax post is actually returning json data and that that json is an array and that it's being parsed correctly.
I had to tweak the ajax call to get the fiddle ajax handlers to work correctly.
Nothing more I can think of.
Hope this helps.

var self=this;
//var self first line in model
$.ajax({
url: ",
dataType: "json",
contentType: 'application/json',
type: "POST",
data: JSON.stringify({ }),
processdata: true,
beforeSend: function () {
$.mobile.loading('show');
},
error: function (xhr, textStatus, errorThrown) {
alert('Sorry!');
},
success: function (data) {
$.mobile.loading('hide');
if (data.result!= '') {
self.vendors(data.result);
} else {
self.vendors({something});
}
}
});
Use self.vendors not this viewModel.vendors

Here is what I done in my MVC .net app with knockout and jquery.
// Scripts/groItems.js
(function () {
var ViewModel = function () {
items = ko.observableArray(),
ItemName = ko.observable(),
Img = ko.observable(),
Qty = ko.observable()
}
$.getJSON('/Items2/AllItems', function (data) {
for (var i = 0; i < data.length; i++) {
self.items.push(data[i]);
}
});
var vm = new ViewModel();
$(function () {
ko.applyBindings(vm);
});
}());
#model IEnumerable<GroModel.Item>
#{
ViewBag.Title = "Index";
}
<p>
#Html.ActionLink("Create New", "Create")
</p>
<div data-bind="text: items().length"></div>
<table class="container table table-hover">
<thead>
<tr>
<th>Item name</th>
<th>img</th>
<th>qty</th>
</tr>
</thead>
<tbody data-bind="foreach: items">
<tr>
<td data-bind="text: ItemName"></td>
<td data-bind="text: Img"></td>
<td data-bind="text: Qty"></td>
</tr>
</tbody>
</table>
#section Scripts {
<script src="~/Scripts/knockout-3.4.2.js"></script>
<script src="~/Scripts/groItems.js"></script>
}
Following is part of my code at the Items2Controller.cs
private GroContext db = new GroContext();
public JsonResult AllItems()
{
return Json(db.Items.ToList(), JsonRequestBehavior.AllowGet);
}
Hope this will help. Thanks

We can use a simple JavaScript util function as a work-around.
Instead of viewModel.vendors(data);, wrapping with eval (research the dangers of eval first) will work.
eval("viewModel.vendors("+JSON.stringify(data)+");");

This is bug I think, Knockout's sample is working when we use it with wrapper class:
public class ResultWrapper
{
public Title {get;set;}
public List<Result> {get;set;}
}
http://learn.knockoutjs.com/#/?tutorial=webmail
But if we return Results directly there is no way to bind it. (without extra applyBindings!)

Related

Why is the MVC controller action method tagged as HttpPost not getting invoked from external javascript file?

Here is the relevant View cshtml code
<tbody>
#foreach (var task in Model.TasksSummaryList)
{
<tr>
<th onclick="OnPatientSelected('#task.PatientID')">#task.PatientName</th>
<th>#task.Status</th>
<th>#task.TaskCount</th>
<th>#task.MostRecentTask</th>
</tr>
}
</tbody>
Here is the external javascript file containing OnPatientSelected
function OnPatientSelected(selectedPatientID) {
$.ajax({
type: "POST",
url: "TasksSummary/TaksSummaryDetailsView",
data: { selectedPatientID: selectedPatientID },
error: function () {
alert("fail");
},
success: function(){
alert("success");
}
});
}
This always shows success but never calls the Controller ActionMethod I want
[AllowAnonymous]
[HttpPost]
public ActionResult TasksSummaryDetailsView(/*data from view*/string selectedPatientID)
{
int i = 09;
//Received PatientID from View (Client)
//Browse to the appropriate view
return View();
}
The path needs to be relative, try something like the following:
url: "../TasksSummary/TaksSummaryDetailsView",
Or even better:
url: '#Url.Action("TasksSummary", "TasksSummaryDetailsView")',
So replaced the url line in ajax call with the following and it works
url: 'TasksSummaryDetailsView'

Autocomplete on Html.TextBox won't work

So, I've searched alot and went through alot of tutorials and even though I do everything exactly as in the tutorial, I just can't seem to get it working. Funny thing is, I have been involved in a project where we used the exact same solution and it worked.
I've got a textbox in my forum where users can search for threads in all categories where I am using ajax to show the result in a div in form of a partial view. This is working.
The problem is that I want the thread subjects that are containing the current search term to show up (in form of a normal string) while the user is typing, but I can't seem to get the implementation of autocomplete right. By the way I am retrieving my information from a MSSQL-database.
This is the javascript that I am using to autocomplete (which is not working) and below that you can see my Ajax-form that I use for the search (that works):
<link href="~/Content/jquery-ui.min.css" rel="stylesheet" />
<script src="~/Scripts/jquery-ui.min.js"></script>
#*Scripts for Ajax to show the partial view in the div with id "partialThreads" at request*#
<script src="~/Scripts/jquery-2.2.1.min.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
<script type="text/javascript">
$(function () {
$("#txtSearch").autocomplete({
source: '#Url.Action("GetThreadsBySearch", "Forum")'
});
});
</script>
#using (#Ajax.BeginForm("Threads", new AjaxOptions() { UpdateTargetId = "partialThreads", InsertionMode = InsertionMode.Replace, HttpMethod = "POST" }))
{
#Html.AntiForgeryToken()
<p><strong>Search for thread in all categories</strong></p>
#Html.TextBox("searchTerm", null, new { id = "txtSearch", style = "width: 1000px" })
<input type="submit" value="Search" />
}
Here is the div where I show the results of the search in form of a partial view:
<div id="partialThreads">
</div>
Here is the action method that I am using for my ajax-form search (the working one):
[HttpPost, ValidateAntiForgeryToken]
public ActionResult Threads(string searchTerm)
{
var model = string.IsNullOrWhiteSpace(searchTerm)
? new List<ThreadsListModel>()
: _threadRepo.GetThreadsBySearch(searchTerm).OrderByDescending(x => x.DateCreated).ToList();
return PartialView("_Threads", model);
}
And here is the method that I use to retrieve the information to my autocomplete (I've tried setting a break point on it, it doesn't even break):
public JsonResult GetThreadsBySearch(string term)
{
var threadNames = _threadRepo.GetThreadsBySearch(term).Select(x => x.Subject).ToList();
return Json(threadNames, JsonRequestBehavior.AllowGet);
}
Note that I use the same db-query to search with the form and for the autocomplete (only difference would be that I select the threadnames as a List in the GetThreadsBySearch method. So that can't be the problem (?). Here is query-method in case you want to have a look:
public ICollection<ThreadsListModel> GetThreadsBySearch(string subject)
{
using (var context = new ForumContext())
{
return
context.Threads.Where(x => x.Subject.ToLower().Contains(subject.ToLower()) && x.IsActive)
.Select(x => new ThreadsListModel()
{
ID = x.ID,
DateCreated = x.DateCreated,
CreatedBy = x.CreatedBy,
Subject = x.Subject,
PostsCount = x.Posts.Count
}).Distinct().ToList();
}
}
Also, I am using Visual Studio 2015 (.NET 4.5.2) MVC 5. I hope that I haven't forgot to write down any helpful information.
Your scripts are in the wrong order and jquery needs to be before jquery-ui (and also ensure that you do not have any duplicated scripts)
$("#MainContent_txtCountry").autocomplete({
source: function (request, response) {
var param = { keyword: $('#MainContent_txtCountry').val() };
$.ajax({
url: "Default.aspx/GetCountryNames",
data: JSON.stringify(param),
dataType: "json",
type: "POST",
contentType: "application/json; charset=utf-8",
dataFilter: function (data) { return data; },
success: function (data) {
response($.map(data.d, function (item) {
return {
value: item
}
}))
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(textStatus);
}
});
},
});

Knockout.js Adding data by using push method not updating view

I'm getting data from WebApi on JSON format and then adding received data to MVC View by using .push() method of KnockoutJS. The JSON data I received on POST response is correct, so I believe it's something wrong on client side - instead of data I'm geting undefined and [Object].
Although after page refresh all data showing correctly.
Here my knockout code:
<script>
var viewModel = {
prepp: ko.observableArray(),
currentPage: ko.observable(-1)
};
$(function () {
getData(viewModel.currentPage() + 1);
ko.applyBindings(viewModel, document.getElementById("prepps"));
});
//This function used for paging, not concern to question directly
function getData(pageNumber) {
if (viewModel.currentPage() != pageNumber) {
$.ajax({
url: "/api/index",
type: "get",
contentType: "application/json",
data: { id: pageNumber }
}).done(function (data) {
if (data.length > 0) {
viewModel.currentPage(viewModel.currentPage() + 1);
for (var i = 0; i < data.length; i++) {
viewModel.prepp.push(data[i]);
}
}
});
};
//Here we call POST action of WebApi.
$(".send-feed").click(function () {
var guid = getguid();
var styles;
var req = { RequestName: $("#request").val(), RequestDescription: $("#request-review").val(), RequestOwner: $("#username").val(), RequestGuid: guid, RequestStyles: [] }
$("div.click").each(function () {
styles = { RequestGuid: guid, StyleId: $(this).text() };
req.RequestStyles.push(styles);
});
var model = JSON.stringify(req);
$.ajax({
url: "/api/index",
type: "POST",
contentType: "application/json, charset: utf-8",
data: model
}).done(function (data) {
viewModel.prepp.push(data);
});
});
}
});
</script>
And here is the MVC View markup:
div class="prepp-blocks-container" data-bind="foreach: prepp" id="prepps">
<div class="prepp-block">
<div class="star" data-bind="if: $data.IsStylistOffer == true">
<img src="../../img/star-yellow.png" alt="added by stylist">
</div>
<a data-bind="attr: {href: 'Request/' + $data.RequestGuid}"><h3 data-bind="text: $data.RequestName"></h3></a>
<span data-bind="foreach: {data: RequestStyles, as: 'style'}">
<div data-bind="text: style" class="taste-prepp"></div>
</span>
<p class="text-small-grey" data-bind="text: $data.PreppsNumber + ' prepps'"></p>
</div>
</div>
I believe your return type from controller should be adjusted so it will match the view model structure like
public model Get()
{
//build your list .
return model ;
}
Try to use ko.mapping.toJS() so knockout advantages are not lost .
Refer knockout doc's you can find more relevant info how we can better use it Here

KnockOut ajax with MVC

This is My KnockOut Controller Class
public class KnockoutController : Controller
{
//
// GET: /Knockout/
private DataLayer data;
public KnockoutController()
{
data=new DataLayer();
}
public ActionResult Index()
{
return View();
}
public ActionResult Grid()
{
ArrayList rows = new ArrayList();
List<Category> categories = data.RetrivingCategories();
foreach (var category in categories)
{
rows.Add(new { Id = category.Id, cell = new object[] { category.Id,
category.Name } });
}
var jsonData = new
{
rows
};
return Json(jsonData, JsonRequestBehavior.AllowGet);
}
}
And This is My Index View
#{
ViewBag.Title = "Test";
}
<h2>Index</h2>
<title></title>
<script src="~/Scripts/jquery-ui-1.8.20.js"></script>
<script src="~/Scripts/knockout-2.1.0.debug.js"></script>
<script src="~/Scripts/knockout-2.1.0.js"></script>
<link href="~/Content/Site.css" rel="stylesheet" />
<table class="table">
<tr>
<th>
Key
</th>
<th>
Value
</th>
<th></th>
</tr>
<tbody data-bind="foreach: lookupCollection">
<tr>
<td data-bind="text: Key"></td>
<td data-bind="text: Value"></td>
</tr>
</tbody>
</table>
<script>
viewModel = {
lookupCollection: ko.observableArray()
};
$(function () {
$.ajax({
type: "GET",
url: "#Url.Action("Grid", "Knockout")",
}).done(function (data) {
$(data).each(function (index, element) {
var mappedItem =
{
Id: ko.observable(element.id),
Key: ko.observable(element.name),
};
viewModel.lookupCollection.push(mappedItem);
});
ko.applyBindings(viewModel);
}).error(function (ex) {
alert("Error");
});
});
</script>``
I am Calling json data from this index view using ajax call method but it's not returning anything, Any problem on my Script Part, Please help me , i am new to the Knockout..
Try this
$(function () {
$.ajax({
type: "GET",
url: "#Url.Action("Grid","Knockout")",
}).done(function (data) {
$.each(data, function (index, element) {
var mappedItem = {
Id: ko.observable(element.id),
Key: ko.observable(element.name),
};
viewModel.lookupCollection.push(mappedItem);
});
ko.applyBindings(viewModel);
}).error(function (ex) {
alert("Error");
});
});
From the jQuery API docs,
The $.each() function is not the same as $(selector).each(), which is
used to iterate, exclusively, over a jQuery object. The $.each()
function can be used to iterate over any collection, whether it is an object or an array.

Exception management in ASP.NET MVC using jquery

I have 2 question, in the first one, I get a list, I'd like if excetpion in C# code (controller) has the possibility to a View (an error view) and display it a specific div. How can I get the view in the .error. ?
HTML
<div>List</div>
jQuery
$(".MnuCustomerList").click(function () {
var jqxhr = $.post("/Customer/List", function (data) {
$('#rightcolumn').html(data);
})
.success(function () { alert("success"); })
.error(function () { alert("error"); })
.complete(function () { alert("complete"); });
})
;
Controller:
public PartialViewResult List()
{
throw new Exception("myexception");
return PartialView("List");
}
Second question :
I have a form
#model MyModel
#using (Html.BeginForm("Save", "Customer", FormMethod.Post))
{
<table style="width:100%;">
<tr>
<td>Code</td>
<td>#Html.TextBoxFor(m => m.Customer.Code, new { id = "tbCode" })</td>
</tr>
<tr>
<td>LastName</td>
<td>#Html.TextBoxFor(m => m.Customer.LastName, new { id = "tb", maxlength = 50, style = "width:40%;" })</td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="A submit button"/></td>
</tr>
</table>
}
In the Controller, I check if the code already exist, if yes CustomerException("Code exist").
My questions, is it possible to post this form using jQuery BUT still using this format that's mean with the model and NOT get one value by one value like the sample below
$.ajax({
type: "POST",
url: "/Customer/Save",
data: {
id: $('#Id').val(),
firstName: $('#FirstName').val(),
lastName: $('#LastName').val(),
isEnable: $('#IsEnable').attr('checked')
},
success: function (html) {
$("#ContentDataSection").html(html);
},
error: function (XMLHttpRequest, textStatus, errorThrown) { }
});
Thanks,
The error callback is only going to fire if there is an error in making the ajax request. If an error occurs server side, you'll need to pass data back (in Json format would be a good choice) to the client indicating there was a failure server side and handle it in the success callback.
Edit to add code showing how to set response code to 500 so it can be handled in the error call back per Ryan's comment:
Controller action:
public ActionResult FiveHundred()
{
Response.StatusCode = 500;
return Json(new {Error = "Uh oh!"});
}
Javascript:
$.ajax({
url: "/home/fivehundred",
type: "POST",
success: function(data) {
// handle normally
},
error: function(data) {
alert("error " + data.Error);
}
});

Categories

Resources