Handeling an async call before transitioning to a new route - ember.js

I want to get the users Geolocation. Once I get the coordinates, I want to load Records based on that Geolocation. If there is more than 1 result, show an index page,
LHW.Store = DS.Store.extend
revision: 11
adapter: DS.RESTAdapter
DS.RESTAdapter.reopen
namespace: 'api'
LHW.Company = DS.Model.extend
name: DS.attr 'string'
address: DS.attr 'string'
city: DS.attr 'string'
LHW.IndexRoute = Ember.Route.extend
setupController: (controller, route) ->
# get Geolocation
_route = #
navigator.geolocation.getCurrentPosition ((location) ->
#success
_route.geoLocation(location)
), (error) ->
#error
switch(error.code)
when error.PERMISSION_DENIED then console.log "User denied the request for Geolocation."
when error.POSITION_UNAVAILABLE then console.log "Location information is unavailable."
when error.TIMEOUT then console.log "The request to get user location timed out."
when error.UNKNOWN_ERROR then console.log "An unknown error occurred."
geoLocation: (location) ->
_route = #
#get('store').findQuery('company',{longitude: location.coords.longitude, latitude: location.coords.latitude}).then ((data) =>
_route.controllerFor('companies').set('content', data)
if data.get('length') > 1
_route.transitionTo('companies');
), (error) ->
console.log "error"
LHW.CompaniesRoute = Ember.Route.extend()
LHW.IndexController = Ember.ArrayController.extend
beforeModel: ->
console.log 'before'
So this is what I got now. But this has a few problems
If you go to /companies instead of / It skips the geolocation
check. But if I move the geolocation to the companies route, then
what is the page the users sees while geolocation is getting
fetched?
Creating a switch and checking the length in a route,
feels a bit dirty, imo the Companyroute should respond to that
So this is mostly a flow issue I can't wrap my head around and the async geolocation isn't making things easier...

Related

How to store the user id in session with ember-simple-auth?

I want to store the user id in the session. I have this authenticate method in a custom authenticator :
authenticate: (credentials) ->
_this = this
new (Ember.RSVP.Promise) (resolve, reject) ->
Ember.$.ajax(
url: 'http://localhost:3000/api/v1/sessions'
type: 'POST'
data: session:
email: credentials.identification
password: credentials.password
).then ((response) ->
_this.set('user_id', response.user_id)
Ember.run ->
resolve token: response.token
), (xhr, status, error) ->
response = JSON.parse(xhr.responseText)
Ember.run ->
reject response.error
It's coffee script but it works like javascript. As you can this, I set 'user_id' in the session.
In app/initializers/custom-session, I have this :
import Ember from "ember";
import Session from "simple-auth/session";
export default {
name: "current-user",
before: "simple-auth",
initialize: function(container) {
Session.reopen({
setCurrentUser: function() {
return this.get('user_id')
}.observes('secure.access_token')
});
}
};
user_id always returns undefined. I found many solutions on the Internet but nothing works.
Is there a simple solution to do that?
You are setting the user_id on the authenticator (_this.set('user_id', response.user_id)) instead of the session. The user_id should be passed to the resolve method in your authenticator. That way your user id is accessible by secure.user_id in your session.

Single test fails in PhantomJS but works in Chrome and Firefox

