knockout mapping (ko.mapping.fromJS()) not updating targets when async $.ajax call - ajax

I am trying to use ko.mapping to transform data to a viewmodel. The result come from an async call to a web service.
Simple view:
<div data-bind="with: myData">
<div data-bind="text: mapWidth"></div>
<div data-bind="text: mapHeight"></div>
<div data-bind="foreach: priceRanges">
<div data-bind="text: title"></div>
</div>
AND the view model is as follows:
var ViewModel = function() {
var self = this;
self.myData = null;
var data = {
action: 'GetPricePoints',
type: 'who cares'
};
$.ajax({
url: 'http://cdt.lotamore.com/api/imap',
data: { "": JSON.stringify(data) },
//async: false, // works!
async: true, // doesn't work!!!!!
type: 'post'
})
.done(function (data) {
self.myData = ko.mapping.fromJS(data);
})
.error(function (data) {
alertError(data);
})
.always(function () {
});
};
When I run this synchronously is works fine. However, when I run it asynchronously it doesn't update the myData within my view model.
I have created a fiddle here:
http://jsfiddle.net/jgergen/jF9pm/20/
I really don't want to make ajax calls that block the UI thread!
What am I doing wrong here?
Thanks,
Jim

Here is the solution:
I have changed the line:
self.myData = null;
to:
self.myData = ko.observable();
This is not all, I tried this before without favorable results. In addition to making myData observable, I had to change the way it is updated. Here is the new .done method:
.done(function (d) {
self.myData(ko.mapping.fromJS(d));
})
So, what I needed to do is treat this field as I would any other observable.
You can see it here at this new fiddle: http://jsfiddle.net/jgergen/jF9pm/47/
I hope that this helps more than me. I searched for a solution to this problem all over the web without any results. So here it is.
Thanks,
Jim

Related

Ajax alert after controller's method success

