Search code examples
javascriptknockout.jsrequirejshottowel

How do I get a new empty object in my array


I had a simple app that used two viewmodels and requirejs to combine them and all was good, now I've been told I need to have multiple versions of my nested view model so I added an array and my first item is fine but I'm not sure how to add my subsequest items.

On another page the user is asked if they want to add another user and if so I need to navigate back to this page with a new empty person object in the people array.

  • If I use the object supplied by requirejs people.push(person), I get back the existing version rather then a new one
  • If I just add a new object to the array people.push({}), then my knockout bindings don't work as they are looking for non existent values.

How do I get a "new" person into people?

here are my view models and view,

Base

define(['viewmodel/person'], function (person) {
    var vm = {
        people: [],
        peopleIndex: 0,
        currentPerson: currentPerson,
    };

    return vm;

    //#region Internal Methods
    function currentPerson() {
        return vm.people[vm.peopleIndex]
    }
})

Common child

define(['services/logger', ], function (logger) {
    var person = {
        title: "",
        firstName: "",
        lastName: "",
    };

    return person;
})

View

<section>
    <h2 class="page-title" data-bind="text: title"></h2>

    <label for="firstName">Title</label><input type="text" name="firstName" data-bind="value: currentPerson().title" />
    <label for="firstName">Fisrt Name</label><input type="text" name="firstName" data-bind="value: currentPerson().fistName" />
    <label for="firstName">Last Name</label><input type="text" name="firstName" data-bind="value: currentPerson().lastName" />
</section>

Solution

  • You need to change your current person object literal into a Person constructor function (you probably want your properties as ko.observable in order to the your value bindings work):

    define(['services/logger', ], function (logger) {
        var Person = function() {
            this.title = ko.observable("");
            this.firstName = ko.observable("");
            this.lastName = ko.observable("");
        };
        return Person;
    })
    

    Then in your viewmodel where you need to create a new person you can use the new keyword to create a new instance:

    define(['viewmodel/person'], function (person) {
    
        //...
        people.push(new person())
    }
    

    Or you can make the people.push({}) work if include all the properties:

    people.push({
         title: ko.observable(""),
         firstName: ko.observable(""),
         lastName: ko.observable(""),
    });
    

    this will work but it will introduce duplication in your code so I don't recommend using this approach.