knockback
Knockback.js provides Knockout.js magic for Backbone.js Models and Collections.
Knockback.js Home
I'm trying to make a simple viewmodel to display a list of objects with Knockback. The View Model is rather simple, looking like this:
var objectives_collection = new ObjectiveCollection();
objectives_collection.fetch();
var view_model = kb.ViewModel.extend({
objectives: kb.CollectionObservable(objectives_collection),
constructor: function() {
_this = this;
kb.ViewModel.prototype.constructor.call(this, model = new Objective(), {});
console.log(this.objectives);
return this;
},
addObjective : function() {
var objective_model = new Objective({name: "New Objective", descriptor: 'Add description here'});
this.objectives.add(objective_model);
objective_model.save();
console.log(objectives);
},
edit: function() {
console.log('Edit');
},
remove: function(objective) {
self.objectives.remove(objective);
}
});
Right now though, the first line of the view model (kb.CollectionObservable(objectives_collection)) is giving me grief. It's calling Underscore.js' bind function and trying to set up the onCollectionChange listener, but Knockback's line here is trying to bind an undefined function:
this.__kb._onCollectionChange = _.bind(this._onCollectionChange, this);
where this._onCollectionChange is apparently undefined. I'm not really sure what to make of this; what am I doing wrong here?
Source: (StackOverflow)
I am using KnockBack (w/ KnockOut & BackBone) and am having trouble getting KB CollectionObservable (CO) values in the same format as KO's ObservableArrays. This is the core essence of KB, of course -- allowing me to use BB models with KO's data-binding; this is why I am so surprised I can't get/use the same syntax to expose the data to the data-binding.
Consider the following:
x = ko.observableArray([[1,2],[3,4]])
//x() == [Array[2], Array[2]]
y = kb.collectionObservable(new Backbone.Collection([[1,2],[3,4]]))
// y() == [ViewModel, ViewModel]
So, assuming I have such a kb.collectionObservable, how can I easily extract its values as an array of arrays, to be used with KO-compatible APIs?
Source: (StackOverflow)
I have a simple application which defines two classes, a Person
and a PersonGroup
, wherein there is a many-to-many relationship in place. A Person can have no group, or be assigned to all groups, and anything in between.
The example on backbonerelational.org suggests using an in-between model for many-to-many relationships, however I can't get this pattern to work with fetching (deserializing) and saving (serializing).
What I want to do is use Backbone to deserialize a JSON similar to the following:
{
People:
[
{
"ID": 1,
"Name": "John"
},
{
"ID": 2,
"Name": "Bob"
},
{
"ID": 3,
"Name": "Tim"
},
],
PeopleGroups:
[
{
"ID": 1,
"Name": "Owners",
"People":
[
1,
2
],
},
{
"ID": 2,
"Name": "Everyone",
"People":
[
1,
2,
3
],
},
]
}
I'm using Knockback/Knockout for data binding so the problem is I need to be able to access the relationships by reference. An array of IDs does not do me any good, unless I can create a Knockback.CollectionObservable to wrap the collection and resolve the IDs to references.
Source: (StackOverflow)
I'm a real big fan of knockoutjs. I've see a number of posts saying that knockoutjs & backbone work well together. But I'm having trouble getting started with backbone and don't understand how the two can be used together. Are there any resources that would show how to use knockoutjs and backbone together?
Note, I'm not looking for a discussion about which is better. There are enough posts on this point.
Source: (StackOverflow)
I've used Backbone.js, I've learned about Knockout.js; however, now I found out about Knockback.js. It is supposed to get the best out of the other two tried& proven frameworks.
Do you have any experience with Knockback in production? I'm wary to use it since it doesn't seem to be mature enough.
Source: (StackOverflow)
I'm trying to make a new Backbone model (working within Knockback) and I'm currently trying to set it up with a RESTful backend server. The issue is that the URL isn't accepted when trying to use the objectives.sync(). It works normally when doing objectives.fetch() however, and correctly pulls the data from the specified URL. What am I doing wrong here?
/**
* Objectives model
*/
var Objective = Backbone.Model.extend({
url: 'api/objective',
// Defaults
defaults: {
category: null,
weight: null,
name: null,
descriptor: null
}
});
/**
* Basic objectives collection
*/
var ObjectiveCollection = Backbone.Collection.extend({
model: Objective,
url: function() {
return "api/objective";
},
initialize: function(models,options) {}
});
The code to actually make use of this collection can be seen here:
var objectives = new ObjectiveCollection();
objectives.fetch();
var view_model = {
objectives: kb.collectionObservable(objectives, {view_model: kb.ViewModel})
};
ko.applyBindings(view_model, $('#objectives').get(0));
// Listener for the click button
$('#click').click(function() {
counter++;
var objective_model = new Objective({name: Math.random(), descriptor: 'What up'});
objectives.add(objective_model);
objectives.sync();
});/**/
Source: (StackOverflow)
I am using knockback.js.I have a list of values "projects"(value comes from the server). and the content of these values are displayed in the <div class="hiddendivcontent">
.
<ul data-bind="foreach: projects" class="list-group">
<li class="list-group-item" data-bind="click: function(data,event){CommunityView.showContents($data.pkey(),$data.folder(), data, event);}">
<span style="border:0px;" class="glyphicon glyphicon-plus pull-right" data-toggle="dropdown" ></span>
<ul class="dropdown-menu labellistdropdown" role="menu" style="min-width:200px;">
<li>..</li>
</ul>
<div class="hiddendivcontent">
<ul data-bind= "foreach : $root.community()" class="list-group">
<ul data-bind = "foreach :items">
<li> <span data-bind="text:cname"></span></li>
</ul>
</ul>
</div>
</li>
</ul>
The "hiddendivcontent" gets the value when "CommunityView.showContents()" is clicked. In other words,"hiddendivcontent" should be displayed when the method is clicked otherwise it remains hidden. The following code is the Jquery code that i have used :
showContents : function(pk,rf,data,event){
event.preventDefault();
$('.hiddendivcontent').toggle();
}
If I use this above code the value is populated in all the list items. I have tried many ways to toggle the div but nothing works. Can anyone please suggest me any ideas?
Thanks in advance
Source: (StackOverflow)
I have a page that's rendering a wijmo combobox via the wijcombobox knockout binding ( http://wijmo.com/wiki/index.php/Using_Wijmo_with_Knockout ). I'm having difficulty testing this using capybara because of a race condition between the tests running and the click event being bound. There are possibly other race conditions as well, but this is the current one tripping me up. This is what I have as a current workaround:
def click_on_combobox_till_options_appear
wait_until(10) do
page.find('.wijmo-wijcombobox-trigger').click
page.has_css?('.wijmo-wijlist-item')
end
end
I'm using 'kb-inject' to setup my ViewModel bindings.
Is there a javascript event I can hook up to or property I can test to indicate that applyBindings
has finished executing, so that I can avoid workarounds like the one above?
Source: (StackOverflow)
Backbone.LocalStorage stores a list of players client-side when calling create
on a players
collection, but the stored models are not being fetched later, even though I can inspect them in localStorage. I can call @collections.players.localStorage.findAll()
to retrieve all the stored objects and manually push them onto my collection.
class App.Models.Player extends Backbone.Model
defaults:
name: 'Unnamed'
team: 'Unassigned'
class App.Collections.Players extends Backbone.Collection
localStorage: new Backbone.LocalStorage('players')
model: App.Models.Player
class App.ViewModels.Game extends kb.ViewModel
constructor: ->
@collections =
players: new App.Collections.Players()
@collections.players.fetch(
success: (collection) =>
console.log(collection) # shows none
console.log(@collections.players.localStorage.findAll())
# shows all the stored players
)
# @players below is rendered by a foreach in a knockout template
@players = kb.collectionObservable @collections.players, { view_model: App.ViewModels.Player }
addPlayer: -> # called when a button is pressed
@collections.players.create(new App.Models.Player({ name: 'New Player' }))
return
Why is Knockback unable to fetch the stored entities automatically?
The following call manually retrieves all objects:
_.each @collections.players.localStorage.findAll(), (elem) =>
@collections.players.add(elem)
Source: (StackOverflow)
I am using Knockback.js. I am a newbie to this whole javascript and html. I am trying to poplulate the hidden field in the function addFolder, But it always showing null.
This is the following javascript code(viewmodel).
GroupsView = LS.ViewModel.extend({
initialize : function(options) {
if(typeof options != 'undefined'){
this.projectskey = options.projectskey;
this.folderkey = options.folderkey;
}
this.options = options;
this._this = this;
this.c = new GroupsCollection(null, options);
this.c.fetch({
add : true
});
this.groups = kb.collectionObservable(this.c);
},
fetch : function() {
$('#loadingAnim').show();
this.groups.collection().fetch({
update : true,
add : true
});
},
addFolder: function(vm, event){
event.preventDefault();
$("#plgGroupsGetContents").val(vm.projectskey);
$("#plgGroupsGetContents1").val(vm.folderkey);
vm.c.addFolder();
$('#dialogAddFolders').modal('hide');
}
});
and this is the following html code
<form action="/groups/createfolder" id="addFoldersForm" data-bind="event: {submit: $root.addFolder.bind() }">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id="myModalLabel" data-bind="text: LANG.getText('plgGroupAddFolder')"></h4>
</div>
<div class="modal-body">
<input type="hidden" name="projectskey" id="plgGroupsGetContents" value="">
<input type="hidden" name="rootfolder" id="plgGroupsGetContents1" value="">
<span data-bind="text: LANG.getText('plgGroupFolderName')"/><input type="text" data-bind="attr: {placeholder: LANG.getText('plgNameDefaultText')}" required title="Folder name" name="foldername" data-icon="U"><br>
</div>
<div class="modal-footer">
<button data-bind="text: LANG.getText('plgGroupAbort')" type="button" class="btn btn-default" data-dismiss="modal"></button>
<button data-bind="text: LANG.getText('plgGroupAddFolderOKBtn')" class="btn btn-primary"></button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
Can you please suggest me some ideas.
Thanks in advance
Source: (StackOverflow)
I am currently creating a page using Knockback.js that displays an employee's tasks in a table. I have a view model for a task, which contains a boolean observable called isSelected. The view model for an employee contains a collection observable of a collection of task view models called 'tasks'.
I now want to add an attribute/function/observable called 'selectedTasks' which only exposes the selected tasks. I want to meet the following requirements:
- Both the 'tasks' and 'selectedTasks' should give me view models, not models.
- When I add a model to the original tasks collection, the 'tasks' observable should get updated.
- When the user selects this newly added model, the 'selectedTasks' should get updated as well.
- There should be only one view model for every task model. Otherwise I might get a view model that states task x is not selected while another view model states that x is selected.
To demonstrate it a bit more clearly, I created this jsfiddle: http://jsfiddle.net/drojoke/cg6d88Lp/14/
So far, I only managed to get everything working for the 'tasks' attribute using a collection observable, as seen here:
this.tasks = kb.collectionObservable(tasks, {
view_model: function (task) {
return new TaskViewModel(task);
}
});
I'm using the view_model
option to turn every task in the tasks collection into a TaskViewModel
. When I add a new task to the tasks collection, the CollectionObservable
is updated as exptected.
I tried to create a selectedTasks
attribute as a CollectionObservable
with a filter option, like so:
this.selectedTasks = kb.collectionObservable(tasks, {
view_model: function (task) {
return new TaskViewModel(task);
}, filters: function (task) {
return task.isSelected(); // isSelected is undefined.
}
});
But unfortunately, the object that gets passed to the filters function is not a TaskViewModel
, but just a task object, so I have no access to the isSelected
observable.
I couldn't find a lot about filtering a collection of view models instead of models. How can I created a selectedTasks observable that filters view models and doesn't create additional view models?
Source: (StackOverflow)
Taking the following code snippet as a quick example:
var Animal = Backbone.Model.extend();
var Zoo = Backbone.Collection.extend({ model: Animal });
var tiger = new Animal({ name: "tiger" });
var zoo = new Zoo(tiger);
var viewModel = {
tiger: kb.viewModel(tiger);
zoo: kb.collectionObservable(zoo);
}
ko.applyBindings(viewModel);
from the $data context you can get a reference to the tiger model:
tiger === $data.tiger().__kb.object;
or
tiger === $data.zoo()[0].__kb.object;
and I assume it exists somewhere on this dependantObservable function, but I can't seem to find the reference to the original Backbone Collection
$data.zoo
Does anyone have any idea of how to get at the original Backbone Collection?
Also, bonus points if you can tell me of any way to get at the Backbone Collection if the viewmodel is this instead:
viewModel = kb.collectionObservable(zoo)
the challenge here is that $data contains the results of the evaluated dependantObservable function.
EDIT
After receiving a perfectly valid answer to the question above I realized that my problem only occurs in my more complicated binding with nested templates:
The templates look like this:
<!-- outer template -->
<script type="text/html" id="tmpl-outer">
<button data-bind="click: $root.outerContext">Outer Context</button>
<div data-bind="template: { name: 'tmpl-inner', data: collection }"></div>
</script>
<!-- inner template -->
<script type="text/html" id="tmpl-inner">
<button data-bind="click: $root.innerContext">Inner Context</button>
<div data-bind="foreach: $data">
<button data-bind="click: $root.modelContext">Model Context</button>
</div>
</script>
Model and View-Model:
var model = new Backbone.Model();
var collection = new Backbone.Collection(model);
var viewModel = {
collection: kb.collectionObservable(collection),
outerContext: function (data) {
console.log(data.collection.collection() === collection);
},
innerContext: function (data) {
console.log("??????? === collection");
},
modelContext: function (data) {
console.log(data.model() === model);
}
};
ko.applyBindings(viewModel);
And finally, somewhere to render everything:
<body>
<div data-bind="template: { name: 'tmpl-outer' }"></div>
</body>
So, my initial question that I over-simplified my example for should have been: how do I get at the underlying collection on the line:
console.log("??????? === collection");
It appears that the collection in this context has been converted to a simple KnockOut observable array - there doesn't seem to be any of the important KnockBack properties.
Source: (StackOverflow)
I'm trying to set something new up with Knockback.js, and right now I'm running into an issue with the knockout/knockback integration. The problem is, I've successfully written an event handler which adds a new model to the Objectives collection, but the UI only registers and adds the first such addition. It does successfully add the new objective to the list, but only the first one--after that, while the collection does successfully add a new model to the list, it doesn't appear in the UI.
<a class="btn" id="click">Click me!</a>
<div id="objectives" data-bind="foreach: objectives">
<h3 data-bind="text: name"></h3>
</div>
And this script:
// Knockback script MUST be located at bottom of the page
$(document).ready(new function() {
// instantiate the router and start listening for URL changes
var page_router = new PageRouter();
Backbone.history.start();
// Get JSON value
var objectives;
$.getJSON('json.php', {table: 'objectives'}).done(function(data) {
objectives = new ObjectiveCollection(data);
var view_model = {
objectives: kb.collectionObservable(objectives, {view_model: kb.ViewModel})
};
ko.applyBindings(view_model, $('#objectives').get(0));
});
$('#click').click(function() {
var objective_model = new Objective({category: 3, name: Math.random(), descriptor: 'What up'});
objectives.add(objective_model);
console.log(objectives);
});
});
Where the only custom models are as seen here:
/**
* Objectives model
*/
var Objective = Backbone.Model.extend({
// Defaults
defaults: {
id: null,
category: null,
weight: null,
name: null,
descriptor: null
},
// Url to pass to
url : function() {
// Important! It's got to know where to send its REST calls.
// In this case, POST to '/donuts' and PUT to '/donuts/:id'
return this.id ? '/objectives/' + this.id : '/objectives';
}
});
/**
* Basic objectives collection
*/
var ObjectiveCollection = Backbone.Collection.extend({
model: Objective,
initialize: function(models,options) {}
});
Source: (StackOverflow)
Given an observable collection in Knockback, how do I remove an item from the underlying collection in response to a knockout.js click event?
Source: (StackOverflow)
This is my first time working with Knockback.js.
I'm working on a Knockback proof of concept, but I'm having a hard time getting the view model to update when I save a model. In this case, what happens is the server returns a new Domain
object with the id
field set, which means that the object now exists on the backend. Once that happens, I would like the UI to change to reflect the fact that it is now saved.
Here is the code I'm using:
<table cellspacing="0" class="listing-table" id="domainListTable">
<thead>
<tr>
<th scope="col">
Domain
</th>
</tr>
</thead>
<tbody data-bind="foreach: domains">
<tr>
<td>
<!-- ko if:save -->
<a data-bind="attr: { href: domainEditLinkdomainId, title: domain},text : domain">
<span data-bind="text: domain"></span>
</a>
<!-- /ko -->
<!-- ko ifnot:save -->
<input type="text" maxlength="250" style="display:inline-block;" class="medium-text-field" data-bind="value: domain"></input>
<input data-bind="click: save" style="display:inline-block;" type="submit" value="Save New Domain" alt="" title=""/>
<!-- /ko -->
</td>
</tr>
</tbody>
</table>
<br />
<input data-bind="click: addDomain" type="submit" value="Add New Domain" alt="" title=""/>
<script type="text/javascript">
var Domain = Backbone.Model.extend({
defaults: function() {
return {
domain: "New Domain"
};
},
});
var Domains = {};
Domains.Collection = Backbone.Collection.extend({
model: Domain,
url: '/cms/rest/poc/${customerId}/domains/'
});
var domains = new Domains.Collection();
domains.fetch();
var DomainViewModel = kb.ViewModel.extend({
constructor: function(model) {
kb.ViewModel.prototype.constructor.apply(this, arguments);
var self = this;
this.save = kb.observable(model, {
key: 'save',
read: (function() {
return !model.isNew();
}),
write: (function(completed) {
return model.save({}, {
wait: true,
success: function (model, response) {
console.log(model);
console.log(response);
console.log(self);
},
error: function(model, response) {
alert("Oh NooooOOOes you broked it!!!11!")
}
});
})
}, this);
this.domainEditLinkdomainId = ko.dependentObservable(function() {
if(!this.save())
return "";
return "cms?action=domainDetail&domainID=" + this.model().id;
}, this);
}
});
var DomainsViewModel = function(collection) {
this.domains = kb.collectionObservable(collection, { view_model: DomainViewModel });
this.addDomain = function() {
this.domains.push(new DomainViewModel(new Domain()));
};
};
var domainsViewModel = new DomainsViewModel(domains);
ko.applyBindings(domainsViewModel);
</script>
The problem seems to be that the XMLHttpRequest
done by model.save()
does not return until after the save kb.observable
is read, and so the html part does not update successfully, since it still sees model.isNew()
as true.
I've been messing around with a couple of different ideas, as you can probably see, including using the valueHasMutated
method on an observable to indicate that the model has been updated, but I can't figure out how to do that either.
Any help would be greatly appreciated!
Source: (StackOverflow)