I have a problem that I was stacked on for a while. I am rather new to this stuff so please be patient. I think it is like very simple to solve but I am running circles now. My intention is to make a simple alert that informs that method was completed. As you can see below my first attempt was made with simple string and if statement. I dont want to reload the page any more but now I have a problem that method ends later than alert script starts.
So I have a controller:
[HttpPost]
public ActionResult Executor(LMCMainViewModel model)
{
var curlsExecutor = _curls;
var applicationPurgeCurl = curlsExecutor.GetApplicationCurl(model);
var temporary = model.SuccessExecutionStatus = curlsExecutor.CurlCaller(model.ApplicationList.ApplicationCurl);
var tempListOfApplications = PopulateModel(model);
model.ApplicationList.ListOfApplications = tempListOfApplications.ApplicationList.ListOfApplications;
model.SuccessExecutionStatus = temporary;
return View("ListOfApplications", model);
}
And I have my view:
#model LMC.Models.LMCMainViewModel
#{
ViewData["Title"] = "Liberation";
}
#using (Html.BeginForm("HeaderString", "LMC"))
{
}
#using (Html.BeginForm("Executor", "LMC", FormMethod.Post))
{
<div class="col-sm-2" asp-action="ListOfApplications">
#Html.DropDownListFor(x => x.ApplicationList.ChosenApplication, Model.ApplicationList.ApplicationListItem, new { #id = "DropdownID" })
</div>
<div class="col-sm-2 col-sm-push-5">
#Html.HiddenFor(x => x.ApplicationList.ApplicationListItem)
<input class="btn ctn-success" id="submit" type="submit" value="Submit" />
<button id="submitButtonAjax" type="button" class="btn btn-success">Ajax button</button>
<div class="col-sm-12">
#Html.Label(null, (Model.SuccessExecutionStatus ? "Success" : " "),
new { Style = Model.SuccessExecutionStatus ? "color: green;" : "color: red;" })
</div>
</div>
}
Than I tried to implement many variations of Ajax script but I got so twisted that I can't even post you the best one... One thing I know is that when I remove: #using (Html.BeginForm("Executor", "LMC", FormMethod.Post)) and try to place it into Ajax it simply doesn't work. My intention is to have something that would work like this:
<script type="text/javascript">
$("#submitButtonAjax").on("click", function () {
$.ajax({
type: 'POST',
url: "/LMC/Executor",
success: function () {
alert("Went well");
}
});
</script>
I tried to convert controller method return into Json but it didnt work as well. I would appreciate any advices, where I can read about anything similar(I am aware that there are probably many similar topics, but I wan't able to implement anything that was working into my code, because I find my question one step backwards in comparison to others), or maybe it is that easy and I am missing something and you can just post a solution. Anyway, many thanks for any help.
The ajax call should look like bellow
$( document ).ready(function() {
$("#submitButtonAjax").on("click", function () {
var postData = {
FirstPropertyOfTheModel: ValueForTheFirstProperty,
SecondPropertyOfTheModel: ValueForTheSecondProperty,
};
$.ajax({
type: 'POST',
url: "/LMC/Executor",
data:postData,
success: function () {
alert("Went well");
},
error: function () {
alert("Opssss not working");
}
});
});
});
data is the value for the model of ActionResult method of the controller. The FirstPropertyOfTheModel and SecondPropertyOfTheModel will be replaced by the name of property and assign the corresponding value respectably. At url you can use
'#Url.Action("Executor", "LMC")'
so the ajax will look like
$( document ).ready(function() {
$("#submitButtonAjax").on("click", function () {
var postData = {
FirstPropertyOfTheModel: ValueForTheFirstProperty,
SecondPropertyOfTheModel: ValueForTheSecondProperty,
};
$.ajax({
type: 'POST',
url: '#Url.Action("Executor", "LMC")',
data:postData,
success: function () {
alert("Went well");
},
error: function () {
alert("Opssss not working");
}
});
});
});

Using ajax to update the currently page with Laravel

I have this ajax inside my file.php (It hits the success callback):
<script type="text/javascript">
$('#selectSemestres').change(function(obj){
var anoSemestre = $(this).val();
$.ajax({
type: 'GET',
url: '{{ route('professor') }}',
data: {anoSemestre: anoSemestre},
success: function(data){
console.log(data);
}
});
})
</script>
Now on my Controller:
public function getProfessorList()
{
$professor = Professor::all();
$ano_semestre = isset($_GET['anoSemestre']) ? $_GET['anoSemestre'] : Horario::first()->distinct()->pluck('ano_semestre');
$semestres = Horario::distinct()->select('ano_semestre')->get()->toArray();
return View::make('professor', compact('professor', 'semestres', 'ano_semestre'));
}
What I want to do:
I have a LIST with professor and their disciplines. What I need to do is:
Whenever I change the value of that select box, I just remake the function with the new parameter.
I'm trying to use ajax to remake that list but nothing change, not even the URL with the professor.php?anoSemestre=xx.
Also, when I try to use the $_GET['anoSemestre'] the page doesnt show any change or any ECHO.
But If I go to Chrome spector>NEtwork and click the ajax I just made, it shows me the page with the data I sent.
Cant find out what I'm doing wrong.
UPDATE
I did what was suggested me, now I'm working with the data I get from the success callback:
<script type="text/javascript">
$('#selectSemestres').change(function(obj){
var anoSemestre = $(this).val();
$.ajax({
type: 'GET',
url: '{{ route('professor') }}',
data: {anoSemestre: anoSemestre},
success: function(data){
var lista = $(data).find('#list-professores'); //Get only the new professor list and thier disciplines
$('#list-professores').remove(); //Remove old list
$('#professores').append(lista); //Append the new list where the old list was before.
}
});
})
</script>
The return of var lista = $(data).find('#list-professores'); is:
Accordion Effect
#list-professores li input[name='item']:checked ~ .prof-disciplinas {
height: auto;
display:block;
min-height:40px;
max-height:400px;
}
This list is an Accordion Menu (using a checkbox and changing it with js&css), so everytime I click on a professor < li>, it's suppose to open and show a sublist (disciplines of that professor I clicked). But it's not opening anymore and no errors on the console. No idea why.
The issue here is what you are returning in your controller and how you do it, you donĀ“t need to redirect or refresh the entire page. This could be achived using a single blade partial for the piece of code you may want/need to update over ajax. Assuming you have an exlusive view for that info, you could solve this with something like this:
in your view:
<div class="tableInfo">
<!--Here goes all data you may want to refresh/rebuild-->
</div>
In your javascript:
<script type="text/javascript">
$('#selectSemestres').change(function(obj){
var anoSemestre = $(this).val();
$.ajax({
type: 'GET',
url: '{{ route('professor') }}',
data: {anoSemestre: anoSemestre},
success: function(){
$('.tableInfo').html(data); //---------> look at here!
}
});
})
</script>
in your controller:
public function getProfessorList()
{
$professor = Professor::all();
$ano_semestre = isset($_GET['anoSemestre']) ? $_GET['anoSemestre'] : Horario::first()->distinct()->pluck('ano_semestre');
$semestres = Horario::distinct()->select('ano_semestre')->get()->toArray();
if (Request::ajax()) {
return response()->json(view('YourExclusiveDataPartialViewHere', ['with' => $SomeDataHereIfNeeded])->render()); //---------> This is the single partial which contains the updated info!
}else{
return View::make('professor', compact('professor', 'semestres', 'ano_semestre'));//---------> This view should include the partial for the initial state! (first load of the page);
}
}

Updating a view from an ajax controller call

Here's the thing, I'm trying to update a view where an image and a DateTime are displayed.
I want the image and the time to update without reloading the page. So after some research I found that using an Ajax call should do it.
For some reason neither the time or the image update after the ajax call and I can't figure out why.
Am I missing something, or it's not the way I should do it?
Please keep in mind, I am new to asp.net and Ajax. Thanks :)
Here's my View with the Ajax script:
#model myApp.Models.ImageViewModel
#{
ViewBag.Title = "Image";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<script>
function refreshImage() {
$.ajax({
//url: "/Controller/ActionName",
async: true,
url: '/Image/GetImage',
type: 'GET',
success: function () {
setTimeout(refreshImage, 5000);
}
});
}
$(document).ready(function () {
setTimeout(refreshImage, 5000);
});
</script>
<div>
<div class="panel panel-info">
<div class="panel-heading"><h4>Image</h4></div>
<div class="panel-body">
<div class="ImgContainer">
<img src=#Model.ImageUrl>
<div>#Model.LastUpdateDate</div>
</div>
</div>
</div>
</div>
And here's the controller method:
public ActionResult GetImage()
{
DateTime lastUpdate = System.DateTime.Now;
string imageUrl = "http://lorempixel.com/1920/1080/animals";
ImageViewModel image = new ImageViewModel(lastUpdate, ImageUrl);
Debug.WriteLine("test");
return View(image);
}
Change your ajax call to this
function refreshImage() {
$.ajax({
dataType:'image/jpg',
url: '/Image/GetImage',
type: 'GET',
success: function () {
setTimeout(refreshImage, 5000);
}
});
}
Ok so I figured out what was going on. Since the url I used wasn't changing I forced the refresh of the image with a dull parameter on the url and also got the time to update at the same time.
Now my ajax call look like this.
$.ajax({
//url: "/Controller/ActionName"
url: '/Image/GetImage',
type: 'GET',
success: function () {
var time = new Date().toLocaleString();
$("#imageCam").attr("src", "#Model.ImageUrl" + "?" + time);
document.getElementById("theTime").innerHTML = time;
setTimeout(refreshImage, 5000);
}
});
Hope this help someone in the future.
See this question for more info
How to reload/refresh an element(image) in jQuery

MVC3 AJAX Cascading DropDownLists

I am having a hard time figuring out how to get cascading drop down lists to work for my asp.net mvc3 application. I have a popup box and I would like to display 2 dropdownlists, the 2nd being populated based on what is selected in the first. Each time I run the application the controller method returns the correct list of values, but instead of hitting the success part of the ajax call I hit the error part. I have done lots of research and followed several examples I have found but something is still not quite right, any help would be greatly appreciated.
Edit:
Further inspection using firebug shows an error 500 internal server error which states: Exception Details: System.InvalidOperationException: A circular reference was detected while serializing an object of type 'System.Data.Entity.DynamicProxies.GameEdition
I have the following jQuery / AJAX:
<script type="text/javascript">
$(function () {
$("#PlatformDropDownList").change(function () {
var gameId = '#Model.GameID';
var platformId = $(this).val();
// and send it as AJAX request to the newly created action
$.ajax({
url: '#Url.Action("Editions")',
type: 'GET',
data: { gameId: gameId, platformId: platformId },
cache: 'false',
success: function (result) {
$('#EditionDropDownList').empty();
// when the AJAX succeeds refresh the ddl container with
// the partial HTML returned by the PopulatePurchaseGameLists controller action
$.each(result, function (result) {
$('#EditionDropDownList').append(
$('<option/>')
.attr('value', this.EditionID)
.text(this.EditionName)
);
});
},
error: function (result) {
alert('An Error has occurred');
}
});
});
});
Here is my controller method:
public JsonResult Editions(Guid platformId, Guid gameId)
{
//IEnumerable<GameEdition> editions = GameQuery.GetGameEditionsByGameAndGamePlatform(gameId, platformId);
var editions = ugdb.Games.Find(gameId).GameEditions.Where(e => e.PlatformID == platformId).ToArray<GameEdition>();
return Json(editions, JsonRequestBehavior.AllowGet);
}
Here is my web form html:
<div id="PurchaseGame">
#using (Html.BeginForm())
{
#Html.ValidationSummary(true, "Please correct the errors and try again.")
<div>
<fieldset>
<legend></legend>
<p>Select the platform you would like to purchase the game for and the version of the game you would like to purchase.</p>
<div class="editor-label">
#Html.LabelFor(model => model.PlatformID, "Game Platform")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.PlatformID, new SelectList(Model.Platforms, "GamePlatformID", "GamePlatformName"), new { id = "PlatformDropDownList", name="PlatformDropDownList" })
</div>
<div class="editor-label">
#Html.LabelFor(model => model.EditionID, "Game Edition")
</div>
<div id="EditionDropDownListContainer">
#Html.DropDownListFor(model => model.EditionID, new SelectList(Model.Editions, "EditionID", "EditionName"), new { id = "EditionDropDownList", name = "EditionDropDownList" })
</div>
#Html.HiddenFor(model => model.GameID)
#Html.HiddenFor(model => model.Platforms)
<p>
<input type="submit" name="submitButton" value="Purchase Game" />
<input type="submit" name="submitButton" value="Cancel" />
</p>
</fieldset>
</div>
}
You cannot send JSON encoded requests using the GET verb. So replace type: 'GET' with type: 'POST' and it will work. Also since you have specified a JSON request you must, well, send a JSON request which is achieved with the JSON.stringify function: data: JSON.stringify({ gameId: gameId, platformId: platformId }),. But since you only have 2 values I think that using GET would be easier. So my recommendation is to remove the contentType: 'application/json' parameter and have your AJAX request look like this:
$.ajax({
url: '#Url.Action("Editions")',
type: 'GET',
data: { gameId: gameId, platformId: platformId },
cache: 'false',
success: function (result) {
$('#EditionDropDownList').empty();
// when the AJAX succeeds refresh the ddl container with
// the partial HTML returned by the PopulatePurchaseGameLists controller action
if(result.length > 0)
{
$.each(result, function (result) {
$('#EditionDropDownList').append(
$('<option/>')
.attr('value', this.EditionID)
.text(this.EditionName)
);
});
}
else
{
$('#EditionDropDownList').append(
$('<option/>')
.attr('value', "")
.text("No edition found for this game")
);
}
},
error: function () {
alert('An Error has occured');
}
});
Also in the DropDownListFor helper in your Razor markup I notice the following:
onchange = "Model.PlatformID = this.value;"
All I can say is that this doesn't do what you might think it does.
UPDATE:
It seems that you are getting a circular object reference error because you are passing your editions domain model to the Json method. Circular reference object hierarchies cannot be JSON serialized. Besides you don't need to waste the bandwidth by sending all the crap contained in this editions to the client. All your client needs is a collection of ids and names. So simply use view models:
public ActionResult Editions(Guid platformId, Guid gameId)
{
var editions = ugdb
.Games
.Find(gameId)
.GameEditions
.Where(e => e.PlatformID == platformId)
.ToArray<GameEdition>()
.Select(x => new
{
EditionID = x.EditionID,
EditionName = x.EditionName
})
.ToArray();
return Json(editions, JsonRequestBehavior.AllowGet);
}

Updating a dropdown via knockout and ajax

I am trying to update a dropdown using knockout and data retrieved via an ajax call. The ajax call is triggered by clicking on a refresh link.
The dropdown is successfully populated when the page is first rendered. However, clicking refresh results in clearing the dropdown instead of repopulating with new data.
Html:
<select data-bind="options: pages, optionsText: 'Name', optionsCaption: 'Select a page...'"></select>
<a id="refreshpage">Refresh</a>
Script:
var initialData = "[{"Id":"231271443653720","Name":"Car2"},{"Id":"439319486078105","Name":"Electronics1.2"},{"Id":"115147185289433","Name":"Product"},{"Id":"145033098963549","Name":"Product2"}]";
var viewModel = {
pages : ko.mapping.fromJS(initialData)
};
ko.applyBindings(viewModel);
$('#refreshpage').click(function() {
$.ajax({
url: "#Url.Action("GetPageList", "FbWizard")",
type: "GET",
dataType: "json",
contentType: "application/json charset=utf-8",
processData: false,
success: function(data) {
if (data.Success) {
ko.mapping.updateFromJS(data.Data);
} else {
displayErrors(form, data.Errors);
}
}
});
});
Data from ajax call:
{
"Success": true,
"Data": "[{"Id":"231271443653720","Name":"Car2"},{"Id":"439319486078105","Name":"Electronics1.2"},{"Id":"115147185289433","Name":"Product"},{"Id":"145033098963549","Name":"Product2"}]"
}
What am I doing wrong?
The problem you have is that you are not telling the mapping plugin what to target. How is it suppose to know that the data you are passing is supposed to be mapped to the pages collection.
Here is a simplified version of your code that tells the mapping what target.
BTW The initialData and ajax result were the same so you wouldn't have noticed a change if it had worked.
http://jsfiddle.net/madcapnmckay/gkLgZ/
var initialData = [{"Id":"231271443653720","Name":"Car2"},{"Id":"439319486078105","Name":"Electronics1.2"},{"Id":"115147185289433","Name":"Product"},{"Id":"145033098963549","Name":"Product2"}];
var json = [{"Id":"231271443653720","Name":"Car2"},{"Id":"439319486078105","Name":"Electronics1.2"},{"Id":"115147185289433","Name":"Product"}];
var viewModel = function() {
var self = this;
this.pages = ko.mapping.fromJS(initialData);
this.refresh = function () {
ko.mapping.fromJS(json, self.pages);
};
};
ko.applyBindings(new viewModel());
I removed the jquery click binding. Is there any reason you need to use a jquery click bind instead of a Knockout binding? It's not recommended to mix the two if possible, it dilutes the separation of concerns that KO is so good at enforcing.
Hope this helps.

Resources