Extremely frustrating reading ajax in a ko.computed

3,039 views
Skip to first unread message

Stacey

unread,
Dec 8, 2012, 7:58:30 PM12/8/12
to knock...@googlegroups.com
I am simply trying to pull some data from an ajax request. The ajax call works - I know the data is retrieved. But it just isn't setting the value of the ko.computed... 

function viewModel() {
this.Id = ko.observable("@Model.Identity()");
this.Points = ko.computed({ 
read: function () {
$.ajax({
url: '/skills/points',
data: { id: this.Id },
type: 'get',
success: function (data) {
return data;
}
});
},
owner: this
});
}

So a call like ...

<span data-bind="text: Points"></span>

Just isn't working. Can anyone help me figure out what I am doing so wrong?

Gunnar Liljas

unread,
Dec 9, 2012, 5:55:13 AM12/9/12
to knock...@googlegroups.com
AJAX calls are asynchronous, so you can't use it like that. Why are you using a computed? Can't see anything that would trigger it, except calling it directly,

Thinus Barnard

unread,
Dec 11, 2012, 4:13:10 PM12/11/12
to knock...@googlegroups.com
The reference to "Id" in the computed should result in the computed firing every time "Id" changes. We couldn't get an async Ajax call in a computed to work either and use a synchronous Ajax call for it. Our scenario is:
- Date of birth changed by user
- Computed observable fires ajax call passing the date of birth and getting an age display string back
- Span bound to computed observable is updated

Any ideas on how to do this asynchronously appreciated.

Gunnar Liljas

unread,
Dec 11, 2012, 5:01:53 PM12/11/12
to knock...@googlegroups.com
Ah, sorry, didn't see that Id was an observable. You have to call it
using Id() though.

Of course, that doesn't really solve the problem. Using "return" in
the "success" callback is futile, since that doesn't return in the
scope of the calling method. It returns its data to JQuery, which
ignores it. You could make a synchronous call (with async:false), but
that is vy highly discouraged. Instead, you could subscribe to changes
on Id, and set the value of a standard observable.

function viewModel() {
var self=this;
this.Id = ko.observable("@Model.Identity()");
this.Points = ko.observable();
this.Id.subscribe(function(newValue){
$.ajax({
url: '/skills/points',
data: { id: this.Id },
type: 'get',
success: function (data) {
self.Points(data);
}
});
});
}

Than again, to calculate the age you really don't have to use an ajax call.

2012/12/11 Thinus Barnard <thin...@gmail.com>:
> --
>
>

Thinus Barnard

unread,
Dec 11, 2012, 5:35:37 PM12/11/12
to knock...@googlegroups.com
Thanks Gunnar, will give that a shot.
You wouldn't think an age calculation requires an ajax call but the age display is formatted according to health industry standards, 30 days, 6 months, 42 years, etc. The standards are to provide a common UI across the health related industries. See http://www.mscui.net if you are really curious. 

Stacey Thornton

unread,
Dec 11, 2012, 6:25:58 PM12/11/12
to knock...@googlegroups.com
I am using "computed" because I am learning, and cannot see any other way to accomplish what I need. I need an ajax call, basically. The data I need to present cannot be presented at the time the View Renders without a lot of extra plumbing.

Thanks for your suggestions, I will try them out. I had to use a very roundabout method for the time being that I am not happy with - but may be trying to re-write it to use some of your suggestions if I have the time.

Dealing with asynchronous stuff seems like a total headache... I have some other questions that are closely related to this, but I will post them separately as I am able to come up with smaller examples.


--
 
 

Stacey

unread,
Dec 14, 2012, 8:20:27 PM12/14/12
to knock...@googlegroups.com
You said that you can't see what would trigger it except being called directly. Shouldn't being data bound be the same thing? Saying 

<span data-bind="text: Points"></span>

call it directly and cause it to evaluate? I really don't understand why it isn't that simple. All of the async stuff I keep looking at makes little to no sense to me.

Stacey

unread,
Dec 14, 2012, 8:25:05 PM12/14/12
to knock...@googlegroups.com
I don't understand why the async stuff even matters. I tell it to bind to a specific observable - and then I update that observable, shouldn't it change? Isn't that the entire point of what knockout is supposed to do? 