I have a single acceptance test in Ember.js 1.10, Ember CLI 0.1.12 which fails on PhantomJS but runs fine in both Chrome and Firefox. I've tried to debug this for 2 days but I'm running out of ideas. Name of the test is user can view logged-only pages when he is logged. Basically when you are not logged and you try to access classic route, for example /about you are redirected to start.login in beforeModel hook of classic route:
beforeModel: ->
if not #controllerFor('application').get 'logged'
#transitionTo 'start.login'
When you are on start.login and you will give correct name and username, logIn action in StartLoginController will be called:
logIn: ->
if not #get('logged')
#get('controllers.application').send 'logIn', #get('username'), #get('password'), #get('rememberMe')
Which calls following action in ApplicationController:
actions:
logIn: (username, password, remember) ->
if not #get 'logged'
$.ajax
url: #get('apiURL') + '/auth/login'
type: 'POST'
data:
name: username
password: password
remember: remember
xhrFields:
withCredentials: true #ensure CORS
.then (response) =>
if response.success
expires = if remember then new Date(response.cookieExpiration) else null
$.cookie 'auth_user_id', response.user.id,
expires: expires
path: '/'
$.cookie 'auth_expiration', response.cookieExpiration,
expires: expires
path: '/'
#setProperties
logged: true
'controllers.auth.userId': response.user.id
#transitionToRoute 'classic.index'
, =>
#send 'openModal', 'modal/wrong-credentials'
false
And this works fine even in PhantomJS. Other tests pass. Actions are called correctly, properties are set correctly, cookies are set correctly. Even beforeModel hook correctly calls(or not) transitionTo method. I've thought that issue with PhantomJS is with some async order of calling things but I've tried wrapping code in Ember.run and andThen in many places. No luck at all.
testem.json:
{
"framework": "qunit",
"test_page": "tests/index.html?hidepassed",
"launch_in_ci": [
"PhantomJS"
],
"launch_in_dev": [
"PhantomJS",
"Chrome",
"Firefox"
]
}
Acceptance test login-test.coffee(failing test is the last one):
`import Ember from 'ember'`
`import startApp from '../helpers/start-app'`
application = null
run = Ember.run
login = ->
visit '/'
fillIn '[placeholder=Login]', 'test'
fillIn '[placeholder=Hasło]', 'test'
click '.button-login'
clearCookies = ->
cookies = $.cookie()
for cookie of cookies
$.removeCookie cookie,
path: '/'
cookies = document.cookie.split ';'
for i in [0...cookies.length] by 1
equals = cookies[i].indexOf '='
name = if equals > -1 then cookies[i].substr(0, equals) else cookies[i]
document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT"
module 'Acceptance: Login',
setup: ->
application = startApp()
clearCookies()
time = new Date()
$.mockjaxSettings.logging = false
$.mockjax
type: 'POST'
url: 'api/v1/auth/login'
dataType: 'json'
responseText:
success: true
user:
id: 1
cookieExpiration: time.setDate time.getDate() + 14
$.mockjax
type: 'GET'
url: '/api/v1/users/1'
dataType: 'json'
responseText:
user:
id: 1
$.mockjax
type: 'GET'
url: '/api/v1/statuses' # ?limitOld=10&friends=true&comments[limit]=5
data:
comments:
limit: 5
friends: true
limitOld: 10
responseText:
statuses: {}
$.mockjax
type: 'GET'
url: '/api/v1/getRandomQuote'
$.mockjax
type: 'GET'
url: '/api/v1/statuses/?friends=true&count=true'
responseText:
count: 0
$.mockjax
type: 'GET'
url: '/api/v1/meals'
$.mockjax
type: 'GET'
url: '/api/v1/notifications'
$.mockjax
type: 'GET'
url: '/api/v1/notifications'
data:
read: false
responseText: {}
return
teardown: ->
$.mockjax.clear()
clearCookies()
run application, 'destroy'
test 'user lands on default page when he is not logged', ->
expect 1
visit '/'
andThen ->
equal currentPath(), 'start.login'
test 'login page is displayed when you are trying to access logged-only page', ->
expect 1
visit '/kitchen'
andThen ->
equal currentPath(), 'start.login'
test 'user can login', ->
expect 2
appController = application.__container__.lookup 'controller:application'
equal appController.get('logged'), false, 'user is not logged before login'
login()
andThen ->
ok appController.get 'logged', 'user is logged when response is success'
test 'user can view logged-only pages when he is logged', ->
expect 2
console.log '-- LOGGED-ONLY TEST START --'
visit '/about'
andThen ->
equal currentPath(), 'start.login'
login()
visit '/about'
andThen ->
equal currentPath(), 'classic.about'
And finally output from tests:
TEST'EM 'SCRIPTS!
Open the URL below in a browser to connect.
http://localhost:7357/
━━━━━━━━━━━━━━┓
PhantomJS 1.9┃ Firefox 37.0
198/199 ✘ ┃ 199/199 ✔
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Acceptance: Login: user can view logged-only pages when he is logged
✘ failed
expected classic.about
actual start.login
-- LOGGED-ONLY TEST START --
I don't think classic.about is source of error, because replacing it with other child routes of classic resource result in same PhantomJS failing test.
Okay so it seems that problem lies in model for ClassicRoute(commenting that makes test pass):
model: ->
new Promise (resolve, reject) =>
#store.find 'user', #controllerFor('auth').get('userId')
.then (user) =>
#controllerFor('auth').set 'user', user
resolve
profile: user.get 'profile'
I've split logic in two hooks instead of one and it works now:
beforeModel: ->
if not #controllerFor('application').get 'logged'
#transitionTo 'start.login'
else
#store
.find 'user', #controllerFor('auth').get('userId')
.then (user) =>
#controllerFor('auth').set 'user', user
#set 'profileId', user.get('profile.id')
model: ->
#store.find 'profile', #get('profileId')

