I am trying to build a list using a BackBone collection.. For some reason, i am not able to get them printed on the page..
js file
var DataCollection = Backbone.Collection.extend({
model : dataModel
});
var dataModel = Backbone.Model.extend({
defaults : {
dataID : 'unknown',
text : 'unknown',
}
});
var link1 = new dataModel({ dataID: "1", text: "OMC" });
var link2 = new dataModel({ dataID: "2", text: "Marvin Gaye" });
var link3 = new dataModel({ dataID: "3", text: "OMC" });
var myDataCollection = new DataCollection([ link1, link2, link3]);
var threatData = {
myDataCollection: myDataCollection
};
var compiledTemplate = Handlebars
.compile(myTemplate);
$('#myTable').html(
compiledTemplate (threatData));
html file
<ul id="Linklist">
{{#each myDataCollection}}
<li><a data-id="{{dataID}}" href="#">{{text}}</a></li>
{{/each}}
</ul>
To get the template rendering to work, you need to first convert the Backbone collection to JSON.
var threatData = {
myDataCollection: myDataCollection.toJSON()
};
DEMO
Related
I have a table with these fields: product, lot, input1, input2. You can clone a line, and you can add a new line.
What I want to do is that for each row you can add a new Lot created by a "number" and by "id" that user write in the input field under the Select lot. And I wanted that the script add the new Lot in the json data and the lot 's option list.
This is the function for add that I tried to do:
$scope.addLot = function() {
var inWhichProduct = row.selectedProduct;
var newArray = {
"number": row.newLot.value,
"id": row.newLot.id
};
for (var i = 0; i < $scope.items.length; i++) {
if ($scope.items[i].selectedProduct === inWhichProduct) {
$scope.items[i].selectedLot.push(newArray);
}
}
};
-->> THIS <<-- is the full code.
Can you help me?
I think your question is a little too broad to answer on Stack Overflow, but here's an attempt:
<div ng-app="myApp" ng-controller="myCtrl">
<table>
<tr ng-repeat="lot in lots">
<td>{{ lot.id }}</td>
<td>{{ lot.name }}</td>
</tr>
</table>
<p>name:</p> <input type="text" ng-model="inputName">
<p>id:</p> <input type="text" ng-model="inputId">
<button ng-click="addLotButton(inputId, inputName)">Add</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-beta.2/angular.min.js"></script>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.lots = [{
name: "test",
id: 1
},
{
name: "test2",
id: 2
}
];
$scope.addLot = function(lotId, lotName) {
var newLotObject = {
name: lotName,
id: lotId
};
$scope.lots.push(newLotObject);
};
$scope.addLotButton = function(id, name) {
$scope.addLot(id, name);
};
$scope.addLot(3, "Another test");
});
</script>
Basically this code just takes some input and adds an object to the scope for that input. The table is created using an ng-repeat of this data. It's not great code at all but it's just a quick example.
The push method adds newArray to selectedLot array. It's not working on the JSON data but on arrays. If you want to have the JSON, you can give a try to :
var myJsonString = JSON.stringify(yourArray);
It will create a JSON string based on the parameter
Maybe you should try to structure your data to make lots as properties of products.
{
products: [
{id: 1, lots: [{id:1}, {id:2}]},
{id: 2, lots: [{id:1}, {id:2}]}
]
}
To add a lot to a product :
product = products[0];
product.lots.push(newArray);
Change the fallowing:
html:
<button ng-click="addLot(row.selectedProduct.id,row.newLot.value,row.newLot.id)">Add</button>
js:
$scope.addLot = function(id,val,lotId) {
// console.log(id);
var inWhichProduct = id;
var newArray = { "value": val, "id": lotId };
//console.log($scope.items)
angular.forEach($scope.items,function(v,i){
if($scope.items[i].id == id )
{
$scope.items[i].lots.push(newArray);
console.log($scope.items[i].lots);
}
});
};
http://plnkr.co/edit/W8eche8eIEUuDBsRpLse?p=preview
Slide View
Select 1Sub Select 1Sub Select 2Sub Select 3
Select 2Sub Select 1Sub Select 2Sub Select 3
Select 3Sub Select 1Sub Select 2Sub Select 3
Please create to me a simple above output use marionette.
here below i use marionette coding
var APP = new Backbone.Marionette.Application();
APP.addRegions({
appMain:"#main"
});
APP.module("APP",function(module, app, Backbone, Marionette, $, _){
module.title = Backbone.Model.extend({});
module.titles = Backbone.Collection.extend({
model:module.title
});
module.subtitle = Backbone.Model.extend({});
module.subtitles = Backbone.Collection.extend({
model:module.subtitle
});
module.iView = Backbone.Marionette.ItemView.extend({
tagName:"li",
template:"#temp-itemview"
});
module.csView = Backbone.Marionette.CompositeView.extend({
template:"#temp-compositeview",
childView:module.iView,
itemViewContainer:"ul",
initialize:function(){
this.collection = this.model.get('subtitles');
}
});
module.collView = Backbone.Marionette.CollectionView.extend({
childView:module.csView
});
module.addInitializer(function(){
var array = [ {title:"Show 1",array:[{title:"Sub Show 1"},{title:"Sub Show 2"},{title:"Sub Show 3"}]},
{title:"Show 2",array:[{title:"Sub Show 1"},{title:"Sub Show 2"},{title:"Sub Show 3"}]},
{title:"Show 3",array:[{title:"Sub Show 1"},{title:"Sub Show 2"},{title:"Sub Show 3"}]}];
var titles = new module.titles(array);
titles.each(function(title){
var array = title.get('array');
var subtitles = new module.subtitles(array);
title.set("subtitles",subtitles);
});
var superView = new module.collView({
collection:titles
});
APP.appMain.show(superView);
});
});
APP.start();
html
<script type="text/template" id="temp-itemview">
<%= title %>
</script>
<script type="text/template" id="temp-compositeview">
<ul></ul>
</script>
Output is not clear.Please correct
The following solution is inspired from the Derick Bailey's post. The following one is adapted to backbone.marionette version 2.2.1:
<div id="tree"></div>
<script id="nodeTemplate" type="text/template">
<li><%= nodeName %></li>
</script>
app = new Backbone.Marionette.Application();
app.addRegions({
mainRegion: "#tree"
});
var TreeView = Backbone.Marionette.CompositeView.extend({
template: "#nodeTemplate",
tagName: "ul",
initialize: function(){
this.collection = this.model.nodes;
},
});
var TreeRoot = Backbone.Marionette.CollectionView.extend({
childView: TreeView
});
treeData = [
{nodeName: "Slide View"},
{nodeName: "Select 1",
nodes: [
{nodeName: "Sub Select 11"},
{nodeName: "Sub Select 12"},
{nodeName: "Sub Select 13"}
]},
{nodeName: "Select 2",
nodes: [
{nodeName: "Sub Select 21"},
{nodeName: "Sub Select 22"},
{nodeName: "Sub Select 23"}
]},
{nodeName: "Select 3",
nodes: [
{nodeName: "Sub Select 31"},
{nodeName: "Sub Select 32"},
{nodeName: "Sub Select 33"}
]},
];
TreeNode = Backbone.Model.extend({
initialize: function(){
var nodes = this.get("nodes");
if (nodes){
this.nodes = new TreeNodeCollection(nodes);
this.unset("nodes");
}
}
});
TreeNodeCollection = Backbone.Collection.extend({
model: TreeNode
});
app.addInitializer(function(options){
var treeView = new TreeRoot({
collection: options.tree
});
app.mainRegion.show(treeView);
});
$(document).ready(function(){
var tree = new TreeNodeCollection(treeData);
app.start({tree: tree});
});
Check this example, to see how it works. Please,pay attention to the external resources.
You can read Derick Bailey's post for explication and details. If you want to understand something, or if you want me to adapt this solution to yours, I mean by putting the same variables names, etc. just let me know. I'll be very pleased to help.
Hope it's useful!
You would use the Marionette Composite View for this: https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.compositeview.md
You want to set the collection for each view and specify a childViewContainer (a jquery selector). If you set a collection for a composite view it will use the same view to render the children in that collection.
You can specify a childView, but I normally don't set that attribute as it should be recursively calling the same view.
I want to filter my JSON using Ember.Select value instead of hardcoding it. Here's my App.js
App = Ember.Application.create({});
App.IndexRoute = Ember.Route.extend({
renderTemplate : function(controller) {
this.render('MyApp', {
controller : controller
});
},
model : function() {
return App.MyTemplateModel.find();
}
});
App.IndexController = Ember.ArrayController.extend({
filteredContent : Ember.computed.oneWay("content"),
last : function() {
var filtered = this.get('content').filterProperty('last_name', "Solow");
this.set("filteredContent", filtered);
}
});
App.MyTemplateModel = Ember.Model.extend({
id : Ember.attr(),
last_name : Ember.attr(),
first_name : Ember.attr(),
suffix : Ember.attr(),
expiration : Ember.attr()
});
App.controller = Ember.Object.create({
selectedProgrammer : null,
content : [Ember.Object.create({
last_name : "Solow",
id : 1
}), Ember.Object.create({
last_name : "Tom",
id : 2
})]
});
App.MyTemplateModel.url = "http://ankur1.local/index.php/api/example/users/";
App.MyTemplateModel.adapter = Ember.RESTAdapter.create();
var existing = App.MyTemplateModel.find();
App.MyTemplateModel.camelizeKeys = true;
In the line,
var filtered = this.get('content').filterProperty('last_name', "Solow");
I am harcoding "Solow" but I want to use Ember.Select value.
I tried doing this,
var selectedProgrammer = App.controller.get("selectedProgrammer");
var filtered = this.get('content').filterProperty('lastName', App.controller.get('selectedProgrammer'));
this.set("filteredContent", filtered);
But it does nothing & moreover, here's what I am doing on HTML,
<script type="text/x-handlebars" data-template-name="MyApp">
{{#each item in filteredContent }}
<tr><td>
{{id}} <p> {{item.first_name}} {{item.expiration}}</p>
</td></tr>
{{/each}}
<button {{action last}}>filter</button>
{{view Ember.Select
contentBinding="App.controller.content"
optionValuePath="content.id"
optionLabelPath="content.last_name"
selectionBinding="App.controller.selectedProgrammer"}}
</script>
Furthermore, here's a sample of JSON, http://pastebin.com/7dyNQi4K
Where I might be wrong or What changes I might have to make?
Your Ember.Select is bound to App.controller.selectedProgrammer, when the select changes, it will receive an object. So in your controller you access the last_name property from that object using App.controller.get('selectedProgrammer.last_name').
App.IndexController = Ember.ArrayController.extend({
filteredContent : Ember.computed.oneWay("content"),
last : function() {
var lastName = App.controller.get('selectedProgrammer.last_name');
var filtered = this.get('content').filterProperty('last_name', lastName);
this.set("filteredContent", filtered);
}
});
I'm using backbone.js. I get a json like this:
{
first_name: 'David',
last_name: 'Smith',
family: [{father: 'David', mother: 'Rose', brother: 'Max'}]
}
first_name and last_name shows in through a PersonView (extending Backbone.View) and family data I want to show in a DetailsView.
So, I was trying like this. First:
personView = new PersonView(model: person)//person it's the json above
PersonView shows well. Then I want to pass the model to DetailsView like this:
detailsView = new DetailsView(model: JSON.parse(person.get('family'));
Well, when I try to pass the model to a template in DetailsView implementation, like this:
DetailsView = Backbone.View.extend({
className: 'tab-pane',
template: _.template($('#detail-tpl').html()),
render: function(){
this.$el.html(this.template(this.model.toJSON()));
return this;
},
});
I get this message:
Uncaught TypeError: Object [object Object] has no method 'toJSON'
I don't know how to get or pass the model to solved this.
I'm trying several ways but I can't make it go.
Hope you can help me.
I think the problem is is because of this line.
model: JSON.parse(person.get('family')
It expects model to be an instance of backbone Model . But I don't think that is the case here.. try defining the Model for family or otherwise change the name of the key
Instead try this approach
familyMembers : JSON.parse(person.get('family')
In your view you can access this as
(this.options.familyMembers.toJSON())
The issue is that you model you are passing in is just an array. Therefore doesn't have the .toJSON method. As grant suggested you could use new Backbone.Model when creating the view but I would recommend using a collection and 2 new views for the family. It would look something like this.
var PersonModel = Backbone.Model.extend({
initialize: function(attributes, options) {
if(attributes.family) {
this.family = new FamilyCollection(attributes.family, options);
}
}
});
var FamilyCollection = Backbone.Collection.extend({
model: FamilyMember,
initialize: function(models, options) {
this.view = new FamilyView(options);
}
});
var FamilyMember = Backbone.Model.extend({
initialize: function(attributes, options) {
this.view = new DetailedView({
model: this
});
}
});
Then you would use a view structure something like this..
<div class="person">
<span class="name-first">David</span> <span class="name-last">Smith</span>
<div class="family-members>
<div class="family-member">
<span class="name-first">Rose</span> <span class="name-last">Smith</span>
</div>
<div class="family-member">
<span class="name-first">David</span> <span class="name-last">Smith</span>
</div>
<div class="family-member">
<span class="name-first">Max</span> <span class="name-last">Smith</span>
</div>
</div>
</div>
The "family" property is an array, you could do one of the following...
var familyArray = model.get('family');
new DetailsView({model: new Backbone.Model(familyArray[0])});
...or add a getFamily function to the person model...
var PersonModel = Backbone.Model.extend({
getFamily: function() {
var familyArray = this.get('family');
return new Backbone.Model(familyArray[0]);
}
});
...
new DetailsView({model: person.getFamily()});
I have an array of menu items, each containing Name and URL like this:
var menuItems = [
{
name : "Store",
url : "/store"
},
{
name : "Travel",
url : "/store/travel"
},
{
name : "Gardening",
url : "/store/gardening"
},
{
name : "Healthy Eating",
url : "/store/healthy-eating"
},
{
name : "Cook Books",
url : "/store/healthy-eating/cook-books"
},
{
name : "Single Meal Gifts",
url : "/store/healthy-eating/single-meal-gifts"
},
{
name : "Outdoor Recreation",
url : "/store/outdoor-recreation"
},
{
name : "Hiking",
url : "/store/outdoor-recreation/hiking"
},
{
name : "Snowshoeing",
url : "/store/outdoor-recreation/hiking/snowshoeing"
},
{
name : "Skiing",
url : "/store/outdoor-recreation/skiing"
},
{
name : "Physical Fitness",
url : "/store/physical-fitness"
},
{
name : "Provident Living",
url : "/store/provident-living"
}
]
I've been trying with no success to render this as an unordered list with a nested UL structure that follows the URL path structure like so:
<ul>
<li>Store
<ul>
<li>Travel</li>
<li>Gardening</li>
<li>Healthy Eating
<ul>
<li>Cook Books</li>
<li>Single Meal Gifts</li>
</ul>
</li>
<li>Outdoor Recreation
<ul>
<li>Hiking
<ul>
<li>Snowshoeing</li>
</ul>
</li>
<li>Skiing</li>
</ul>
</li>
<li>Physical Fitness</li>
<li>Provident Living</li>
</ul>
</li>
</ul>
All of the examples I've seen begin with a data structure that reflects the parent-child relationship (e.g. xml or JSON), but I'm having a very difficult time pulling this out of the URL and using it to render the new structure.
If anyone could please steer me in the right direction for how to do this using jQuery, I'd really appreciate it. I realize I probably need to use some recursive functions or maybe jQuery templates, but these things are still a bit new to me.
Thanks
I think the best solution is firstly to convert your data structure to a tree one, with parent/children relations. Render this structure will then be easier, as the UL itself has a tree structure.
You can convert menuItems using these couple of functions
// Add an item node in the tree, at the right position
function addToTree( node, treeNodes ) {
// Check if the item node should inserted in a subnode
for ( var i=0; i<treeNodes.length; i++ ) {
var treeNode = treeNodes[i];
// "/store/travel".indexOf( '/store/' )
if ( node.url.indexOf( treeNode.url + '/' ) == 0 ) {
addToTree( node, treeNode.children );
// Item node was added, we can quit
return;
}
}
// Item node was not added to a subnode, so it's a sibling of these treeNodes
treeNodes.push({
name: node.name,
url: node.url,
children: []
});
}
//Create the item tree starting from menuItems
function createTree( nodes ) {
var tree = [];
for ( var i=0; i<nodes.length; i++ ) {
var node = nodes[i];
addToTree( node, tree );
}
return tree;
}
var menuItemsTree = createTree( menuItems );
console.log( menuItemsTree );
The resulting menuItemsTree will be an object like this
[
{
"name":"Store",
"url":"/store",
"children":[
{
"name":"Travel",
"url":"/store/travel",
"children":[
]
},
{
"name":"Gardening",
"url":"/store/gardening",
"children":[
]
},
{
"name":"Healthy Eating",
"url":"/store/healthy-eating",
"children":[
{
"name":"Cook Books",
"url":"/store/healthy-eating/cook-books",
"children":[
]
},
{
"name":"Single Meal Gifts",
"url":"/store/healthy-eating/single-meal-gifts",
"children":[
]
}
]
},
{
"name":"Outdoor Recreation",
"url":"/store/outdoor-recreation",
"children":[
{
"name":"Hiking",
"url":"/store/outdoor-recreation/hiking",
"children":[
{
"name":"Snowshoeing",
"url":"/store/outdoor-recreation/hiking/snowshoeing",
"children":[
]
}
]
},
{
"name":"Skiing",
"url":"/store/outdoor-recreation/skiing",
"children":[
]
}
]
},
{
"name":"Physical Fitness",
"url":"/store/physical-fitness",
"children":[
]
},
{
"name":"Provident Living",
"url":"/store/provident-living",
"children":[
]
}
]
}
]
You mentioned you already have html renderer for trees, right? If you need further help let us know!
12 simple lines of code:
var rootList = $("<ul>").appendTo("body");
var elements = {};
$.each(menuItems, function() {
var parent = elements[this.url.substr(0, this.url.lastIndexOf("/"))];
var list = parent ? parent.next("ul") : rootList;
if (!list.length) {
list = $("<ul>").insertAfter(parent);
}
var item = $("<li>").appendTo(list);
$("<a>").attr("href", this.url).text(this.name).appendTo(item);
elements[this.url] = item;
});
http://jsfiddle.net/gilly3/CJKgp/
Although I like the script of gilly3 the script produces list with different element nesting of <li> and <ul> than was originally asked. So instead of
<li>Store
<ul>
<li>Travel</li>
...
</ul>
</li>
It produces
<li>Store
</li>
<ul>
<li>Travel</li>
...
</ul>
This may cause incompatibilities for utilities or frameworks working with such generated menu and producing interactive menu with animation (e.g. superfish.js).
So I updated the 12 lines script
var rootList = $("<ul>").appendTo("body");
var elements = {};
$.each(menuItems, function() {
var parent = elements[this.url.substr(0, this.url.lastIndexOf("/"))];
var list = parent ? parent.children("ul") : rootList;
if (!list.length) {
list = $("<ul>").appendTo(parent);
}
var item = $("<li>").appendTo(list);
$("<a>").attr("href", this.url).text(this.name).appendTo(item);
elements[this.url] = item;
});
http://jsfiddle.net/tomaton/NaU4E/
It's not in jQuery, but maybe this could help. I developed this after seeking the web to do exactly what you want.
http://www.chapleau.info/article/ArrayofUrlsToASitemap.html
Or maybe complete jQuery plugin http://jsfiddle.net/9FGRC/
(EDIT)
An update to previous version http://jsfiddle.net/9FGRC/1/
This version supports following case
var menuItems = [
{
name : "Store",
url : "/store"
},
{
name : "Cook Books",
url : "/store/healthy-eating/cook-books"
},
{
name : "Single Meal Gifts",
url : "/store/healthy-eating/single-meal-gifts"
}
]
Since there is skipped
{
name : "Healthy Eating",
url : "/store/healthy-eating"
},
It will produce following html
<ul>
<li>Store
<ul>
<li>Cook Books</li>
<li>Single Meal Gifts</li>
</ul>
</li>
</ul>
I guess it won't be the case, but could be helpful to someone
try something like this.
function Directory(parentNode) {
//Structure for directories. Subdirectories container as a generic object, initially empty
this.hasSubdirectories = false;
this.subdirectories = {};
//Render in steps. Until subdirectories or a link are added, all it needs is an LI and a blank anchor
this.nodeLi = document.createElement("li");
parentNode.appendChild(this.nodeLi);
this.nodeA = document.createElement("a");
this.nodeLi.appendChild(this.nodeA);
//if a subdirectory is added, this.nodeUl will be added at the same time.
}
Directory.prototype.setLabel = function (sLabel) {
this.nodeA.innerHTML = sLabel;
}
Directory.prototype.setLink = function (sLink) {
this.nodeA.href = sLink;
}
Directory.prototype.getSubdirectory = function (sPath) {
//if there were no previous subdirectories, the directory needs a new UL node.
if (!this.hasSubdirectories) {
this.nodeUl = document.createElement("ul");
this.nodeLi.appendChild(this.nodeUl);
this.hasSubdirectories = true;
}
//split the path string into the base directory and the rest of the path.
var r = /^\/?(?:((?:\w|\s|\d)+)\/)(.*)$/;
var path = r.exec(sPath);
//if the desired path is in a subdirectory, find or create it in the subdirectories container.
var subDirName = path[1] || path[2];
var subDir;
if (this.subdirectories[subDirName] === undefined) this.subdirectories[subDirName] = new Directory(this.nodeUl);
subDir = this.subdirectories[subDirName];
if (path[1] && path[2]) {
return subDir.getSubdirectory(path[2]);
} else {
return subDir;
}
}
function main(whichNode, aMenuItems) {
//whichNode is the node that is to be the parent of the directory listing.
//aMenuItems is the array of menu items.
var i;
var l = aItems.length;
var topDir = new Directory(whichNode);
//for each menu item, add a directory and set its properties.
var dirToAdd;
for (i = 0; i < l; i++) {
dirToAdd = topDir.getSubdirectory(aMenuItems[i].url);
dirToAdd.setLabel(aMenuItems[i].name);
dirToAdd.setLink(aMenuItems[i].url);
}
//and that's it.
}
how's that work?