Stacey

unread,
Dec 14, 2012, 8:29:33 PM12/14/12
to knock...@googlegroups.com
I've tried updating it with some code that RPN suggested, and it is still being troublesome. I'm just not understanding what I am doing wrong here ... this is the latest attempt at pulling anything from a URL and into an observable, and it still doesn't work. The data gets loaded - that little 'console.log(data);' part in the success function runs and shows me the JSON returned. But the HTML binding never updates... 

<script type="text/javascript">
//an observable that retrieves its value when first bound
ko.onDemandObservable = function (callback, target) {
var _value = ko.observable();  //private observable

var result = ko.computed({
read: function () {
//if it has not been loaded, execute the supplied function
if (!result.loaded()) {
callback.call(target);
}
//always return the current value
return _value();
},
write: function (newValue) {
//indicate that the value is now loaded and set it
result.loaded(true);
_value(newValue);
},
deferEvaluation: true  //do not evaluate immediately when created
});

//expose the current state, which can be bound against
result.loaded = ko.observable();
//load it again
result.refresh = function () {
result.loaded(false);
};

return result;
};

$(document).ready(function () {
function viewModel() {
var self = this;

this.user = ko.onDemandObservable(function(){
return $.ajax({
url: '/home/user/',
data: { id: 1 },
dataType: 'json',
context: this,
success: function (data) {
console.log(data);
return data;
}
}).pipe(function(data) { return data });
}, this);
};
var model = new viewModel();
ko.applyBindings(model);
model.user();
});
</script>

Then in the HTML side ..


<span data-bind="text: user().Name"></span>

Stacey

unread,
Dec 14, 2012, 8:37:24 PM12/14/12
to knock...@googlegroups.com
This is another example more similar to the link RPN gave me, but it still isn't functioning. 

function User(id, name) {
this.Id = ko.observable(id);
this.Name = ko.observable(name);
}
function viewModel() {
var self = this;

this.User = ko.onDemandObservable(this.Update, this);

this.Update = function () {
return $.ajax({
url: '/home/user/',
data: { id: 1 },
dataType: 'json'
}).pipe(function (data) {
return new User(data.Id, data.Name);
});
};
};
var model = new viewModel();
ko.applyBindings(model);
model.User();
});
</script>

Gunnar Liljas

unread,
Dec 15, 2012, 1:16:03 PM12/15/12
to knock...@googlegroups.com
It's asynchronous. By definition, it can't return a value.

return new User(data.Id, data.Name) ends up nowhere..

replace with

self.User(new User(data.Id, data.Name))

/G



2012/12/15 Stacey <stacey.ci...@gmail.com>:
> return new User(data.Id, data.Name);

Stacey Thornton

unread,
Dec 15, 2012, 2:42:35 PM12/15/12
to knock...@googlegroups.com
Trying that yields the following error;

Uncaught TypeError: Cannot call method 'call' of undefined

It appears to be on this line ..

//if it has not been loaded, execute the supplied function
if (!result.loaded()) {
callback.call(target);
Uncaught TypeError: Cannot call method 'call' of undefined
}


On Sat, Dec 15, 2012 at 12:16 PM, Gunnar Liljas <gunnar...@gmail.com> wrote:
self.User(new User(data.Id, data.Name))

Stacey Thornton

unread,
Dec 15, 2012, 2:50:37 PM12/15/12
to knock...@googlegroups.com
By the way, I'd like to just pause and thank you and RPN both - both for patience and help. Last night I was in an extremely frustrated place and I'm sorry if my posts sounded ungrateful.

You're clearly smarter than me here - so I've taken the liberty of putting all of the files into a single pastie so that you can see everything I do. Perhaps I am just doing something extremely stupid.

Here is a link


And in case that is frowned upon (external linking is annoying to some people) here is the whole thing pastied here.

