Upload
sunilkaspo
View
224
Download
0
Embed Size (px)
DESCRIPTION
Controlling Route Traversal
Citation preview
URLs/accounts
/accounts/12
/transfers
/transfers/78
/statements
/statements/august
/tour
/contact
/login
/login/one-time-password/setup
/login/one-time-password/select-delivery-method
/login/one-time-password/authenticate
/login/one-time-password/register-device
/login/electronic-consent
/login/terms-of-service-consent
/login/reset-password /login/complete
/logout
/recover-username
/recover-password
Resources/accounts
/accounts/12
/transfers
/transfers/78
/statements
/statements/august
/tour
/contact
/login
/login/one-time-password/setup
/login/one-time-password/select-delivery-method
/login/one-time-password/authenticate
/login/one-time-password/register-device
/login/electronic-consent
/login/terms-of-service-consent
/login/reset-password /login/complete
/logout
/recover-username
/recover-password
Actions/accounts
/accounts/12
/transfers
/transfers/78
/statements
/statements/august
/tour
/contact
/login
/login/one-time-password/setup
/login/one-time-password/select-delivery-method
/login/one-time-password/authenticate
/login/one-time-password/register-device
/login/electronic-consent
/login/terms-of-service-consent
/login/reset-password /login/complete
/logout
/recover-username
/recover-password
ComparisonResource Action Flow
Route name is a noun. Route name is a verb. Route name is a verb.
Appears in the history stack. Appears in the history stack. Depends. One or more entries in the history stack.
Doesn't appreciably change application state. Changes application state. Flow state incrementally or
transactionally committed.
Newly rendered page. Flash messaging and/or the newly created resource.
Next step, results page, or the finalized resource.
Reload the model state. Reload a model (if any) and possibly the controller state.
Possibly drop the user back into the state machine.
No reset necessary after view. Reset typical after completion. Reset typical after completion.
Router.map(function() { this.resource('authenticated', { path: '/' }, function() { this.resource('accounts', { path: '/' }, function() { this.route('details', { path : 'account/:account_id' }); }); this.resource('transfers'); this.resource('statements', function() {}); this.route('tour'); }); ! this.resource('login', function() { this.resource('one-time-password', function() { this.route('setup'); this.route('select-delivery-method'); this.route('authenticate'); this.route('register-device'); }); this.route('electronic-consent'); this.route('terms-of-service-consent'); this.route('reset-password'); this.route('complete'); }); ! this.route('logout'); this.route('recover-username'); this.route('recover-password'); this.route('contact'); });
https://github.com/alexdiliberto/emberconf-2014-demo/blob/master/app/router.js
https://docs.google.com/spreadsheet/ccc?key=0ApnSRONV3LTVdGJuLUlnOGxzY1FadGZHN050ZVJXcWc
Description Steps
Already logged in. login.index accounts.index
Login is clean. login.index login.electronic-consent?
login.terms-of-service-consent?
tour? accounts.index
Invalid credentials. login.index login.index!flash message
Dismiss message on keypress.
Login, secondary challenge, no OTP method.
login.index contact
Login, secondary challenge, single OTP method.
login.index one-time-password.authenticate
one-time-password.register-device
login.electronic-consent?
login.terms-of-service-consent?
tour? accounts.index
Login, secondary challenge, multiple OTP methods.
login.index one-time-password.select-delivery-method
one-time-password.authenticate
one-time-password.register-device
login.electronic-consent?
login.terms-of-service-consent?
tour? accounts.index
Login, needs to set up OTP. login.index one-time-password.setup
one-time-password.register-device
login.electronic-consent?
login.terms-of-service-consent?
tour? accounts.index
Login using a temporary password. login.index login.reset-password login.reset-password-complete
login.electronic-consent?
login.terms-of-service-consent?
tour? accounts.index
Login using a temporary password, needs to set up OTP
login.index login.reset-password login.reset-password-complete
one-time-password.setup
one-time-password.register-device
login.electronic-consent?
login.terms-of-service-consent?
tour? accounts.index
Login using a temporary password, secondary challenge, no OTP method.
login.index contact
Login using a temporary password, secondary challenge, single OTP method.
login.index one-time-password.authenticate
one-time-password.register-device
login.reset-password login.reset-password-complete
login.electronic-consent?
login.terms-of-service-consent?
tour? accounts.index
Login using a temporary password, secondary challenge, multiple OTP methods.
login.index one-time-password.select-delivery-method
one-time-password.authenticate
one-time-password.register-device
login.reset-password login.reset-password-complete
login.electronic-consent?
login.terms-of-service-consent?
tour? accounts.index
Recover Username recover-username login.index!flash message
Recover Password, no OTP method. recover-password contact
Recover Password, single OTP method.
recover-password one-time-password.authenticate
login.reset-password login.reset-password-complete
login.electronic-consent?
login.terms-of-service-consent?
tour? accounts.index
Recover Password, multiple OTP methods.
recover-password one-time-password.select-delivery-method
one-time-password.authenticate
login.reset-password login.reset-password-complete
login.electronic-consent?
login.terms-of-service-consent?
tour? accounts.index
Login, forced OTP review, login.index one-time-password.setup?edit=true
one-time-password.register-device
login.electronic-consent?
login.terms-of-service-consent?
tour? accounts.index
login
otp.select-delivery-method
otp.setup otp. authenticate
otp. register-device
accounts - or -
deep linkelectronic-
consentterms-of-service-consent
tourcomplete
login
otp.select-delivery-method
otp.setup otp. authenticate
otp. register-device
accounts - or -
deep linkelectronic-
consentterms-of-service-consent
tourcomplete
{ isIdentified: true, isAuthenticated: false, hasOneDeliveryMethod: deliveryMethods.length, }
login
otp.select-delivery-method
otp.setup otp. authenticate
otp. register-device
accounts - or -
deep linkelectronic-
consentterms-of-service-consent
tourcomplete
login
otp.select-delivery-method
otp.setup otp. authenticate
otp. register-device
accounts - or -
deep linkelectronic-
consentterms-of-service-consent
tourcomplete
reset-password
recover-password
reset-password-complete
Playing Alonghttp://alexdiliberto.com/emberconf-2014-demo
https://github.com/alexdiliberto/emberconf-2014-demo
// Direct Login ic.ajax.defineFixture('/session', { response: { /* Successfully identified username and password. */ isIdentified: true, !
/* Related to one-time-passcode. */ willSetupOTP: false, isAuthenticated: true, !
hasElectronicConsent: true, hasTermsOfServiceConsent: true, showTour: false }, textStatus: 'success' });
// One Time Password with selection. ic.ajax.defineFixture('/session', { response: { /* Successfully identified username and password. */ isIdentified: true, !
/* Related to one-time-passcode. */ willSetupOTP: false, isAuthenticated: false, !
hasElectronicConsent: true, hasTermsOfServiceConsent: true, showTour: false }, textStatus: 'success' });
A Naive Approachgit clone https://github.com/alexdiliberto/emberconf-2014-demo git checkout tags/naive
General Strategy• Load the session state from a route-global injection.
• Reset the controller on setupController.
• Delegate identification of where to go next to the current route.
• Traverse the longest possible path through the application so that every route can identify whether or not they should be stopped at.
Inject The Session Statevar session = Ember.Object.extend(); Ember.Application.initializer({ name: 'session', initialize: function(container, app) { app.register('session:main', session); app.inject('route', 'session', 'session:main'); } });
beforeModel
beforeModel: function() { if (this.get('session.isAuthenticated')) { this.replaceWith('one-time-password.register-device'); } }
{{action}}
actions: { authenticate: function() { ic.ajax.raw('/authenticate').then(function(result) { if (result.response.isAuthenticated) { this.set('session.isAuthenticated', true); this.replaceWith('one-time-password.register-device'); } }); } }
General Strategy• Define all progression in a separate location.
• Load the Flow and its state from a route-global injection.
• Rely on Alex Matchneer's new primitive for repopulating controller state?
• Delegate identification of where to go next to the Flow itself.
• Call back into the Flow to progress.
• Your flow tracks which edges you've traversed in case you need that information in your application.
Definitions
LoginFlow.addEdge({ from: 'login.index', to: 'accounts.index', weight: 1, conditions: ['isIdentified', 'isAuthenticated'] });
Inject The Login Flowvar login = Ember.Object.extend(); Ember.Application.initializer({ name: 'login', initialize: function(container, app) { app.register('flow:login', login); app.inject('route', 'login', 'flow:login'); } });
beforeModel
beforeModel: function() { // Looks up the current flow. // Identifies where the user should be. this.get('flow').check(); }
{{action}}actions: { authenticate: function() { var Flow = this.get('flow'); !
ic.ajax.raw('/authenticate').then(function(result) { if (result.response.isAuthenticated) { Flow.set('isAuthenticated', true); Flow.progress(); } }); } }
ember-flowshttps://github.com/nathanhammond/ember-flows
Resources• https://signalvnoise.com/posts/1926-a-shorthand-for-designing-ui-flows
• https://docs.google.com/spreadsheet/ccc?key=0ApnSRONV3LTVdGJuLUlnOGxzY1FadGZHN050ZVJXcWc
• http://alexdiliberto.com/emberconf-2014-demo
• https://github.com/alexdiliberto/emberconf-2014-demo
• https://github.com/nathanhammond/ember-flows
• https://code.google.com/p/libphonenumber/