Search code examples
javascripthtmltemplatesknockout.jsknockout-templating

JavaScript inside KO Template


How to put script inside knockout template?

This doesn't work:

<script type="text/html" id="some-template">
    <div>
    ...
        <script type="text/javascript"> <!-- This is the problem -->
            CoinWidgetCom.go({
                wallet_address: "ENTER-YOUR-BITCOIN-WALLET-ADDRESS"
                , currency: "bitcoin"
                , counter: "count"
                , alignment: "bl"
                , qrcode: true
                , auto_show: false
                , lbl_button: "Donate"
                , lbl_address: "My Bitcoin Address:"
                , lbl_count: "donations"
                , lbl_amount: "BTC"
            });
        </script>
    ...
    </div>
</script>

...

<script src="http://coinwidget.com/widget/coin.js"></script>

This is the script that I'm trying to run inside each of the elements that use some-template. Script could maybe be modified, but I would rather use original version.


Solution

  • What you want is not possible. I don't think script tags with executable javascript inside a text/html script will have their code executed when the template is rendered.

    However, like other commenters said: you don't need to do this. Rework your design, make use of Knockout's features for these type of things. There are several alternative solutions, including:

    Creating your own bindingHandler to activate the widget on a rendered template. You have posted only a small portion of code, but here's what that would look like:

    ko.bindingHandlers.myWidget = {
        init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
            // This will be called when the binding is first applied to an element
            // Set up any initial state, event handlers, etc. here
            console.log('Initializing widget with ' + ko.toJSON(allBindings()['myWidget']));
        },
        update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
            // This will be called once when the binding is first applied to an element,
            // and again whenever any observables/computeds that are accessed change
            // Update the DOM element based on the supplied values here.
        }
    };
    
    var VmForTemplate = function() { }
    
    var ViewModel = function() {
      this.subVm = new VmForTemplate();
    };
    
    ko.applyBindings(new ViewModel());
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
    
    <script type="text/html" id="some-template">
      <div data-bind='myWidget: {
                          wallet_address: "ENTER-YOUR-BITCOIN-WALLET-ADDRESS"
                          , currency: "bitcoin"
                          , counter: "count"
                          , alignment: "bl"
                          , qrcode: true
                          , auto_show: false
                          , lbl_button: "Donate"
                          , lbl_address: "My Bitcoin Address:"
                          , lbl_count: "donations"
                          , lbl_amount: "BTC"
                        }'>
      ... template ...
      </div>
    </script>
    
    
    <!-- ko template: { name: 'some-template', data: subVm } -->
    <!-- /ko -->

    Alternatively, use the afterRender attribute of the template binding, like so:

    var VmForTemplate = function() { }
    
    var ViewModel = function() {
      this.subVm = new VmForTemplate();
      this.initWidget = function() { CoinWidgetCom.go({
                    wallet_address: "ENTER-YOUR-BITCOIN-WALLET-ADDRESS"
                    , currency: "bitcoin"
                    , counter: "count"
                    , alignment: "bl"
                    , qrcode: true
                    , auto_show: false
                    , lbl_button: "Donate"
                    , lbl_address: "My Bitcoin Address:"
                    , lbl_count: "donations"
                    , lbl_amount: "BTC"
                }); };
    };
    
    ko.applyBindings(new ViewModel());
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
    
    <script type="text/html" id="some-template">
        <div>
           Template. No javascript here.
        </div>
    </script>
    
    <script>
      // Mock the widget
      var CoinWidgetCom = { go: function(opts) { console.log('Running widget with options: ' + ko.toJSON(opts)); } };
    </script>
    
    <!-- ko template: { name: 'some-template', data: subVm, afterRender: initWidget } -->
    <!-- /ko -->