_Layout.cshtml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<meta name="viewport" content="width=device-width" />
	<title>@ViewBag.Title</title>
	<!--jQuery References-->
	<script src="http://code.jquery.com/jquery-1.8.3.js" type="text/javascript"></script>
	<script src="http://knockoutjs.com/downloads/knockout-2.2.0.debug.js"></script>

	<script type="text/javascript">
		//an observable that retrieves its value when first bound
		ko.onDemandObservable = function (callback, target) {
			var _value = ko.observable();  //private observable

			var result = ko.computed({
				read: function () {
//if it has not been loaded, execute the supplied function
					if (!result.loaded()) {
						callback.call(target);
					}
//always return the current value
					return _value();
				},
				write: function (newValue) {
					//indicate that the value is now loaded and set it
					result.loaded(true);
					_value(newValue);
				},
				deferEvaluation: true  //do not evaluate immediately when created
			});

			//expose the current state, which can be bound against
			result.loaded = ko.observable();
			//load it again
			result.refresh = function () {
				result.loaded(false);
			};

			return result;
		};

		$(document).ready(function () {
			function User(id, name) {
				this.Id = ko.observable(id);
				this.Name = ko.observable(name);
			}
			function viewModel() {
				var self = this;

				this.User = ko.onDemandObservable(this.Update, this);

				this.Update = function () {
$.ajax({
						url: '/home/user/',
						data: { id: 1 },
						dataType: 'json'
					}).pipe(function (data) {

						self.User(new User(data.Id, data.Name))

					});
				};
			};
			var model = new viewModel();
			ko.applyBindings(model);
			model.User();
		});
	</script>

</head>
<body>
	@RenderBody()
</body>
</html>

Index.cshtml (View)

1
2
3
4
5
6
7
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<span data-bind="text: User"></span>

HomeController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace KnockoutTests.Controllers {
	public class HomeController : Controller {
		//
		// GET: /Home/
		public ActionResult Index() {
			return View();
		}

		public JsonResult User(int id) {
			return Json(new User {
				Id = "users/1",
				Name = "Stacey",
				Points = new Random(DateTime.UtcNow.Millisecond).Next()
			}, JsonRequestBehavior.AllowGet);
		}
	}
}

User.cs

1
2
3
4
5
6
7
8
9
10
using System;

namespace KnockoutTests {
	public class User {
		public string Id { get; set; }
		public string Name { get; set; }
		public int Points { get; set; }
	}
}

Stacey Thornton

unread,
Dec 15, 2012, 3:38:20 PM12/15/12
to knock...@googlegroups.com
Following more instructions, I have narrowed down some of the problem. Defining the callback as a function on the viewModel doesn't seem to work (I am not sure why) but declaring an inline function does yield ... something different. Still isn't working though. Here is an update.



            this.User = ko.onDemandObservable(function(){

                $.ajax({
                    url: '/home/user/',
                    data: { id: 1 },

                    dataType: 'json',
                    success: function (data) {
                        self.User(new User(data.Id, data.Name));
                    }
                });
            }, this);

            //this.Update = function () {
            //  $.ajax({
            //      url: '/home/user/',
            //      data: { id: 1 },
            //      dataType: 'json',
            //      success: function (data) {
            //          self.User(new User(data.Id, data.Name));
            //      }
            //  });
            //};

        };
        var model = new viewModel();
        ko.applyBindings(model);
        model.User();
    });
</script>

And then trying to display any of the data retrieved still fails.

<span data-bind="text: User.Name"></span>

Stacey Thornton

unread,
Dec 15, 2012, 3:46:04 PM12/15/12
to knock...@googlegroups.com

A bit of a breakthrough! If I change the declarative binding to look like this ..

<span data-bind="with: User">
    <span data-bind="text: Id"></span>
    <span data-bind="text: Name"></span>
</span>

Then I am starting to see results. I think I am almost getting there...

Stacey Thornton

unread,
Dec 15, 2012, 3:58:23 PM12/15/12
to knock...@googlegroups.com
Okay, so I think - I think I have this working. But I'd love someone who understands what in the world is going on to look at it and tell me if this is doing what I think it is doing, or if I am just being fooled. Do I really have an async observable functioning?! or am I just dreaming... 


And the entire source code, for those who do not want to look at the pastie.

