Issue with knockout template bind, when using nested template binding getting Message: Unexpected token ')'

219 Views Asked by At

I have upgraded knockout and jquery version to 3.5.1 but getting below issue

Uncaught SyntaxError: Unable to process binding "template: function(){return {name:"sectionTemplate",foreach:sections,templateOptions:{ deleteSection:deleteSection,openAddDocumentsDialog:openAddDocumentsDialog}} }" Message: Unexpected token ')'

Is there any issue with knockout library when we use nested template.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.js"></script>
    <script src="https://knockoutjs.com/downloads/jquery.tmpl.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.1/knockout-latest.js"></script>
    
</head>
<body>
    
    <div data-bind="template: { name: 'items', data: breakfast, templateOptions: { header: 'Breakfast Items', message: 'Wake up to these delicious items!', class: 'breakfast' } }"></div>
     
    <script id="items" type="text/html">
        <h3>${$item.header}</h3>
        <div class="${$item.class}">
            {{each $data}}
            <p>
                <span class="name">${name}</span>
                <span class="price">${price}</span>
            </p>
            {{/each}}
        </div>
        <p><em>${$item.message}</em></p>
        <hr />
        <div data-bind="template: { name: 'items2', data: newItems, templateOptions: { header: 'New Items', message: 'Wake up to these delicious items!', class: 'breakfast'} }"></div>

    </script>
    <script id="items2" type="text/html">
        <h3>${$item.header}</h3>
        <div class="${$item.class}">
            {{each $data}}
            <p>
                <span class="name">${name}</span>
                <span class="price">${price}</span>
            </p>
            {{/each}}
        </div>
        <p><em>${$item.message}</em></p>
        <hr />


    </script>

    <script type="text/javascript">
        var viewModel = {
            breakfast: [
                { name: "toast", price: 2.50 },
                { name: "pancakes", price: 4.00 },
                { name: "eggs", price: 3.25 }],
            
            
            newItems: [
                { name: "Oats", price: 2.50 }],
        };

        ko.applyBindings(viewModel);
    </script>

</body>
</html>

Specially error is getting when I use nested template item2

Please help me out, Thanks in advance

1

There are 1 best solutions below

2
Nathan Fisher On

There seems to be an issue with using templateOptions inside a template. Removing that from the item template makes it work. To get around that I would suggest making a view model for the template with the properties that need to be passed in rather than using templateOptions.

Also noticed that to access the newItems property, you need to pass in the $root to the first template binding rather than the breakfast property.

I know its not a fix, but hopefully helps with your debugging.

var viewModel = {

  breakfast: [{
      name: "toast",
      price: 2.50,
    },
    {
      name: "pancakes",
      price: 4.00
    },
    {
      name: "eggs",
      price: 3.25
    }
  ],


  newItems: [{
    name: "Oats",
    price: 2.50
  }],
};

ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://knockoutjs.com/downloads/jquery.tmpl.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script id="items" type="text/html">
  <h3>${$item.header}</h3>
  <div class="${$item.class}">
    {{each $data.breakfast}}
    <p>
      <span class="name">${name}</span>
      <span class="price">${price}</span>
    </p>
    {{/each}}
  </div>
  <p><em>${$item.message}</em></p>
  <hr />
  <div data-bind="template: { name: 'items2', data: newItems }"></div>
</script>

<script id="items2" type="text/html">
  <h3>${$item.header}</h3>
  <div class="${$item.class}">
    {{each $data}}
    <p>
      <span class="name">${name}</span>
      <span class="price">${price}</span>
    </p>
    {{/each}}
  </div>
  <p><em>${$item.message}</em></p>
  <hr />


</script>

<div data-bind="template: { name: 'items', data: $root, templateOptions: { header: 'Breakfast Items', message: 'Wake up to these delicious items!', class: 'breakfast' }}">
</div>

Important

Also a note about using jquery.tmpl from the knockout documentation - Note 6 -

Please note that, as of December 2011, jQuery.tmpl is no longer under active development. We recommend the use of Knockout’s native DOM-based templating (i.e., the foreach, if, with, etc. bindings) instead of jQuery.tmpl or any other string-based template engine.

Just using knockoutjs and removing jquery.tmpl

The following code has removed the need for jquery.tmpl and just using knockoutjs syntax as an alternative way of solving the problem.

function templateVm(header, className, message,  data ){
  var self = this;
  self.header = header;
  self.className = className;
  self.message = message;
  self.data = data;
}


var viewModel = {
  
  breakfast: new templateVm('Breakfast Items','breakfast','Wake up to these delicious items!', [{
      name: "toast",
      price: 2.50,
    },
    {
      name: "pancakes",
      price: 4.00
    },
    {
      name: "eggs",
      price: 3.25
    }
  ]),


  newItems: new templateVm('New Items','breakfast','Wake up to these delicious items!',[{
    name: "Oats",
    price: 2.50
  }]),
};

ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script id="items" type="text/html">
  <div data-bind="template: { name: 'items2', data: breakfast }"></div>
  <div data-bind="template: { name: 'items2', data: newItems }"></div>
</script>

<script id="items2" type="text/html">
  <h3 data-bind="text: header"></h3>
  <div data-bind="class: className, template: {name: 'name-price',  foreach: data}"></div>
  <p><em data-bind="text: message"></em></p>
  <hr />
</script>

<script id="name-price" type="text/html">
    <p>
      <span class="name" data-bind="text: name"></span>
      <span class="price" data-bind="text: price"></span>
    </p>
</script>

<div data-bind="template: { name: 'items', data: $root}">
</div>