Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
200 views
in Technique[技术] by (71.8m points)

javascript - How to push object to an observableArray

I have this model, a Foo have many FooItem. How can I add an object to a FooItem?

I have this code, you can suggest or even erase the whole code that works best for this scenario

var Foo = function (selected) {
    this.id = ko.observable();
    this.name = ko.observable();
    this.fooItems = ko.observableArray([]);
    this.isSelected = ko.computed(function() {
        return selected() === this;
    }, this);
};

var FooItem = function () {
    this.id = ko.observable();
    this.selectedItemId = ko.observable();
    this.selectedName = ko.observable();
    this.remarks = ko.observable();
};

var foosFromDb = [{ id: 1, name: 'foo1' },
    { id: 2, name: 'foo2' },
    { id: 3, name: 'foo3' },
    { id: 4, name: 'foo4' }];

var fooItemsFromDb = [
    { id: 1, fooItem: 'fooItem1' },
    { id: 2, fooItem: 'fooItem2' },
    { id: 3, fooItem: 'fooItem3' },
    { id: 4, fooItem: 'fooItem4' },
    { id: 5, fooItem: 'fooItem5' },
    ];

var vm = (function () {
    var 
    	foos = ko.observableArray([]),
        fooItemsList = ko.observableArray([]),
    	loadFoos = function() {
            for(var i = 0; i < foosFromDb.length; i++) {
                foos.push(new Foo(selectedFoo)
                          .id(foosFromDb[i].id)
                          .name(foosFromDb[i].name));
            }
            
            selectFoo(foos()[0]);
        },
        loadFooItemsList = function() {
            for(var i = 0; i < fooItemsFromDb.length; i++) {
                fooItemsList.push({ id: fooItemsFromDb[i].id, name: fooItemsFromDb[i].fooItem });
            }
        },
        selectedFoo = ko.observable(),
        selectFoo = function(item) {
            selectedFoo(item);
        },
        newFoo =  function(item) {
           var id = foos().length + 1;
           var aFoo = new Foo(selectedFoo)
           				.id(id)
           				.name('');
            
            foos.push(aFoo);
            selectFoo(aFoo);
        },
        addFooItem = function(item){
            console.log(item); // In here I think I am getting the Selected Foo.
        };
    
    return {
        foos: foos,
        loadFoos: loadFoos,
        newFoo: newFoo,
        selectedFoo: selectedFoo,
        selectFoo: selectFoo,
        loadFooItemsList: loadFooItemsList,
        fooItemsList: fooItemsList,
        addFooItem: addFooItem
    };
}());

