James Fleming
James Fleming

Reputation: 2589

Knockout mapping add issue

I have a method where I am trying to add a child to my parent, but when the call is made to the method, the context of the calling parent is undefined.

The code (abbreviated for clarity) Fiddle http://jsfiddle.net/poundingCode/THghy/

// Initialized the namespace
var my = {};
    my.models = {};

    // View model declaration
    my.vm = (function (model) {
        var memberVm = {
            id: ko.observable(model.Id),
            company: ko.observable(model.Company),
            fName: ko.observable(model.FName),
            lName: ko.observable(model.LName),
            name: ko.observable(model.Name),
            positions: ko.observableArray([]),
            totalPositions: ko.observable(),
            totalProjects: ko.observable()
        };

        // children
        memberVm.loadPositions = function (positions) {
            memberVm.totalPositions = 0;
            memberVm.totalProjects = 0;
            $.each(positions, function (i, p) {
                memberVm.positions.push( new my.models.Position()
                .company(p.Company)
                .companyId(p.CompanyId)
                 .description(p.Description)
                .id(p.Id)
                .memberId(p.MemberId)
                .name(p.Name)
                .title(p.Title)
                .projects(p.Projects) 
                );
                memberVm.totalPositions++;
                memberVm.totalProjects += p.Projects.length;
            });// end for each
        }; //end load positions

        memberVm.loadPositions(model.Positions);

        memberVm.fullName = ko.computed(function () {
            return this.fName() + ' ' + this.lName();
        }, memberVm);


        // Computed observable function.
        // We append it to the ViewModel here.

        // return object
        var vm = {
            id: memberVm.id,
            company: memberVm.company,
            fName: memberVm.fName,
            fullName: memberVm.fullName,
            lName: memberVm.lName,
            name: memberVm.name,
            positions: memberVm.positions,
            totalPositions: memberVm.totalPositions,
            totalProjects: memberVm.totalProjects,    

        };
        return vm;
    });

    /////////////////////////////////////
    // Add a position - or at least try to! this is where I get into trouble.

    my.vm.addPosition = (function () {
        var pos = new my.models.Position();
        pos.memberId = my.vm.id;
        my.vm.positions.push(pos);
    });
    /////////////////////////////////////
    // TypeError: my.vm.positions is undefined my.vm.positions.push(pos);
    my.models.Member = (function () {
            id = ko.observable();
            company = ko.observable(); 
            fName = ko.observable();
            lName = ko.observable();
            name = ko.observable();
            positions = ko.observableArray([]);

    });
    my.models.Position = (function () {
        this.company = ko.observable();          
        this.id = ko.observable();           
        this.memberId = ko.observable();
        this.name = ko.observable();
        this.title = ko.observable();
        // place holders
        this.totalProjects = ko.observableArray();
        this.totalCredits = ko.observableArray();
    });


 var viewModel = my.vm(data);
        ko.applyBindings(viewModel);
my.setUI();

The HTML

      <script type="text/html" id="positionItemTemplate">
          <div class="summary" data-bind="attr : {onClick: 'my.showDetails(' + id() + ')', href: '#detail_'+ id()}">
                                    <a data-bind="attr : { href: '#detail_'+ id()}">
                                    <h2><label data-bind="text: company().Name"></label>: <label data-bind="text: title"></label></h2> </a>
                                    <h3><label data-bind="text: title" ></label> <label data-bind="text: startDate" ></label> - <label data-bind="value: endDate" ></label></h3>

                                </div>
                                <div data-bind="attr : {id: 'detail_' + id() }" class="details" style="right: 580px;">
                                  <div class="positionOverview">
                                   <h2><label data-bind="text: company().Name"></label>: <label data-bind="text: title"></label></h2> </a>
                                     <a data-bind="attr : {href: '#detail_'+ id()}">   <div class="editor-label"><label>Position</label></div></a>
                                        <div class="editor-field">
                                            <input type="text" data-bind="value: company().Name" class="textbox-long">
                                        </div>
                                        <div class="editor-label">
                                            <label>Title</label>
                                        </div>
                                      <div class="editor-field">
                                          <input type="text" data-bind="value: title" class="textbox-long">
                                      </div>
                                        <div class="editor-label">
                                            <label>Summary</label>
                                        </div>
                                        <div class="editor-field">
                                           <textarea data-bind="value: summary"  rows="4" cols="60"></textarea>
                                        </div>
                                        <div class="editor-label"><label>Compensation</label></div>
                                        <div class="editor-field">
                                            <select data-bind="options: $parent.compensations, value: compensationId, optionsText: 'Name', optionsValue: 'Id', optionsCaption: 'Select'"></select>
                                        </div>
                                        <div class="editor-label"><label for="HoursPerWeek" class="hourly">Hours/Week</label></div>
                                        <div class="editor-field">
                                            <input type="text" data-bind="value: hoursPerWeek" class="number-short">
                                        </div>
                                      <div class="div-table">
                                          <div class="div-table-row">
                                              <div class="div-table-col"><label for="StartDate">Start Date</label></div>
                                              <div class="div-table-col"><label for="EndDate">End Date</label></div>
                                              <div class="div-table-col"></div>
                                          </div>
                                          <div class="div-table-row">
                                              <div class="div-table-col"><input type="text" data-bind="value: startDate" class="date"></div>
                                              <div class="div-table-col"><input type="text" data-bind="value: endDate" class="date"></div>

                                          </div>
                                          <div class="div-table-row salary">
                                              <div class="div-table-col"><label for="SalaryStart">$/Hr Start</label></div>
                                              <div class="div-table-col"><label for="SalaryStart">$/Hr End</label></div>
                                          </div>
                                          <div class="div-table-row salary">
                                              <div class="div-table-col"><input type="text" data-bind="value: salaryStart" class="date"></div>
                                              <div class="div-table-col"><input type="text" data-bind="value: salaryEnd" class="date"></div>
                                          </div>
                                      </div>
                                      <input id="btnAddProject" type="button" value="Add Project" data-bind="attr : {onClick: 'addProject(new project())'}">
                                        <input type="button" data-bind="attr : {onClick: 'my.showDetails(' + id() + ')'}" value="Update"/>
                                    </div>                                                                   
                                </div>
      </script>
</head>
    <body>
        <div id="main">
            <div class="marquee center">
                <aside class="aside">
                    <div class="display">
                     <label data-bind="text: 'Total Positions: ' + totalPositions" ></label><input value="Add Position" type="button" data-bind="attr : {onClick: 'my.vm.addPosition()'}"/><br />

                    </div>
                    <div class="adPanel">
                        <h4><div id="message"></div></h4>
                    </div>
                </aside>
            </div>
            <section id="primary" class="primary">
                <article id="article1">
                    <section >
                        <div id="positions">
                            <div class="position" data-bind="template: { name: 'positionItemTemplate', foreach: positions, as: 'position' }">
                            </div>
                        </div>
                    </section>
                </article>
<!--            <footer class="footer">
                <p>Copyright © 2008 All Rights</p>
            </footer>-->
        </section>

        </div>
    </body>
</html>

Upvotes: 1

Views: 204

Answers (1)

RP Niemeyer
RP Niemeyer

Reputation: 114792

The main issue is that your my.vm is a constructor function that you can use to create an instance of a my.vm. However, the addPosition function was added directly to my.vm and tries to push to my.vm.positions.

Ideally what you want to do is make the function available on an instance of your vm and push to that instance's positions array.

So, you would put addPosition inside your vm declaration and have it operate on the vm variable that you are returning. At that point your bindings run into issues when you add a new position, as company is empty and some of the bindings refer to company().Name

Upvotes: 2

Related Questions