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>
The main issue is that your
my.vmis a constructor function that you can use to create an instance of amy.vm. However, theaddPositionfunction was added directly tomy.vmand tries to push tomy.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
positionsarray.So, you would put
addPositioninside your vm declaration and have it operate on thevmvariable that you are returning. At that point your bindings run into issues when you add a new position, ascompanyis empty and some of the bindings refer tocompany().Name