How to Change body background for different pages in ember - javascript

I am developing ember app and I would like to have white background for signup page and login page and the rest of the pages should have grey background.
At the moment I am changing colours of body tag in controller (application.js file)
import Ember from 'ember';
export default Ember.Controller.extend({
isVerified:false,
bgColour: function(){
if(['users.login','users.signup'].indexOf(this.get('currentPath')) != -1){
return 'white';
}
else{
return 'bg-grey';
}
}.property('currentPath'),
});
Any advices how to make this better/cleaner way?

The answer from Kit is pretty common, we do the same for our app. But you can simplify it a lot by avoiding generating the classname if you really only need it for a background change:
export default Ember.Route.extend({
activate () {
Ember.$('body').addClass('bg-white');
},
deactivate () {
Ember.$('body').removeClass('bg-white');
}
});

We have to deal with a lot of arbitrary requests from our clients where the easiest way is simply to target a specific page, so we add the route name to the body.
Ember.Route.reopen({
activate: function() {
var cssClass = this.toCssClass();
// you probably don't need the application class
// to be added to the body
if (cssClass != 'application') {
Ember.$('body').addClass(cssClass);
}
},
deactivate: function() {
Ember.$('body').removeClass(this.toCssClass());
},
toCssClass: function() {
return this.routeName.replace(/\./g, '-').dasherize();
}
});
That said it's a big dirty, you could create a ThemeMixin and set it on the routes. It's a simpler version of this: https://github.com/ronco/ember-cli-meta-tags

Related

Force template refresh ember.js

I'm using I18n localization package to take care of the switching language part of my app. It uses a global variable to set the language wanted and a json file to store the translations.
As the switching of a language is just a change in a global variable ember doesn't detect it and doesn't render the templates automatically. I forced it via an action in the application controller :
Extranet.ApplicationController = Ember.ObjectController.extend(
{
actions:
{
localToFr: function()
{
this.localisation('fr'); // this changes the global variable
this.get('target.router').refresh(); // this is what refresh the template
},
localToEn: function()
{
this.localisation('en');
this.get('target.router').refresh();
}
},
localisation: function(lg)
{
I18n.locale = lg;
}
})
I have two problems with that solution :
1) The application template isn't rerendered via my
this.get('target.router').refresh();
2) And my other problem, it doesn't work on templates which don't request a server access ( e.g. : the nest of routes 'authSession' )
Extranet.Router.map(function()
{
this.resource(
'parkings', {path:'/'}, function ()
{
this.route('parking', {path:'/parking/:parking_id'});
this.route('historique', {path:'/parking/:parking_id/historique'});
this.route('sessAct', {path:'/parking/:parking_id/sessAct'});
this.route('rapport', {path:'/parking/:parking_id/rapport'});
}
);
this.resource(
'authSession', function ()
{
this.route('login');
this.route('logout');
}
);
}
);
I was having a similar issue. I just went with View.rerender() on the main view, which was a form in my case.

Knockout.js and Sammy.js wrong behaviour

I'm developing admin panel to my website and decided to do it with knockout and sammy. But I am faced with a routing problem. I have two pages:
http://localhost/admin/element and http://localhost/admin/category.
On my element page I have the following Sammy config:
Sammy(function() {
this.get('#:currentpage', function() {
self.reloadPage(this.params.currentpage);
});
this.get('', function() {
this.app.runRoute('get', '#1');
});
}).run();
Everything works perfect but if I try to go to another page (by usual link, e.g. Edit Categories) I just get to the empty route on the same page, so I just cannot go to another page with link. Any ideas how to fix that?
Don't use '' in your Sammy configuration. Try '/' for root page or '/admin/element' for your elements instead.
var Router = function() {
var sammy = new Sammy.Application(function() {
this.get('#:currentpage', function(context) {
alert(context.params.currentpage);
});
this.get('/admin/element', function () {
this.app.runRoute('get', '#1');
});
}),
run = function() {
sammy.run();
};
return {
run: run
};
};
$(function() {
var r = new Router();
r.run();
});
PS: The example uses version of Sammy 0.7.1. In version 0.6.3 there is another behavior.
This works if you have an action link and want to click through to another page
`<li>#Html.ActionLink("Admin Action Link Test", "Admin", "Home")</li>
this.get('/Home/Admin', function ()
{
location.assign("/Home/Admin");
});`
or you can do this using the hash
<li>About Full Path</li>
this.get('#/Home/About', function ()
{
location.assign("/Home/About");
});

Organzing javascript / jQuery for multiple web pages

