Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EmberJS Router App: Views vs Controllers

Tags:

ember.js

I'm creating a router-based EmberJS app (strongly modelled on the excellent router guide). However, I'm quite muddled over what belongs in a view vs in a controller.

I totally get that {{action showFoo}} often indicates a state change and that the Router is the state machine for my app. But some of my actions don't fall into that category.

Here's an example from my actual code (html simplified but mustaches intact). I want to have a login form that works via ajax (i.e. the html form doesn't post directly to the server, it tells my ember app to attempt a login via json).

<form>
    Email Name: {{view Ember.TextField valueBinding="email"}}
    Password:  {{view Ember.TextField valueBinding="password"}}
    <button type="submit" {{ action logIn target="this" }}>Sign in</button>  
</form> 

The valueBindings are fields in my loginController but the logIn handler is in my view (because I couldn't figure out how to tell the template to call the controller). I feel like this is a weird distribution & I'm not sure what the right Ember approach is to this.

I don't think the router should be handling the action because requesting a login attempt isn't really a state change. The loginController feels like the right place to try the login. After a login response is received then that controller could trigger the state change.

like image 781
Boon Avatar asked Oct 23 '12 03:10

Boon


2 Answers

I don't think the router should be handling the action because requesting a login attempt isn't really a state change.

I think that's exactly the case: attempting a login should transition to an authenticating state, where for example another click to "login" is ignored.

So IMHO this should be handled by the router. I'm thinking about something like this, see http://jsfiddle.net/pangratz666/97Uyh/:

Handlebars:

<script type="text/x-handlebars" >
    {{outlet}}
</script>

<script type="text/x-handlebars" data-template-name="login" >
    <p class="info">{{message}}</p>
    Login to view the admin area <br/>
    Email: {{view Ember.TextField valueBinding="email" }} <br/>
    Password: {{view Ember.TextField valueBinding="password" }} <br/>
    <button {{action login}} >Login</button>
</script>

<script type="text/x-handlebars" data-template-name="authenticating" >
    Communicating with server ...
</script>

<script type="text/x-handlebars" data-template-name="admin" >
    Hello admin!
</script>

JavaScript:

App = Ember.Application.create();

App.ApplicationController = Ember.Controller.extend({
    login: function() {
        // reset message
        this.set('message', null);

        // get data from login form
        var loginProps = this.getProperties('email', 'password');

        // simulate communication with server
        Ember.run.later(this, function() {
            if (loginProps.password === 'admin') {
                this.set('isAuthenticated', true);
                this.get('target').send('isAuthenticated');
            } else {
                this.set('message', 'Invalid username or password');
                this.set('isAuthenticated', false);
                this.get('target').send('isNotAuthenticated');
            }
        }, 1000);

        // inform target that authentication is in progress        
        this.get('target').send('authenticationInProgress');
    },
    logout: function() {
        this.set('isAuthenticated', false);
    }
});
App.ApplicationView = Ember.View.extend({
    templateName: 'application'
});

App.LoginView = Ember.View.extend({
    templateName: 'login'
});
App.AdminView = Ember.View.extend({
    templateName: 'admin'
});
App.AuthenticatingView = Ember.View.extend({
    templateName: 'authenticating'
});

App.Router = Ember.Router.extend({
    root: Ember.Route.extend({
        index: Ember.Route.extend({
            route: '/',
            loggedOut: Ember.Route.extend({
                route: '/',
                connectOutlets: function(router) {
                    router.get('applicationController').connectOutlet('login');
                },
                login: function(router) {
                    router.get('applicationController').login();
                },
                authenticationInProgress: function(router) {
                    router.transitionTo('authenticating');
                }
            }),
            authenticating: Ember.State.extend({
                enter: function(router) {
                    router.get('applicationController').connectOutlet('authenticating');
                },
                isAuthenticated: function(router) {
                    router.transitionTo('loggedIn');
                },
                isNotAuthenticated: function(router) {
                    router.transitionTo('loggedOut');
                }
            }),
            loggedIn: Ember.Route.extend({
                route: '/admin',
                connectOutlets: function(router) {
                    if (!router.get('applicationController.isAuthenticated')) {
                        router.transitionTo('loggedOut');
                    }
                    router.get('applicationController').connectOutlet('admin');
                },
                logout: function(router) {
                    router.get('applicationController').logout();
                }
            })
        })
    })
});​
like image 96
pangratz Avatar answered Sep 28 '22 19:09

pangratz


You can use the controller for this, the template your using have access to the controller.

<script type="text/x-handlebars" data-template-name="loginTemplate">
  {{#if controller.login}}
    Logged in!
  {{else}}
    Login failed
  {{/if}}
</script>

This fiddle shows a small app, which does that: fiddle

Then after login has occured you can make an actioncall to your router, or show the user that login failed.

like image 45
TommyKey Avatar answered Sep 28 '22 20:09

TommyKey