5

In this jsfiddle I have a simple example of what I want to do working. It's all working just fine. But it seems like there has to be a better way to create these arrays with some loops or something. I've been trying all day and haven't succeeded. Can someone tell me if

  1. it's possible
  2. what voodoo I need to perform in order for it to work.

Here's the code from the fiddle.

View:

<table>
<tbody>
    <tr>
        <td></td>
        <!-- ko foreach: topvals -->
        <td >
            <input type="text" data-bind="value: val"/>
        </td>
        <!-- /ko -->
    </tr>
    <tr>
        <td><input type="text" data-bind="value:sidevals()[0].val"/></td>
        <!-- ko foreach: intersections()[0] -->
        <td><span data-bind="text: val"></span></td>
        <!-- /ko -->
    </tr>
    <tr>
        <td ><input type="text" data-bind="value:sidevals()[1].val"/></td>
        <!-- ko foreach: intersections()[1] -->
        <td><span data-bind="text: val"></span></td>
        <!-- /ko -->
    </tr>
</tbody>
</table>

Viewmodel:

function ViewModel() {

    this.topvals = ko.observableArray([
        { val: ko.observable(6) },
        { val: ko.observable(1) },
        { val: ko.observable(1) },
        { val: ko.observable(1) }
    ]);

    this.sidevals = ko.observableArray([
        { val: ko.observable(1) },
        { val: ko.observable(1) },
        { val: ko.observable(1) }
    ]);

    this.intersections = ko.observableArray([
        [
            { val: ko.computed(function () { return this.topvals()[0].val() * this.sidevals()[0].val(); }, this) },
            { val: ko.computed(function () { return this.topvals()[1].val() * this.sidevals()[0].val(); }, this) },
            { val: ko.computed(function () { return this.topvals()[2].val() * this.sidevals()[0].val(); }, this) },
            { val: ko.computed(function () { return this.topvals()[3].val() * this.sidevals()[0].val(); }, this) }
        ],
        [
            { val: ko.computed(function () { return this.topvals()[0].val() * this.sidevals()[1].val(); }, this) },
            { val: ko.computed(function () { return this.topvals()[1].val() * this.sidevals()[1].val(); }, this) },
            { val: ko.computed(function () { return this.topvals()[2].val() * this.sidevals()[1].val(); }, this) },
            { val: ko.computed(function () { return this.topvals()[3].val() * this.sidevals()[1].val(); }, this) }
        ]
    ]);
}

ko.applyBindings(new ViewModel());
2
  • It would be especially helpful if I could abstract away an "intersection" especially since there's a moderately complicated equation that needs to be done with the topval, sideval, and a constant. I haven't figured that out yet either :-/ Commented Dec 13, 2013 at 21:31
  • Make sure to manually add KO to the jsfiddle dependencies .. doesn't work at all here. Commented Dec 13, 2013 at 23:48

2 Answers 2

3
  • To create the topvals and sidevals arrays you can use ko.mapping.fromJS.
  • To create the computed intersections you can return an "array of arrays" using the ko.utils.arrayMap function.

Here is the final code (and a jsfiddle):

function ViewModel() {
    var self = this;

    var calculateIntersection = function(topval, sideval) {
        return topval * sideval;
    };

    var topvals = [{ val: 1 }, { val: 2 }, { val: 3 }];
    var sidevals = [{ val: 1 }, { val: 2 }, { val: 3 }];

    self.topvals = ko.mapping.fromJS(topvals);
    self.sidevals = ko.mapping.fromJS(sidevals);
    self.intersections = ko.computed(function() {
        return ko.utils.arrayMap(self.sidevals(), function(sideval) {
            return ko.utils.arrayMap(self.topvals(), function(topval) {
                return calculateIntersection(topval.val(), sideval.val());
            });
        });
    });
}

ko.applyBindings(new ViewModel());

And here is the view:

<table>
    <tbody>
        <tr>
            <td></td>
            <!-- ko foreach: topvals -->
            <td >
                <input type="text" data-bind="value: val"/>
            </td>
            <!-- /ko -->
        </tr>
        <!-- ko foreach: sidevals -->
        <tr>
            <td><input type="text" data-bind="value: val"/></td>
            <!-- ko foreach: $root.intersections()[$index()] -->
            <td><span data-bind="text: $data"></span></td>
            <!-- /ko -->
        </tr>
        <!-- /ko -->
    </tbody>
</table>
Sign up to request clarification or add additional context in comments.

Comments

1

This simple view model already does it:

function ViewModel(vals1, vals2) {
    this.topvals = ko.observableArray(mapToObservables(vals1));
    this.sidevals = ko.observableArray(mapToObservables(vals2));
    this.calc = function (val1, val2) {
        return ko.unwrap(val1) * ko.unwrap(val2);
    };
}

ko.applyBindings(new ViewModel([6, 5, 4, 3], [1, 2, 3]));

where mapToObservables stands for either the mapping plugin or a function like this one:

function mapToObservables(vals) {
    return ko.utils.arrayMap(vals, function (val) {
        return { val: ko.observable(val) };
    });
};

and this view

<table>
<tbody>
    <tr>
        <td></td>
        <!-- ko foreach: topvals -->
        <td><input type="text" data-bind="value: val"/></td>
        <!-- /ko -->
    </tr>
    <!-- ko foreach: sidevals -->
    <tr>
        <td><input type="text" data-bind="value: val"/></td>
        <!-- ko foreach: $root.topvals -->
        <td><span data-bind="text: $root.calc(val, $parent.val)"></span></td>
        <!-- /ko -->
    </tr>
    <!-- /ko -->
</tbody>
</table>

See: http://jsfiddle.net/dt9kL/

A variant would be to use a separate sub-model for the values: http://jsfiddle.net/dt9kL/1/. For encapsulating a single value this is overkill, but it might be useful for more complex structures.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.