I'm building a small program to allow display of some details of a group of people. There is an option to update the details. However, the updates are not being rendered by the view:
Here is my server.js:
var express= require("express"),
bodyparser= require("body-parser");
var app= express();
app.use(express.static(__dirname+ "/public"));
app.use(bodyparser.json());
app.get("/", function(req, res){
res.render("index.jade");
});
app.listen(3002);
My index.jade is:
doctype html
link(rel="stylesheet", href="backbui.css")
#main
script(src= "jquery.js")
script(src= "underscore.js")
script(src= "backbone.js")
script(src= "laconic.js")
script(src= "moment.js")
script(src= "backbui.js")
script(src= "theapp.js")
My theapp.js file is:
var Person= Backbone.Model.extend({}),
People= Backbone.Collection.extend({});
var Genders= new Backbone.Collection(
[
{name: "Female", id: 1},
{name: "Male", id: 0}
]);
var people= new People([
{name: "John Doe", gender: Genders.get(0), birthday:
new Date(1990, 5, 15), married: false},
{name: "Jane Smith", gender: Genders.get(1), birthday:
new Date(1985, 10, 10), married: true},
{name: "Tom Smith", gender: Genders.get(0), birthday:
new Date(1980, 1, 20), married: true},
{name: "Sally Fox", gender: Genders.get(1), birthday:
new Date(1998, 7, 31), married: false}
]);
var table= new Backbone.UI.TableView({
model: people, // collection can also be passed
columns: [
{title: "Name", content: "name"},
{title: "Gender", content: "gender",
format: function(model){ return model.get("name"); }},
{title: "Birthday", content: "birthday",
format: function(date){ return date.toString(); }},
{title: "Married", content: "married",
format: function(married){ return married? "yes": "no"; }}
]
});
var sally= people.models[3];
var textbox= new Backbone.UI.TextField({
model: sally,
content: "name",
});
var radios= new Backbone.UI.RadioGroup({
model: sally,
content: "gender",
alternatives: Genders,
altLabelContent: "name",
});
var Appview= Backbone.View.extend({
el: "#main",
render: function(){
this.$el.append(table.render().el);
this.$el.append(textbox.render().el);
this.$el.append(radios.render().el);
return this;
}
});
new Appview().render();
I want to see real-time update of 4th name in the table viz. Sally Fox when I make changes in the textbox area. Similarly I would like to see the table gender of Sally Fox change when I make the change in the radio button.
Basically I want my Backbone.TableView to listen to changes made in other Backbone.UI widgets like Backbone.UI.RadioGroup and Backbone.UI.TextField
You're on the right path. Add listeners in your views to listen for when changes happen to the people collection. Changes to individual models (i.e. sally) will also bubble up through the collection.
I'm not too familiar with Backbone.UI, but the changes would go something like this:
// Make the table listen to changes in the passed-in collection & re-render
// when needed
var table= new Backbone.UI.TableView({
model: people, // collection can also be passed
columns: [
{title: "Name", content: "name"},
{title: "Gender", content: "gender",
format: function(model){ return model.get("name"); }},
{title: "Birthday", content: "birthday",
format: function(date){ return date.toString(); }},
{title: "Married", content: "married",
format: function(married){ return married? "yes": "no"; }}
],
initialize: function() {
// When a change event happens to the model/collection, re-render
this.listenTo(model, 'change', this.render);
}
});
You would do similar things for other views that you want to react to data changes. The key is to have the views share the same instance of the model/collections you're changing, and then listen for specific changes.
If this doesn't get you going, post comments and/or a JSFiddle and we can make progress that way.
While the answer given by Peter (above) is also correct, we can also make the changes directly in the backbone-ui.js library file:
Inside the initialize method for tableview (search for tableview in the file backbone-ui.js, find its initialize function) add:
this.model.on("change", this.render, this);
and you'll be fine.
(You may want to add the same line inside the initialize method of the list property of backbone-ui.js file to get responsive views for lists)
Related
I would like to populate the options of a select field with the attributes from a "static" model. For example I have a Model and a collections of US states:
State = Backbone.Model.extend({
label:'',
value:''
}) ;
STATES = Backbone.Collection.extend({
model: State
});
states = [
{label: "Select ", value: '__' },
{label: "Alabama ", value: 'AL' },
{label: "Alaska ", value: 'AK' },
{label: "Arizona ", value: 'AZ' },
....];
localstates = new app.STATES(states); // or fetch the list of states from a RESTful site.
I then using back form have any address view and I want to pass localstates into the Form to populate the options of the state field:
UserAddress = Backform.Form.extend({
el: $("#personalInformation"),
fields: [
{name: "address1", label: "Address1", control: "input"},
{name: "address2", label: "Address2", control: "input"},
{name: "city", label: "City", control: "input"},
{name: "state", label: "State", control: "select",
options: **localstates**,
{name: "zip", label: "Zip Code", control: "input"},
{control: "button", label: "Save to server"}
],
});
I'm guessing I need to somehow pass the states collection into the User Address view and then access the attributes. But I have not been able to find a good example of how to do this.
edit1: Ok this is a bit silly in this case but:
newstate = new app.STATES(app.states);
var allstates =[];
app.newstate.forEach(function(state){
allstates.push({"label": state.get("label"), "value" : state.get("value")});
})
Gives me and array I can use at localstate. Basically I just re-created my original array in this case. In the case where it was fetch from a server it would be useful, but is there a better way?
You can do localstates.toJSON() to get a copy of the values to use in the template.
See Collection.toJSON()
Basically, I'm working on an HTML page where I have to print out text that's stored in another .js file. The thing is, the text is stored in an object... in an array... in a function... in a variable. So there's lots of digging to be done, just to access ONE piece of data.
Here's the data (or something similar) in the accompanying .js file:
var TestDataSet= (function() {
var reviews = [
{ Id: "abcd1234",
Title: "This Is Title Text",
Number: 5,
Body: "text",
CreateDate: new Date(2012,5,23,14,12,10,0),
Owner: {
Id: "Person1234",
Name: "James Smith",
}
},
]
How would I make the browser return "abcd1234" from the First ID? How about "Person1234" from the nested ID?
The best I've got so far is this:
var data1 = new reviews;
console.log(data1.reviews[0].Id);
But this does nothing. I get a whole lot of "not defined" errors.
Apparently you haven't posted your complete function.
However, I suggest to return the array value as in a getter function and use it if is safe for the rest of your script:
var TestDataSet= (function() {
var reviews = [
{ Id: "abcd1234",
Title: "This Is Title Text",
Number: 5, Body: "text",
CreateDate: new Date(2012,5,23,14,12,10,0),
Owner: {
Id: "Person1234",
Name: "James Smith",
}
},];
return reviews;
});
Then you can access data using:
TestDataSet[0].Id
so this my sample data. which il be loading form server in json format construct into below looking objec graph.
Its an array of "Choice" objects each will be having id, name, stages & currentStageId properties. "stages" property in "Choice" object is an array of "Stage" object which would be having id, name &
value properties.The "Choice" object will go through no.of stages from "First Stage" to "Fourth Stage" so user can select a "Stage" from the give dropdown list and save it. "currentStageId" is the property which stores "id" of stage object which will give in which stage the respective "Choice" object is in
Note: each choice can have different types of stages for brevity kept simple as possible
i.e for Choice 1 the current saved stage is 4
var data = [
new Choice({
id: 1,
name: "One",
stages: [
new Stage({
id: 1,
name: "First Stage",
value: 25
}),
new Stage({
id: 2,
name: "Second Stage",
value: 50
}),
new Stage({
id: 3,
name: "Third Stage",
value: 75
}),
new Stage({
id: 4,
name: "Fourth Stage",
value: 100
})],
currentStageId: 4
}),
new Choice({
id: 2,
name: "Two",
stages: [
new Stage({
id: 1,
name: "First Stage",
value: 25
}),
new Stage({
id: 2,
name: "Second Stage",
value: 50
}),
new Stage({
id: 3,
name: "Third Stage",
value: 75
}),
new Stage({
id: 4,
name: "Fourth Stage",
value: 100
})],
currentStageId: 3
}),
new Choice({
id: 3,
name: "Three",
stages: [
new Stage({
id: 1,
name: "First Stage",
value: 25
}),
new Stage({
id: 2,
name: "Second Stage",
value: 50
}),
new Stage({
id: 3,
name: "Third Stage",
value: 75
}),
new Stage({
id: 4,
name: "Fourth Stage",
value: 100
})],
currentStageId: 2
})];
Here is "Choice" & "Stage" Modles to hold data and ViewModel for binding
function ViewModel(data) {
var self = this;
self.choices = ko.observableArray(data);
//dont require pre selection so kept it with empty observable so it
//will be set to first item in the dropdown list
self.selectedChoice = ko.observable();
}
function Choice(data) {
//debugger;
this.id = data.id;
this.name = data.name;
//require pre selection of stage as choice can go through no of
//stages and selected stage name and value will be stored
this.selectedStage = ko.observable(ko.utils.arrayFirst(data.stages, function (item) {
return item.id === data.currentStageId;
}));
this.stages = ko.observableArray(data.stages);
}
function Stage(data) {
this.id = data.id;
this.name = data.name;
this.value = data.value;
}
ko.applyBindings(new ViewModel(data));
Here is my view
<select data-bind="options: choices, optionsText: 'name', value: selectedChoice"></select>
<select data-bind="options: selectedChoice().stages, optionsText: 'name', value: selectedChoice().selectedStage"></select>
Knockout.js 2x version
Pre selection of saved stage is working
Selected stage for choice is updated into underlying observable
Here is the Working sample with KO 2x in js
Knockout.js 3x version
Pre selection of saved stage is not working
Selected stage for choice is not preserved. When choice is changed the selectedStage is set to first item in the dropdown list each and every time the choice is changed.
Here is the Working sample with KO 3x
Finally the actual part. Question!
Why the same code behaving differently with two different versions of KO. Am i missing something new in KO? or is it a bug in KO?
What code changes should be done to produce same fucntionality as in with later version of KO, using the latest version of KO? because my project is being developed with latest version konckout.js 3.1.0 and i don't wanna switch back to older version for this functionality.
Which behavior of KO version is correct whether 2x or 3x? What is happening internally? which is causing this behavior discrepancies?
Thanks in advance.
I think it is related to 2. Bindings are now refreshed independently
Now you should use selectedChoice move out from options binding, e.g.
<div data-bind="with: selectedChoice">
<select data-bind="options: stages, optionsText: 'name', value: selectedStage"></select>
</div>
Being very new to Kendo UI and Knockout (3 days!), I'm trying to convert a legacy Java applet to Javascript. So far, creating and binding the kendoGrid was relatively easy:
function JobViewModel() {
var self = this;
self.jobData = new ko.observableArray([]);
self.grdJobs = {
scrollable: true,
sortable: true,
filterable: false,
columnMenu: true,
resizable: true,
columns: [
{ title: "Job ID", field: "jobID" },
{ title: "Owner", field: "owner" },
{ title: "Description", field: "description" },
{ title: "State", field: "runState" },
{ title: "Start Time", field: "timeStart" },
{ title: "End Time", field: "timeEnd" },
{ title: "Elapsed Time", field: "timeElapsed" },
{ title: "Progress", field: "progress" },
{ title: "Agent", field: "agent" },
{ title: "Last Action", field: "lastAction" }
],
selectable: "single"
};
};
The data coming back from the server are an array of JSON objects (of any length) something like the following (shortened for brevity):
[{0: 1, 2: 3, 4: 5, ...}, {0: 1, 2: 21, 4: 5, ...}, {0: 1, 2: 23, 4: 5, ...}]
This array of objects which are numeric key/value pairs are mapped to a dictionary object where the numeric keys are paired with string values something like:
{
0: "State"
1: "Finished"
2: "JOB ID"
3: "54759"
4: "Owner"
5: "John"
...
21: "54758"
...
23: "54757"
...
}
What I need to get is the mapped strings from the dictionary as they relate to the array of JSON objects. The approach I tried was to JSON.stringify(obj[i]) but then I wasn't sure what I could do with it (maybe use it in a ko.observable that could be assigned to the kendoGrid?) Anyway, what I'm after based on the sample data I've shown above is a grid with column headers "JOB ID", "Owner", and "State" and 3 rows of jobs (54757, 54758, 54759) all owned by John and State Finished.
I hope this all made sense. I guess what I really need is how to take JSON and convert it into something usable that can be populated into a Kendo Grid. Thanks for helping out a JS/JSON/Kendo/Knockout neophyte!
You could try to map those two arrays you have to another array with elements you can use.
I made a simple jsFiddle to demonstrate this: jsFiddle
The code is pretty straight forward:
var data = [{ 0: 1, 2: 3 }, { 0: 4, 2: 3 }];
var mapping = { 0: 'State', 1: 'Finished', 2: 'Owner', 3: 'John', 4: 'Pending' };
var realData = [];
$.each(data, function(index, obj) {
var arrayElem = {};
$.each(obj, function(key, value) {
arrayElem[mapping[key]] = mapping[value];
});
realData.push(arrayElem);
});
I'm new to Backbone. I have an arbitrarily deep nested list where each node in the list could potentially have a child collection. Eg:
item 1
item a
item i
item ii
item b
item c
item 2
item 3
etc
I am wondering what is the simplest way to represent this data structure using Backbone.
If it matters, I do not have a preference between loading the entire structure initially, or loading each level as it is needed. Whatever is easiest.
I would prefer not to go the route of Backbone-Relational, as I have a feeling that is overkill for something like this.
Thanks (in advance) for your help.
Assuming your data structure looks something like this
[
{
title: "item 1",
nodes: [
{title: "item a",
nodes: [
{title: "item i"},
{title: "item ii"}
]
},
{title: "item b"
}
]
},
{
title: "item 2"
}
]
you could set up your hierarchy by overriding the parse method of your models:
var Node = Backbone.Model.extend({
parse: function(data) {
this.nodes = new Nodes(data.nodes, {parse: true});
return _.omit(data, 'nodes');
}
});
var Nodes = Backbone.Collection.extend({
model: Node
});
var c = new Nodes(data_structure, {parse: true});
// parse: true is only needed if you pass the data as an argument
Node.parse extracts the nodes property from the data hash to build a custom attribute on the object and then returns the rest to let Backbone handle the other attributes. You then access the collection with model.nodes. And a Fiddle to play with http://jsfiddle.net/C8HGY/
I would make a simple model, maybe with default attribute children set to new Collection (if needed). The main part is loading from JSON data to models.
var MyModel = Backbone.Model.extend({
});
var MyCollection = Backbone.Collection.extend({
model: MyModel,
load: function(data) {
for(var i in data) {
if (_.isString(data[i])) {
this.add(new MyModel({
title: data[i]
}));
} else {
var collection = new MyCollection();
collection.load(data[i]);
this.add(new MyModel({
title: i,
children: collection
}));
}
}
}
});
var collection = new MyCollection();
collection.load({
'item1': {
'item a': ['item i', 'item ii'],
0: 'item b',
1: 'item c'
},
0: 'item2',
1: 'item3'
});
console.log(collection);
Only that in this example items with "0" and "1" keys are going to the beggining of the collection, but you can see the idea.