-1

I made a small fiddle demonstrating my issue here http://jsfiddle.net/LkqTU/33025/

the datepicker made using a component does not fire

below is my original post...

I am using the bootstrap 3 date time picker and it is working as follows.

html

<div data-bind="css: formControlCss">
                                                 <label class="control-label" for="earlyPickup">Early Pickup:</label>  
                                                   <div class='input-group date' id='earlyPickup'>
                                                     <input type='text' class="form-control" />
                                                     <span class="input-group-addon">
                                                        <span class="glyphicon glyphicon-calendar"></span>
                                                     </span>
                                                   </div>
                                            </div>

javascript.

 $('#earlyPickup').datetimepicker(
                                  {
                                   defaultDate: d.DB2VXRVSMRecord.DtErlstPickup + " " + d.DB2VXRVSMRecord.TiErlstPickup
                                  });

this works fine however I have several of these date time pickers so I attempted to create a knockout component.

component.

 ko.components.register('datetime-picker', {
            viewModel: function (params) {
                this.label = params.label
                this.id = params.id
                this.hasError = params.hasError
                this.formControl = ko.pureComputed(function () {
                    return (this.hasError()) ? "form-group has-error" : "form-group";
                }, this);
            },
            template:
               '<div data-bind="css: formControl">\
                 <label class="control-label" \
                 data-bind ="attr: {for: id}"> \
                  <span data-bind="text: label"></span>:</label>\
                  <div class="input-group date"\
                  data-bind= "attr: {id: id}" >\
                      <input type="text" class="form-control" />\
                         <span class="input-group-addon">\
                              <span class="glyphicon glyphicon-calendar"></span>\
                         </span>\
                   </div>\
                 </div>'
        });

and I changed my HTML to.

<datetime-picker
                                         params="label: 'Early Pickup', 
                                         id: 'earlyPickup',
                                         hasError: ErlstPickupHasErrors">
                                      </datetime-picker>

unfortunately my date time picker is no longer instantiated. I am assuming because since I am now using a component I can not refer to id directly? when calling the date time picker $('#earlyPickup').datetimepicker( does it not know what earlyPickup is at this point?

Bryan Dellinger
  • 4,724
  • 7
  • 33
  • 79
  • How are you attaching the picker to the elements? If you do it immediately on load, chances are, the id's won't be applied yet so you may miss it. – Jeff Mercado Dec 29 '16 at 03:45
  • 1
    you could also use a binding handler for the `datetimepicker` control and use it wherever you have that control – gkb Dec 29 '16 at 05:28

1 Answers1

1

The problem is that components are loaded asynchronously. Your components may or may not have been rendered on the page by the time you try to attach the date picker. Additionally, an id is required for the picker to work. So you'll have to wait til the components are rendered, and the bindings are applied.

One way you could tackle this is to add a preprocessNode handler to inspect the nodes as they are traversed. You could then determine whether a node needs to have the picker attached to it. Then attach the picker if an id is set, otherwise retry some amount of times.

e.g.,

ko.components.register('form-input', {
  viewModel: function(params) {
    this.inputValue = params.value;
    this.label = params.label;
    this.id = params.id;
  },
  template: '<div class ="form-group">\
    <label class="control-label col-sm-2" \
           data-bind ="attr: {for: id}"> \
      <span data-bind="text: label"></span>:\
    </label>\
    <div class="col-sm-9">\
      <input type="text"\
             class="form-control datepicker"\
             data-bind="textInput: inputValue, attr: {id: id}"/>\
    </div>\
  </div>'
});

ko.bindingProvider.instance.preprocessNode = function (node) {
  if (node.nodeType == 1) {
    if ($(node).hasClass('datepicker')) {
      if (node.id) { // an id is required
        attachDatepicker(node);
      } else {
        retryAttachDatepicker(node, 3);
      }
    }
  }

  function attachDatepicker(node) {
    $(node).datepicker();
    ko.utils.domNodeDisposal.addDisposeCallback(node, function () {
      $(node).datepicker('destroy');
    });
  }

  // would be easier to use a microtask but this is pre 3.4
  function retryAttachDatepicker(node, tries) {
    if (tries > 0) {
      setTimeout(function () {
        if (node.id) {
          attachDatepicker(node);
        } else {
          retryAttachDatepicker(node, tries - 1)
        }
      }, 10);
    } else {
      console.warn('unable to attach datepicker to node %o', node);
    }
  }
};

fiddle

Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
  • thank you, my son also suggested this solution however I think I am going to try and use the ko binding handler as gkb recommended, that is probably the knockout way to do it. – Bryan Dellinger Dec 29 '16 at 10:20
  • Ah you know what, I answered a question about the datepicker and using it in a binding handler. http://stackoverflow.com/questions/25838821/knockout-js-with-jquery-ui-datepicker-missing-instance-data-for-this-datepick. It certainly will be a lot cleaner to combine this with the component. At least there, you have access to the element and as I mention there, the id is key to the datepicker working. – Jeff Mercado Dec 29 '16 at 15:38