_Layout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<meta name="viewport" content="width=device-width" />
	<title>@ViewBag.Title</title>
	<!--jQuery References-->
	<script src="http://code.jquery.com/jquery-1.8.3.js" type="text/javascript"></script>
	<script src="http://knockoutjs.com/downloads/knockout-2.2.0.debug.js" type="text/javascript"></script>


			function User(id, name, points) {

				this.Id = ko.observable(id);
				this.Name = ko.observable(name)
;
				this.Points = ko.observable(points);
			
}
			function viewModel() {
				var self = this;

				this.User = ko.onDemandObservable(function(){
					$.ajax({
						url: '/home/user/',
						data: { id: 1 },
						dataType: 'json',
						success: function (data) {

							self.User(new User(data.Id, data.Name, data.Points));
						}
					});
				}, this);

				this.Refresh = function () {
					self.User.loaded(false);
				
};
			};
			var model = new viewModel();
			ko.applyBindings(model)
;
		});
	</script>
</head>
<body>
	@RenderBody()
</body>
</html>

Index (View)

1
2
3
4
5
6
7
8
9
10
11
12
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<span data-bind="with: User">
	<span data-bind="text: Id"></span>
	<span data-bind="text: Name"></span>
<span data-bind="text: Points"></span>
</span>
<button data-bind="click: Refresh">Refresh</button>

Stacey Thornton

unread,
Dec 15, 2012, 4:13:09 PM12/15/12
to knock...@googlegroups.com
Alright! I think I am in action. Now - one more question I have - unless you see something glaringly wrong with the way I have done the above. 

Can this work with an observableArray? So far I haven't been able to. I made another controller action that returns an array of Users and tried the following.

this.Users = ko.onDemandObservable(function () {
$.ajax({
url: '/home/users/',
dataType: 'json',
success: function (data) {
ko.utils.arrayMap(data, function (option) {
self.Users.push(new User(data.Id, data.Name, data.Points));
});
}
});
}, this);

And it doesn't seem to function. Do I need to do something else to make an Array compatible version?

Stacey Thornton

unread,
Dec 15, 2012, 4:49:24 PM12/15/12
to knock...@googlegroups.com
(obviously, I want to use custom mapping for results retrieved for an array)

Gunnar Liljas

unread,
Dec 15, 2012, 5:11:27 PM12/15/12
to knock...@googlegroups.com
Is there any special reason you are using this "onDemandObservable" stuff? Is there anything particularly "on demand" in your scenario?  Just fetching stuff via AJAX and setting the return value in an observable can be done without all that fluff. The scenario in your demo certainly doesn't need this complexity.

To answer the question about arrays, you would have to create a ko.onDemandObservableArray, which would look exactly like  ko.onDemandObservable, but with the inner "_value" changed to a ko.observableArray() 


2012/12/15 Stacey Thornton <stacey.ci...@gmail.com>
--
 
 

Stacey Thornton

unread,
Dec 15, 2012, 5:14:14 PM12/15/12
to knock...@googlegroups.com
Doing it without the onDemandObservable was my first approach - but when it didn't work and I started to ask questions, I was told it wasn't possible to load anything asynchronously. If this can be done more easily, I'm open to it. The big problem is that the data I need to put on the view is just extremely large, and it needs to be broken into pieces, because it has to post back to the server often to update small parts at a very rapid rate - so it cannot just be included in the initial page render, it needs to be fetched after the page loads, and able to sync up with the database again on demand.


--
 
 

Stacey Thornton

unread,
Dec 15, 2012, 5:19:26 PM12/15/12
to knock...@googlegroups.com
Hrnm. Making a version with an observableArray still doesn't let it access the "push" function.

Stacey Thornton

unread,
Dec 15, 2012, 5:28:48 PM12/15/12
to knock...@googlegroups.com
Even if this isn't the most efficient way of doing this, I am learning more about knockout by trying this - which is always good. 

So I made this; 

//an observable that retrieves its value when first bound
ko.observableArray.async = function (callback, target) {
var _value = ko.observableArray([]);  //private observable

var result = ko.computed({
read: function () {
//if it has not been loaded, execute the supplied function
if (!result.loaded()) {
callback.call(target);
}
//always return the current value
return _value();
},
write: function (newValue) {
//indicate that the value is now loaded and set it
result.loaded(true);
_value(newValue);
},
deferEvaluation: true  //do not evaluate immediately when created
});

//expose the current state, which can be bound against
result.loaded = ko.observable();
//load it again
result.refresh = function () {
result.loaded(false);
};

return result;
};

