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.
Related
I'm pretty new to using Backbone and Underscore..well Web Development in general.
I'd like to learn how to retrieve individual "model" data on-click from this template format to populate a pop-up modal. Any direction is much appreciated!
Currently, I have a list of projects that is rendered by passing my collection into this template and not using an individual view for each model item. I'm wondering how I can retrieve each project's data to populate a modal on-click.
I've tried getting the html data from e.currentTarget.html but I get undefined
<% _.each(collection, function(model){ %>
<div class="col-md-4 col-sm-6 portfolio-item">
<div class="thumbnail">
<div class="caption">
<h3><a href= <%= model.url %>><%= model.caption %></a></h3>
</div>
<img class="img-responsive" src= <%= model.image %> alt=<%= model.alt %>/>
</div>
<h3 class="project-title text-center"><%= model.title %></h3>
</div><%});%>
I had thought about following the method used in the To-Do List example by Addy Osmani, but I am trying not to have to define a View for the collection of items and a View for the individual models. I can see how this method would be able to assign a click listener for the individual models and pass that model to the modalView render, but again, trying not to do it this way (if possible).
Here is an example of the data
{
"projects": [{
"title": "Portfolio Website",
"caption": "My Showcase",
"dates": " ",
"url": "https://google.ca",
"description": "Lorem Etc Etc",
"image": "picture.jpeg",
"alt": "Portfolio Image"
}, {
"title": "Online Resume",
"caption": "Learn About Me!",
"dates": " ",
"url": "resume.com",
"description": "Look at my resume",
"image": "resume.jpeg",
"alt": "Resume Image"
}, {
"title": "Project",
"caption": "Coming Soon",
"dates": " ",
"url": "",
"description": "Lorem I Don't know what comes after Lorem",
"image": "picture.jpeg",
"alt": "Image"
}]
}
This is the Collection and View I'm using
app.projectCollection = Backbone.Collection.extend({
model: app.projectDetails,
url: '/profile.json',
parse: function(attrs){
return attrs.projects;
}
});
var projects = new app.projectCollection();
projects.fetch();
// View
app.portfolioView = Backbone.View.extend({
el: '.portfolio-body',
projectTemplate: template('portfolio-template'),
initialize: function(options){
this.listenTo(this.collection, 'add', this.render);
},
events: {
'click .portfolio-item': 'showModal' // listen for click to show modal
},
showModal: function(e){
e.preventDefault();
modalView.render(); // render the modal
// console.log("clicked" + e.currentTarget.attr('caption'));
},
render: function(){ // projects render just fine
this.$el.html(this.projectTemplate({collection: this.collection.toJSON()})); // pass in collection data for template to iterate though projects
return this;
}
});
var portfolioView = new app.portfolioView({collection: projects}); // pass in JSON
// Modal View
app.modalView = Backbone.View.extend({
className: 'modal fade',
modalTemplate: template('modal-template'),
attributes: {
tabindex: '-1',
role: 'dialog'
},
render: function(){
this.$el.html(this.modalTemplate()).modal();
return this;
}
});
var modalView = new app.modalView();
Thanks for your time and help!
As I understand, you want to have a link to your model inside template. But we can only have html there. The solution will be to have some unique id for each model.
You can add id to all your models, put it in the template like this
<div class="col-md-4 col-sm-6 portfolio-item" data-id="<%= model.id %>">
get this id in event handler
var id = $(e.currentTarget).data('id');
and get model from collection by this id
var model = this.collection.get(id);
By the way, I highly recommend you to try Marionette, it is super nice view level extension for Backbone.
I have an issue with Knockout.js . What I try to do is filter a select field. I have the following html:
<select data-bind="options: GenreModel, optionsText: 'name', value: $root.selectedGenre"></select>
<ul data-bind="foreach: Model">
<span data-bind="text: $root.selectedGenre.id"></span>
<li data-bind="text: name, visible: genre == $root.selectedGenre.id"></li>
</ul>
And the js:
var ViewModel = function (){
self.selectedGenre = ko.observable();
self.Model = ko.observableArray([{
name: "Test",
genre: "Pop"
}
]);
self.GenreModel = ko.observableArray([
{
name: "Pop",
id: "Pop"
},
{
name: "Alle",
id: "All"
}
]);
};
var viewModel = new ViewModel();
ko.applyBindings(viewModel);
JSFiddle: http://jsfiddle.net/CeJA7/1/
So my problem is now that the select list does not update the binding on the span inside the ul and I don't know why...
The value binding should update the property selectedGenre whenever the select value changes, shouldn't it?
Any ideas are welcome.
There are a lot of issues in your code:
1) self is not a magical variable like this. It's something people use to cope with variable scoping. Whenever you see self somewhere in a JavaScript function be sure there's a var self = this; somewhere before.
2) KnockoutJS observables are not plain variables. They are functions (selectedGenre = ko.observable()). ko.observable() returns a function. If you read the very first lines of documentation regarding observables you should understand that access to the actual value is encapsulated in this retured function. This is by design and due to limitations in what JavaScript can and cannot do as a language.
3) By definition, in HTML, <ul> elements can only contain <li> elements, not <span> or anything else.
Applying the above fixes leads to this working updated sample:
HTML:
<select data-bind="options: GenreModel, optionsText: 'name', value: selectedGenre"></select>
<span data-bind="text: $root.selectedGenre().id"></span>
<ul data-bind="foreach: Model">
<li data-bind="text: name, visible: genre == $root.selectedGenre().name"></li>
</ul>
JavaScript:
var ViewModel = function (){
var self = this;
self.selectedGenre = ko.observable();
self.Model = ko.observableArray([
{
name: "Test",
genre: "Pop"
}
]);
self.GenreModel = ko.observableArray([
{
name: "Pop",
id: "Pop"
},
{
name: "Alle",
id: "All"
}
]);
};
var viewModel = new ViewModel();
ko.applyBindings(viewModel);
I have a drop down list that is bound to a SelectedFormat value. The lists options are populated from external source on load and matches the view models data.Format object base on id.
Take a look at the js fiddle
Can anyone tell me why the model updates but the UI is not updating with the correct Format.Name
Thanks.
HTML:
<div data-bind="text:data.Format.Name"></div>
<select data-bind="
options:Controls,
optionsText: 'Name',
value: data.SelectedFormat"></select>
Model:
var jsonData = {
Id: "abc-123",
Name: "Chicken Cheese",
Format: {
Id: 2,
Name: 'Medium',
Other: 'Bar'
}
};
var self = this;
self = ko.mapping.fromJS(data);
self.SelectedFormat = ko.observable(
//return the first match based on id
$.grep(vm.Controls,function(item){
return item.Id === self.Format.Id();
})[0]
);
//when changed update the actual object that will be sent back to server
self.SelectedFormat.subscribe(function (d) {
this.Format = d;
},self);
In your code, you have Format and SelectedFormat. The former isn't an observable and so can't trigger updates. You have to use SelectedFormat instead.
<div data-bind="text:data.SelectedFormat().Name"></div>
Example: http://jsfiddle.net/QrvJN/9/
I'm trying to put together a data-driven menu system in Ember.js. Here's the javascript, js/nodal.js:
window.Form = Ember.Application.create();
var menuOptions = [
{id: "root",
options: [ "a", "b" ]},
{id: "a",
name: "Option A",
options: [ "c", "d" ]},
{id: "b",
name: "Option B",
options: [ "e", "f" ]},
{id: "c",
name: "Option C"},
{id: "d",
name: "Option D"},
{id: "e",
name: "Option E"},
{id: "f",
name: "Option F"}
]
Form.MenuOption = Ember.Object.extend({});
var builtOptions = menuOptions.map( function(attrs) {
return Form.MenuOption.create( attrs );
});
Form.options = {};
for( i = 0; i < builtOptions.length; i++ ) {
var opt = builtOptions[i];
Form.options[opt.id] = opt;
}
Form.MenuRoute = Ember.Route.extend({
model: function(params){
console.log( params );
if( !params || !params.index_id || params.index_id == "undefined") {
params.index_id = "root";
}
var keys = Form.options[params.index_id].options;
var options = new Array();
keys.map(function(k){ options.push( Form.options[k] ); });
return options;
}
});
Form.IndexRoute = Ember.Route.extend({
redirect: function(){
/* This here is the problem line... */
this.transitionTo( "menu", Form.options.root );
}
});
Form.Router.map(function(){
this.resource( "menu", { path: "/menu/:index_id" } );
});
And here's the HTML, such as it is:
<title>Hello</title>
<meta name="description" content="My First Ember.js App">
<meta name="author" content="Your Name Here">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<script data-template-name="application" type="text/x-handlebars">
<h1>Title Goes Here</h1>
{{outlet}}
</script>
<script data-template-name="menu" type="text/x-handlebars">
<ul>
{{#each controller}}
<li>{{#linkTo menu this}}{{name}}{{/linkTo}}</li>
{{/each}}
</ul>
</script>
<script src="js/libs/jquery-1.9.1.js"></script>
<script src="js/libs/handlebars.js"></script>
<script src="js/libs/ember-1.0.0-rc.1.js"></script>
<script src="js/nodal.js"></script>
</body>
</html>
What I want is for the user to be redirected to /menu/root as soon as they open the page. So far I haven't been able to find the right incantation to make this work. When I load the above, I get this exception:
Error: assertion failed: an Ember.CollectionView's content must implement Ember.Array. You passed <(generated menu controller):ember172>
If I load index.html#/menu/root directly, it works - it's only the redirect that's not working. Replacing the this.transitionTo line in the IndexRoute definition with this:
this.transitionTo( "menu", [Form.options.root] );
does redirect, but it redirects to /menu/undefined, not /menu/root. That's ugly, and obviously broken.
What have I missed?
EDIT
As suggested, I tried adding an ArrayController:
Form.MenuController = Ember.ArrayController.extend({});
This gives me a new error:
arrangedContent.addArrayObserver is not a function
The problem is that your URL assumes you are working with one model, but in transitioTo and your model hook your are passing/returning an array. Your MenuRoute is always about one MenuOption, right?
Therefore i would suggest:
Form.MenuRoute = Ember.Route.extend({
model: function(params){
console.log( params );
if( !params || !params.index_id || params.index_id == "undefined") {
params.index_id = "root";
}
var menuOption = Form.options[params.index_id];
return menuOption;
}
});
Your corresponding template should look like this in this case:
<script data-template-name="menu" type="text/x-handlebars">
<ul>
{{#each option in controller.content.options}}
<li>{{#linkTo menu option}}{{option.name}}{{/linkTo}}</li>
{{/each}}
</ul>
</script>
The error message basically says that view expects either array or array controller as the view's content but receives a generated controller instead(controller is generated when no controller is defined for route). The route's model method returns array thus it must be wrapped with appropriate controller - the ArrayController. You should define a custom controller for the menu route which extends ArrayController - this will eliminate the error message.
Edit
And about routing: I think you should have defined each of our menu options as a route nested in the menu resource, like this:
Form.Router.map(function(){
this.resource( "menu", { path: "/menu/:index_id" }, function () {
this.route('root');
this.route('a');
this.route('b');
...
});
});
This will also require defining the Route class for each route..
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