Can I use nested composite view to make the output like this?
Nike
Football, Basketball
Reebok
Football, Basketball, Running
I did write nested composite view in this way. I'm not sure is there any other better way or best practice way to implement this.
Structure:
BrandCompositeView
itemView: Layout1
#brand<-BrandView (Nike, Reebok)
#sport<-SportCompositView
itemView: SportView
BrandCompositeView (generate Nike, Reebok),
itemView of BrandCompositeView is a layout1 with div id are(#brand, #sport)
SportCompositView (generate Football, Basketball, Running)
itemView of SportCompositView is SportView
BrandView and SportView are itemView
Layout1 is Marionette.Layout
inside Layout1.js
initialize:function(){
this.brandView = new BrandView({"model": this.model});
this.sportView = new SportCompositView({"collection":collection});
}
onRender: function () {
this.brand.show(this.brandView);
this.sport.show(this.sportView);
}
Yes it's possible.
Approach I have used for tree structures - article.
How about if I want to make this (collection with more than 1 child?)
Car: [
{
Car1
name: car1
image: [{img1}, {img2}, {img3}]
comment: [{comment1}, {comment2}, {comment3}]
price: $1000
},
{
Car2
name: car1
image: [{img1}, {img2}, {img3}]
comment: [{comment1}, {comment2}, {comment3}]
price: $2000
}
]
<script id="car-template" type="text/template">
<li><%= name %></li>
<div id="photo-container"></div>
<li><%= price %></li>
<div id="comment-container"></div>
</script>
I actually just wrote an answer that might help you a couple days ago. Check out this thread - Construct views for data nested several levels deep
Basically it has you use backbone relational to automatically handle all of your nesting. Then you can just abstract off of that for your composite view inside of your composite view.
Related
Edit: Fixed JSFiddle Link
So i've been playing with Backbone and Marionette since a couple of weeks. I did some courses on Udemy, read the docs for both Backbone and Marionette. I can grasp most of the logic but somehow my mind can't wrap itself around the best way to approach a SPA I am trying to create.
API
So I have a rest api that returns some data:
http://localhost:3000/index
returns the following:
[
{
"id": 1,
"magazineTitle": "Title",
"magazineEditie": "Date",
"indexTitle": "Index Title",
"indexSubtitle": "Index Subtitle",
"mediaType": "image", //can also be "video"
"mainMedia": "https://source.unsplash.com/B0iF3I4bLBQ"
}
]
What I want
I want to be able to use this response and populate it over 2 seperate views.
one view will use the data to create a navigation bar
the other view will use it to create a hero header
What I can't seem to understand
Somehow I can't wrap my head around how this would be set up without making this 'illogical'
I feel like loading 2 views with the same model inside my Marionette.Application doesn't make any sense? Or the fact that I fetch my Collections and/or Models there...
I need some help clearing up some Layout issues and best practices I guess.
My code
Besides the fact that I get the data from a localhost url and I have my app setup with webpack this is more or less the code that I am using:
JSFiddle Demo
I have figured out what I needed to do. Based on the documentation (which was kind of confusing me) I figured out a way to render two views with it's needed data.
I was using a CollectionView to read a single data point (1 model) I somehow couldn't figure out a way to immediately get a single Model.
So far the model I had to do:
index.model.js
const IndexModel = Backbone.Model.extend({
urlRoot: "http://localhost:3000/index",
default: {
id: 1,
magazineTitle: "Mag Title",
magazineEditie: "Date",
indexTitle: "Title",
indexSubtitle: "Subtitle",
mediaType: "image",
mainMedia: "http://placehold.it/1900x800/",
},
});
The urlRoot argument here is what I need to do the exact call.
Then I was still confused how to structure my app but I ultimately used Regions and Marionette.View to setup the application.
App.js
export default Marionette.Application.extend({
region: "#content",
onBeforeStart() {
const router = new Router();
},
onStart() {
this.showView(new AppView());
},
});
app.view.js
const AppView = Marionette.View.extend({
tagName: "main",
id: "app",
template: template,
regions: {
navigationRegion: "#main-navigation",
appRegion: "#main-region",
pagesRegion: "#pages-region",
},
initialize() {
this.headerData = new IndexModel({ id: 1 });
this.pagesData = new PagesCollection();
},
onRender() {
this.showChildView("appRegion", new HeroView({ model: this.headerData, }));
this.showChildView("pagesRegion", new PagesView({ collection: this.pagesData, }));
},
});
I had to create a wrapping AppView that utilises regions to specify where child views should render.
Suppose that I have an Angular view that allows a user to check books out of a library. My data model consists of two arrays of Book entities which each have a unique ID field plus a title field. The first array contains an entity for every book in the library and the second array contains an entity for every book that the user has checked out.
libraryBooks = [{
id: 0,
title: "The Adventure of Tom Sawyer"}, {
id: 1,
title: "Moby Dick" }, {
id: 2,
title: "To Kill a Mockingbird" }, {
id: 3,
title: "The Three Little Pigs" }];
checkedOutBooks = [{
id: 0,
title: "The Adventure of Tom Sawyer"}, {
id: 3,
title: "The Three Little Pigs" }];
In short, the library has four books and the user has checked out two. If I want to list the books from both arrays, I can write this:
<h1>Library Books</h1>
<div ng-repeat="book in libraryBooks">
{{ book.title }}
</div>
<h1>Checked out Books</h1>
<div ng-repeat="book in checkedOutBooks">
{{ book.title }}
</div>
Suppose I want to display a third list: the subset of library books that the user has not checked out.
I have seen examples where the Angular "filter" is used to specify one particular value that should not be matched in order to narrow down a list, but in this case, I want to exclude multiple values, so how do I go about doing this?
I have seen examples where a custom filter is added to an Angular module, but I think that in this case, any custom filter should be scoped to this controller.
I've got this figured out. The solution is to write a filter function and attach it to $scope like so:
function filter_notCheckedOut(book) {
var i;
for (i = 0; i < that.libraryBooks.length; i += 1) {
if (that.libraryBooks[i].id === page.id) {
return false;
}
}
return true;
}
In the view, it can then be referenced like this:
<h1>Books not checked out</h1>
<div ng-repeat="book in libraryBooks | filter:filter_notCheckedOut">
{{ book.title }}
</div>
I have an API that is producing GeoJSON data of a number of Venues and Events that are occurring at each Venue.
See an example output:
{
"crs":null,
"type":"FeatureCollection",
"features":[
{
"geometry":{
"type":"Point",
"coordinates":[
-122.330056,
47.603828
]
},
"type":"Feature",
"id":39,
"properties":{
"city_slug":"seattle",
"neighborhood_name":"Downtown",
"events__all":[
{
"category__category":"Gallery",
"eventid":16200847,
"description":"A Wider View, curated by Onyx Fine Arts Collective, features 60 works by 23 artists of African descent.",
"title":"A Wider View",
"cost":"Free",
"category__slug":"gallery",
"slug":"a-wider-view"
}
],
"venue_name":"City Hall Lobby Gallery",
"venue_address":"600 4th Avenue, Seattle, WA 98104, USA",
"city_name":"Seattle",
"neighborhood_slug":"downtown",
"venue_slug":"city-hall-lobby-gallery"
}
},
{
"geometry":{
"type":"Point",
"coordinates":[
-122.348512,
47.6217233
]
},
"type":"Feature",
"id":42,
"properties":{
"city_slug":"seattle",
"neighborhood_name":"Downtown",
"events__all":[
{
"category__category":"Museums",
"eventid":15455000,
"description":"The Art of Video Games tackles a 40-year history, with a focus on video game as art form. Nerdy heartstrings will be tugged in this nostalgia-inducing retrospective, including everything from the Atari VCS to Playstation 3.",
"title":"The Art of Video Games",
"cost":"$20",
"category__slug":"museums",
"slug":"the-art-of-video-games"
},
{
"category__category":"Museums",
"eventid":15213972,
"description":"There's just something about the black leather jacket. It's a garment that invariably comes with context, that cannot help but be an icon. Worn to Be Wild: The Black Leather Jacket explores the evolution of the leather jacket from \"protective gear to revolutionary garb.\"",
"title":"Worn to Be Wild: The Black Leather Jacket",
"cost":"$20",
"category__slug":"museums",
"slug":"worn-to-be-wild-the-black-leather-jacket"
}
],
"venue_name":"Experience Music Project | Science Fiction Museum.",
"venue_address":"325 5th Avenue North, Seattle, WA 98109, USA",
"city_name":"Seattle",
"neighborhood_slug":"downtown",
"venue_slug":"experience-music-project-science-fiction-museum"
}
}
],
"bbox":[
-122.348512,
47.6035448,
-122.3233742,
47.6217233
]
}
I want to map this into a Collection called VenueEvents. VenueEvents contains models called JsonVenues, and each of these Venues then have contain a collection called EventSet, containing a number of Event models (side topic: is naming a model 'Event' a recipe for disaster?).
My models are outlined as such:
var Event = Backbone.Model.extend({
parse: function(response){
return {
id: response.eventid,
slug: response.slug,
title: repsonse.title,
description: response.description,
category: response.category__category,
cost: response.cost
}
}
});
var EventSet = Backbone.Collection.extend({
model: Event,
}
});
var JsonVenue = Backbone.Model.extend({
initialize: function(attributes) {
console.log(attributes)
},
parse: function(response){
// var eventSet = new EventSet(response.properties.events__all);
return {
name: response.properties.venue_name,
address: response.properties.venue_address,
neighborhood: response.properties.neighborhood_name,
//eventSet: eventSet
}
}
});
// Is this actually a model?
var VenueEvents = Backbone.Collection.extend({
model: JsonVenue,
url: '/json/',
parse: function(response){
return response.features;
}
});
The VenueEvents and JsonVenue objects get created as expected, with the exception that the response.properties.events__all object doesn't seem to make it's way to the JsonVenue model (which is where I'd expect to use it to create the EventSet collection). I've put a console.log(attributes) in the initialize parameter of the JsonVenue object and it shows that while all the other values within features.properties of a JsonVenue make its way to the model, the events__all does not.
Is there any reason why this would be happening? Is this method of loading nested JSON data into models even possible? In most examples, people are only including the id of the nested object in their JSON output, and then (I assume) building a model out of that object in another JSON string and relating them based on the ID. This seems like it would require more traffic between the server and client. I also see people side-loading data, is this the recommended method of relating models in a single API call?
Thanks!
Well.. Ive just tried your code, using:
new VenueEvents(json, {parse: true});
to create your collection. And... it works just fine it seems...
Still, Backbone-relational might have the behavior you want to simplify your code (this is just an assumption, I've never tested it myself, nor have had a real look at it).
I'm messing around with backbone, marionnette, and wondering how I should be dealing with relational models. My main reason is i'd like to be able to use data in two different related models in the same template. e.g.
Thing = Backbone.Model.extend({defaults: {label: null, uri: null}});
Things = Backbone.Collection.extend({model: Thing});
Relationship = Backbone.Model.extend({defaults: {subject: null, predicate: null, object: null}});
Relationships = Backbone.Collection.extend({model: Relationship});
var things = new Things([
new Thing({label: 'Sam', uri: 'AAAA'}),
new Thing({label: 'is friends with', uri: 'BBBB'}),
new Thing({label: 'Violet', uri: 'CCCC'}),
new Thing({label: 'Fred', uri: 'DDDD'})
]);
var relationships = new Relationships([
new Relationship({subject: "AAAA", predicate: "BBBB", object: "CCCC"}),
new Relationship({subject: "AAAA", predicate: "BBBB", object: "DDDD"})
]);
So, the relationships array is just holding references to items in the Things array matching the uri attribute. What I want to do in the Relationship template is something like this (using dot notation to get at the associated Thing model data):
<script type="text/template" id="relationship-template">
<td><%= subject.label %></td>
<td><%= predicate.label %></td>
<td><%= object.label %></td>
</script>
Any ideas the best way to handle this? I want to avoid duplicating things in memory.
Result should be:
Sam is friends with Violet
Sam is friends with Fred
I've looked at Backbone relational but haven't had any luck getting it working :(
This is what I tried:
Relationship = Backbone.RelationalModel.extend({
relations: [
{
type: Backbone.HasOne,
key: 'subject',
keySource: 'uri',
keyDestination: 'subjectObject',
relatedModel: Thing
}
]
});
Ah - i figured it out -
It's looking for an ID field to map whatever you have in key to - so I renamed uri to id and all is well. Too bad you can't explicitly tell it which field to map to.
I've been using Marionette for a couple of weeks and just discovered Backbone Relational so I'm trying to figure out how to integrate the two. Ideally, I would like to use a composite view to render data that is structured like this where each 'item' has its own item view:
list : {
name : 'List 1',
items : [
item1 : {
name : 'Item 1',
id : 1
},
item2 : { ... }
item3 : { ... }
]
}
Normally with composite views you need to have a collection of models that it will iterate through to render each item. With relational, I've just got one model (the list) and that model has a collection (items) within it. Is it possible to render this out using Marionette's views or do I need to use a plain Backbone view and handle the rendering and iteration myself?
This is quite common, and easy to do. In your CompositeView definition, you can specify the collection to use in the initialize method.
Backbone.Marionette.CompositeView.extend({
// ...
initialize: function(){
this.collection = this.model.get("childcollection");
}
});