Ember.js transaction.rollback only works once (ember form reset)

Trying to get form reset functionality working. This implementation works once, but then will not work a second time.
The second time the cancel event is triggered the following error occurs:
Uncaught Error: Attempted to handle event willSetProperty on while in state rootState.deleted.saved. Called with {reference: [object Object], store: , name: name}
App.NewPageRoute = Ember.Route.extend
model: (params) ->
transaction = #get('store').transaction()
transaction.createRecord(App.Page, siteId: params.site_id)
App.NewPageController = Ember.ObjectController.extend
submit: ->
#content.get('transaction').save()
cancel: ->
#content.get('transaction').rollback()
From the docs of DS.Transaction::rollback:
After the transaction is rolled back, any records that belong to it
will return to the store's default transaction, and the current
transaction should not be used again.
A simple fix would be to create a new transaction after you roll back.
cancel: ->
siteId = #get('model.siteId')
#content.get('transaction').rollback()
transaction = #get('store').transaction()
newModel = transaction.createRecord(App.Page, siteId: siteId)
#set('model', newModel)

How to send a POST request from two different views?

This question is similar to my previous one: how to communicate with Rails in Ember but some things have changed. Now I know that a kudo is modelled in the server and has the following fields:
value
comment
It belongs to a user. The current implementation is as below:
<a href="#" class="btn btn-primary" {{action "addKudo" user}}>add</a>
Sks.ApplicationRoute = Ember.Route.extend
events:
addKudo: (user) ->
console.log event
self = #
token = $('meta[name="csrf-token"]').attr 'content'
currentUserCon = #controllerFor 'currentUser'
kudosLeft = currentUserCon.get 'kudosLeft'
showFlash = (type, message) ->
$("#flash")
.addClass(type)
.empty()
.append(message)
.show()
.fadeIn()
.delay(2000)
.fadeOut 'slow'
$.post("/kudos", user_id: user.get("id"), authenticity_token: token)
.done((data, status) =>
if kudosLeft > 0
currentUserCon.decrementKudos 1
showFlash 'alert-success', 'You\'ve added a kudo!'
else
showFlash 'alert-error', 'There\'re no kudos left!'
)
.fail (data, status) ->
showFlash 'alert-error', 'Oops! An error occured!'
But now, I'd like to make use of Ember-Data and send a POST request to /kudos (and I'd like to get rid of the jQuery ajax part). Please have a look:
Having the following model:
Sks.Kudo = DS.Model.extend
user: DS.belongsTo 'Sks.User'
value: DS.attr 'number'
comment: DS.attr 'string'
how can I achieve this? What are the neccessary steps to do this? Could anyone shed some light?
The next question is: where select and text area value should point to (bindings)?

Create a restricted area for authenticated user only

I've made a login system with Ember.js and Rails . So I tried to make redirection for non-authenticated users. I don't know where I must do the redirection (the application controller or in the application route ? ).
Here's my auth system :
window.AuthApp = Ember.Object.extend(Ember.Evented).create
authToken: null
currentUserId: null
signIn: (data) ->
if data == null
data = {}
if data['remember'] == true
cookie = true
$.ajax 'api/login',
type: 'POST'
dataType: 'JSON',
contentType: 'application/json; charset=utf-8'
data: JSON.stringify(data)
error: ->
alert('Error signIn')
success: (data) ->
console.log(data)
AuthApp.set 'authToken', data['auth_token']
if cookie == true
$.cookie 'remember_token', data['auth_token'],
expires: 7
AuthApp.Module.RememberMe = Ember.Object.create
recall: ->
if ($.cookie('remember_token'))
data = {}
data['auth_token'] = $.cookie('remember_token')
AuthApp.signIn(data)
return true
As you can see, I've just to call AuthApp.Module.Remember.recall() for check if an user is connected or not.
Thanks for your help
You should take a look at the router-facelift. One of the design goals was to make authentication-based apps less difficult to implement.
https://gist.github.com/machty/5723945