Knockout JSON data reloading issue with ASP.NET MVC - asp.net-mvc

I've loaded an ASP.NET MVC viewModel into KnockoutJS using ko.mapping.fromJS(Model).
My viewModel looks something like this:
public IEnumerable<FunkyThing>funkyThings;
public FunkyThing selectedFunkyThing;
Each FunkyThing has a string property funkyThingName.
The mapping worked fine and I can see all the funky things in the table with their names.
I want to add a quick refresh button. So I've created a simple button and then data bound the buttons click to a knockout function refresh which looks something like this:
model.refresh= function () {
var url = '#Url.Action(MVC.FunkyThings.RefreshJSON())';
$.getJSON(url, function (returnedData) {
ko.mapping.fromJS(returnedData, {}, model.funkyThings);
});
The refresh function is succesfully called which in turn calls the RefreshJSON method on the server. The server passes back JSON data - an updated array of funkyThings, which I can see within chrome when I hover over returnedData in chrome's debugger.
However unfortunately after the mapping function has been called the bindings break:
Uncaught Error: Unable to parse bindings.
Message: ReferenceError: funkyThingName is not defined;
Bindings value: text: funkyThingName
And I'm not sure why...?

Is model.funkyThings an observable? If it is, then you can try passing it into the mapping method as a function:
ko.mapping.fromJS(returnedData, {}, model.funkyThings());
Failing that, are you sure that the structure of the JSON returned by the refresh method is correct?
Ah, if you're getting a JSON string back, so you need to call the fromJSON method of the mapping plugin:
ko.mapping.fromJSON(returnedData, {}, model.funkyThings);
You might need the parenthesis on funkyThings here too, but try it without first.

If the returned object is in the correct format and also the binding is well done you
just need to do:
model.FunkyThings(returnedData).

Related

Filterrific, trigger submitFilterForm on event from another lib

I'm using Foundation 6 and Filterrific gem in a Rails project. I'd like to trigger the following:
$(document).on('changed.zf.slider', Filterrific.submitFilterForm);
'changed.zf.slider' is the event that Foundation emits. Filterrific.submitFilterForm is the function I'd like to call. However, it doesn't work even if I bind to the event like this:
$(document).on('changed.zf.slider', function() { Filterrific.submitFilterForm() });
The code above is defined AFTER Filterrific code is loaded in the browser.
Is this an issue with Filterrific jquery code or should I use a different method for binding to event?
It works fine if I simply copy the original Filterrific.submitFilterForm method body to my binding like
$(document).on('changed.zf.slider', function() {
var form = $("#filterrific_filter"),
url = form.attr("action");
$.ajax({...
... but it feels like it's not the way to go right? ;)
The tricky thing here is that Filterrific.submitFilterForm depends on this context passed to it and looks for the parent <form> element.
So you need to call the function with a right context, e.g.:
$(document).on('changed.zf.slider', function() {
Filterrific.submitFilterForm.call($('#filterrific_filter .slider'))
});

Knockout mapping is not updating my model

I'm having trouble with a knockout model that is not binding on a subscribed update. I have a C# MVC page that delivers a model to the template which is parsed to Json and delivered raw as part of a ViewModel assignment for ko.applyBindings. I have a subscription to an observable that calls a method to perform an update of the viewModel's data. Irrelevant stuff pulled out and renamed for example usage:
var myViewModel = function (data) {
var self = this;
self.CurrentPage = ko.observable();
self.SomeComplexArray= ko.observableArray([]);
self.Pager().CurrentPage.subscribe(function (newPage) {
self.UpdateMyViewModel(newPage);
});
self.UpdateMyViewModel= function (newPage) {
var postData = { PageNumber: newPage };
$.post('/Article/GetMyModelSearchByPage', postData, function (data) {
ko.mapping.fromJS(data, {}, self);;
});
};
When I perform logging, I can see all of the data, and it all looks correct. The same method is used to produce both the initial model and the updated model. I've used this technique on other pages and it worked flawlessly each time. In this case however, I'm looking for it to bind/update SomeComplexArray, and that's just not happening. If I attempt to do it manually, I don't get a proper bind on the array I get blank. I'm wondering if there is something obvious that I'm doing wrong that I'm just flat out missing.
Edit: I don't know that ko.mapping can be pointed to as the culprit. Standard model changes are also not affecting the interface. Here is something that is not working in a bound sense. I have a p element with visible bound to the length of the array and a div element with a click bound to a function that pops items off of SomeComplexArray. I can see in the console log that it is performing its function (and subsequent clicks result in 'undefined' not having that function). However, the p element never displays. The initial array has only 2 items so a single click empties it:
<p data-bind="visible: SomeComplexArray().length === 0">nothing found</p>
<div data-bind="click: function() { UpdateArray(); }">try it manually</div>
-- in js model
self.UpdateArray = function () {
console.log(self.SomeComplexArray());
console.log(self.SomeComplexArray().pop());
console.log(self.SomeComplexArray());
console.log(self.SomeComplexArray().pop());
console.log(self.SomeComplexArray());
});
Edit 2: from the comment #Matt Burland, I've modified how the pop is called and the manual method now works to modify the elements dynamically. However, the ko.mapping is still not functioning as I would expect. In a test, I did a console.log of a specific row before calling ko.mapping and after. No change was made to the observableArray.
I created a test of your knockout situation in JSFiddle.
You have to call your array function without paranthesis. I tested this part:
self.UpdateArray = function () {
self.SomeComplexArray.pop();
};
It seems to be working on JSFiddle side.
I'm not really sure why, but it would seem that ko.mapping is having difficulty remapping the viewmodel at all. Since none of the fields are being mapped into self my assumption is that there is an exception occurring somewhere that ko.mapping is simply swallowing or it is not being reported for some other reason. Given that I could manually manipulate the array with a helpful tip from #MattBurland, I decided to backtrack a bit and update only the elements that needed to change directly on the data load. I ended up creating an Init function for my viewModel and using ko.mapping to populate the items directly there:
self.Init = function (jsonData) {
self.CurrentPage(0);
self.Items(ko.mapping.fromJS(jsonData.Items)());
self.TotalItems(jsonData.TotalItems);
// More stuff below here not relevant to question
}
The primary difference here is that the ko.mapping.fromJS result needed to be called as a function before the observableArray would recognize it as such. Given that this worked and that my controller would be providing an identical object back during the AJAX request, it was almost copy/past:
self.UpdateMyViewModel= function (newPage) {
var postData = { PageNumber: newPage };
$.post('/Article/GetMyModelSearchByPage', postData, function (data) {
self.Items(ko.mapping.fromJS(JSON.parse(data).Items)());
});
};
This is probably not ideal for most situations, but since there is not a large manipulation of the viewModel occurring during the update this provides a working solution. I would still like to know why ko.mapping would not remap the viewModel at the top level, but in retrospect it probably would have been a disaster anyway since there was "modified" data in the viewModel that the server would have had to replace. This solution is quick and simple enough.

knockout js binding limitation on complex view model

I am 3 months into learning KnockoutJS and it has been great so far. However, I am facing an issue with binding.
This is the scenario:
I am using MVC with KO.
MVC model is passed down to the view, converted into a knockout object and pushed into the viewModel variable:
var data = ko.mapping.fromJS(#Html.Raw(Json.Encode(Model)));
var viewModel = new HP.ViewModels.CertificationPathViewModel(data);
ko.applyBindings(viewModel);
Within viewModel, I reference the MVC model as self.data:
ViewModels.CertificationPathViewModel = (function (data) {
var self = ViewModels.BaseEntityViewModel.apply(this, [data]);
// some other code
return { Data: self.Data, };
}
ViewModels.BaseEntityViewModel = (function (data) {
var self = this;
self.data = ko.observable(data);
// other code
return { Data: self.data, };
}
On the view, I data-bind like this:
<div id="drpControl" data-bind="CustomDropdown: Data().BusinessUnits.SelectedGroup, optionSettings: { CustomOptions: Data().Units.Groups, CustomOptionsCaption: '-- Select Group --' }"></div>
I try to update the self.data after an ajax call. I return the entire MVC model object and attempt to replace self.data like this :
self.data(updatedModel)
My expectation is that KO will take care of the update and no extra binding is needed. It works great for simple binding (ex. Value: Data().Something) but it doesn't work for complex binding (ex. value: Data().BusinessUnits.SelectedGroup ).
The controls that have complex binding are still bound to the old model, so KO doesn't know what to pass back next time I submit an ajax request.
Is this a limitation of KO, or I am not doing something properly?
Thanks
the ko.mapping plugin changes every property on self.data into an observable. During your update, you need to remap the updated data.
Since you didn't actually post your code, just unformatted snippets I can't help a whole bunch, but you should start by changing this line: self.data(updatedModel) to this:
ko.mapping.fromJS(updatedModel, self.data);
see the Knockout.JS mapping documentation
Protip for stack overflow - include your full code, to the extent that it's possible. Also, if you can, make a jsfiddle that reproduces your problem.

HTMLDocument object from HTTP read()

I need to call at server side, an URL and work with the HTML content off the response. For this I'm using the HTTP library from Dart like this :
http.read('myUrl').then((contents) {
//contents to HTMLDocument format //Need to transform the String contents to HTML object
});
And I want to convert the response to a HTMLDocument (or other object I don't know) to be able to retrieve element in it by HTML tag or CSS class, like with JQuery for example.
Does anybody have an idea to perform this ?
You canuse html5lib package from pub. It allows to parse HTML and present it DOM like element tree on server side. The element tree will eventually "be compatible with dart:html, so the same code will work on the client and the server" in the future. See the readme for a getting started example.
"I need to call at server side"
Not sure exactly what you mean.
If you are running in the browser and calling the server you could try using a DocumentFragment. Something like this:
http.read(url).then((html) {
var fragment = new DocumentFragment(html);
var element = fragment.query('.foo');
// code here...
});
Otherwise if you're running server side, as the other answer mentions, html5lib is the way to go. Last time I looked the query() method in html5lib only supported tagname queries not classes, or ids.

Grails g:remoteLink response

How can I get the response from an ajax call made with g:remoteLink, using jquery ?
I have tried using nSuccess="removeTask(e)" and getting the response with e.responseText or e.response, but nothing works.
When using Grails with the JQuery plug in and using the remote functions like remoteLink, the code that is generated for the remote function is something like this:
success: function(data, textStatus){ jQuery('#results').html(data); }
This is if for example you set the update parameter as "[success:'results']". As you can see the main function receives a data parameter which I think is what your looking for, so if you need to call another function that uses that value, you could do something like this:
<g:remoteLink controller="yourcontroller" action="youraction" update="[success: 'results']" onSuccess="yourFunction(data) ">Your link</g:remoteLink>
This will generate javascript code like this:
success:function(data,textStatus){ jQuery('#results').html(data); yourFunction(data); }
Hope this helps!!

Resources