2

I have a simple list view which displays items, with the help of Knockout. A set of 'Things' are hard coded into an array and Knockout lists them at page load. It works fine so far.

Now, I wish to add a new function: newFunc(). A new item will be created in the function with a property, but without a name. The item can be added to the list later, if the user wishes. A name can be given to the item by the user then.

Q: In this situation, how can I (or can I) use the constructor function 'Item' to create a new item, in the new function? The 'Item' function requires an argument 'item', yet in the new function, I cannot pass an argument, since the new item is being created and a name is not given. How should I think and code differently to achieve the functionality described?

Any help and tips would be appreciated.

// HTML //

<ul data-bind='foreach: itemList'>
    <li data-bind='text: name'></li>
</ul>

// Script //

var Things = [
    { name: 'name1', property: 'prop1' },
    { name: 'name2', property: 'prop2' },
    ...
];

var Item = function(item) {
    this.name = item.name;
    this.property = item.property;
};

// The new function in Question
var aNewItem;
newFunc = function() {
    // I'd like to create an 'Item' with a given property, but w/o a name.
    aNewItem = new Item(); // ???
    aNewItem.property = 'propertyN'; // **Edit**: also a variable set by user or callback.
};

var viewModel = function {
    self = this;
    self.itemList = ko.observableArray();
    Things.forEach(function(item) {
        self.itemList.push(new Item(item));
    });
};

ko.applyBindings(new viewModel());    
Yui
  • 67
  • 1
  • 5
  • Instead of creating the item and then setting the properties, can't you just gather the properties, then call the constructor? Otherwise, you'll have to remove the `item` parameter and set the properties explicitly after instantiation. – Hunan Rostomyan Dec 04 '15 at 00:01
  • Yes, it certainly can be a solution, I think. Collect data into a variable or an array and use it later as needed with the constructor. Thanks for a good suggestion! I don't think removing the `item` parameter might not be the best solution, since I am looking for something scalable and reading in data from an array `Things`. – Yui Dec 04 '15 at 05:06

2 Answers2

1

You can pass an object that only has a property property in it.

aNewItem = new Item({
  property: 'propertyN'
});
Mike Cluck
  • 31,869
  • 13
  • 80
  • 91
  • It seems like a very simple solution. Thank you very much. In this case, does the `name` property of `aNewItem` set as a `null` value, automatically? (Is there any documentation, which I can read about the usage of a constructor like this?) – Yui Dec 04 '15 at 05:11
  • @Yui In JS, you have two "null-like" values. `null` and `undefined` ([see MDN for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures)). By default, a variable (or property) has the value of `undefined` unless it's explicitly given another value. As for how this constructor works, all that you're doing is giving it an object with the properties you require. [If it looks like a duck and quacks like a duck, it's a duck.](http://stackoverflow.com/a/3379721/371184) – Mike Cluck Dec 04 '15 at 15:39
0

You have a couple of options (in order of best practice I adopt):

Using default options in the constructor

You can specify default options for your constructor's properties by using the || (or) operator in the assignment (= if value before the || is falsy, assign the value after). If you have an object as parameter (which is the easiest), you can re-assign item to an empty object if it is not passed, which makes testing for its properties easier.

var Item = function(item) {
    var item = item || {};
    this.name = item.name || '';
    this.property = item.property || 'propertyN';
};

Using default options in the caller (newFunc)

Although I think it is cleaner to have default values as previously shown, it is also possible to specify them in the function that invokes the constructor:

newFunc = function() {
    aNewItem = new Item({property: 'propertyN'});
};

Or if you specified defaults in the constructor, your function will work just as well in its current state.

Adding only properties specified by the item param

You can also choose to add all properties that are given in item with a for..in loop. Disclaimer: I would not advise this as it makes using a constructor pretty pointless.

var Item = function(item) {
    var item = item || {};
    for (var prop in item) 
      this[prop] = item[prop];
};

You could then do:

newFunc = function() {
    aNewItem = new Item({property: 'propertyN', foo: 'foo'});
    bNewItem = new Item(); 
    console.log(aNewItem.foo); // 'foo'
    console.log(bNewItem['whatever']); // undefined
};
webketje
  • 10,376
  • 3
  • 25
  • 54
  • Thank you for suggesting various solution options. Your answer is very informative! When using a default option constructor (the first option), what cases are classified as a 'false'? A null? – Yui Dec 04 '15 at 05:33
  • @Yui, for example null, false, undefined, a zero-width string (''), a string with '0', the number 0, a zero-width array.. [MDN](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) also lists some. Basically everything that will return `true` when you do `myFalsyValue == false` (not 3 equals, only in non-strict comparison). – webketje Dec 04 '15 at 23:02