I'm having a issue where transitioning is not occurring on a page reload/refresh. When I start the application and click on the links, everything works perfectly, but when I reload the route - I get an empty page (blank). This is happening for me on the MovieIndexRoute below.
// Router
MediaUi.Router.map(function () {
this.resource('movies', { path: '/'}, function() {
this.resource('movie', { path: 'movie/:id' }, function() {
this.route('edit', { path: '/edit' });
});
});
});
// Movies Route
MediaUi.MoviesRoute = Ember.Route.extend({
model: function() {
var media;
media = MediaUi.Media.find();
return media;
}
});
// Movie Route
MediaUi.MovieRoute = Ember.Route.extend({
serialize: function(model) {
return { id: model.get('_id') };
}
});
// Movie Index Route
MediaUi.MovieIndexRoute = Ember.Route.extend({
model: function(params) {
return this.modelFor('movie');
}
});
You can also access the repo here: https://github.com/alvincrespo/media-ui/tree/nested-resources on the nested-resources branch.
I've also added the following screenshot, showing the page and console.
Any help with this would be greatly appreciated. Thank You!
Related
I'm using Parse-SDK-JS, Handlebars.js and hash routing to create a dynamic webpage. When a user clicks on any link, I call a template using a URL in the following way: http://www.website.com/#/admin.
Router
BlogApp.Router = Parse.Router.extend({
start: function () {
Parse.history.start({root: '/beta/'});
},
routes: {
'': 'index',
'blog/:url': 'blog',
'category/:url': 'category',
'admin': 'admin',
'login': 'login',
'reset': 'reset',
'logout': 'logout',
'add': 'add',
'register': 'register',
'editprofile': 'editprofile',
'changeprofilepic': 'changeprofilepic',
':username': 'userprofile'
},
index: function () {
BlogApp.fn.setPageType('blog');
$blogs = [];
if (!currentUser) {
Parse.history.navigate('#/register', {trigger: true});
console.log("There is no logged in user.");
} else {
var groupId = currentUser.get('groupId');
var designsQuery = new Parse.Query(BlogApp.Models.Blog).equalTo('groupId', groupId).include('author').descending('lastReplyUpdatedAt').limit(50);
designsQuery.find({success: function (blogs) {
for (var i in blogs) {
var des = blogs[i].toJSON();
des.author = blogs[i].get('author').toJSON();
$blogs.push(des);
}
// console.log(blogs);
BlogApp.fn.renderView({
View: BlogApp.Views.Blogs,
data: {blogs: $blogs}
});
}, error: function (blogs, e) {
console.log(JSON.stringify(e));
}});
}
},
});
View
BlogApp.Views.Blogs = Parse.View.extend({
template: Handlebars.compile($('#blogs-tpl').html()),
className: 'blog-post',
render: function () {
var collection = {blog: []};
collection = {blog: this.options.blogs};
this.$el.html(this.template(collection));
},
});
My problem is that upon loading a new template, the user is not sent to the top of the page, i.e. to the following div:
<div id="main-nav"></div>
The users' scroll position on the page doesn't change if the new page is longer than the current page. The user just ends up somewhere down the middle of the page because the new template is loaded but they are not anchoring anywhere new.
Normally in HTML I would open a new page to a particular anchor with something like this: http://www.website.com/page#container if I wanted to, but with the way I set up my hash routing the anchor is the template call itself, so I can't do something like this: http://www.website.com/#/admin#container.
I hope this makes sense.
How can I always send the user to the div "container" upon loading a new template into my view?
I solved this by scrolling into an element after the View was generated.
cookies: function () {
BlogApp.fn.setPageType('cookies');
BlogApp.fn.renderView({
View: BlogApp.Views.Cookies
});
document.getElementById('main-nav').scrollIntoView();
},
Better... by adding the scrollIntoView() function after data is rendered into the View object, so that this works for all links in the router without so much copy pasta.
BlogApp.fn.renderView = function (options) {
var View = options.View, // type of View
data = options.data || null, // data obj to render in the view
$container = options.$container || BlogApp.$container, // container to put the view
notInsert = options.notInsert, // put the el in the container or return el as HTML
view = new View(data);
view.render();
if (notInsert) {
return view.el.outerHTML;
} else {
$container.html(view.el);
document.getElementById('main-nav').scrollIntoView();
}
};
So I have the following backbone route:
Nightbird.Routers.Errors = Nightbird.Routers.Core.extend({
routes: {
'server_error': 'serverError',
},
initialize: function(){
console.log('dasddasd');
},
serverError: function() {
console.log('asdasdasd');
var serverErrorView = new Nightbird.Views.ServerError();
serverErrorView.render();
}
});
it does come into this class because the initialize function is being called, when this route loads I see: dasddasd in the console, but I do no see asdasdasd
The url is localhost:9000/#server_error
Can some one explain what I am doing wrong? I am not sure what else I am suppose to provide for further information so please ask for any additional details.
Additional
The following is how the app gets registered:
window.Nightbird = {
Models: {},
Collections: {},
Views: {},
Routers: {},
blogId: 0,
initialize: function() {
if (window.Development === undefined && window.Production === undefined) {
throw 'Production class (Production.config.js) cannot be missing. App Cannot load.';
}
if (window.Development !== undefined) {
this.blogId = window.Development.BLOG_ID;
} else {
this.blogId = window.Production.BLOG_ID;
}
new Nightbird.Routers.Posts();
new Nightbird.Routers.Errors();
if (!Backbone.History.started) {
Backbone.history.start();
} else {
Backbone.history.stop();
Backbone.history.start();
}
}
}
This class extends:
Nightbird.Routers.Core = Backbone.Router.extend({
serverError: function(){
Backbone.history.navigate("server_error", {trigger: true});
}
});
Why such a simple abstraction, because this way any issue getting or posting or what have you can redirect you to a server error route.
Then in my index.html I do:
<!DOCTYPE html>
<html>
<body>
<div id="manage">
</div>
</body>
<script src="js/compiled.js"></script>
<script>
Nightbird.initialize();
</script>
</html>
I guess that the problem is the way that you're instantiating the Backbone Router
Try to create the Backbone Router inheriting from Backbone.Router.
When you check if Backbone.History.started is true, it probably is not. so it will go to else statement, and there at that moment Backbone.History.star() is undefined. So it is never starting the Backbone.History
Hope it helps.
I get these error messages upon loading the page:
Assertion failed: The value that #each loops over must be an Array.
You passed (generated snippets.index controller) ember-1.0.0.js:394
Uncaught TypeError: Object [object Object] has no method 'addArrayObserver'
Here is my template code:
<script type="text/x-handlebars" data-template-name="snippets/index">
{{#each}}
{{title}}
{{/each}}
</script>
My 'Snippet' model is quite simple:
TSLibrary.Snippet = DS.Model.extend({
title: DS.attr('string')
});
TSLibrary.Snippet.FIXTURES = [{id: 1, title: 'Learn Ember.js'}];
My app and my router:
window.TSLibrary = Ember.Application.create();
TSLibrary.ApplicationAdapter = DS.FixtureAdapter.extend();
// ---
TSLibrary.Router.map(function () {
this.resource('snippets', { path: '/' }, function () {
});
});
TSLibrary.SnippetsRoute = Ember.Route.extend({
model: function () {
// My guess is that this does not return the expected array
//
// This logs 'Class {toString: function, constructor: function, reason: null, isPending: undefined, isSettled: undefined…}'
console.log(this.store.find('snippet'));
return this.store.find('snippet');
}
});
So my guess is that this.store.find('snippet') is not returning the right data.
I've also installed the Ember debug extension in Chrome and it shows me all the right data in my model.
Ember version: 1.0.0
Ember Data version: v1.0.0-beta.1-140-ga51f29c a51f29c (2013-09-07 16:34:55 -0700)
Handlebars version: 1.0.0
jQuery version: 1.10.2
TSLibrary.Router.map(function () {
this.resource('snippets', { path: '/' }, function () {
});
});
This way creates the 'snippets.index' path, which requires a Route called SnippetsIndexRoute.
TSLibrary.Router.map(function () {
this.resource('snippets', { path: '/' });
});
This one is just 'snippets', which correctly uses the SnippetsRoute that you have defined.
I found the solution by accident, now:
TSLibrary.Router.map(function () {
this.resource('snippets', { path: '/' }, function () {
});
});
has to be
TSLibrary.Router.map(function () {
this.resource('snippets', { path: '/' });
});
I'm not sure how to express this in code, as I can't seem to locate the problem, but my issue is that Backbone.history seems to be recording two items when a user clicks on a list item in my app.
This is not consistent.
My app has a 4 item navigation at the bottom that links to 4 main sections (the first one being home - routed to '/'). If I load up the app, go to one of the other navigation pages, then click the 'Home' button again and then click one of the navigation options I get a list of items to choose from. If I then choose one two entries are added - Firstly, for some reason, a reference to the home route with /# at the end and then the route for the item I clicked.
The end result is that 'back' then inexplicably takes me to the home page.
If it helps, my router looks like this...
var siansplanRouter = Backbone.Router.extend({
initialize: function () {
var that = this;
this.routesHit = 0;
//keep count of number of routes handled by your application
Backbone.history.on('route', function() { that.routesHit++; }, this);
window.SiansPlanApp.render();
window.SiansPlanApp.router = this;
},
routes: {
'': 'showHome',
'home': 'showHome',
'hub': 'showHome',
'samples': 'showJqmSamples',
'mealplanner': 'showCurrentMealPlanner',
'mealplanner/:planId': 'showMealPlanner',
'recipes': 'showRecipeSearch',
'recipes/:recipeId': 'showRecipe',
'settings': 'showSettings',
'versioninfo': 'showVersionInfo',
'*other': 'showHome'
},
routesHit: 0,
back: function() {
if(this.routesHit > 1) {
window.history.back();
} else {
//otherwise go to the home page. Use replaceState if available so
//the navigation doesn't create an extra history entry
this.navigate('/', { trigger: true, replace: true });
}
},
showHome: function () {
SiansPlanApp.renderHome();
},
showJqmSamples: function () {
SiansPlanApp.renderView(new SiansPlanApp.views.Hub.Samples());
},
showMealPlanner: function (planId) {
SiansPlanApp.renderView(new SiansPlanApp.views.Planner.MealPlanner({ id: planId }));
},
showCurrentMealPlanner: function () {
SiansPlanApp.renderView(new SiansPlanApp.views.Planner.MealPlanner({ current: true }));
},
showRecipeSearch: function () {
SiansPlanApp.renderView(new SiansPlanApp.views.Recipes.Search());
},
showRecipe: function (recipeId) {
SiansPlanApp.renderView(new SiansPlanApp.views.Recipes.Recipe({ id: recipeId }));
},
showSettings: function () {
SiansPlanApp.renderView(new SiansPlanApp.views.System.Settings());
},
showVersionInfo: function () {
SiansPlanApp.renderView(new SiansPlanApp.views.About.VersionInfo.ListView());
}
});
I've got some basic elements in a kick off file too here...
define(['router', 'regions/r-app', 'jquery', 'domReady'],
function (SiansPlanRouter, AppRegion) {
var run = function () {
// Global click event handler to pass through links to navigate
$(document).on("click", "a:not([data-bypass])", function (e) {
var href = { prop: $(this).prop("href"), attr: $(this).attr("href") };
var root = location.protocol + "//" + location.host + SiansPlanApp.root;
if (href.prop && href.prop.slice(0, root.length) === root) {
e.preventDefault();
Backbone.history.navigate(href.attr, true);
}
});
$.ajaxPrefilter(function (options, originalOptions, jqXhr) {
//options.url = '/api' + options.url;
});
// Create the global namespace region object.
window.SiansPlanApp = new AppRegion();
// Adds the authorization header to all of the API requests.
$(document).ajaxSend(function (e, xhr, options) {
xhr.setRequestHeader("Authorization", 'SiansPlan ' + SiansPlanApp.cookies.getSessionData());
});
// Load up session data if any is present yet - this can't happen until the XHR headers are set up.
SiansPlanApp.session.loadSession();
// Instantiate the router.
window.SiansPlanApp.router = new SiansPlanRouter();
// Boot up the app:
Backbone.history.start();
};
return {
run: run
};
});
I've searched various other similar StackOverflow questions but none of them seemed to offer a solution. I have the following backbone router setup:
var AppRouter = Backbone.Router.extend({
routes: {
"" : "homeAction",
"/portfolio" : "portfolioAction",
"/about_us" : "aboutUsAction",
"/contact" : "contactAction"
},
initialize: function () {
},
homeAction: function () {
alert("User has navigated home");
},
portfolioAction : function() {
alert('user have navigated to portfolio');
},
servicesAction: function () {
alert("User has navigated to services");
},
aboutUsAction: function () {
alert("User has navigated to about us");
},
contactAction: function () {
alert("User has navigated home");
},
requestQuoteAction: function () {
alert("User has requested to submit a quote");
}
});
var app = new AppRouter();
$(function() {
Backbone.history.start();
});
When I navigated to mydomain.com/# The homeAction route gets called as expected. However if I try navigating to mydomain.com/#/portfolio, nothing happens. Any idea why this method is not getting called?
Removing the slash at the begining of your route should work.
var AppRouter = Backbone.Router.extend({
routes: {
"" : "homeAction",
"portfolio" : "portfolioAction",
"about_us" : "aboutUsAction",
"contact" : "contactAction"
},
...
Then try to go to mydomain.com/#portfolio for example.