I have a leftMenu section where I want to display 4 menus which are categorized by each subject. Every menu is a list and I want them to be displayed in the same View. I created 4 JSON files and to each one of them a Collection and a Model.
Usually i do it like this, first i define in router
this.mainMenuCollection = new MainMenuCollection();
this.mainMenuView = new MainMenuView({el:'#nav', collection:this.mainMenuCollection});
So, now I have these 4 collections defined in the router which I want in one view:
this.allcategoryMenuCollection = new AllCategoryMenuCollection();
this.natcategoryMenuCollection = new NatCategoryMenuCollection();
this.intcategoryMenuCollection = new IntCategoryMenuCollection();
this.topcategoryMenuCollection = new TopCategoryMenuCollection();
usually i render the collection in the View like this:
$(this.el).html(this.template({mainmenu:this.collection.toJSON()}));
Please help
Send in the collections as an object when you create your view:
this.mainView = new MainView({
el:'#nav'
});
this.mainView.collections = {
allcategoryMenuCollection: this.allcategoryMenuCollection
natcategoryMenuCollection: this.natcategoryMenuCollection
intcategoryMenuCollection: this.intcategoryMenuCollection
topcategoryMenuCollection: this.topcategoryMenuCollection
}
Access your collections inside the view by calling this.collections.collectionName
This may not seem like a semantically accurate way to do it, but to take advantage of backbone automatically assigning the model and collection properties in options, I do this:
var view = new MyView({
collection: {
comments: new CommentsCollection(),
images: new ImagesCollection()
}
});
Works for me. Otherwise you have to put a line in the initialize function to extend this, or use a base class. Personally I find this more elegant, even if it's missing a couple of pluralised properties.
Then, later on, you can use this.collection.comments.toJSON() for your template properties.
There's nothing wrong with housing a number of sub-views within an outer view. Your outer view could be something as simple as this. If you need to wait for all of your collections to be fetched before you render you could try:
var MyView = Backbone.View.extend({
initialize : function (options) {
var done, collections = options.collections;
done = _.invoke(collections, 'fetch');
$.when.apply($, done).done(this.onDataReady);
},
onDataReady : function () {
// Make a second pass at render after setting the flag.
this.ready = true;
this.render();
},
render : function () {
if(this.ready === true) {
// Now render the whole lot.
} else {
// Any non-data depending rendering.
}
}
});
Then you can either initialize the outer view and pass an array of collections in:
var view = new MyView({
collections: [...]
});
If you don't need all the collections to have fetched their data before hand it's even simpler. Just pass in the collections array and set them up as you need:
var MyView = Backbone.View.extend({
initialize : function (options) {
this.collectionA = options.collections[0];
},
render : function () {
}
});
Related
aye folks!
I'm currently learning to do stuff with vue.js. unfortunately i'm stuck atm. what i want to do is sending a request to my sample API which responds with a simple json formatted object.
I want to have this object as data in my component – but it doesn't seem to do that for whatever reason.
Ofc i tried to find a solution on stackoverflow but maybe i'm just blind or this is just like the code other people wrote. i even found this example on the official vue website but they're doing the same thing as i do .. i guess?
btw. When i run the fetchData() function in a separate file it does work and i can access the data i got from my API. no changes in the code .. just no vue around it. i'm really confused right now because i don't know what the mistake is.
code:
var $persons = [];
and inside my component:
data: {
persons: $persons,
currentPerson: '',
modal: false
},
created: function() {
this.fetchData()
},
methods: {
fetchData: function () {
var ajax = new XMLHttpRequest()
ajax.open('GET', 'http://api.unseen.ninja/data/index.php')
ajax.onload = function() {
$persons = JSON.parse(ajax.responseText)
console.log($persons[0].fname)
}
ajax.send()
}
},
[...]
link to the complete code
First, make sure that the onload callback is actually firing. If the GET request causes an error, onload won't fire. (In your case, the error is CORS-related, see this post suggested by #Pradeepb).
Second, you need to reference the persons data property directly, not the $persons array that you initialized persons with.
It would look like this (inside your fetchData method):
var self = this;
ajax.onload = function() {
self.persons = JSON.parse(ajax.responseText)
console.log($persons[0].fname)
}
I have problems binding this JSON to my list view.
http://pubapi.cryptsy.com/api.php?method=marketdatav2
No data is displayed.
Data.js
(function () {
"use strict";
var _list;
WinJS.xhr({ url: 'http://pubapi.cryptsy.com/api.php?method=marketdatav2' }).then(
function (response) {
var json = JSON.parse(response.responseText);
_list = new WinJS.Binding.List(json.return.markets);
},
function (error) {
//handle error
}
);
var publicMembers =
{
itemList: _list
};
WinJS.Namespace.define("DataExample", publicMembers);
})();
HTML:
<section aria-label="Main content" role="main">
<div id="listItemTemplate" data-win-control="WinJS.Binding.Template">
<div class="listItem">
<div class="listItemTemplate-Detail">
<h4 data-win-bind="innerText: label"></h4>
</div>
</div>
</div>
<div id="listView" data-win-control="WinJS.UI.ListView" data-win-options="{itemDataSource : DataExample.itemList, itemTemplate: select('#listItemTemplate'), layout: {type: WinJS.UI.GridLayout}}"></div>
</section>
I feel that the API is not that well formed.
Isnt this part a bit odd?
"markets":{"ADT/XPM":{...}...}
There are three things going on in your code here.
First, a ListView must be bound to a WinJS.Binding.List's dataSource property, not the List directly. So in your HTML you can use itemDataSource: DataExample.itemList.dataSource, or you can make your DataExample.itemList dereference the dataSource at that level.
Second, you're also running into the issue that the declarative binding of itemDataSource in data-win-options is happening well before DataExample.itemList is even populated. At the point that the ListView gets instantiated, _list and therefore itemList will be undefined. This causes a problem with trying to dereference .dataSource.
The way around this is to make sure that DataExample.itemList is initialized with at least an empty instance of WinJS.Binding.List on startup. So putting this and the first bit together, we have this:
var _list = new WinJS.Binding.List();
var publicMembers =
{
itemList: _list.dataSource
};
With this, you can later replace _list with a different List instance, and the ListView will refresh itself.
This brings us to the third issue, populating the List with your HTTP response data. The WinJS.Binding.List takes an array in its constructor, not an object. You're passing the parsed JSON object straight from the HTTP request, which won't work.
Now if you have a WinJS.Binding.List instance already in _list as before, then you can just walk the object and add items directly to the List as follows:
var jm = json.return.markets;
for (var i in jm) {
_list.push(jm[i]);
}
Alternately, you could populate a separate array and then create a new List from that. In this case, however, you'll need to assign that new List.dataSource to the ListView in code:
var jm = json.return.markets;
var markets = [];
for (var i in jm) {
markets.push(jm[i]);
}
_list = new WinJS.Binding.List(markets);
var listview = document.getElementById("listView").winControl;
listview.itemDataSource = _list.dataSource;
Both ways will work (I tested them). Although the first solution is simpler and shorter, you'll need to make sure to clear out the List if you make another HTTP request and repopulate from that. With the second solution you just create a new List with each request and hand that to the ListView, which might work better depending on your particular needs.
Note also that in the second solution you can remove the itemDataSource option from the HTML altogether, and also eliminate the DataExample namespace and its variables because you'll assign the data source in code each time. Then you can also keep _list entirely local to the HTTP request.
Hope that helps. If you want to know more about ListView intricacies, see Chapter 7 of my free ebook from MSPress, Programming Windows Store Apps with HTML, CSS, and JavaScript, Second Edition.
I'm trying to drag Ember objects from one list to another. If I drag an item to a new list, the item should be removed from its current list and moved to the new one.
Thanks to Drag&Drop with Ember.js and Ember.js - drag and drop list, I figured out how to copy an item to a different list. However, I am unable to determine from which list a dragged object originated. I have dozens of lists on the page, so I'd rather not do a O(n*k) search for the original object.
Currently, I'm using Ember views and the HTML 5 API. It seems like the Handelbars action helper should achieve my goal more easily. Ember's action supports the drop event, but I can't get it to fire: {{ action foo on="drop" }}. It probably has something to do with the nuanced event propagation defaults of the HTML 5 drag-and-drop implementation.
If you know how to solve this problem using actions instead of views, I'd much prefer that solution.
Here's how I'm currently transferring objects:
// this is heavily inspired by http://jsfiddle.net/ud3323/5uX9H/
// Draggable items
App.ItemView = Ember.View.extend({
templateName: 'item',
attributeBindings: 'draggable',
draggable: 'true',
dragStart: function(event) {
var dataTransfer = event.originalEvent.dataTransfer;
// The view's context is the item to transfer
var item = this.get('context');
// Use HTML 5 API to transfer object as JSON.
// There must be a more elegant way to do this.
dataTransfer.setData('application/json', JSON.stringify(item));
}
});
// Item list drop zone
App.ItemListView = Ember.View.extend({
templateName: 'itemList',
dragEnter: function(event) {
event.preventDefault();
return false;
},
dragOver: function(event) {
event.preventDefault();
return false;
},
drop: function(event) {
event.preventDefault();
// Extract the transferred data
var rawData = event.dataTransfer.getData('application/json');
// Create a new Ember object from the data
var item = App.Todo.create(JSON.parse(rawData));
this.get('controller').send('add', item);
return false;
}
});
Check out JS Bin for the complete code.
Thanks in advance for your help. Very much appreciated.
This is maybe not the full solution to your problem, but it satisfies the need to use the action helper instead of the itemView. Here is your modified jsbin http://jsbin.com/ibufeh/15/edit?html,javascript,live, the drop event fires and is catched at the ApplicationRoute level, from where you can then redirect your function call to the appropriate controller, have a look! it's not working correctly but it solves part of your problem - using an action helper. You need still to figure out from which list the item originated, but this will be easy I guess.
hope it helps
Just getting started with Backbone and still making sense of the ins and outs.
I'm trying to simply display some JSON using Underscore and Backbone. I'm able to make it to work just using Underscore and $.getJSON, but when I try to wire it up with Backbone I get a variety of errors depending upon what I try.
I've also been able to get Backbone to work by hardcoding values in to the model, but I'm running in to a wall when I try to bring it all together. Any help is appreciated.
Here is my Underscore template:
<script type="text/html" id='trailTemplate'>
<% _.each(trails,function(trail){ %>
<%= trail.trailname %><br />
<% }); %>
</script>
And here is my Backbone code:
var Trail = Backbone.Model.extend({
urlRoot: "trails.json"
});
var trail = new Trail({});
var TrailView = Backbone.View.extend({
el: '.page',
template: _.template($("#trailTemplate").html(), {trails:trail.fetch()}),
render: function(){
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var trailView = new TrailView({
model: trail
});
trailView.render();
And in case you need it, here is trails.json
[
{
"trailhead": "Bear Lake",
"trailname": "Bear Lake",
"distance": ".5",
"gain": "20",
"level": "easy"
},
{
"trailhead": "Bear Lake",
"trailname": "Nymph Lake",
"distance": ".5",
"gain": "225",
"level": "fairly easy"
}
]
Your trails.json file contains an array with 2 objects, which both represent a single 'Trail'. So you should have a collection 'Trails' instead of a single model
var Trails = Backbone.Collection.extend({
url: '/trails.json'
});
var trails = new Trails();
The underscore template function can be used in 2 ways:
_.template(templateString) - compiles the templateString into function that can be evaluated when necessary
_.template(templateString, data) - compiles and immediately evaluates the template with the given data
Now the way you are using is number 2 (the way you declare the template) combined with number 1 (how you use it inside render). Let's examine the template declaration:
template: _.template($("#trailTemplate").html(), {trails:trail.fetch()})
This is all good up until the point you try to give it the data -attribute. First of all you don't need to give the data at this point, you just want to create the template function that can be evaluated when the View renders. Second, the stuff you are trying to pass as data is not at all what you think it is.
trail.fetch() doesn't return the the fetch results, it returns the ajax handle for the ajax call that is made with fetch. Thankfully Backbone is made so you don't have to think about all this painful ajax stuff, but instead you can trust the events that Backbone emits. So whip out the Backbone Catalog o' Events and check out reset
"reset" (collection, options) — when the collection's entire contents have been replaced.
This is the event you collection will emit, after fetch (also sync, i think). Before this event is emitted, your collection will be empty, so there is no point in doing anything with it before hearing this reset event. So let's bring it all together now:
var TrailView = Backbone.View.extend({
el: '.page',
template: _.template($("#trailTemplate").html()), // no data attribute here
initialize: function() {
this.collection.on('reset', this.render); // render after collection is reset
this.collection.fetch(); // fetch the collection from the trails.json file
}
render: function(){
// evaluate the template here
this.$el.html(this.template(this.collection.toJSON()));
return this;
}
});
var trailView = new TrailView({
collection: trails
});
// trailView.render(); <- No need for this, because it will render itself
Hope this helps!
I have recently started working with backbone.js and i am finally started to get my head around after many tutorials.
One thing i am stuck on is how to use the routing to allow a list to pull different rest request.
Say i have the following in my collection
var NewsCollection = Backbone.Collection.extend({
model : News,
url: 'http://api.example.com/index.php/news/all/format/json',
});
From my understanding correct me if i am wrong backbone stores all the data pulled from the above feed into my model that extends this collection, this will all work i will pull in the feed and then display it in the view
This is where i get confused within my routing i have the following.
var NewsRouter = Backbone.Router.extend({
routes: {
"": "defaultRoute",
"news/:country_code":"updatedRoute"
},
defaultRoute: function () {
console.log("defaultRoute");
var movies = new NewsCollection()
new NewsView({ collection: movies });
movies.fetch();
//setInterval(function(){movies.fetch({success: function(){}});}, 60000);
},
updatedRoute:function (country_code) {
//confused
this.movie = this.movies.get(country_code);
}
})
I need to run the updatedRoute function when that will display a list of news based on cat of country code see below.
http://api.example.com/index.php/news/country/gb/format/json
How do i update the whole feed when a list item is click so the browser url would be.
http://localhost:8888/backbonetut/#news/gb
my list item is.
<li><a href='#news/gb'>GB</a></li>
I can get that in the updateRoute function with
this.movie = this.movies.get(country_code);
Can someone please help
You can either override the fetch function on your collection or temporarily change the url of the collection in your router action.