Don Wei
Don Wei

Reputation: 431

Ember.js Controller & View Binding (The ember.js way...)

I apologize for the length of this question but I lack the insight to make it short.

I am trying to learn Ember.js & I am having a little trouble understanding the relationship between a controller it's view. Particularly as it relates to controller property & view bindings. I have a bit of an application that works... But I don't really understand why & I feel that (rather obviously) this is not the 'Ember.js' way to do it.

Some things to consider: This application is broken out into directory/files according to what I have interpreted in the guides/documentation to be the 'correct' structure, This application is running in the context of a sinatra application. This application, although incomplete, does work the way I expect it to so far.

The application is incomplete but here is a general overview of what I expect & what I am trying to do:

1) A user arrives at the root URL '/' and is transitioned to the '/locate' URL.

2) The LocateController grabs the users location, populates two form fields with the lat/lng.

3) The form is submitted to the server as POST to the sinatra '/locate' route as ajax.

(request is processed by server)

4) User is transitioned to the ember.js '#/located' route.

5) Information comes back from the server as a JSON object & displayed

I have thus far only implemented step one. The form could be sent if I added a submit button but the idea is to have it happen automatically.

The fields in the locate view are populated with the correct values for the form fields. However, I am using straight jQuery to update the values and that to me seems like it is not the 'correct' way to do this in an ember.js app

Here follows some of my code. For the sake of brevity I will just provide snippets as I feel like if you know the right answer you will be able to derive it from the provided excerpts.

My views are set up like this: I use slim in my sinatra app

script type="text/x-handlebars" data-template-name="application"
    | {{ outlet }}
script type="text/x-handlebars" data-template-name="locate"
    | {{ message }}
    | <form id="coordinates">
    |     {{view Ember.TextField id="latitude" name="latitude" valueBinding="latitude" class="latitude"}}
    |     {{view Ember.TextField id="longitude" name="longitude" valueBinding="longitude" class="longitude"}}
    | </form>
 script type="text/x-handlebars" data-template-name="located"
    | <p id="message">{{ message }}</p>
    | <p id="latitude">{{ latitude }}</p>
    | <p id="longitude">{{ longitude }}</p>

script src="assets/js/app/application.js"

applicationView.js

Application.ApplicationView = Ember.View.extend({
  templateName: 'application'
});

Application.LocateView = ApplicationView.extend({
  templateName: 'locate'
});

Application.LocatedView = ApplicationView.extend({
  templateName: 'located'
});

applicationController.js

var GeoLocation;

GeoLocation = (function(location){
    $('#latitude').val(location.coords.latitude),
    $('#longitude').val(location.coords.longitude)
});

navigator.geolocation.getCurrentPosition(GeoLocation)

Application.ApplicationController = Ember.Controller.extend({
  message: "Hello from Application"
});

Application.LocateController = Ember.Controller.extend({
    message: "Hello from Locate",
    latitude: "-- lat --", 
    longitude: "-- lng --",
});

Application.LocatedController = Ember.Controller.extend({
    message: "Hello from Located",
    latitude: "-- lat --",
    longitude: "-- lng --",
});

router.js

Application.Router.map(function() {
  this.route('index');
  this.route('locate');
  this.route('located');
});

Application.ApplicationRoute = Ember.Route.extend({
  events: {
    goToLocate: function() {
      this.transitionTo('locate');
    },
    goToLocated: function() {
      this.transitionTo('located');
    }
  }
});

Application.IndexRoute = Ember.Route.extend({
  redirect: function() {
        this.render('application');
    this.transitionTo('locate');
  }
});

Application.LocateRoute = Ember.Route.extend({
  redirect: function() {
        this.render('locate');
    //this.transitionTo('located');
  }
});

});

Application.LocatedRoute = Ember.Route.extend({
  renderTemplate: function(controller) {
    this.render('located');
  }
});

I have read the Guides & The API Documentation on the ember.js website as well as looked at the repo on GitHub. I feel like the 'correct' implementation would use computed properties for the latitude & longitude attributes but I don't quite understand how that works in practice.

Also, I know that there is most likely a better way to deal with getting the information to the server (perhaps as JSON or ember-data when I get that far?), and feel free to share any insights you may have regarding that. But for now however, as I am comfortable with sinatra and how it works I would prefer a solution that does not require major refactoring of my backend yet. (that is to say, being able to access the location data from sinatra's params[:hash]).

Thanks in advance for your help & taking the time to read this rather long question.

Upvotes: 2

Views: 3218

Answers (1)

mspisars
mspisars

Reputation: 854

I do agree that setting the value of an input via jQuery in an ember app is not the way to go. Ember does really well with binding to properties. I would suggest that since you redirect to Locate route on initial load, move the getLoaction logic to the LocateController or set it inside the route setupController hook see here (see here for setupController example)... You can also set the properties this on your App instance, if you wanted to have access to them.

So, instead of this:

var GeoLocation;

GeoLocation = (function(location){
    $('#latitude').val(location.coords.latitude),
    $('#longitude').val(location.coords.longitude)
});

navigator.geolocation.getCurrentPosition(GeoLocation)

I would do it in the route:

App.LocateRoute = Ember.Route.extend({
  setupController: function(controller, model) {
    navigator.geolocation.getCurrentPosition(controller.geoLocation)
  }
});

App.LocateController = Ember.Controller.extend({
  geoLocation: function(location){
    this.set('latitude', location.coords.latitude);
    this.set('longitude', location.coords.longitude);
  }
});

Now your Locate controller has the 2 properties 'latitude' and 'longitude' defined. You can bind to them in your template. (BTW, your template already look correct for the binding.)

You can also set the properties this on your App instance, if you wanted to have access to them globally, then change the bindings to point to 'App.longitude' and 'App.latitude'.

App.LocateController = Ember.Controller.extend({
  geoLocation: function(location){
    Ember.set(App, 'latitude', location.coords.latitude);
    Ember.set(App, 'longitude', location.coords.longitude);
  }
});

Upvotes: 4

Related Questions