Is it possible to pause the initial attribute knockout evaluation until the user changes the associated observables?

I am working on a website feature that allows the user to make live changes (like size and url) to an embedded YouTube video using knockout.js. The page initially loads the iframe with both default hardcoded attributes and data bindings , but not yet bound .

<iframe data-bind="attr: {src: videoUrl() }, style: {width: width() + 'px', height: height() + 'px', }" style="width: 420px; height: 315px;" src="//www.youtube.com/embed/OvxlHa6yjqM" frameborder="0" allowfullscreen></iframe>

      

The binding is applied to the iframe after some user interaction . Let's say you click the Show Options button.

$('some_button').click(function() { ko.applyBindings(viewModel) });

      

The view model contains video options.

var viewModel = {
    width: ko.observable(420),
    height: ko.observable(315),
    videoId: ko.observable('OvxlHa6yjqM')
}

      

iframe src is a computed observable.

viewModel.videoUrl = this.ko.computed( function () {
    return 'http://www.youtube.com/embed/' + this.videoId();
}, viewModel );

      

Once executed ko.applyBindings()

, knockout.js evaluates all bound observables and outputs them to HTML, even if they are the same as hardcoded. Attributes like width and height are re-rendered instantly, but the videoUrl

computed observable that drives the attribute src

causes the iframe to redraw and flicker for a second, even though it src

doesn't change. If the user enters a new one videoId

, the iframe will flicker and re-render with the new video, which is perfectly fine. But is there a way to prevent / pause the initial evaluation videoUrl

until it actually changes
to something else, such as by introducing a new one videoId

?

Sample JS Script

I tried the parameter deferEvaluation

, but it doesn't work for me (maybe its purpose is different). I've tried some conditional bindHandlers but can't figure out the correct logic.

Update

Knockout.js does not load in document where original iframe is loaded. It is loaded into an options container, which is a separate iframe. This means the knockout only loads and applies the bindings to the iframe in the parent document after the user opens the options (clicks the button).

The view model is applied separately to the list of options and the iframe because of this.

+3


source to share


2 answers


I haven't been able to find one magic solution, but in the end my question is pretty specific. I was able to figure out several ways to solve this problem.

Method 1: apply bindings after the first change

Since the ViewModel is applied separately to the list of options and the iframe, the simplest solution would be to apply it only to the list of options first, and apply it to the iframe only after any parameters have changed. This way, the iframe will not flicker when entering edit mode, but it will flicker on any first change - even if it is a resize.

Method 2: Prevent the initial src binding of the iframe



I found a good technique here: How to stop knockout.js binding bindings on children

Using the link stopBindings: true

(read the discussion above) of the bindings to the parent of the iframe element, I can apply the bindings normally and only ignore the iframe itself. Now I can detect the change of the video src (video id) through viewModel.videoId.subscribe( function () {})

and check if this is the initial change through ko.computedContext.isInitial()

. I can set it to stopBindings

false. The downside is that now, in order to watch for changes in the src, I have to restart the bindings with ko.cleanNode(element)

and then again ko.applyBindings

. But it works and now the video only flickers after src change, which is the idea.

Method 3: two view models

I haven't even started looking into this solution, but it basically means having a separate view model for videoId

that will be applied after the changes are made, as was done above. This will prevent rebinding, but I think it is quite expensive.

+1


source


The easiest way is to call applyBindings

immediately when the DOM is loaded, and only show / hide the parameters on button click.

First remove the explicit attribute src

and have Knockout set the attribute on binding:

<iframe data-bind="attr: {src: videoUrl() }, style: {width: width() + 'px', height: height() + 'px', }" style="width: 420px; height: 315px;" frameborder="0" allowfullscreen></iframe>

      



Then move the call applyBinding

outside of the click handler:

$('bind').addEvent('click', function () {
    this.hide();
    $('options').show();
});

ko.applyBindings(viewModel);

      

See Fiddle

+1


source







All Articles