Search code examples
knockout.jsknockout-mapping-plugin

Knockout mapping nested array with computed property


I nees to compute a set of datetimes serverside, allowing to change date/time clientside. The code below works as per load server data and add the iso field I need back on the server.

How can I make the iso field dynamically computable when the user change the date/time input field?

Knockout JS so far:

var ViewModel = function() {
    var self = this;
    // ...other first-level observables and funct

    function scadenzeNuove(payload) {
    ko.mapping.fromJS(payload, {}, this);

    this.iso = ko.pureComputed( function() {
                var data_re = /^(\d{2})\/(\d{2})\/(\d{4})T(\d{2}):(\d{2})/;
                if (!payload.data) {
                    return false;
                };
                var data_string = payload.data;

                if (!payload.ora) {
                    data_string = data_string + 'T00:00';
                } else {
                    data_string = data_string + 'T' + payload.ora;
                };

                var data_match = data_re.exec(data_string) ? data_re.exec(data_string) : false;
                if (!data_match) { return false; };
                var data = new Date(data_match[3], data_match[2]-1, data_match[1], data_match[4], data_match[5])
                return data;
    }, this);

    };
    var scadenzeNuoveMapping = {
        create: function(options) {
            return new scadenzeNuove(options.data);
        },
    };

    self.scadenzeNuove = ko.observableArray();
    self.compScadenze = function(form) {
    $.getJSON('{{ compute_scadenza }}', $(form).serialize(), function(data){
        ko.mapping.fromJS(data, scadenzeNuoveMapping, self.scadenzeNuove);
    });
    };
};
var vm = new ViewModel()
ko.applyBindings(vm);

Html:

<div data-bind="if: scadenzeNuove">
      <ul data-bind="foreach: scadenzeNuove">
        <li>
          <span data-bind="text: iso().toLocaleFormat()"></span>
          <input type="text" name="n_data" data-bind="value: data">
          <input type="text" name="n_ora" data-bind="value: ora">
          <input type="text" name="n_desc" data-bind="value: desc">
        </li>
      </ul>
</div>

Solution

  • My fault: I referred static data payload instead of dynamic observable parent:

    function ScadenzeNuove(payload) {
        var parent = this;
        ko.mapping.fromJS(payload, {}, parent);
    
        parent.iso = ko.pureComputed( function() {
            var data_re = /^(\d{2})\/(\d{2})\/(\d{4})T(\d{2}):(\d{2})/;
            if (!parent.data()) {
                console.log("Not entered any date");
                return false;
            };
            var data_string = parent.data();
    
            if (!parent.ora()) {
                data_string = data_string + 'T00:00';
            } else {
                data_string = data_string + 'T' + parent.ora();
            };
    
            var data_match = data_re.exec(data_string) ? data_re.exec(data_string) : false;
            if (!data_match) {
                console.log('Not a valid date format');
                return false;
            };
    
            console.log(data_match);
    
            // Array [ "08/06/2017T23:50:00", "08", "06", "2017", "23", "50" ]
            var data = new Date(data_match[3], data_match[2]-1, data_match[1], data_match[4], data_match[5])
            console.log(data.toLocaleFormat());
            console.log(data.getTimezoneOffset());
            return data;
        }, parent);
    
    
        parent.toCal = ko.observable(false);
    };