vm.loadFoos();
vm.loadFooItemsList();
ko.applyBindings(vm);
.container {
    float: left;
    width: 200px;
    height: 250px;
    border: 1px solid #ccc;
}
.selected  {
    background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div class="container">
    <a href="#" data-bind="click: newFoo">New Foo</a>
    <table>
        <tr>
            <th>Foo Id</th>
            <th>Foo Name</th>
        </tr>
        <tbody data-bind="foreach: foos" style="cursor: pointer">
            <tr data-bind="click: $root.selectFoo, css: { selected: isSelected }">
                <td data-bind="text: id"></td>
                <td data-bind="text: name"></td>
            </tr>
        </tbody>
    </table>
</div>
<div data-bind="with: selectedFoo">
    <div class="container">
        <label>Id</label><br /><input type="text" data-bind="value: id" /><br />
        <label>Name</label><br /><input type="text" data-bind="value: name" /><br />
    </div>
    <div class="container">
        <label>Foo Item</label><br /><select data-bind="options: $root.fooItemsList, 
                                                        optionsText: 'name', 
                                                        optionsValue: 'id'"></select><br />
        <label>Remarks</label><br /><input type="text" /><br />
        <a href="#" data-bind="click: $root.addFooItem">Add Foo Item</a>
    </div>
        <div class="container">
        <table>
            <thead>
                <tr>
                    <th>FooItem</th>
                    <th>Remarks</th>
                </tr>
            </thead>
        </table>
    </div>
</div>
<div style="clear: both"></div>
<pre data-bind="text: ko.toJSON($root.selectedFoo, null, 2)"></pre>
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You are missing a number of things that are necessary to hook everything up. Your select was not bound to anything, and you did not have an observable for it to bind to. I added a selectedItem member to Foo for this.

The optionsValue for the select I reverted to be the item being selected, because I don't want to look it up. The value isn't a FooItem, but is a simple object with id and name. In addFooItem, I create a new FooItem from the object data and push it onto the fooItems list.

The table body wasn't defined, so I set that up to display. For some reason, saving a remark causes all the previously saved remarks to update. I don't know why that is, but I find the whole construction a bit hard to follow.

var Foo = function(selected) {
  this.id = ko.observable();
  this.name = ko.observable();
  this.fooItems = ko.observableArray([]);
  this.isSelected = ko.computed(function() {
    return selected() === this;
  }, this);
  this.selectedItem = ko.observable('quack');
};

var FooItem = function() {
  this.id = ko.observable();
  this.selectedItemId = ko.observable();
  this.selectedName = ko.observable();
  this.remarks = ko.observable();
};

var foosFromDb = [{
  id: 1,
  name: 'foo1'
}, {
  id: 2,
  name: 'foo2'
}, {
  id: 3,
  name: 'foo3'
}, {
  id: 4,
  name: 'foo4'
}];

var fooItemsFromDb = [{
  id: 1,
  fooItem: 'fooItem1'
}, {
  id: 2,
  fooItem: 'fooItem2'
}, {
  id: 3,
  fooItem: 'fooItem3'
}, {
  id: 4,
  fooItem: 'fooItem4'
}, {
  id: 5,
  fooItem: 'fooItem5'
}, ];

var vm = (function() {
  var
    foos = ko.observableArray([]),
    fooItemsList = ko.observableArray([]),
    loadFoos = function() {
      for (var i = 0; i < foosFromDb.length; i++) {
        foos.push(new Foo(selectedFoo)
          .id(foosFromDb[i].id)
          .name(foosFromDb[i].name));
      }

      selectFoo(foos()[0]);
    },
    loadFooItemsList = function() {
      for (var i = 0; i < fooItemsFromDb.length; i++) {
        fooItemsList.push({
          id: fooItemsFromDb[i].id,
          name: fooItemsFromDb[i].fooItem
        });
      }
    },
    selectedFoo = ko.observable(),
    selectFoo = function(item) {
      selectedFoo(item);
    },
    newFoo = function(item) {
      var id = foos().length + 1;
      var aFoo = new Foo(selectedFoo)
        .id(id)
        .name('');

      foos.push(aFoo);
      selectFoo(aFoo);
    },
    addFooItem = function(foo) {
      var itemData = foo.selectedItem(),
          item = new FooItem();
      item.id(itemData.id);
      item.selectedName(itemData.name);
      var r = vm.remark();
      console.debug("Remark:", r);
      item.remarks(r);
      foo.fooItems.push(item);
    };

  return {
    foos: foos,
    loadFoos: loadFoos,
    newFoo: newFoo,
    selectedFoo: selectedFoo,
    selectFoo: selectFoo,
    loadFooItemsList: loadFooItemsList,
    fooItemsList: fooItemsList,
    remark: ko.observable(),
    addFooItem: addFooItem
  };
}());

vm.loadFoos();
vm.loadFooItemsList();
ko.applyBindings(vm);
.container {
  float: left;
  width: 200px;
  height: 250px;
  border: 1px solid #ccc;
}
.selected {
  background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div class="container">
  <a href="#" data-bind="click: newFoo">New Foo</a>
  <table>
    <tr>
      <th>Foo Id</th>
      <th>Foo Name</th>
    </tr>
    <tbody data-bind="foreach: foos" style="cursor: pointer">
      <tr data-bind="click: $root.selectFoo, css: { selected: isSelected }">
        <td data-bind="text: id"></td>
        <td data-bind="text: name"></td>
      </tr>
    </tbody>
  </table>
</div>
<div data-bind="with: selectedFoo">
  <div class="container">
    <label>Id</label>
    <br />
    <input type="text" data-bind="value: id" />
    <br />
    <label>Name</label>
    <br />
    <input type="text" data-bind="value: name" />
    <br />
  </div>
  <div class="container">
    <label>Foo Item</label>
    <br />
    <select data-bind="options: $root.fooItemsList, 
                       optionsText: 'name', 
                       value: selectedItem"></select>
    <br />
    <label>Remarks</label>
    <br />
    <input type="text" data-bind="value: $root.remark" />
    <br />
    <a href="#" data-bind="click: $root.addFooItem">Add Foo Item</a>
  </div>
  <div class="container">
    <table>
      <thead>
        <tr>
          <th>FooItem</th>
          <th>Remarks</th>
        </tr>
      </thead>
      <tbody data-bind="foreach: fooItems">
        <td data-bind="text:selectedName"></td>
        <td data-bind="text:$root.remark"></td>
        </tbody>
    </table>
  </div>
</div>
<div style="clear: both"></div>
<pre data-bind="text: ko.toJSON($root.selectedFoo, null, 2)"></pre>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...