Authenticated and public routes in Backbone Marionette - javascript

We have a large Marionette app, with sub apps/modules.
Each of these registers its own router within the App.addInitializer.
What is the best way to flag certain routes as public and others as requiring authentication?
I have a way in the app to check if the user is authenticated or not, but I'm trying to avoid having to implement that check in every route handler.
PrivateModuleRouter.Router = Marionette.AppRouter.extend({
appRoutes: {
"privateRoute(/)" : "handlePrivateRoute",
}
});
var API = {
handlePrivateRoute: function() {
//I don't want to repeat this everywhere..
if(!Auth.isAuthenticated()) {
App.navigate('/login', {trigger:true});
} else {
PrivateRouteController.showForm();
}
};
App.addInitializer(function(){
new PrivateModuleRouter.Router({
controller: API
});
});
Is there way in the route definition to flag it as private, and then a top level route handler performs this check?
If it's on a Router event though, this may not trigger if the route handler was triggered directly (not passing trigger:true, and calling API.handlePrivateRoute() directly.

Disclaimer: as I don't personally use Marionette, this answer is based on Backbone only.
The execute function
Backbone provides the execute function in the router as a way to handle that kind of logic. Even the example has authentication logic in it:
var Router = Backbone.Router.extend({
execute: function(callback, args, name) {
if (!loggedIn) {
goToLogin();
return false;
}
args.push(parseQueryString(args.pop()));
if (callback) callback.apply(this, args);
}
});
The authentication router
One way to avoid repeating the execute in each router would be to make a base router for your app.
var BaseRouter = Backbone.Router.extend({
constructor: function(prefix, opt) {
// get the hash
this.auth = _.result(this, "auth", {});
BaseRouter.__super__.constructor.apply(this, arguments);
},
// requires auth by default?
authDefault: false,
/**
* Check the `auth` hash for a callback. Returns `authDefault` if
* the callback is not specified.
* #param {String} callbackName name of the function.
* #return {Boolean} true if the callback is private.
*/
hasAuth: function(callbackName) {
return _.result(this.auth, callbackName, this.authDefault);
},
// To easily override the auth logic in a specific router
checkAuth: function(){
return Auth.isAuthenticated();
},
execute: function(callback, args, name) {
if (this.hasAuth(name) && !this.checkAuth()) {
this.navigate('/login', { trigger: true });
return false;
}
}
});
Defining the specific routers
Then for each of your router, extend BaseRouter.
var SpecificRouter = BaseRouter.extend({
routes: {
'*otherwise': 'home', // notice the catch all
'public': 'publicRoute',
'private': 'privateRoute',
'unspecified': 'defaultAccessRoute'
},
/**
* The auth hash works like this:
* "functionName": [boolean, true if needs auth]
*
* home and publicRoute could be left out as it's the default here.
*/
auth: {
home: false, // public
publicRoute: false, // public
privateRoute: true, // needs authentication
// defaultAccessRoute will be public because BaseRouter
// defines `authDefault: false`.
},
home: function() {},
publicRoute: function() {},
privateRoute: function() {},
defaultAccessRoute: function() {},
});
And for a router which all routes are private by default:
var PrivateRouter = BaseRouter.extend({
authDefault: true,
routes: {
'*otherwise': 'home', // private
'only-private': 'onlyPrivate', // private
},
// ...snip...
/**
* Optional example on how to override the default auth behavior.
*/
checkAuth: function() {
var defaultAuthResult = PrivateRouter.__super__.checkAuth.call(this);
return this.specificProperty && defaultAuthResult;
}
});

In github you can find many solution for calling some methods before router's execution. For marionette you can use ideas from marionette-lite extension based in filters system.
You should define filter, for example RequresAuthFilter as:
import { Filter } from 'marionette-lite';
const RequresAuthFilter = Filter.extend({
name: 'requresAuth', // name is used in controller for detect filter
async: true, // async mode
execution: Filter.Before,
handler(fragment, args, next) {
// Requesting server to check if user is authorised
$.ajax({
url: '/auth',
success: () => {
this.isSignedIn = true;
next();
},
error: () => {
Backbone.navigate('login', true);
}
});
},
});
or short sync way:
import { Filter } from 'marionette-lite';
const RequresAuthFilter = Filter.extend({
name: 'requresAuth',
handler(fragment, args) {
if (!window.isSignedIn) {
Backbone.navigate('login', true);
}
},
});
And add this filter to Controller as:
const AppController = Marionette.Object.extend({
// Add available filters map
filtersMap: [
new RequresAuthFilter()
],
filters: {
// e.g. Action that need authentication and if user isn't
// authenticated gets redirect to login page
requresAuth: ['logout', 'private'],
},
logout() { /* ... */ },
private() { /* ... */ }
});

Related

In Ember.js, is it possible to force computed properties defined on a route to update when the model refreshes?

In Ember.js, is it possible to force computed properties defined on a route to update when the model refreshes?
I am noticing that the first time I hit a route, I will see the functions that back computed properties invoked, but if the route's model is refreshed because a query parameter (e.g. page) changes, the functions backing the computed properties are not invoked again. I assume that this is because Ember doesn't believe the computed properties to have changed, in part because it doesn't believe the properties they are dependent on to have changed. For example:
// routes/example.js
export default Ember.Route.extend({
limit: 10,
queryParams: {
page: {
refreshModel: true
}
},
offset: computed("limit", function() {
console.log("IN: offset");
let params = this.paramsFor(this.get("routeName"));
if (params.page) {
// calculate offset using the "params.page" value
}
return 0;
}),
paginator: computed("limit", "offset", "query", function() {
console.log("IN: paginator");
// Calls: this.get("offset") and this.get("query") as part of its invocation
// Returns an object that can resolve the pagination of the model.
...
}),
query: computed(function() {
console.log("IN: query");
...
}),
model(params) {
console.log("IN: model");
let paginator = this.get("paginator");
...
},
setupController(controller, model) {
this._super(controller, model);
controller.setProperties({
offset: this.get("offset"),
limit: this.get("limit"),
pageCount: model.meta.total,
});
}
});
... and the controller:
// controllers/example.js
export default Ember.Controller.extend({
actions: {
firstPage: function() {
this.transitionToRoute({
queryParams: {
page: 1
}
});
},
lastPage: function() {
let lastPage = Math.ceil(this.get('pageCount')/this.get('limit'));
this.transitionToRoute({
queryParams: {
page: lastPage
}
});
},
nextPage: function() {
let totalPages = Math.ceil(this.get('pageCount')/this.get('limit'));
if(this.incrementProperty('page') > totalPages) {
this.set('page', totalPages);
}
this.transitionToRoute({
queryParams: {
page: this.get('page')
}
});
},
previousPage: function() {
if(this.decrementProperty('page') < 1) {
this.set('page', 1);
}
this.transitionToRoute({
queryParams: {
page: this.get('page')
}
});
}
},
...
/**
* The result page that a user is currently viewing.
*/
page: 1,
/**
* The query parameters.
*/
queryParams: ["page"],
});
The first time I load the route by hitting the associated URL in the browser I see:
IN: model
IN: paginator
IN: offset
IN: query
If I click on a button in the rendered template which changes the ?page query param, such as with the nextPage action, I see that it calls in to refresh the model but does not invoke any of the logic behind the computed properties, e.g.:
IN: model
I've tried adding controller.page to the list of dependent properties, but this does not seem to change the observed behavior. As an aside, functions appear to always be called (as I would expect).
Should I not be using computed properties in the route? It seems like if I want to use computed properties I need to somehow notify Ember to re-process them when the model is refreshed whether Ember believes them to have changed or not.

Ember Simple Auth transition after login

I have login code on my application route, as per examples in the docs, but the call to authenticate does not seem to return a promise. The response I get in 'then' is undefined. Therefore the transition does not work. I have to manually refresh the page, and then the top redirect is called.
import Ember from 'ember';
// Make 'session' available throughout the application
import ApplicationRouteMixin from 'simple-auth/mixins/application-route-mixin';
export default Ember.Route.extend(ApplicationRouteMixin, {
redirect: function () {
this.transitionTo('orders');
},
actions: {
authenticate: function () {
var data = {
identification: this.controller.get('identification'),
password: this.controller.get('password')
};
this.get('session').authenticate('simple-auth-authenticator:oauth2-password-grant', data).then(
function(response) {
console.log(response); // undefined
this.transitionTo('orders'); // can't call on undefined
}
);
},
}
});
My issue was 'this' inside the function call was the wrong object. Solved by using var _this = this;
I'll post the full working code.;
import Ember from 'ember';
// Make 'session' available throughout the application
import ApplicationRouteMixin from 'simple-auth/mixins/application-route-mixin';
export default Ember.Route.extend(ApplicationRouteMixin, {
redirect: function () {
this.transitionTo('orders');
},
actions: {
authenticate: function () {
var data = {
identification: this.controller.get('identification'),
password: this.controller.get('password')
};
var _this = this;
this.get('session').authenticate('simple-auth-authenticator:oauth2-password-grant', data).then(
function(response) {
console.log(_this.get('session')); // this correctly gets the session
_this.transitionTo('orders');
}
);
},
}
});
The promise returned by the session's authenticate method doesn't resolve with a value. You can access data that the authenticator resolves with via the session's secure property, e.g. this.get('session.secure.token)'.

