我正在尝试使用<pre>
标签调试 ko(版本:2.2.1):
<pre data-bind="text:ko.toJSON($root,null,2)"></pre>
但我得到了这个:
Uncaught Error: Unable to parse bindings.
Message: TypeError: Object object has no method 'getType';
Bindings value: text:ko.toJSON($root,null,2)
更新:
似乎 $root 对象实际上是 windows 对象,为什么会发生,我如何确保它仍然在我的 ViewModel 范围内?
场景是这样的:
我有一个视图,其中还包含一个弹出模板。当用户单击一个按钮时,弹出窗口会显示,这就是我收到此错误的时候($root 失去了 ViewModel 范围并成为窗口对象)。
看法:
<a href="#" class="caspButton" data-bind="visible: editMode(), click:setEditMode">Done</a>
<!-- ko if: entities().length > 0 -->
//THIS OPENS THE PROBLEMATIC POPUP
<a href="#caspPopupAddEntities" class="caspButton caspPlus" data-bind="visible: editMode(), click:bindPopup.bind($root)"></a>
<!-- /ko -->
<!-- ko if: parentChildArr().length > 0 -->
<a href="sp/book/addChildEntity" class="caspButton caspPlus" data-bind="visible: editMode(),cafeHrefParam: {entity: 'entity',st:'st',parentEntityType: parentTypeName,parentPropName:parentPropName }" title="Add Entity" data-cafeDialog="popup"></a>
<!-- /ko -->
<header>
<h2 class="caspFancy" data-bind="text: $root.title"></h2>
</header>
<div class="caspData">
<!-- ko if: entities().length > 0 -->
<ul data-bind="foreach: entities">
<li data-bind="css: {caspEditable: $root.editMode(),caspListSeperator: !$root.editMode()}">
<a class="caspDelete caspPosition" href="#removeEntities" title="Delete" data-bind="visible: $root.editMode(),click: $root.bindPopup"
data-delete="true"></a>
<div class="caspInnerData">
<a class="caspBookLink" data-bind="attr: {href: 'sp/book/book' }, cafeHrefParam: { chapter: 'chapter', page: 'page', id: $data.id }, text: name,css: {caspBullet: !$root.editMode()}"></a>
<!-- ko if: shortDescription -->
<p data-bind="text: shortDescription"></p>
<!-- /ko -->
</div>
<!-- end .caspInnerData -->
</li>
</ul>
<!-- /ko -->
<!-- ko if: parentChildArr -->
<ul data-bind="foreach: parentChildArr">
<!-- ko if: children().length > 0 -->
<li data-bind="css: {caspEditable: $root.editMode()}">
<div class="caspInnerData">
<div class="caspParent">
<span data-bind="text: $root.parentTypeName"></span> :
<a data-bind="attr: {href: 'sp/book/book' }, cafeHrefParam: { chapter: $root.parentTypeNamePlural, page: 'page', id: $data.id }, text: parent" class="caspBookLink"></a>
</div>
<ul data-bind="foreach:children">
<li>
<a class="caspDelete" href="#removeEntities" title="Delete" data-bind="visible: $root.editMode(),click: $root.bindPopup"
data-delete="true"></a>
<a data-bind="attr: {href: 'sp/book/book' }, cafeHrefParam: { chapter: 'chapter', page: 'page', id: $data.id }, text: name" class="caspBookLink caspBullet"></a>
</li>
</ul>
</div>
<!-- end .caspInnerData -->
</li>
<!-- /ko -->
</ul>
<!-- /ko -->
</div>
<!-- end .caspData -->
//HERE IS THE PROBLEM - '$root' becomes the windows object when i open this popup
<div id="caspPopupAddEntities" data-role="popup" class="popup">
<div class="caspBtnWrapper">
<a href="#" data-role="button" class="caspButton cafeRight" data-bind="click:$root.saveEntity.bind($root)">Done</a>
<a href="#" data-role="button" class="caspButton cafeLeft" data-bind="click:$root.cancel">Cancel</a>
</div>
<!-- end .caspBtnWrapper -->
<header>
<h1 class="caspFancy">Add New</h1>
</header>
<div class="caspDataWrapper" data-bind="with:$root.addItem()">
<fieldset data-role="fieldcontain">
<label for="title">Title</label>
<input type="text" name="title" id="title" data-bind="value:name"
/>
</fieldset>
<fieldset data-role="fieldcontain">
<label for="shortDescription">Overview</label>
<textarea class="caspTextarea" type="text" name="shortDescription"
id="shortDescription" data-bind="value:shortDescription" />
</fieldset>
<fieldset data-role="fieldcontain">
<label for="mission">Mission</label>
<textarea class="caspTextarea" type="text" name="mission"
id="mission" data-bind="value:description" />
</fieldset>
<fieldset data-role="fieldcontain">
<label for="period">Period</label>
<input type="text" placeholder=" Set date and Time" name="period"
id="period" data-bind="value:periodName" />
</fieldset>
<fieldset data-role="fieldcontain">
<label for="ownedBy">Owned By</label>
<input placeholder= "Select" type="text" name="ownedBy" id="ownedBy" data-bind="value:ownedBy,cafeAutoComplete: {source: 'users'}">
</fieldset>
<fieldset data-role="fieldcontain">
<label for="assignedTo">Assigned to</label>
<input placeholder= "Select" type="text" name="assignedTo" id="assignedTo" data-bind="value:assignedTo,cafeAutoComplete: {source: 'users'}">
</fieldset>
</div>
<!-- end .caspDataWrapper -->
</div>
<!-- end #caspPopupAddEntities -->
</div>
<!-- end content -->
</div>
<!-- end page -->
视图模型:
/**
* The module for the common Entities ViewModel
*/
define(['ko', 'komap', 'kopost', 'ca', 'sp/utils', 'sp/db'], function (ko, komap, kopost, ca, util, db) {
/**
* The Entities ViewModel. This VM just exposes a collection of entities in a single property.
* The model to retrieve is obtained from the last portion of the URL path.
*/
function EntitiesViewModel(ctx, data) {
this.init(ctx,data);
}
// EntitiesViewModel.prototype = new ca.ViewModel({});
// EntitiesViewModel.prototype.constructor = EntitiesViewModel;
ko.utils.extend(EntitiesViewModel.prototype,(function(){
init = function(ctx,data){
var self = this;
strategyId = ctx.param.st;
entitySet = db.getEntitySetFromElementType(ctx.param.entity);
parentEntitySet = _getParentEntitySet(entitySet);
childPropName = util.getChildEntityPropertyName(parentEntitySet);
self.editMode = ko.observable(false);
self.entities = ko.observableArray([]);
self.parentChildArr = ko.observableArray([]);
self.users = ko.observableArray([]);
self.parentPropName = util.getParentEntityPropertyName(entitySet);
self.parentTypeName = util.getParentTypeName(entitySet);
self.parentTypeNamePlural = util.getPluralName(self.parentTypeName);
self.title = ko.computed(function () {
// temporary method to get the plural name.
return util.getPluralName(entitySet.collectionName);
});
self.addItem = ko.observable(entitySet.addNew());
self.addItem().name = ko.observable("");
self.addItem().shortDescription = ko.observable("");
self.addItem().mission = ko.observable("");
self.addItem().period = ko.observable("");
self.addItem().ownedBy = ko.observable("");
self.addItem().assignedTo = ko.observable("");
_loadData(self);
},
bindPopup = function (o, e) {
openPopup(o, e);
selectedEntity = o;
var popupId = $(e.target).attr('href');
ko.applyBindings(self, $('#overlay').find(popupId).get(0));
db.User.toArray( this.users );
},
startEdit = function (o, e) {
self.editMode(true);
e.stopPropagation();
},
cancel = function (o, e) {
closePopup(o, e);
this.addItem (entitySet.addNew());
},
saveEntity = function (o, e) {
var self = this;
var parent = parentEntitySet.addNew({ id: strategyId });
parentEntitySet.attachOrGet(parent);
var entity = {
name: self.addItem().name,
shortDescription: self.addItem().shortDescription,
description: self.addItem().description
}
entity[self.parentPropName] = parent;
var newEntity = entitySet.addNew(entity);
entitySet.attachOrGet(newEntity);
entitySet.add(newEntity);
db.saveChanges().done(function (i) {
closePopup(o, e);
_loadData(self);
self.addItem (entitySet.addNew());
kopost.publish("ca.sp.entitiesChanged", "Add new Entity");
}).fail(function (error) {
self.addItem (entitySet.addNew());
console.log('Error = ' + error);
});
//detach the entities after save to avoid having them saved again
entitySet.detach(newEntity);
parentEntitySet.detach(parent);
e.stopPropagation();
};
return {
init: init,
setEditMode: setEditMode,
bindPopup: bindPopup,
cancelDelete: cancelDelete,
startEdit: startEdit,
cancel: cancel,
deleteEntity: deleteEntity,
saveEntity: saveEntity
}
}()));
return EntitiesViewModel;
});