cdsln
cdsln

Reputation: 888

knockout binding issue: Anonymous template defined, but no template content was provided

I have some javascript code which opens a modal and applies bindings to my knockout viewmodel. When the modal opens there is a second tab and when clicked, it fires an ajax call and sets an observableArray with the data returned (people, sometimes an empty array). The html code below iterates through the array, and displays the names of the people.

This works fine the first time I click to open the modal, but it doesn't work for any other click, displaying the error Anonymous template defined, but no template content was provided in console. I have spent 2 days on this and cannot see where the issue is, although I suspect it could be in my binding method.

I cannot recreate the error in a pen because it uses an Ajax call and I don't know how to imitate that.

HTML:

...<other html, modal, tabs etc...>
            <div id="people" class="tab-pane">
                <form class="well form-horizontal" id="peopleForm">
                    <fieldset>
                        <legend>Registered</legend>
                        <div class="form-group" data-bind="visible: people().length > 0">
                            <div data-bind="foreach: people">
                                <div class="form-group">
                                    <div class="col-md-6 inputGroupContainer">
                                        <div class="input-group">
                                            <span class="input-group-addon"><i class="glyphicon glyphicon-user"></i></span>
                                            <input type="text" class="joinEventModalButtons" data-bind="value: $data.FullName" disabled />
                                        </div>
                                    </div>
                                    <div class="col-md-4 inputGroupContainer">
                                        <input type="button" value="Remove" class="btn btn-danger" />
                                    </div>
                                </div>
                            </div>
                        </div>
                    </fieldset>
                </form>
            </div>

JS:

var myModel = function myViewModel() {
      self.people = ko.observableArray([{ "FullName": "Joe", "Email": "[email protected]" }]);

  // setTimeout to try mimic ajax call?
  setTimeout(function() {
    self.people([])
  }, 6000)
....
// ajax call within 'myModel' but also doesn't work  if I put it in the section where I show the modal
 $.ajax({
            url: "/api/people",
            type: "GET",
            success: function (data) {                
                self.people(data);
            },
            error: function (err) {
                alert(err.responseJSON.message);
            }
        });
}

JS For showing Modal, apply bindings etc...:

.. on some button click...:

 $('#myPeopleModal').on('hidden.bs.modal', function () {
     // do something....
 });

 ko.cleanNode(document.getElementById('myPeopleModal'));

 var myM = new myModel();
 ko.applyBindings(myM, document.getElementById('myPeopleModal'));
 $('#myPeopleModal').removeClass("hide").modal('show');
  1. If I comment the section with foreach: people the modals will open and close without any issue.
  2. If I remove the ajax call and just keep the observable set once, the modals open and close without issue.
  3. I also set other observables in the model, and they are working perfectly (removed them for simplification)
  4. I have also tried the ko comment syntax but got the exact same result

Knockout 3.5.1.

Thanks for any help.

Upvotes: 1

Views: 1612

Answers (1)

Sam
Sam

Reputation: 1707

see the first function for faking an async call with a simple promise on a timeout :)

if you don't want to hook onto the bs events from withing your knockout just remember to reset all knockout properties to their init states so next modal-show everything looks right.

this is probably not the 'cleanest' way since we're resetting to the first tab with jquery on closing of the modal but honestly if you only need the applyBindings on the modal, what the heck it works.

console.clear();
var $fakeAsync = function(api, result) {
  var dfd = $.Deferred(() => setTimeout(function() {
    dfd.resolve(result);
  }, 500));
  return dfd.promise();
};

ko.applyBindings(() => {
  var self = this;
  self.people = ko.observableArray([]);
  self.loading = ko.observable(false);

  self.activatePeople = function() {
    self.loading(true);
    self.people([]);
    $fakeAsync('/api/fake', ['john', 'karen', 'franz']).done(function(result) {
      self.people(result);
    }).always(() => self.loading(false));
  };
  self.resetOnClose = function() {
    $('#exampleModal #home-tab').click();
    self.people([]);
    self.loading(false);
    return true;
  };
}, $('#exampleModal').get(0));
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" />
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.1/knockout-latest.min.js"></script>
<!-- Button trigger modal -->
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal" data-backdrop="static" data-keyboard="false">
  Launch demo modal
</button>

<!-- Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close" data-bind="click: resetOnClose">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        <ul class="nav nav-tabs" id="myTab" role="tablist">
          <li class="nav-item">
            <a class="nav-link active" id="home-tab" data-toggle="tab" href="#home" role="tab" aria-controls="home" aria-selected="true">Home</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" id="people-tab" data-toggle="tab" href="#people" role="tab" aria-controls="people" aria-selected="false" data-bind="click: activatePeople">People</a>
          </li>
        </ul>
        <div class="tab-content" id="myTabContent">
          <div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab">click the people-tab above</div>
          <div class="tab-pane fade" id="people" role="tabpanel" aria-labelledby="people-tab"><span data-bind="visible: loading">loading..</span>
            <ul data-bind="foreach: people">
              <li data-bind="text: $data"></li>
            </ul>
          </div>
        </div>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-dismiss="modal" data-bind="click: resetOnClose">Close</button>
        <button type="button" class="btn btn-primary" data-bind="click: resetOnClose">Save changes</button>
      </div>
    </div>
  </div>
</div>

Upvotes: 2

Related Questions