and am using it like this ... (decided to avoid push)

this.Users = ko.observableArray.async(function () {
$.ajax({
url: '/home/users/',
dataType: 'json',
success: function (data) {
self.Users(ko.utils.arrayMap(data, function (option) {
return new User(data.Id, data.Name, data.Points);
}));
}
});
}, this);

And it actually populates the array, but the bindings don't let me look at it.

Stacey Thornton

unread,
Dec 15, 2012, 5:30:22 PM12/15/12
to knock...@googlegroups.com
Aha! I was using 'data.Id' instead of 'options.Id'.

Okay, I have it working with both an observable and an observableArray. Thank you so much for the help.

You say this can be done without all of this async stuff, though? I'm not understanding how. When I tried before I was told it wasn't possible.

My initial approach was to just declare the viewModel with normal observables, and then have an UpdateModel function that did the Ajax call and filled the observables. And I just could not get it to work.

Gunnar Liljas

unread,
Dec 15, 2012, 5:37:40 PM12/15/12
to knock...@googlegroups.com
Oh, you need the async, just not the "on demand" wrapping.


function viewModel({
    var self this;
    this.User ko.observable();
    this.GetUser function({

        $.ajax({
            url'/home/user/',
            data{
                id1
            },
            dataType'json',
            successfunction(data{
                self.User(new User(data.Iddata.Namedata.Points));
            }
        });
    };

    this.Refresh this.GetUser//well, yeah, not really necessary...
    this.GetUser()//initial call to populate it directly on instantiation. Could be triggered from something else.
};


2012/12/15 Stacey Thornton <stacey.ci...@gmail.com>
--
 
 

Stacey Thornton

unread,
Dec 15, 2012, 6:32:26 PM12/15/12
to knock...@googlegroups.com
Yes, see - that is exactly what I was trying and wasn't getting it to work. That's when I was told I had to follow this async path. Thanks for all of the help, I will try to get better at making intelligent view models.


--
 
 

dave...@groovbird.com

unread,
Dec 16, 2012, 5:58:28 AM12/16/12
to knock...@googlegroups.com
I've been going through the whole thread and my impression is that you have this whole MVVM thing backwards. I made a small example that I think does what you want, and pasted it here. Is this what you want to do?

Dave

dave...@groovbird.com

unread,
Dec 16, 2012, 6:00:13 AM12/16/12
to knock...@googlegroups.com, dave...@groovbird.com
I should have turned it into a fiddle first time around: 

Stacey Thornton

unread,
Dec 16, 2012, 9:41:18 PM12/16/12
to knock...@googlegroups.com
I'm sorry, but I have tried that very structure multiple times and it just never worked. I'm not sure what good subscribing to "Id" is. That never changes. Why does everyone keep using that as the example?


--
 
 

Gunnar Liljas

unread,
Dec 17, 2012, 4:10:41 AM12/17/12
to knock...@googlegroups.com
Sorry, Stacey, got confused by Thinus post in the thread. Id is irrelevant.

My example should work. Note the very big difference between a "return" in the ajax callback and actually setting the value of the observable.

/G

Jeff Tchang

unread,
Dec 19, 2012, 1:06:42 AM12/19/12
to knock...@googlegroups.com
What I always do in my code is have an init() function that runs when I instantiate the viewModel for the page.

It is here I slowly make AJAX calls out to populate my observable array of stuff. If the AJAX call returns a lot of data I use pagination and slowly return it.

Once the data comes back I usually create observable properties out of the object and push it to the array.

--
 
 

Dave Van den Eynde

unread,
Dec 19, 2012, 2:35:56 AM12/19/12
to knock...@googlegroups.com
Perhaps the idea of an "on-demand observable" object is an indication that you don't fully understand the problem that Knockout is trying to solve. 



--
 
 

Stacey Thornton

unread,
Dec 19, 2012, 2:37:58 AM12/19/12
to knock...@googlegroups.com

I understand it fine. Not all programs work the same way and sometimes you just have to do things that were not intended by the original framework.

--
 
 

Steve Gordon

unread,
Dec 20, 2012, 8:47:13 AM12/20/12
to knock...@googlegroups.com
I'm wondering if we're all making Stacey's problem a bit more complicated than it needs to be.

Stacey, you're saying that the Id property never changes, so there's no need to subscribe to the Id change because it will never happen.  You are saying that at some point you want an AJAX call to retrieve the value of your Points property, and upon successful return have the view bound to the resulting Points data.  Is that correct?

What precisely should trigger the AJAX method to execute?  Should it be executed as soon as the ViewModel is instantiated and ready to be bound?  If so, you likely just have to execute the AJAX call in your ViewModel's constructor like this.  

Also note that if the Id property isn't changing, it's more efficient to define it as a primitive type rather than an observable.

Stacey Thornton

unread,
Dec 20, 2012, 11:21:07 PM12/20/12
to knock...@googlegroups.com
Thanks for all of the responses. My internet has been damaged by a huge sand storm so I've been a bit delayed in getting back to you all.

The latest solution is more what I was needing. I just needed to load data in that I could not pass down right through the view. I needed to use ajax because it needs to keep posting back to the server on a frequent basis.

I have managed to finally make it work, but I had to go with a lot of unorthodox stuff that I don't like. I'll try and change it soon, but the biggest problem for me is that I was having an issue where getting the data back from the server didn't update the UI, even though the ViewModel had changed. I'll actually be posting a new question with more details on this soon - I think the ajax problem was solved a long time ago, it was this UI issue that was disconnecting me from it working right.


--
 
 

Dave Van den Eynde

unread,
Dec 21, 2012, 2:24:48 AM12/21/12
to knock...@googlegroups.com
I'll express my opinion once more, but I'll be more specific this time: there's nothing in Knockout or the MVVM pattern that helps you. If you need to load in more data asynchronously, that's what your view model should do for you, not Knockout. We all do this, and we all use Knockout but we understand what it is and what it isn't. It's simple enough, but making it more complex and trying to shoehorn it into what you want it to do will only blind you from the truth.


--
 
 

Stacey Thornton

unread,
Dec 21, 2012, 2:34:46 AM12/21/12
to knock...@googlegroups.com

@Dave

That's a pretty narrow minded view, and a rather hateful response to the specific question. You've no idea what is being asked beyond the scope of a very limited question that was really aimed at nothing more than getting data to an observable after the dom has loaded, and the root of the problem turned out to be different than believed at first.

Seriously, just because it isn't the way you do it doesn't mean trying it is wrong.

Having a problem with one aspect of a framework doesn't mean I don't understand the concepts, and it certainly doesn't mean that the way you've done it up until now is the only way.

Ease up on the hateraide a bit.

--
 
 

Stacey Thornton

unread,
Dec 21, 2012, 2:50:39 AM12/21/12
to knock...@googlegroups.com

Also, my 'understanding' of MVVM isn't really the question.

I had a project, I needed to accomplish something. The things I were trying were not working, so I came to other people who work with the same framework and might have encountered the same problem.

I never asked for an 'on demand observable'. I was directed to one. It was the best lead I had so I followed it.

Isn't that kind of how the debugging process works?

Dave Van den Eynde

unread,
Dec 21, 2012, 5:16:20 AM12/21/12
to knock...@googlegroups.com
I don't know what your idea of MVVM is, but in my book, an 'on demand observable' is a laughable contradiction. Looks like what you're trying to do is simple enough, but you're spending your time walking in the wrong direction. If you think my view is narrowminded, or even that my response is hateful, then please, ignore me, keep walking, and I will refrain from posting anymore replies to this thread.


--
 
 

isdon...@gmail.com

unread,
Dec 21, 2012, 6:45:39 AM12/21/12
to knock...@googlegroups.com
Just to be clear, are you after the this.Points being evaluated as a single call on page load, or as a recurring ajax call when something changes (current ID?)
I believe the .subscribe refered to later is possibly what you need to look at if the latter. What is the trigger to re-pull the ajax value? this.Points needs to be a subscriber to that trigger change (whether its an ID field as in the fiddle Dave@goovebird set up, or any other item that changes, or you could just leave as a computed so it runs once at the start)

The other thing to note from the thread examples is that they seem to set the value to something like Points("loading...") then set it again in the ajax success function to the real value Points(data). I believe that's what you need, but I could be missing it as well.

Don
have a great day


On Sunday, December 9, 2012 11:58:30 AM UTC+11, Stacey wrote:
I am simply trying to pull some data from an ajax request. The ajax call works - I know the data is retrieved. But it just isn't setting the value of the ko.computed... 

function viewModel() {
this.Id = ko.observable("@Model.Identity()");
this.Points = ko.computed({ 
read: function () {
$.ajax({
url: '/skills/points',
data: { id: this.Id },
type: 'get',
success: function (data) {
return data;
}
});
},
owner: this
});
}

So a call like ...

<span data-bind="text: Points"></span>

rpn

unread,
Dec 21, 2012, 8:56:06 AM12/21/12
to knock...@googlegroups.com, da...@groovbird.com
@Dave-
The on-demand observable was based on a technique that I wrote about here: http://www.knockmeout.net/2011/06/lazy-loading-observable-in-knockoutjs.html.  It is a way to encapsulate the act of lazy loading an observable's value at the time that it is actually bound/accessed.  Typically this would be used in scenarios where there is data that is not bound immediately and may not ever be bound depending on the user's actions.  It is pretty straightforward to make this happen using manual subscriptions or a computed, but this is a way to make it happen automatically as soon as the observable is bound/accessed, no matter what caused it to be bound.


Just wanted to explain the point of it.  It looks like Stacy was exploring it to see if it met his needs.  It appears that the information may be bound right away, so triggering an AJAX request on load might be the way that he is leaning now.

Stacey Thornton

unread,
Dec 21, 2012, 2:17:27 PM12/21/12
to knock...@googlegroups.com

Yes. Thank you all very much for the answers. It is good to see the knockout community still has so many helpful people. I was actually considering a switch to kendo's mvvm framework but the fact that they don't have people like you guys dissuaded me to stay with knockout.

The solution to this problem was several fold, and since I don't want to cause any trouble, I'll try to clarify everything here.

First. I wanted to get a value from a URL because I only needed knockout for a very specific part of the page, and was already passing down a REALLY big view model from the MVC controller. I thought a simple ajax request was less invasive, because the things I needed were not on the original view model.

Second, the on demand observable was actually suggested to me on stack overflow.

Third, party of the problem kept coming with the fact that updating the VIEW MODEL was not updating the actual DOM. this is where the biggest disconnect came from and why I kept assuming the suggestions were not working. I will be posting a follow-up with more detail soon on a different thread.

And fifth, outright curiosity. Once I had made it do one thing, I wanted to see how far I could push the idea just for the sake of exploring it.

--
 
 

andrea....@gmail.com

unread,
Dec 23, 2012, 12:41:25 PM12/23/12
to knock...@googlegroups.com
Try using "around".

I have seen this kind of problem in the ko docos and if you look for it I am sure you'll find it :)

Regards.

On Saturday, December 15, 2012 2:25:05 AM UTC+1, Stacey wrote:
I don't understand why the async stuff even matters. I tell it to bind to a specific observable - and then I update that observable, shouldn't it change? Isn't that the entire point of what knockout is supposed to do? 

On Friday, December 14, 2012 7:20:27 PM UTC-6, Stacey wrote:
You said that you can't see what would trigger it except being called directly. Shouldn't being data bound be the same thing? Saying 

<span data-bind="text: Points"></span>

call it directly and cause it to evaluate? I really don't understand why it isn't that simple. All of the async stuff I keep looking at makes little to no sense to me.

Stacey Thornton

unread,
Dec 24, 2012, 2:08:10 PM12/24/12
to knock...@googlegroups.com
I'm not trying to sound ungrateful, andrea - but I think you might have replied to the wrong topic. Or I'm not understanding what you mean.


--
 
 

Reply all
Reply to author
Forward
0 new messages