AMouat
AMouat

Reputation: 755

Knockout observableArray not being populated by inherited datepicker value

I am having trouble populating an observableArray with a value inherited from a datepicker. I have a disabled textbox that displays the value of the datepicker as part of the data collection section. As it is disabled and not being typed in, it is not updating the observableArray.

I have created an example jsfiddle where I have stripped down and localised the problem.

Any help getting the value to appear in the observableArray would be great as I am really struggling to figure this one out!

HTML

<!--Date Load -->
<span><b>Select a date:</b></span>
<span><input id="theDate" data-bind="datepicker: viewModelWardStaff.dateMonthYear, datepickerOptions: { dateFormat: 'dd/mm/yy' } "></span>

<!--Input Form -->
<span><h4>Input New Entries</h4></span>
<div style="border: solid 1px;" data-bind="with: viewModelWardStaff">
    <form class="grid-form" id="dataCollection">
        <fieldset>
            <div data-row-span="1">
                <div data-field-span="1">
                    <label>Date</label>
                    <input id="cDate" class="autosend" data-bind="textInput: dateMonthYear, enable: false">
                </div>
                <div data-field-span="1">
                    <label>Status</label>
                    <input id="cStatus" maxlength="200" class="autosend" data-bind="textInput: wardstaff.Status" type="text">
                </div>
            </div>
        </fieldset>
        <div style="margin: 5px;">
            <a style="margin-left: 300px;" id="addFileButton" class="button-link" data-bind="click: viewModelWardStaff.addEntry">Add</a>
        </div>
    </form>
</div>
<h4>View Model Ward Staff</h4>
    <div data-bind="with: viewModelWardStaff">
    <pre data-bind="text: ko.toJSON($data, null, 2)"></pre>
</div>

KnockoutJS

moment.locale('en-gb');

function WardStaff(data) {
    var self = this;
    self.Date = ko.observable(data.Date());
    self.Status = ko.observable(data.Status());
};

function oWardStaff() {
    var self = this;
    self.Date = ko.observable();
    self.Status = ko.observable();
};

var viewModelWardStaff = function () {

    var self = this;

    self.wardstaff = new oWardStaff();

    self.dateMonthYear = ko.observable();

    self.entries = ko.observableArray([]);

    self.addEntry = function () {
        self.entries.push(new WardStaff(self.wardstaff));
    }

    self.removeEntry = function (entry) {
        self.entries.remove(entry);
    }
};

// dateString knockout
ko.bindingHandlers.dateString = {
    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var value = valueAccessor(),
            allBindings = allBindingsAccessor();
        var valueUnwrapped = ko.utils.unwrapObservable(value);
        var pattern = allBindings.datePattern || 'YYYY-MM-DD HH:mm:ss';
        if (valueUnwrapped == undefined || valueUnwrapped == null) {
            $(element).text("");
        }
        else {
            var date = moment(valueUnwrapped, "YYYY-MM-DDTHH:mm:ss"); //new Date(Date.fromISO(valueUnwrapped));
            $(element).text(moment(date).format(pattern));
        }
    }
}

//datepicker knockout
ko.bindingHandlers.datepicker = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        //initialize datepicker with some optional options
        var options = allBindingsAccessor().datepickerOptions || {};
        $(element).datepicker(options);
        //WORK
        //handle the field changing
        ko.utils.registerEventHandler(element, "change", function () {
            var observable = valueAccessor();

            if (moment($(element).datepicker("getDate")).local().format('YYYY-MM-DD') == 'Invalid date') {
                observable(null);
            }
            else {
                observable(moment($(element).datepicker("getDate")).local().format('YYYY-MM-DD'));
            }

        });

        //handle disposal (if KO removes by the template binding)
        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            $(element).datepicker("destroy");
        });

    },
    //update the control when the view model changes
    update: function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        current = $(element).datepicker("getDate");

        if (moment(value).format('DD/MM/YYYY') == 'Invalid date') {
            $(element).datepicker("setDate", null);
        }

    }
};

// Master View Model
var masterVM = function () {
    var self = this;

    self.viewModelWardStaff = new viewModelWardStaff();

};

// Activate Knockout
ko.applyBindings(masterVM);

Upvotes: 2

Views: 74

Answers (1)

user3297291
user3297291

Reputation: 23372

I think the problem is that your observable date, dateMonthYear, lives in your master view model. The Date property of self.wardstaff is never set.

You could solve this by sharing the observable in your master view model with the one in the wardstaff property:

function oWardStaff(obsDate) {
    var self = this;
    self.Date = obsDate;
    self.Status = ko.observable();
};

/* ... */

self.dateMonthYear = ko.observable();
self.wardstaff = new oWardStaff(self.dateMonthYear);

Now, whenever you pick a new date, it writes it to the observable referenced by both viewmodels.

This line suddenly becomes useful:

function WardStaff(data) {
    var self = this;
    self.Date = ko.observable(data.Date());      // <-- here
    self.Status = ko.observable(data.Status());
};

since Date is now actually set.

Fiddle that I think now works correctly: https://jsfiddle.net/n0t91sra/ (let me know if I missed some other desired behavior)

Upvotes: 1

Related Questions