To keep organized, I'd like to keep all the javascript for my site in a single file:
scripts.js
However, some of my scripts are only used on on some pages, other scripts are only used on other pages.
In my document-ready function it looks like this:
function home_page() {
// image rotator with "global" variables I only need on the home page
}
$('#form')... // jQuery form validation on another page
The problem with this, is that I am getting javascript to execute on pages it's not even needed. I know there is a better way to organize this but I'm not sure where to start...
One thing you could do would be to use classes on the <html> or <body> tag to establish the type of each page. The JavaScript code could then use fairly cheap .is() tests before deciding to apply groups of behaviors.
if ($('body').is('.catalog-page')) {
// ... apply behaviors needed only by "catalog" pages ...
}
Even in IE6 and 7, making even a few dozen tests like that won't cause performance problems.
I usually do something like this, or some variation (a little pseudo code below) :
var site = {
home: {
init: function() {
var self=this; //for some reference later, used quite often
$('somebutton').on('click', do_some_other_function);
var externalFile=self.myAjax('http://google.com');
},
myAjax: function(url) {
return $.getJSON(url);
}
},
about: {
init: function() {
var self=this;
$('aboutElement').fadeIn(300, function() {
self.popup('This is all about me!');
});
},
popup: function(msg) {
alert(msg);
}
}
};
$(function() {
switch($('body').attr('class')) {
case 'home':
site.home.init();
break;
case 'about':
site.about.init();
break;
default:
site.error.init(); //or just home etc. depends on the site
}
});
I ususally have an init() function that goes something like this:
function init() {
if($('#someElement').length>1) {
runSomeInitFunction()
}
... more of the same for other elements ...
}
Basically just check to see if the element exists on the page, if it does, run its own initialization function, if not, skip it.
The whole JS codes is cached by the browser after the first page load anyway, so there's no point in fragmenting your JS file down into page-specific pieces. That just makes it a maintenance nightmare.
You could use for each page object literals to get different scopes.
​var home = {
other: function() {
},
init: function() {
}
};
var about = {
sendButton: function(e) {
},
other: function() {
},
init: function() {
}
}
var pagesToLoad = [home, about];
pagesToLoad.foreach(function(page) {
page.init();
});​

How to retain Javascript state on page change

I have an accordion style navigation list set up so that when categories are clicked it opens up to show sub-categories that link to pages.
What I would like to do is have the accordion navigation list keep it's open or closed state when the new page opens.
I've gathered that cookies work to retain the state on refresh, but how do I retain the state when a different page is visited? All the pages have the same accordion navigation list.
Try Web Storage. Store the state of the tabs on page unload, restore the state on the page load event.
I found a solution, it uses the accordian plug-in found here, http://www.i-marco.nl/weblog/archive/2010/02/27/yup_yet_another_jquery_accordi and the jquery cookie.js plug-in
I added id's to the header anchor tages in the HTNL mark-up like so,
<li>
<a id="m1" class="label" href="#">Sound/Audio Systems</a>
<ul class="acitem">
<li>PA Systems</li>
<li>Loudspeakers</li>
<li>Microphones </li>
<li>DJ Equipment</li>
<li>Sound Processing Equipment</li>
</ul>
</li>
And modified the accordian.js code, I added the lines beginning with $.cookie, and the If statement in the document.ready funciton.
jQuery.fn.initMenu = function() {
return this.each(function(){
var theMenu = $(this).get(0);
$('.acitem', this).hide();
$('li.expand > .acitem', this).show();
$('li.expand > .acitem', this).prev().addClass('active'),
currentID = "";
$('li a', this).click(
function(e) {
e.stopImmediatePropagation();
var theElement = $(this).next();
var parent = this.parentNode.parentNode;
if($(parent).hasClass('noaccordion')) {
if(theElement[0] === undefined) {
window.location.href = this.href;
}
$(theElement).slideToggle('normal', function() {
if ($(this).is(':visible')) {
$(this).prev().addClass('active');
currentID = $(this).prev().attr('id');
$.cookie('menustate', currentID, {expires: 2, path: '/'});
}
else {
$(this).prev().removeClass('active');
$.cookie('menustate', null, {expires: 2, path: '/'});
}
});
return false;
}
else {
if(theElement.hasClass('acitem') && theElement.is(':visible')) {
if($(parent).hasClass('collapsible')) {
$('.acitem:visible', parent).first().slideUp('normal',
function() {
$(this).prev().removeClass('active');
$.cookie('menustate', null, {expires: 2, path: '/'});
}
);
return false;
}
return false;
}
if(theElement.hasClass('acitem') && !theElement.is(':visible')) {
$('.acitem:visible', parent).first().slideUp('normal', function() {
$(this).prev().removeClass('active');
$.cookie('menustate', null, {expires: 2, path: '/'});
});
theElement.slideDown('normal', function() {
$(this).prev().addClass('active');
currentID = $(this).prev().attr('id');
$.cookie('menustate', currentID, {expires: 2, path: '/'});
});
return false;
}
}
}
);
});
};
$(document).ready(function() {
$('.menu').initMenu();$('#side-navigation_frame').show();
if ($.cookie('menustate')) {
var anchor = "",
elementID = $.cookie('menustate');
anchor = document.getElementById(elementID);
$(anchor).addClass('active');
$(anchor).next().show();
}
});
It works nicely, not bad for a beginner, thanks for all the advise.
Rob Fenwick
Cookies "retain state" across the full path and domain for which they are specified. So if you can get them to work for just one page, you should have them work automatically on all pages of your site.
You can still use cookies, you just have to make sure they're not specific to the one page. For example:
document.cookie = 'openitem=5; expires=somedate; path=/';
will be accessible to all pages on the site. More about cookies.
Ok so I took a look at the library you are using, it's a decent library and all but you might find it easier to find solutions to your problems if you use a more standard library like jQuery UI, it has an accordion control http://jqueryui.com/demos/accordion/ and like I mentioned there are so many people using it that the answer to most problems can be found.
But like I mentioned I did take a look at your library. As others have mentioned you would use a cookie to store the value. This library supports 'pre expanding' a particular section of the accordian, to do that you would add the expand class to the element. You can either do that server side or you can do it using JavaScript before initMenu() is called.
The other less elegant option is to trigger the click event on the anchor tag after the call to initMenu. Finally you can use jQuery's show() to show expand the section without animation.
The first thing you have to do is find out which section was clicked on, then you would store that sections name in a cookie. On page load you would get that value and expand the appropriate according section. This is what the code should kinda look like - note this is psuedo code and you have fill in the appropriate parts.
$(function() {
$(".menu.collapsible .label").click(function() {
var accordianSection = $(this).text();
rememberSection(accordianSection);
});
var section = recallSection();
if(section !== undefined) {
expandSection(section);
}
});
The expandSection function can look something like this:
var sectionLink = $(".menu.collapsible .label").filter(function() {
return $(this).text() == section;
});
sectionLink.trigger('click');

