2

Is there a way to bind a array element as a value of a input or a select?

Something like this...

var ViewModel = function() {
  var self = this;

  self.array = ko.observableArray([undefined, undefined, undefined]);

  self.text = ko.computed(function() {
    return self.array()[0] + self.array()[1] + self.array()[2];
  });
}

var viewModel = new ViewModel();
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input data-bind="value: $root.array[0]" />
<input data-bind="value: $root.array[1]" />
<input data-bind="value: $root.array[2]" />
<p data-bind="text: $root.text"></p>

EDIT

Sorry guys, I'm really terrible to make questions, but my problem is a little more tricky, My real entries are selects that is a filter of an array and the values selecteds. like this:

var ViewModel = function() {
  var self = this;
  self.family = ko.mapping.fromJS(data.family);

  self.array = ko.observableArray([ko.observable(undefined), ko.observable(undefined), ko.observable(undefined)]);

  self.filterFamily1 = function() {
    return self.family().filter(function(i) {
      return i.parent() == null;
    });

  }
  self.filterFamily2 = ko.computed(function() {
    return ko.utils.arrayFilter(self.family, function(i) {
      return i.parent() === self.array()[0]().name();
    });

  });
   self.filterFamily3 = ko.computed(function() {
    return ko.utils.arrayFilter(self.family, function(i) {
      return i.parent() === self.array()[1]().name();
    });

  })

}

var data = {
  family: [{
    name: "John",
    parent: null
  }, {
    name: "Mary",
    parent: null
  }, {
    name: "Erick",
    parent: "John"
  }, {
    name: "Paul",
    parent: "John"
  }, {
    name: "Marshall",
    parent: "Mary"
  }, {
    name: "Ross",
    parent: "Paul"
  }]
};

var viewModel = new ViewModel();
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>

<select data-bind="options: $root.filterFamily1(),optionsText: 'name', value: $root.array()[0]"></select>
<select data-bind="options: $root.filterFamily2(),optionsText: 'name', value: $root.array()[1]"></select>
<select data-bind="options: $root.filterFamily3(),optionsText: 'name', value: $root.array()[2]"></select>

but the answers were right for the problem that I describe before.

3 Answers 3

4

Since your question changed a lot, I'm posting a new answer:

Your snippet had console errors: your initially selected values weren't set nor did you check for undefined. Include these checks and everything works.

Do note that you can definitely improve readability and refactor recurring code sections.

var ViewModel = function() {
  var self = this;
  self.family = ko.mapping.fromJS(data.family);




  self.filterFamily1 = ko.computed(function() {
    return self.family().filter(function(i) {
      return i.parent() === null;
    });

  });

  self.array = ko.observableArray([
    ko.observable(self.filterFamily1()[0]),
    ko.observable(undefined),
    ko.observable(undefined)
  ]);

  self.filterFamily2 = ko.computed(function() {
    return self.family().filter(function(i) {
      return self.array()[0]() && i.parent() === self.array()[0]().name();
    });

  });
  self.filterFamily3 = ko.computed(function() {
    return self.family().filter(function(i) {
      return self.array()[1]() && i.parent() === self.array()[1]().name();
    });

  })

}

var data = {
  family: [{
    name: "John",
    parent: null
  }, {
    name: "Mary",
    parent: null
  }, {
    name: "Erick",
    parent: "John"
  }, {
    name: "Paul",
    parent: "John"
  }, {
    name: "Marshall",
    parent: "Mary"
  }, {
    name: "Ross",
    parent: "Paul"
  }]
};

var viewModel = new ViewModel();
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>

<select data-bind="options: $root.filterFamily1(),optionsText: 'name', value: $root.array()[0]"></select>
<select data-bind="options: $root.filterFamily2(),optionsText: 'name', value: $root.array()[1]"></select>
<select data-bind="options: $root.filterFamily3(),optionsText: 'name', value: $root.array()[2]"></select>

Sign up to request clarification or add additional context in comments.

Comments

3

Yessir, there certainly is. But:

  1. The items themselves must be observables.
  2. You should execute the self.array function before binding to one of its elements.

Here's an example:

var ViewModel = function() {
  var self = this;

  self.array = ko.observableArray([ko.observable(""), ko.observable(""), ko.observable("")]);

  self.text = ko.computed(function() {
    return self.array()[0]() + self.array()[1]() + self.array()[2]();
  });
}

var viewModel = new ViewModel();
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input data-bind="value: $root.array()[0]" />
<input data-bind="value: $root.array()[1]" />
<input data-bind="value: $root.array()[2]" />
<p data-bind="text: $root.text"></p>

However, why not use the foreach binding and some other nice improvements?

var ViewModel = function() {
  var self = this;

  self.array = ko.observableArray([
    { txt: ko.observable("a") },
    { txt: ko.observable("b") },
    { txt: ko.observable("c") },
    { txt: ko.observable("d") }
  ]);

  self.text = ko.computed(function() {
    return self.array().map(function(obs) { return obs.txt(); }).join("");
  });
}

var viewModel = new ViewModel();
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>

<!-- ko foreach: array -->
<input data-bind="textInput: txt">
<!-- /ko -->
<p data-bind="text: text"></p>

1 Comment

as I said above, the solution works fine, but my real problem is different, I thought that if it works for inputs maybe works for selects too.
2

If you want to access the array by index, you need to evaluate the observable first using ().

If you want the value binding to work two-ways (i.e.: not only set it initially, but also update the values in your viewmodel after a change), you'll have to bind them to ko.observable variables.

Other improvements:

If you want to join all values together, you could use reduce or a combination of map and join to append the strings.

EDIT: I was wrong here! You could also use a foreach binding and use $data to refer to the current value if you need inputs for all your array's values.

Actually, you can't create a two way binding using the $data keyword. You can if you use the $index observable.

Reference: https://github.com/knockout/knockout/issues/708

var ViewModel = function() {
  var self = this;

  self.array = ko.observableArray([ko.observable("a"), ko.observable("b"), ko.observable("c")]);

  self.text = ko.computed(function() {
    return self.array()[0]() + self.array()[1]() + self.array()[2]();
  });

  self.reducedText = ko.computed(function() {
    return self.array()
      .reduce(function(prev, curr) {
        return prev + curr();
      }, "");

  });
};

var viewModel = new ViewModel();
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input data-bind="value: array()[0]" />
<input data-bind="value: array()[1]" />
<input data-bind="value: array()[2]" />
<p data-bind="text: text"></p>

<!-- EDIT: this does not create a two way binding
<div data-bind="foreach: array">
  <input data-bind="value: $data" />
</div>
 -->

<!-- EDIT: this does work -->
<div data-bind="foreach: array">
  <input data-bind="value: $parent.array()[$index()]" />
</div>

<p data-bind="text: reducedText"></p>

2 Comments

as I said above, the solution works fine, but my real problem is different, I thought that if it works for inputs maybe works for selects too.
I've posted a second answer, since the included snippet after your update changes a lot... You can find it here: stackoverflow.com/a/37416047/3297291

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.