0

I want to make a dynamic dialog box. For that I need some data like title, content and viewmodel. The title and the content will be shown. But the viewmodel not bind correctly. To see the problem, I prefilled the first_name with 'foo'. But it should be 'my first name'.

To make the modal dialog dynamic, we have a modalDialog object that must be filled with the specific data. The openDialog function fill this data and open it.

Normally the dynamic part will be load by requirejs. For demonstrate the problem, I have made it here much easier.

Here the html code:

<!-- the dynamic modal dialog -->
<div id="modal-dialog" class="modal fade" role="dialog" data-backdrop="static" data-bind="css: modalDialog.classes">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-bind="click: modalDialog.close">&times;</button>
        <h4 class="modal-title" data-bind="html: modalDialog.title"></h4>
      </div>

      <div data-bind="with: modalDialog.viewModel">
        <div class="modal-body" data-bind="html: $parent.modalDialog.content"></div>
      </div>
      <div class="modal-footer" data-bind="foreach: modalDialog.buttons">
        <button type="button" class="btn btn-primary" data-bind="click: action, text: label"></button>
      </div>
    </div>

  </div>
</div>

<!-- Test button for open the dialog -->
<button type="button" class="btn btn-info" data-bind="click: testDialog">Open modal</button>

Here the javascript code for the modal dialog viewmodel:

// the main viewmodel
var appVM = {
  modalDialog:
  {
    title: ko.observable(''),
    content: ko.observable(''),
    viewModel: ko.observable(this),
    buttons: ko.observableArray([]),
    classes: ko.observable(''),
    open: function()
    {
      $("#modal-dialog").modal('show');
    },
    close: function()
    {
      $("#modal-dialog").modal('hide');
    }
  },
  openDialog: function(title, content, viewModel, buttons, classes)
  {
    if (!viewModel)
      viewModel = appVM;
    if (!buttons)
      buttons = [{action: appVM.modalDialog.close, label: 'Close'}];
    if (!classes)
      classes = '';

    appVM.modalDialog.title(title);
    appVM.modalDialog.content(content);
    appVM.modalDialog.buttons(buttons);
    appVM.modalDialog.classes(classes);
    appVM.modalDialog.viewModel(viewModel);
    appVM.modalDialog.open();
  },
  testDialog: function()
  {
    var vm = new userViewModel();
    var title = 'Test Title';
    var html = dialogContent;
    var buttons = [
      { action: vm.onSave, label: 'Apply' },
      { action: appVM.modalDialog.close, label: 'Cancel' }
    ];

    appVM.openDialog(title, html, vm, buttons);
  }
};
ko.applyBindings(appVM);

At least the code for the dynamic data:

// the user data
function User(data)
{
  var self = this;
  self.first_name = ko.observable(data.first_name).extend({required: true});
  self.last_name = ko.observable(data.last_name).extend({required: true});
}

// the user viewmodel
function userViewModel()
{
  var self = this;
  self.user = ko.observable(new User(
  {
    first_name: 'my first name', 
    last_name: 'my last name'
  }));
  self.onSave = function()
  {
    alert('save data');
  };
}

// The loaded content for the dialog
var dialogContent = ' \
<div class="clearfix"> \
  <div class="col-xs-12"> \
    <div class="row form-group"> \
      <label class="col-xs-12 col-sm-4 col-md-3">Vorname:</label> \
      <input class="col-xs-12 col-sm-8 col-md-9" type="text" data-bind="textInput: user().first_name" title="" value="foo"/> \
    </div> \
    <div class="row form-group"> \
      <label class="col-xs-12 col-sm-4 col-md-3">Nachname:</label> \
      <input class="col-xs-12 col-sm-8 col-md-9" type="text" data-bind="textInput: user().last_name" title=""/> \
    </div> \
  </div> \
</div> \
';

You can try it here: http://jsfiddle.net/p8zbfw65/

Update
With the Tip from Roy it works like expected. I inserted the boundHtml binding

ko.bindingHandlers.boundHtml = {
    update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        const contentHtml = ko.unwrap(valueAccessor());

        element.innerHTML = contentHtml;
        ko.applyBindingsToDescendants(bindingContext, element)
    }
};

and changed the html: $parent.modalDialog.content
to boundHtml: $parent.modalDialog.content

Detlef
  • 3
  • 3

1 Answers1

0

The html binding inserts HTML but does not apply bindings to it. You will need a custom binding handler to do that. I wrote a simple one here.

Community
  • 1
  • 1
Roy J
  • 42,522
  • 10
  • 78
  • 102