React/reflux how to do proper async calls

I recently started to learn ReactJS, but I'm getting confused for async calls.
Lets say I have a Login page with user/pass fields and login button. Component looks like:
var Login = React.createClass({
getInitialState: function() {
return {
isLoggedIn: AuthStore.isLoggedIn()
};
},
onLoginChange: function(loginState) {
this.setState({
isLoggedIn: loginState
});
},
componentWillMount: function() {
this.subscribe = AuthStore.listen(this.onLoginChange);
},
componentWillUnmount: function() {
this.subscribe();
},
login: function(event) {
event.preventDefault();
var username = React.findDOMNode(this.refs.email).value;
var password = React.findDOMNode(this.refs.password).value;
AuthService.login(username, password).error(function(error) {
console.log(error);
});
},
render: function() {
return (
<form role="form">
<input type="text" ref="email" className="form-control" id="username" placeholder="Username" />
<input type="password" className="form-control" id="password" ref="password" placeholder="Password" />
<button type="submit" className="btn btn-default" onClick={this.login}>Submit</button>
</form>
);
}
});
AuthService looks like:
module.exports = {
login: function(email, password) {
return JQuery.post('/api/auth/local/', {
email: email,
password: password
}).success(this.sync.bind(this));
},
sync: function(obj) {
this.syncUser(obj.token);
},
syncUser: function(jwt) {
return JQuery.ajax({
url: '/api/users/me',
type: "GET",
headers: {
Authorization: 'Bearer ' + jwt
},
dataType: "json"
}).success(function(data) {
AuthActions.syncUserData(data, jwt);
});
}
};
Actions:
var AuthActions = Reflux.createActions([
'loginSuccess',
'logoutSuccess',
'syncUserData'
]);
module.exports = AuthActions;
And store:
var AuthStore = Reflux.createStore({
listenables: [AuthActions],
init: function() {
this.user = null;
this.jwt = null;
},
onSyncUserData: function(user, jwt) {
console.log(user, jwt);
this.user = user;
this.jwt = jwt;
localStorage.setItem(TOKEN_KEY, jwt);
this.trigger(user);
},
isLoggedIn: function() {
return !!this.user;
},
getUser: function() {
return this.user;
},
getToken: function() {
return this.jwt;
}
});
So when I click the login button the flow is the following:
Component -> AuthService -> AuthActions -> AuthStore
I'm directly calling AuthService with AuthService.login.
My question is I'm I doing it right?
Should I use action preEmit and do:
var ProductAPI = require('./ProductAPI')
var ProductActions = Reflux.createActions({
'load',
'loadComplete',
'loadError'
})
ProductActions.load.preEmit = function () {
ProductAPI.load()
.then(ProductActions.loadComplete)
.catch(ProductActions.loadError)
}
The problem is the preEmit is that it makes the callback to component more complex. I would like to learn the right way and find where to place the backend calls with ReactJS/Reflux stack.
I am using Reflux as well and I use a different approach for async calls.
In vanilla Flux, the async calls are put in the actions.
But in Reflux, the async code works best in stores (at least in my humble opinion):
So, in your case in particular, I would create an Action called 'login' which will be triggered by the component and handled by a store which will start the login process. Once the handshaking ends, the store will set a new state in the component that lets it know the user is logged in. In the meantime (while this.state.currentUser == null, for example) the component may display a loading indicator.
For Reflux you should really take a look at https://github.com/spoike/refluxjs#asynchronous-actions.
The short version of what is described over there is:
Do not use the PreEmit hook
Do use asynchronous actions
var MyActions = Reflux.createActions({
"doThis" : { asyncResult: true },
"doThat" : { asyncResult: true }
});
This will not only create the 'makeRequest' action, but also the 'doThis.completed', 'doThat.completed', 'doThis.failed' and 'doThat.failed' actions.
(Optionally, but preferred) use promises to call the actions
MyActions.doThis.triggerPromise(myParam)
.then(function() {
// do something
...
// call the 'completed' child
MyActions.doThis.completed()
}.bind(this))
.catch(function(error) {
// call failed action child
MyActions.doThis.failed(error);
});
We recently rewrote all our actions and 'preEmit' hooks to this pattern and do like the results and resulting code.
I also found async with reflux kinda confusing. With raw flux from facebook, i would do something like this:
var ItemActions = {
createItem: function (data) {
$.post("/projects/" + data.project_id + "/items.json", { item: { title: data.title, project_id: data.project_id } }).done(function (itemResData) {
AppDispatcher.handleViewAction({
actionType: ItemConstants.ITEM_CREATE,
item: itemResData
});
}).fail(function (jqXHR) {
AppDispatcher.handleViewAction({
actionType: ItemConstants.ITEM_CREATE_FAIL,
errors: jqXHR.responseJSON.errors
});
});
}
};
So the action does the ajax request, and invokes the dispatcher when done. I wasn't big on the preEmit pattern either, so i would just use the handler on the store instead:
var Actions = Reflux.createActions([
"fetchData"
]);
var Store = Reflux.createStore({
listenables: [Actions],
init() {
this.listenTo(Actions.fetchData, this.fetchData);
},
fetchData() {
$.get("http://api.com/thedata.json")
.done((data) => {
// do stuff
});
}
});
I'm not big on doing it from the store, but given how reflux abstracts the actions away, and will consistently fire the listenTo callback, i'm fine with it. A bit easier to reason how i also set call back data into the store. Still keeps it unidirectional.

