backbone.js routing issue - javascript

I am very new to js and backbone.js, and I assure you I have searched extensively in docs and on here. I am trying to build a simple site that displays photos (eventually other things) and I want to be able to click a photo to see a different 'page' with another view/template. I cant seem to get this to work and now I am getting a "Uncaught TypeError: Cannot call method 'get' of undefined" in my router's photoById method. Code below... THANKS!
//------ main.js
var photoListTemplate = Handlebars.compile($('#photo-list-template').html());
var photoTemplate = Handlebars.compile($('#photo-template').html());
var navTemplate = Handlebars.compile($('#navigation-template').html());
var Photo = Backbone.Model.extend({
defaults: function() {
return {
};
},
initialize: function(options) {
}
});
var PhotoCollection = Backbone.Collection.extend({
model: Photo
});
var PhotoView = Backbone.View.extend({
events: {
},
//tagName: 'td',
className: 'photo',
template: photoTemplate,
initialize: function(options) {
_.bindAll(this, 'render');
this.model.on('change', this.render);
},
render: function() {
$(this.el).empty().append(this.template(this.model.toJSON()));
return this;
}
});
var PhotoCollectionView = Backbone.View.extend({
tagName: 'table',
className: 'photo-list',
template: photoListTemplate,
events: {
},
initialize: function(options) {
this.collection.on('add remove', this.render, this);
},
render: function() {
$(this.el).empty();
this.collection.each(function(photo){
$(this.el).append(new PhotoView({model: photo}).render().el);
}, this);
return this;
}
});
var PhotoRouter = Backbone.Router.extend({
routes: {
"": "list",
"photo/:id": "photoById"
},
initialize: function(options) {
},
allPhotos: function() {
this.photoList = new PhotoCollection();
this.photoCollectionView = new PhotoCollectionView({collection: this.photoList});
this.photList.fetch();
$('#content').empty().append(this.photoCollectionView.render().el);
},
photoById: function(id){
this.photo = this.photoList.get(id);
this.photoView = new PhotoView({model: this.photo});
$('#content').empty().append(this.photoView.render().el);
}
});
var photos = null;
$.getJSON('http://localhost:3000/photos.json', function(response){
photos = new PhotoCollection(response);
$('#container').append(new PhotoCollectionView({collection: photos}).render().el);
var photoRouter = new PhotoRouter();
Backbone.history.start();
});
/----- index.html
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/bootstrap.css">
<link rel="stylesheet" href="css/main.css">
<script src="js/vendor/modernizr-2.6.2.min.js"></script>
</head>
<body>
<!--[if lt IE 7]>
<p class="chromeframe">You are using an <strong>outdated</strong> browser. Please upgrade your browser or activate Google Chrome Frame to improve your experience.</p>
<![endif]-->
<!-- Add your site or application content here -->
<!-- <p>Replace Me! Replace Me!</p> -->
<div class='container' id='container'>
</div>
<script type="text/x-handlebars-template" id='navigation-template'>
<div class='navbar'>
<div class='navbar-inner'>
<a class='brand' href=#>ohayon</a>
<ul class='nav'>
</ul>
</div>
</div>
</script>
<script type="text/x-handlebars-template" id='photo-template'>
<div class='lead'>
<a href='#photo/{{id}}'>{{name}}</a>
<img src='http://localhost:3000{{image.url}}' height='200' width='200'>
</div>
</script>
<script type="text/x-handlebars-template" id='photo-list-template'>
<div class='lead'>
<a href='#photos/{{id}}'>{{name}}</a>
<img src='http://localhost:3000{{image.url}}'>
</div>
</script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="js/vendor/jquery-1.8.2.min.js"><\/script>')</script>
<script src="js/plugins.js"></script>
<!-- If we want Boostrap components -->
<script src="js/bootstrap.js"></script>
<!-- Templating with Handlebars -->
<script src="js/handlebars.js"></script>
<!-- We Need Underscore.js -->
<script src="js/underscore.js"></script>
<!-- And Finally Backbone -->
<script src="js/backbone.js"></script>
<!-- Your Code goes here -->
<script src="js/main.js"></script>
<!-- Google Analytics: change UA-XXXXX-X to be your site's ID. -->
<script>
var _gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];
(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
s.parentNode.insertBefore(g,s)}(document,'script'));
</script>
</body>
</html>

