1

[This seems to be impossible. I will answer this question by myself later]

This question largely continues my pervious one. I use "async computed" approach to refresh some parts of my page. Thanks to Michael Best I solved issue with updates of invisible parts of UI. But one annoying thing still here. How can I set initial (default) value to computed observable? I try to avoid multiple ajax calls during page load. Instead of it I embed json into page to load everything at once. Seems trivial (common)? But I can't supress first evaluation of my async computed. So ajax call will be made in any case. I could use this approach:

var isFirstEval = ko.observable(true);
updateComputed = ko.pureComputed(function () {
    updateTrigger();
    if(isFirstEval()){
        isFirstEval(false);
        result(initialValue);
    }
    else
        result(evaluator.call(owner));
});

But I face the same issue, as in the previous question: this computed will never subscribe on evaluator changes, because of approach knockout uses to reevaluate computed observables. The suggestion from similar question works because it checks for the first evaluation after var value = self.product() * self.quantity();. So the computed is always tracking product and quantity. I can't use this approach, because I can't call evaluator just to care about dependency as evaluator can make ajax call...

Does any way exists to supress the first evaluation of computed (or pure computed)? Or maybe way to set initial value? Any suggestions? Other workarounds?

Community
  • 1
  • 1
shameleo
  • 344
  • 2
  • 13
  • I already gave some example: https://jsfiddle.net/mbest/bpr88bp3/2/. I can't provide you real code. Lazy loads, automatic reloads depending on user selections... how else can I do it? As for embeding json, this greatly speeds up first page load. Will be happy to know other suggestions – shameleo Apr 19 '16 at 09:06
  • Letting a complete answer sit there for three days without so much as a comment is extremely rude, by the way. It took me several hours of my time to write that all up, test it and make a working jsFiddle and you cannot even be bothered to give *any kind* of feedback. That's poor style. – Tomalak Apr 22 '16 at 07:04
  • @Tomalak. I just didn't know what could I tell you. I see you spend a lot of you time, but I really don't know how to apply your post to my question. I already have some solution, that helps me to make lazy loading and automatic reloads. I provide you the link several times. The main goal of the question is to invent some way to set some _initial_ value, and if it is set, to _prevent_ ajax call until some dependency will change. I don't see solution. You answer has some useful ideas, and maybe they will be used. I really appreciate you for this. But that is in progress. I have no answer yet. – shameleo Apr 22 '16 at 07:29
  • My answer did exactly that: Set the initial value, prevent the first Ajax call. I'm sorry that you did not see how to apply it to your situation. Apparently you have a comment to it, it's a shame you did not make it when there still was time. The point is, you can't expect anyone to read five code samples and previous question just in order to figure out what your question is. Write. it. down. Complete, self-contained, structured. Make references, but don't rely on people having the time or the inclination of reading 500 lines of foreign code to get any kind of context. – Tomalak Apr 22 '16 at 07:34
  • I'm afraid, if I would include all nuances in question itself, it would become three screens long. I can't expect anyone to read it. Not every question is simple enough to write it down easily and clearly. Especially if english is not your native language. But what about you? You didn't asked me specific detailes that is not clear for you. And your answer doesn't demonstrate requested feature. Do I need to finish it by myself? I don't know how. Did you examined if I'm smart enough? So I failed. I'm simply juniour who works alone, far away from any team. My reputation is 0.05% of yours. So? – shameleo Apr 22 '16 at 08:15
  • When I need to finish the question for myself, then you will need to finish the answer for yourself. (In all honesty, you *always* need to finish an answer for yourself, especially in a complex question. Copy-paste answers are not a reasonable expectation to have, really.) Reputation also is completely irrelevant here. It's your job to make a self-contained question with enough context to be meaningful on its own. That's simply how this site works. If your question has to many nuances, break it up into sub-questions. Asking a good question about a difficult problem is hard work. – Tomalak Apr 22 '16 at 08:47
  • Writing an [MCVE](http://stackoverflow.com/help/mcve) is hard work. It can take all day just to cut just the problematic bit of code out of your application and make a working example from it that reproduces the core of your issue. And still it's what you have to do when your problem is beyond what can be solved by a jQuery three-liner. Saying "but I can't show you my real code" does not cut it, saying "but the problem is too difficult to explain, please instead read those three answers that almost-but-not-quite solve it" does not cut it, either. – Tomalak Apr 22 '16 at 08:52
  • All the tools to reproduce your situation exist. StackOverflow has a feature for runnable code snippets, there is jsFiddle and its cousins, you can use jQuery-mockjax to mockup Ajax requests/responses and there's a ton of other tools that could help. It's completely impossible that your situation cannot be modeled independently and abstractly. – Tomalak Apr 22 '16 at 09:00
  • @Tomalak - Well, I understood a root of misunderstanding. My bad, I could make this question clearer. Maybe I will rewrite it later. But look carefully, I'm talking about embedded json. That means, my tab _already_ _has_ some content after page is loaded. And I don't want it to require server for _the same_ content _until_ some [dependency] = [parameter of ajax call] changed. I use json embedding to speed up page load and avoid flickering. Sorry for wasting your time – shameleo Apr 22 '16 at 11:05
  • Ironically that is exactly what my answer did. Granted, it did not do it with JSON, but with plain text (it started off with `"..."` which then got replaced with a server-supplied value), it had auto-reload when a dependency (parameter of the Ajax call) changed, and it did not uselessly request data it already had. – Tomalak Apr 22 '16 at 11:31
  • Yes. You _replaced_ "...", because it is simply load indicator. I need _leave_ it, as if it was usefull data. _Until_ you press "increment" button. In your demo if I make tab active, ajax call will be made at once. See the point? – shameleo Apr 22 '16 at 11:48
  • Yes, that's a minimal change to my original code. Compare the original https://jsfiddle.net/Tomalak/7anod4vm/0/ and the modification https://jsfiddle.net/Tomalak/7anod4vm/1/. Note how manually setting a value sets the state to "isLoaded" and how it does not refresh data as long as "isLoaded" is true and the Ajax dependency does not change. – Tomalak Apr 22 '16 at 12:15
  • Oops... (facepalm). I did check this way in my mind while was looking thought your code, but smth went wrong and I desided it won't work. I didn't try it in reality... I learned this lesson. You can restore your answer, I will accept it later. Also I will make some edits in this topic. Thank you – shameleo Apr 22 '16 at 12:40
  • @Tomalak - I took a closer look to second demo and noticed your code actually doesn't work properly. I simply added some alert to show it: https://jsfiddle.net/wp0ubodb/. PromiseProvider is evaluated right after you subscribe on its result. I've looked through knockout source and now I know this is by design and can't be avoided. Because dependencies technically can not be revealed without evaluation of computed's read function. So I should explicitly pass dependencies I have to subscribe in order to make it work. Smth like: { initValue: content, dependencies: [selParam1, selParam2, ...] }. – shameleo Apr 23 '16 at 18:32
  • 1
    Aww snap, I did not realize that subscribing to an computed triggers its evaluation, which means the "promiseProvider" will be called no matter what. I have rewritten the approach: https://jsfiddle.net/Tomalak/7anod4vm/2/ This time only the Ajax dependency is passed to the extender, not the Ajax request itself. This means one level of abstraction lost, since the whole thing is now tightly coupled with jQuery Ajax. I will spend some time thinking about how to make it more flexible again. – Tomalak Apr 24 '16 at 10:22
  • I missed the idea to use ajax options object as dependency declaration. Nice, but I need some time to figure out possible limitations. This solution is completely different from what I've started. – shameleo Apr 25 '16 at 07:09

1 Answers1

2

You can use deferEvaluation option:

    var c1 = ko.computed({
        read: function() {
            // computations
            return "some value";
        },
        deferEvaluation: true
    });

You can use computed context to determine initial computation (the same document).

Update

You can use "isInitial" to determine whether computed is being evaluated first time:

var myComputed = ko.computed(function() {
    var isFirstEvaluation = ko.computedContext.isInitial();
    if(isFirstEvaluation) {
        // some code
    }
    else {
        // some code
    }
});
Community
  • 1
  • 1
TSV
  • 7,538
  • 1
  • 29
  • 37
  • This option deferes evaluation until smth will require for value of your computed. The meaning of world "defered" which I use is "do not execute code withing computed until some dependency will actually change". – shameleo Apr 19 '16 at 08:52
  • Using of "computed context" is interesting options. Don't know if it can help in other cases, but in my case I can't use it as "for pure computed observables, isInitial() is always undefined". I forced to use pure computed because of tricky hacky way to make computed inactive, provided by M.Best – shameleo Apr 19 '16 at 08:59
  • @shameleo - I've updated the answer with isInitial usage example – TSV Apr 19 '16 at 09:02
  • Even if you ignore the fact it doesn't work with pure computed, isInitial() seems has the same issues as the example (http://stackoverflow.com/a/15478429/3602201) which I mentioned in my question. – shameleo Apr 19 '16 at 10:59
  • Small demo which proves that isInitial will not work: https://jsfiddle.net/qeucpqtg/. You can add code that will be executed for first evaluation, but you can't DO NOT execute code only for first evaluation – shameleo Apr 20 '16 at 08:26
  • I've updated your fiddle - https://jsfiddle.net/qeucpqtg/1/ . "var result = dep();" is needed to subscribe "comp" computed on its ("dep") future changes. Otherwise "comp" is not subscribed to the "dep" due to doesn't get it's value during first computation. – TSV Apr 20 '16 at 08:45
  • This is exactly the point which I mentioned in my question... In my case evaluating of dep means ajax call. That is what I'm trying to prevent... – shameleo Apr 20 '16 at 09:09
  • If ajax call is initiated by user, lets say via clicking on some button, you can separate ajax call processing and knockout view model operation. Is it suitable for you? (https://jsfiddle.net/qeucpqtg/3/) In this case UI will be updated after async function updates an observable. – TSV Apr 20 '16 at 09:21