Marionette: Starting and stopping modules based on route regexp

I'm implementing an application which has two separate submodules within top level application module.
I have an admin module with a convention for routes to start with /admin and user module having routes that start with /user. Top level application defines a rootRoute so that when you navigate to http://url/ you are redirected to either admin or user page based on permissions. What i'm trying to understand is whether it is possible to start and stop specific modules based on the route. Here is an example of what i mean:
Let's assume i have a top level application (in coffeescript)
#ClientApp = do (Backbone, Marionette) ->
App = new Marionette.Application
navigate: (route, options = {}) ->
Backbone.history.navigate route, options
App.on "start", (options) ->
if Backbone.history
Backbone.history.start()
App.on "stop", ->
App.module("AdminApp").stop()
App.module("UserApp").stop()
class App.Router extends Marionette.AppRouter
initialize: (options) ->
#route /^admin(.*)/, 'startAdminApp', options.controller.startAdminApp
#route /^user(.*)/, 'startUserApp', options.controller.startUserApp
appRoutes:
"": "redirectToRoot"
App.addInitializer ->
new App.Router
controller: API
API =
redirectToRoot: ->
# some redirect logic that will lead you to either /admin or /user
startAdminApp: ->
App.mainRegion.show new App.Layouts.Admin
App.module("AdminApp").start()
startUserApp: ->
App.mainRegion.show new App.Layouts.User
App.module("UserApp").start()
App
Inside admin and user submodules i also have defined routes
#ClientApp.module "AdminApp.DashboardApp", (DashboardApp, App, Backbone, Marionette, $, _) ->
_.extend DashboardApp, Backbone.Wreqr.radio.channel("dashboard")
class DashboardApp.Router extends Marionette.AppRouter
appRoutes:
"admin/dashboard": "statistics"
API =
getLayout: ->
new DashboardApp.Layout.View
statistics: ->
DashboardApp.StatisticsAp.start()
DashboardApp.on "start", ->
#layout = API.getLayout().render()
API.statistics()
App.addInitializer ->
new DashboardApp.Router
controller: API
If i navigate to / the application works as expected, i'm redirected to necessary namespace and a specific sub-module is started. However if i define some other routes within a submodule, they seem to override the existing regexp matchers. So if i open the browser and navigate to /admin/statistics it will not start the admin application and the callback for /admin/statistics will fail with error. That is because the admin application won't start and the mainRegion is not filled with a corresponding layout. Note that the file containing top level application definition is required before any of the submodules (i guess that is why routes are overridden). I also understand that backbone router will invoke route callback when the first match is met.
So the question is whether it's possible to implement a kind of route manager that will check current route with a regular expression and start or stop the corresponding application (either admin or user) with all defined sub-routes being persistent and bookmarkable?
Had close task to resolve, haven't found any existing solution, so here is a small stub - project i've created
To resolve such task there are few problems to resolve :
1) Async routing. something like rootRouter should load app module and moduleRouter should call controller methods
2) Clear up backbone history handlers on module stop. The problem is even after module stop, route and handler still exist in BB history
So my hack, i mean solution :)
We need some router that will watch URL change and load module, Let it be ModuleManager
define(
[
'application'
],
function(App) {
App.module('ModuleManager', function(ModuleManager, Application, Backbone, Marionette) {
var currentPageModule = false,
stopModule = function(name) {
name && Application.module(name).stop();
},
startModule = function(name) {
Application.module(name).start();
};
ModuleManager.getModuleNameByUrl = function() {
var name = Backbone.history.getHash().split('/')[0];
return name ? (name.charAt(0).toUpperCase() + name.slice(1)) : 'Home'
};
ModuleManager.switchModule = function(name) {
if (!name) return;
stopModule(currentPageModule);
startModule(name);
currentPageModule = name;
};
ModuleManager.requireModule = function(name, callback) {
require(['apps/pages/' + name + '/index'],
callback.bind(this),
function() {
require(['apps/pages/404/index'], function() {
ModuleManager.switchModule('404');
})
}
);
};
/*
* this is key feature - we should catch all routes
* and load module by url path
*/
ModuleManager.FrontRouter = Marionette.AppRouter.extend({
routes: {
'*any': 'loadModule'
},
loadModule: function() {
var name = ModuleManager.getModuleNameByUrl();
ModuleManager.requireModule(name, function() {
ModuleManager.switchModule(name);
})
}
});
ModuleManager.addInitializer(function() {
new ModuleManager.FrontRouter;
});
ModuleManager.addFinalizer(function() {
delete ModuleManager.FrontRouter;
});
});
}
);
Great, that will load module with routes inside. But we'll get another problem - on sub module start we init its router, but we already routed to the page on sub-router init and URL still same. So sub-router will not be invoked till next navigation. So we need special router, that will handle such situation. Here is 'ModuleRouter':
App.ModuleRouter = Marionette.AppRouter.extend({
forceInvokeRouteHandler: function(routeRegexp, routeStr, callback) {
if(routeRegexp.test(Backbone.history.getHash()) ) {
this.execute(callback, this._extractParameters(routeRegexp, routeStr));
}
},
route: function(route, name, callback) {
var routeString = route,
router = this;
if (!_.isRegExp(route)) route = this._routeToRegExp(route);
if (_.isFunction(name)) {
callback = name;
name = '';
}
if (!callback) callback = this[name];
// проблема - RouterManager уже стригерил событие route, загрузил саб-роутер.
// при создании саб роутера его колбэк уже вызван не будет, поскольку адрес страницы не изменился
// при добавлении роутов используется нативный ВВ route - который вещает колбэк на указанный фрагмент
// расширяем - если мы уже находимся на фрагменте на который устанавливается колбэк - принудительно вызвать
// выполнение обработчика совпадения фрагмента
/*
* PROBLEM : AppsManager already triggered 'route' and page fragments still same,
* so module router will not be checked on URL matching.
*
* SOLUTION : updated route method, add route to Backbone.history as usual, but also check if current page
* fragment match any appRoute and call controller callback
* */
this.forceInvokeRouteHandler(route, routeString, callback);
Backbone.history.route(route, function(fragment) {
var args = router._extractParameters(route, fragment);
router.execute(callback, args);
router.trigger.apply(router, ['route:' + name].concat(args));
router.trigger('route', name, args);
Backbone.history.trigger('route', router, name, args);
});
return this;
},
// implementation destroy method removing own handlers anr routes from backbone history
destroy: function() {
var args = Array.prototype.slice.call(arguments),
routKeys = _.keys(this.appRoutes).map(function(route) {
return this._routeToRegExp(route).toString();
}.bind(this));
Backbone.history.handlers = Backbone.history.handlers.reduce(function(memo, handler) {
_.indexOf(routKeys, handler.route.toString()) < 0 && memo.push(handler)
return memo;
}, []);
Marionette.triggerMethod.apply(this, ['before:destroy'].concat(args));
Marionette.triggerMethod.apply(this, ['destroy'].concat(args));
this.stopListening();
this.off();
return this;
}
})
Please fill free to ask question or chat, i guess there are some point might need to be clarified.

