27

This may seem like a duplicate question, but none of the other answers have helped me. I have the following HTML (it's a Razor template, but no Razor specifics here).

<p class="search-results-summary">
    Results 
    <!-- ko if: SearchTerms.Query -->
    for <span data-bind="html: SearchTerms.Query"></span>
    <!-- /ko -->
    <!-- ko if: SearchTerms.Names -->
    for Names <span data-bind="html: SearchTerms.Names.join(', ')"></span>
    <!-- /ko -->
    <!-- ko if: SearchTerms.Location && AlternativeLocations && AlternativeLocations.length -->
        within <span data-bind="text: SearchTerms.LocationRadio"></span>
        miles of <span data-bind="html: SearchTerms.Location"></span>. 
        <!-- ko if: AlternativeLocations && AlternativeLocations.length > 1 -->
            <a class="more alternative-locations" href="#">more</a>
            <ul id="other-location-matches" data-bind="foreach: AlternativeLocations.slice(1).sort()" style="display: none">
                <li>&gt; Did you mean <a data-bind="html: $data, attr: { href: Edge.API.CurrentSearchResponse.SearchTerms.mutate({ Location: $data }).getUrl() }"></a>?</li>
            </ul>
        <!-- /ko -->
    <!-- /ko -->
    <!-- ko if: SearchTerms.Location && (!AlternativeLocations || AlternativeLocations.length == 0) -->
    <span class="error">We couldn't find '<span data-bind="html: SearchTerms.Location"></span>' on the map. Your search ran Worldwide.
    </span>
    <!-- /ko -->
</p>

When I try to bind this template using Knockout, I get this error:

Error: Cannot find closing comment tag to match: ko if: SearchTerms.Location && AlternativeLocations && AlternativeLocations.length 

I have tried:

  • Upgrading Knockout from 2.2.1 to 2.3.0. No use
  • Verifying HTML/XML structure. It's good!
  • Removing the <ul id="other-location-matches"...> seems to get rid of the issue... but I need that <ul>!!

Any ideas? Am I looking at a Knockout.js bug?

Mauricio Morales
  • 988
  • 1
  • 9
  • 16

8 Answers8

65

I have encountered the same issue except with table tags.

Doesn't work - produces the same issue as indicated by Mauricio

<table>
<!-- ko: foreach: { data: SomeData, as: 'item' } -->
   <tr>
      <td data-bind="text: item"></td>
   </tr>
<!-- /ko -->
</table>

Works:

<table>
   <tbody>
   <!-- ko: foreach: { data: SomeData, as: 'item' } -->
      <tr>
         <td data-bind="text: item"></td>
      </tr>
   <!-- /ko -->
   </tbody>
</table>
Sash
  • 1,134
  • 11
  • 23
  • 1
    On this particular scenario, I do happen to know that even though you don't put a `` there, the browser will do that for you behind the scenes... so that's why it would work with a ``. I guess `` doesn't allow non-`` tags in there.
    – Mauricio Morales Mar 12 '14 at 22:18
  • 3
    Wow! The was the way to make it work! Thanks!! It was driving me crazy... – jbartolome May 18 '14 at 21:06
  • This has just saved my life. Thank you very very mutch – DalekSupreme Nov 20 '14 at 16:12
9

Short answer:

HTML doesn't allow block element inside P element. So the P element is closed right before the UL element. The ko comment open tag end in the P element and the closing tag outside. Knockout require both open and closing comment tag to be in the same element.


Original answer:

thanks to @Sash, I've understood why the <tbody> tag is mandatory.

I was having the same problem with this piece of html:

<table>
    <thead>
        <th>ID
        <!-- ko if: showName() --> <th>Name <!-- /ko -->
    <tbody data-bind="foreach: data">
...

Obviously, it doesn't work for the same reason. Why it doesn't work striked me when I added </th> until it work. I needed to add the closing tag before the opening ko comment. As soon I have seen that, I recalled SGML 101. Optional tag come after comment. So the actual DOM tree look like that for my code:

─┬─Table
 ├─┬─THead
 │ ├─┬─Th
 │ | ├─#Data(ID)
 │ | └─#Comment(ko if:)
 │ └─┬─Th
 │   ├─#Data(Name)
 │   └─#Comment(/ko)
 └─┬─TBody
   ┊

You can notice the opening and closing tag are on two branch of the node tree. To get the comment at the right position, optionnal tag need to be explictely placed. @michael best explain why this affect the original poster.

Community
  • 1
  • 1
gkr
  • 469
  • 7
  • 11
8

I had the same error caused by a self closing div tag i.e.

<div /> 

changed to

<div></div>

now all is good again

MemeDeveloper
  • 6,457
  • 2
  • 42
  • 58
  • Thanks!! This is what was the isssue for me. Had a self closing `` tag (for FontAwesome icon) that was nested inside other elements that were wrapped by the ko comment binding. Changed that to have an explicit closing tag and voila! – Jaans Aug 30 '18 at 13:31
  • 1
    Really interesting. I wouldn't imagine this was the answer. Half of day wasted trying other approaches. – theRonny Jan 31 '19 at 01:57
  • 1
    @ron.camaron if it makes you feel any better, same issue here, (almost) exactly a year later. – Madbreaks Jan 31 '20 at 22:19
  • I had the same problem with self closing – Aralox Mar 19 '21 at 02:55
4

I know this is an old thread but just in case someone finds this. Mine was way simpler

I had a colon after the ko in the opening comment:

<!-- ko: foreach:stuff --> instead of <!-- ko foreach:stuff -->

Max Vollmer
  • 8,412
  • 9
  • 28
  • 43
Dennis
  • 41
  • 2
2

Well... After a while of struggling I luckily found the fix. This still doesn't explain why it is failing to parse that particular HTML template (nor I would agree it should be rejecting it) but, by replacing the <p> enclosing the entire thing with a <div>, the issue goes away.

So I'm sure that DOM behaviors for <p> and <div> are different, and apparently affect Knockout's template parsing logic.

Mauricio Morales
  • 988
  • 1
  • 9
  • 16
  • 6
    `
      ` elements aren't allowed inside `

      ` elements. See http://www.w3.org/TR/html5/grouping-content.html#the-p-element So what does the browser do? It end the `

      ` element right before the `

        ` and now your structure is messed up.
    – Michael Best Sep 10 '13 at 20:30
  • Thanks for the explanation, @MichaelBest, I myself was wondering why it did that... :) – Filip Vondrášek Sep 10 '13 at 20:37
2

This may also be caused if your HTML is poorly formed in general - for example if you have stray opening or closing tags that don't have a match.

In my case I had an extra <tr> tag. Deleting it fixed the problem.

Andrew
  • 18,680
  • 13
  • 103
  • 118
1

The <div> and <p> tags shouldn't interfere with the <!-- ko --> comment tags. I can't see why the code you have here, with the comment ko tag structure, wouldn't work. Here's a jsfiddle sample of the same structure (minus the html stuff) that will show/hide the appropriate sections based on the values.

If you have all the matching <!-- /ko --> tags, you may have an error in your html tags. If switching the <p> to <div> is acceptable. Call it a day, otherwise, I'd remove all of your html and leave just the ko comment tags. If there's no problem, add each html element back one at a time to track down the offending html. If that turns up nothing..., recreate the error in a jsfiddle and update your question.

nwayve
  • 2,291
  • 23
  • 33
0

I faced a similar issue and after hours of tag matching and UI build failures I figured that I wrote

<!--ko if: someConditionCheck() --> 

instead of

<!-- ko if: someConditionCheck() -->

The space between <!-- and ko was the root cause of it all.

Hope it helped someone!