Using Backbone.Router for a slideshow

I'm creating a slideshow using Backbone.js. My slideshow view is finished, each slide is a model and all the models are inside a collection. Now I want to apply a little hashbang magic to my slideshow :-)
This is my code structure
application.js
models/slideshow/slide.js
collections/slideshow/slides.js
views/slideshow.js
In application.js I create my router:
var App = {};
App.Modules = {
Views: {},
Models: {},
Collections: {}
};
App.slideshow = undefined; // Use this to maintain state between calls.
App.router = (function() {
var Router = Backbone.Router.extend({
routes: {
'slideshow/:id/:page': 'slideshow'
},
slideshow: function(id, page) {
// Whenever this route handler triggers, I want to either:
// 1) Instantiate the slideshow, or:
// 2) Change the page on an already instantiated slideshow
if (App.slideshow && App.slideshow.options.id === id) {
App.slideshow.goToPage(page);
} else {
App.slideshow = new App.Modules.Views.Slideshow({
id: id,
page: page
});
}
}
});
return new Router;
})();
// Using jQuery's document ready handler.
$(function() {
Backbone.history.start({
root: '/'
});
});
This works as I expect. My slideshow works as an overlay so no matter what page it's instantiated on, it will just show itself on top of the existing document.
My first question is how do I close the slideshow (App.slideshow.close()); when the user hits the browser back button or navigates to another hashbang, which doesn't follow the /slideshow/:id/:page syntax?
My last question has to do with the 'navigate' method in Routers. In my slideshow view, I make sure to update the hash fragment whenever the page changes. This is what I do in my view:
pageChange: function(page) {
App.router.navigate('slideshow/' + this.options.id + '/' + page, false);
}
This makes sure the fragment gets updated so that the user at any point can copy the URL and it will open on the same page. The problem is that my 'slideshow' method in my instantiated router triggers even though I pass false in the second 'navigate' parameter (triggerRoute). Why is this?
So, I think I've figured it out. Please let me know if there are cleaner ways to do this.
After reading http://msdn.microsoft.com/en-us/scriptjunkie/hh377172 I saw you can do this in Backbone.js:
var router = Backbone.Router.extend({
routes: {
'*other': 'defaultRoute'
},
defaultRoute: function() {
if (App.slideshow) App.slideshow.close();
}
};
This makes sure everything that doesn't match /slideshow/:id/:page will close the slideshow if it's been instantiated.
With regard to 'navigate' apparently it's because I did App.vent = _.extend({}, Backbone.events); Apparently, I have to do:
App.vent = {};
_.extend(App.vent, Backbone.events);

Categories