ember.js Uncaught TypeError: Object data-size has no method 'transitionTo'

I am very new to ember and trying to implement authentication via facebook
I am using ember-facebook.js library to connect with facebook. Once the authentication is successful, I want to transition to some other route e.g. '/index'. This library creates a App.FBUser object in mixin which is populated from the facebook response. The blog say following:
Whenever the user changes (login, logout, app authorization, etc) the method updateFBUser is called, updating the App.FBUser object on your application. You can do whatever you want with this binding, observe it, put it in the DOM, whatever.
Ember.Facebook = Ember.Mixin.create({
FBUser: void 0,
appId: void 0,
fetchPicture: true,
init: function() {
this._super();
return window.FBApp = this;
},
appIdChanged: (function() {
var _this = this;
this.removeObserver('appId');
window.fbAsyncInit = function() {
return _this.fbAsyncInit();
};
return $(function() {
var js;
js = document.createElement('script');
$(js).attr({
id: 'facebook-jssdk',
async: true,
src: "//connect.facebook.net/en_US/all.js"
});
return $('head').append(js);
});
}).observes('appId'),
fbAsyncInit: function() {
var _this = this;
FB.init({
appId: this.get('appId'),
status: true,
cookie: true,
xfbml: true
});
this.set('FBloading', true);
FB.Event.subscribe('auth.authResponseChange', function(response) {
return _this.updateFBUser(response);
});
return FB.getLoginStatus(function(response) {
return _this.updateFBUser(response);
});
},
updateFBUser: function(response) {
console.log("Facebook.updateFBUser: Start");
var _this = this;
if (response.status === 'connected') {
//console.log(_this);
return FB.api('/me', function(user) {
var FBUser;
FBUser = user;
FBUser.accessToken = response.authResponse.accessToken;
if (_this.get('fetchPicture')) {
return FB.api('/me/picture', function(path) {
FBUser.picture = path;
_this.set('FBUser', FBUser);
return _this.set('FBloading', false);
});
} else {
_this.set('FBUser', FBUser);
return _this.set('FBloading', false);
}
});
} else {
this.set('FBUser', false);
return this.set('FBloading', false);
}
}//updateFBUser
});
Update :
Adding following observer in my LoginController, I am able to capture the App.FBUser update event(it is update after getting response from FB; as indicated by the blog).
From this observer method, when I try to 'transitionTo' my index route I get following error
Uncaught TypeError: Object data-size has no method 'transitionTo'. Following is the code
App.LoginController = Ember.Controller.extend({
onSuccess: (function(){
var self = this;
/*
//tried all these method to redirect but error is the same
var attemptedTransition = this.get('attemptedTransition');
attemptedTransition.retry();
*/
/*
//tried all these method to redirect but error is the same
var router = this.get('target.router');
router.transitionTo('index');
*/
//tried all these method to redirect but error is the same
this.transitionToRoute('index');
}).observes('App.FBUser')
});
Index Route
App.AuthenticatedRoute = Ember.Route.extend({
beforeModel: function(transition){
var self = this;
if(!App.FBUser){
self.redirectToLogin(transition);
}
},
redirectToLogin: function(transition){
var loginController = this.controllerFor('login');
loginController.set('attemptedTransition', transition);
this.transitionTo('login');
}
});
I am not able to get my head around it.
Any help is greatly appreciated. Thanks
How can I access this object in my Route.beforeModel() hook.
Depending on what route's beforModel hook you are talking about, this is how you could do it:
App.SomeRoute = Ember.Route.extend({
beforeModel: function(transition) {
if (!Ember.isNone(App.FBUser)) {
// calling 'transitionTo' aborts the transition, redirects to 'index'
this.transitionTo('index');
}
}
});
Update in response to your last comment
The addon you are using is slightly outdated and the proposed implementation method for the mixin in your application will not work with the current version of ember:
App = Ember.Application.create(Ember.Facebook)
App.set('appId', 'yourfacebookappid');
starting from version 1.0.0-rc3 of ember you should rather do it like this:
App = Ember.Application.creatWithMixins(Ember.Facebook);
App.set('appId', 'yourfacebookappid');
After that you should be able to have access to the App.FBUser object as mentioned above.
Update 2
If you want to be able to be notified when some events happend, like login, logout etc. you should (as the Author of the addon states on it's blog post) override the updateFBUser method and do in there your transitions.
Since the addon is trough the mixin available in our App namespace you should be able to do the following:
App = Ember.Application.creatWithMixins(Ember.Facebook, {
updateFBUser: function() {
this._super();
// we are calling super to let the addon
// do it's work but at the same time we get
// notified that something happened, so do at this
// point your transition
}
});
Hope it helps.
As per Issue 1 adding
attributeBindings: [],
to:
return Ember.FacebookView = Ember.View.extend({
solved the issue.

Categories

Resources