The way you've got it coded, there's no guarantee this.photoList will have been created by the time you attempt to use it in photoById. You might want to create it in the router's init function:
initialize: function(options) {
this.photoList = photos;
},
Also remove a few lines from allPhotos -- you don't want to re-create PhotoCollection or call fetch, since it was already created and populated in the first step of your app. Should look like this:
allPhotos: function() {
this.photoCollectionView = new PhotoCollectionView({collection: this.photoList});
$('#content').empty().append(this.photoCollectionView.render().el);
},
(One other problem, you're using container sometimes and content others.)
It's running here. (I used some dummy data to test with.)

Related

AngularJS 2 OnSelect Returns Error

I'm trying to follow the Angular 2 tutorial, which is in TypeScript, but in JavaScript. Unfortunately I've hit a snag and I can't find a solution searching online. Right now I'm on the Master/Detail step. I'm defining an onSelect element but when I define my onSelect function I get back the following error:
Uncaught TypeError: Cannot read property 'annotations' of undefined
Here is my code:
app.component.js:
(function(app) {
app.AppComponent =
ng.core.Component({
selector: 'my-app',
template:`
<h2>My Heros</h2>
<ul class="heroes">
<li *ngFor="let hero of Heroes" [class.selected]="hero === selectedHero" (click)="onSelect(hero)">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
</ul>
<div *ngIf="selectedHero">
<h2>{{selectedHero.name}} details!</h2>
<div><label>id: </label>{{selectedHero.id}}</div>
<div>
<label>name: </label>
<input [(ngModel)]="selectedHero.name" placeholder="name"/>
</div>
</div>
`
})
.Class({
constructor: function() {
this.title = 'Tour of Heros';
this.Heroes = Heroes;
this.onSelect(hero) {
this.selectedHero = hero;
};
}
});
})(window.app || (window.app = {}));
function Hero (id, name) {
this.id = id;
this.name = name;
};
var Heroes = [
new Hero(11, 'Mr. Nice'),
new Hero(12, 'Narco'),
new Hero(13, 'Bombasto'),
new Hero(14, 'Celeritas'),
new Hero(15, 'Magneta'),
new Hero(16, 'RubberMan'),
new Hero(17, 'Dynama'),
new Hero(18, 'Dr. IQ'),
new Hero(19, 'Magma'),
new Hero(20, 'Tornado')
];
index.html:
<html>
<head>
<title>Angular 2 QuickStart JS</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<!-- 1. Load libraries -->
<!-- IE required polyfill -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/rxjs/bundles/Rx.umd.js"></script>
<script src="node_modules/#angular/core/bundles/core.umd.js"></script>
<script src="node_modules/#angular/common/bundles/common.umd.js"></script>
<script src="node_modules/#angular/compiler/bundles/compiler.umd.js"></script>
<script src="node_modules/#angular/platform-browser/bundles/platform-browser.umd.js"></script>
<script src="node_modules/#angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js"></script>
<!-- 2. Load our 'modules' -->
<script src='app/app.component.js'></script>
<script src='app/main.js'></script>
</head>
<!-- 3. Display the application -->
<body>
<my-app>Loading...</my-app>
</body>
</html>
main.js:
(function(app) {
document.addEventListener('DOMContentLoaded', function() {
ng.platformBrowserDynamic.bootstrap(app.AppComponent);
});
})(window.app || (window.app = {}));
If I remove the this.onSelect(hero) {this.selectedHero = hero;}; it works fine, minus the ability to select an element. I've tried defining selectedHero with a default value and still the same error occurs. How do I define the onSelect function?
I guess
this.onSelect(hero) {
should be
this.onSelect = function (hero) {

Angularjs fails to dynamic change image src

Hello i'm building an application where i want to dynamically change the source of an image in order to force reload it . The problem is that in order of this i only get a broken image on the browser. Instead , if a run the function manually by a button it runs perfect .
HTML document
<!DOCTYPE html>
<html lang="en" ng-app='cameraApp'>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Node JS Camera</title>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
<script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
<script src='https://code.angularjs.org/1.4.4/angular-sanitize.min.js'></script>
<script src="cameraApp.js"></script>
</head>
<body>
<div class="container-fluid">
<div class="jumbotron">
<h1>Welcome to NodeJS Camera v1</h1>
</div>
<div ng-controller="HomeController">
<div class="cameraControl col-md-5">
<p>Here is the camera control</p>
<button class="btn btn-default" ng-click="getSnapshot()">Snapshot</button>
<button class="btn btn-info" ng-click="intervalFunction()">Start Feed</button>
</div>
<div class="lifeFeed col-md-7">
<p>Here is the live feed</p>
<p><button class="btn btn-default" ng-click="readSnapshot()">Snapshot Read</button></p>
<img width='600' height='600' ng-src="{{snapshot}}" alt="SnapShot taken">
</div>
</div>
</div>
</body>
</html>
cameraApp.js
var cameraApp = angular.module('cameraApp',[]);
cameraApp.controller('HomeController', function($scope,$http,$timeout) {
function updateImage() {
var img = 'snapshots/camera.jpg'+ '?decache=' + Math.random();
console.log('Snapshot Loaded');
$scope.snapshot = img;
};
$scope.readSnapshot = updateImage;
$scope.getSnapshot = function() {
$http.get('/api/getSnapshot')
.then(function(response) {
// this callback will be called asynchronously
// when the response is available
console.log('Snapshot captured');
$scope.readSnapshot();
}, function(response) {
console.log('Error in capturing...');
});
}
$scope.intervalFunction = function() {
$timeout(function() {
$scope.getSnapshot();
$scope.intervalFunction();
}, 2000);
};
// Kick off the interval
$scope.intervalFunction();
});
There are two solutions I've used for this in the past.
1) Use an ng-if/ng-show on your img tag. This will prevent the broken image from displaying.
<img ng-if='snapshot'>
2) Set a default image that will load and then be replaced once the other images load.
$scope.snapshot = 'snapshots/default.png';

Ember.js: Having problems creating a controller subclass

I am trying to create a custom controller in Ember.js, and am having some trouble trying to make this work. Here is my app.js code:
App = Ember.Application.create();
App.LoginController = Ember.Controller.extend({
actions: {
alert: function() {
alert('yo');
}
}
});
App.LoginManagerView = Ember.View.extend({
templateName: 'login/manager',
})
App.LoginManagerController = Ember.Controller.extend({
needs: ['loginController'],
})
App.Router.map(function() {
this.route("root", {path: "/"});
this.resource("login", function() {
this.route("user");
this.route("manager");
})
});
And here is my html code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Ember Starter Kit</title>
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<script type="text/x-handlebars">
<div class="navbar">
<div class="navbar-inner">
{{#link-to 'root'}}Home{{/link-to}}
{{#link-to 'login.user'}}User{{/link-to}}
{{#link-to 'login.manager'}}Manager{{/link-to}}
</div>
</div>
{{outlet}}
</script>
<script type="text/x-handlebars" id="root">
<h1>Home</h1>
</script>
<script type="text/x-handlebars" id="login/user">
<h1>User</h1>
</script>
<script type="text/x-handlebars" id="login/manager">
<h1>Manager</h1>
<p>
Name: {{controller}}
</p>
<button {{action controllers.loginController.alert}}>Alert</button>
</script>
<script src="js/libs/jquery-1.10.2.js"></script>
<script src="js/libs/handlebars-1.1.2.js"></script>
<script src="js/libs/ember-1.4.0.js"></script>
<script src="js/app.js"></script>
</body>
When I click User I can see the user view, but when I click manager I cant see the manager view. In fact, it seems its not even creating the Manager route. If I remove these lines:
App.LoginManagerController = Ember.Controller.extend({
needs: ['loginController'],
})
it works, but I lose access to the login controller instance.
EDIT:
When I look at the Ember console and I cant see the routes, if I go back to js console I get this message:
needs [ controller:loginController ] but it could not be found
How can I make it find the loginController instance?

Uncaught TypeError: Cannot call method 'navigate' of undefined backbonejs

Im trying to create a simple page navigation program using backbone in eclipse. My page is ..when i click a button , it goes to a particular page(result). But it throws error
Uncaught TypeError: Cannot call method 'navigate' of undefined backbone-min.js:
<html>
<head>
<title>Button Click</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="http://twitter.github.com/hogan.js/builds/2.0.0/hogan-2.0.0.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.3/underscore-min.js" type="text/javascript"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.2/backbone-min.js" type="text/javascript"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/backbone-localstorage.js/1.0/backbone.localStorage-min.js" type="text/javascript"></scr
<script src="http://twitter.github.com/hogan.js/builds/2.0.0/hogan-2.0.0.js"></script>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />
</head>
<body>
<script type="text/mustache" id="scriptemplate">
<input type="submit" class = "btn" value="Search">
</script>
<div id="search_container"></div>
<script type="text/javascript">
buttonview = Backbone.View.extend({
initialize: function(){
this.render();
},
render: function(){
var template = _.template( $("#scriptemplate").html(), {} );
// Load the compiled HTML into the Backbone "el"
this.$el.html( template );
},
events:{
"click input[type=submit]":"doSearch"
},
doSearch: function() {
console.log('click event reached');
var MyApp = new Backbone.Router();
MyApp.navigate('/result', {trigger: true});
}
});
var search_view = new buttonview({ el: $("#search_container") });
</script>
</body>
</html>
Two things to be done:
Change your version of Backbone.
http://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.0/backbone-min.js
Add this code here.
var MyApp = new Backbone.Router();
Backbone.history.start();
MyApp.navigate('/result', {trigger: true});
See it in document:
http://backbonejs.org/#Router

Sorting a Collection in Backbone

Ok so I have an API that returns a JSON feed of podcasts.
These podcasts show up on the frontend, but the order is in the exact opposite order that I need. I'm even more confused because the JSON feed it's calling is in the correct order I want but I can't get it to work like that on the frontend. Please take a look at the code I'm working with:
http://pastebin.com/bSamtFUw
var cache = new CacheProvider;
var Photo = Backbone.Model.extend({
subalbum: function() { return 'c' + gallery._currentsub; }
});
var PhotoCollection = Backbone.Collection.extend({
model: Photo,
comparator: function(item) {
return item.get('pid');
}
});
function removeFallbacks(){
var query = $('.jstest,.gallery');
if(query.length){
query.remove();
}
}
var IndexView = Backbone.View.extend({
el: $('#main'),
indexTemplate: $("#indexTmpl").template(),
render: function() {
removeFallbacks();
var sg = this;
this.el.fadeOut('fast', function() {
sg.el.empty();
$.tmpl(sg.indexTemplate, sg.model.toArray()).appendTo(sg.el);
sg.el.fadeIn('fast');
});
return this;
}
});
var SubalbumView = Backbone.View.extend({
el: $('#main'),
indexTemplate: $("#subindexTmpl").template(),
initialize: function(options){
},
render: function() {
var sg = this;
removeFallbacks();
this.el.fadeOut('fast', function() {
sg.el.empty();
$.tmpl(sg.indexTemplate, sg.model.toArray()).appendTo(sg.el);
sg.el.fadeIn('fast');
});
return this;
}
});
var PhotoView = Backbone.View.extend({
el: $('#main'),
itemTemplate: $("#itemTmpl").template(),
initialize: function(options) {
this.album = options.album;
},
render: function() {
var sg = this;
removeFallbacks();
this.el.fadeOut('fast', function() {
sg.el.empty();
$.tmpl(sg.itemTemplate, sg.model).appendTo(sg.el);
sg.el.fadeIn('fast');
});
return this;
}
});
var Gallery = Backbone.Controller.extend({
_index: null,
_photos: null,
_album :null,
_subalbums:null,
_subphotos:null,
_data:null,
_photosview:null,
_currentsub:null,
routes: {
"": "index",
"subalbum/:id": "hashsub",
"subalbum/:id/" : "directphoto",
"subalbum/:id/:num" : "hashphoto"
},
initialize: function(options) {
var ws = this;
if (this._index === null){
$.ajax({
url: 'http://mikegradio.com/api/v1/podcasts/export/?tags__title=sports&sort_by=-rank&limit=50',
dataType: 'json',
data: {},
success: function(data) {
ws._data = data;
ws._photos = new PhotoCollection(data);
ws._index = new IndexView({model: ws._photos});
Backbone.history.loadUrl();
}
});
return this;
}
return this;
},
index: function() {
this._index.render();
},
hashsub:function(id){
var properindex = id.replace('c','');
this._currentsub = properindex;
this._subphotos = cache.get('pc' + properindex) || cache.set('pc' + properindex, new PhotoCollection(this._data[properindex].subalbum));
this._subalbums = cache.get('sv' + properindex) || cache.set('sv' + properindex, new SubalbumView({model: this._subphotos}));
this._subalbums.render();
},
directphoto: function(id){
},
hashphoto: function(num, id){
this._currentsub = num;
num = num.replace('c','');
if(this._subphotos == undefined){
this._subphotos = cache.get('pc' + num) || cache.set('pc' + num, new PhotoCollection(this._data[num].subalbum));
}
this._subphotos.at(id)._view = new PhotoView({model: this._subphotos.at(id)});
this._subphotos.at(id)._view.render();
}
});
gallery = new Gallery();
Backbone.history.start();
http://pastebin.com/VZ6aTj9T#
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="gallery.css" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="shadows.css" type="text/css" media="screen" charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="buttons.css" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="ipad.css" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="desktop.css" type="text/css" media="screen" charset="utf-8" />
<script id="indexTmpl" type="text/x-jquery-tmpl">
<div class="item drop-shadow round">
<div class="item-image">
<a href="#subalbum/${cid}"><img src="${attributes.image}" alt="${attributes.title}" />
</a>
</div>
</div>
</script>
<script id="subindexTmpl" type="text/x-jquery-tmpl">
<div class="track drop-shadow round">
<div class="item-image subalbum">
<div class="item-artist">${attributes.title}</div>
<audio controls preload="none" id="audioControl" id="audio-player" src="${attributes.media}" type="audio/mp3" controls="controls"></audio>
<div class="item-price">${attributes.duration}</div>
Shop
</div>
</div>
</script>
<script id="itemTmpl" type="text/x-jquery-tmpl">
<div class="item-detail">
<div class="item-image drop-shadow round"><img src="${attributes.large_image}" alt="${attributes.title}" /></div>
<div class="item-info">
<div class="item-artist">${attributes.artist}</div>
<div class="item-title">${attributes.title}</div>
<div class="item-price">$${attributes.price}</div>
<br />
<div class="item-link">Buy this item</div>
<div class="back-link">« Back to Albums</div>
</div>
</div>
</script>
</head>
<body>
<div id="container">
<div id="main">
</div>
</div>
<script src="LAB.min.js" type="text/javascript"></script>
<script type="text/javascript">
$LAB
.script("jquery-1.4.4.min.js").wait()
.script("jquery.tmpl.min.js")
.script("underscore-min.js")
.script("backbone-min.js")
.script("cacheprovider.js").wait()
.script("gallery.js");
</script>
</body>
</html>
http://mikegradio.com/api/v1/podcasts/export/?tags__title=sports&sort_by=-rank&limit=50
Any help would be greatly appreciated!
Have you tried to reverse sort the collection: ( note the negative to return the collection reversed? )
comparator: function(item) {
return -item.get('pid');
}

Categories

Resources