property in route undefined in controller - ember.js

In the IndexRoute of my Ember hello world app, I start a setInterval function that I wish to allow the end user to turn off (with clearInterval) by clicking a dom element in the template, which triggers an action in the IndexController. So, the setIntervalId is set in the IndexRoute, and I need to pass it to clearInterval in the IndexController, but the way I have it below, the setIntervalId is undefined. I also tried to use App.IndexRoute.setIntervalId to no avail.
How would I accomplish this?
(function() {
window.App = Ember.Application.create({
LOG_TRANSITIONS: true,
LOG_ACTIVE_GENERATION: true
});
App.IndexRoute = Ember.Route.extend({
setIntervalId: 0,
model: function() {
this.setIntervalId = setInterval(this.someInterval, 5000)
},
someInterval: function(){
var datasource = 'http://hackernews/blahblah';
return new Ember.$.ajax({url: datasource, dataType: "json", type: 'GET'}).then(function(data){
return data;
})
},
});
App.IndexController = Ember.ObjectController.extend({
actions: {
clearTimeout: function(){
console.log('clearing interval', this.setIntervalId); //undefined
clearInterval(this.setIntervalId);
}
}
})
})();
template
<script type="text/x-handlebars" data-template-name="index">>
<h1>Hi Babe</hi>
{{ outlet }}
<label {{action "clearTimeout" on="click"}}>clear timeout</label>
</script>

To set the model, you need to return the value in the route’s model function:
model: function() {
return this.setIntervalId = setInterval(this.someInterval, 5000)
}
To access the model in the controller, you need to use this.get('model').
actions: {
clearTimeout: function(){
console.log('clearing interval', this.get('model');
clearInterval(this.get('model'));
}
}

Related

Ember json search with multiple TextFields

Ember noob here. I'm basically trying to have multiple input fields for multiple parameters. As the user types into the fields, this sends off a request to a PHP script which returns the relevant JSON and displays it.
Ember 1.6.1 (latest version is a pain to learn as all of the docs are
out of date)
Handlebars 1.3.0
jQuery 1.11.1
Here's the code so far (not working for multiple).
index.html
<script type="text/x-handlebars" data-template-name="search">
{{view App.SearchTextField elementId="bedrooms" valueBinding=bedrooms upKeyAction="searchProperties" placeholder="Bedrooms"}}
{{view App.SearchTextField elementId="suburb" valueBinding=suburb upKeyAction="searchProperties" placeholder="Sydney"}}
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="search/results">
{{#each}}
<h1>{{bedrooms}} - {{street}} {{suburb}}</h1>
{{/each}}
</script>
apps.js
App = Ember.Application.create();
App.Router.map(function() {
this.resource('search', {path: '/'}, function(){
this.route('results', {path: '/search/:suburb/:bedrooms'});
});
});
App.SearchRoute = Ember.Route.extend({
actions: {
searchProperties: function(suburb, bedrooms) {
console.log(suburb);
this.transitionTo('search.results', suburb, bedrooms);
}
}
});
App.SearchResultsRoute = Ember.Route.extend({
model: function(params) {
return Ember.$.getJSON('../test/data.php?suburb='+params.suburb+'&bedrooms='+params.bedrooms);
}
});
App.SearchTextField = Ember.TextField.extend({
keyUp: function (e) {
if (e.target.id == 'bedrooms') {
var bedrooms = e.target.value;
} else if (e.target.id == 'suburb') {
var suburb = e.target.value;
}
console.log(suburb + bedrooms);
this.sendAction('action', suburb, bedrooms);
}
});
After some playing around I got it to work using this (looking more jQuery than Ember, but hey it works)
App = Ember.Application.create();
App.Router.map(function() {
this.resource('search', {path: '/'}, function(){
this.route('results', {path: '/search/:suburb/:bedrooms'});
});
});
App.SearchRoute = Ember.Route.extend({
actions: {
searchProperties: function(data) {
this.transitionTo('search.results', data.suburb, data.bedrooms);
}
}
});
App.SearchResultsRoute = Ember.Route.extend({
model: function(params) {
return Ember.$.getJSON('../test/data.php?suburb='+params.suburb+'&bedrooms='+params.bedrooms);
}
});
App.SearchTextField = Ember.TextField.extend({
keyUp: function (e) {
var data = {suburb:$('#suburb').val(), bedrooms:$('#bedrooms').val()};
this.sendAction('upKeyAction', data);
}
});
Is there a better way to do this?
You are kind of over complicating things IMO,
I'd prefer to observe for the value changes in the controller and act accordingly. Result in much reduced code, and in fact you are actually exploiting the features, the framework provides.
Sample implementation, may need to modify to fulfill your needs
App.SearchController = Ember.ObjectController.extend({
suburb : null,
bedrooms : null,
doSearch : function(){
var model = [{street: this.get('suburb'), bedrooms: this.get('bedrooms')}];
//var model = Ember.$.getJSON('../test/data.php?suburb='+this.get('suburb')+'&bedrooms='+this.get('bedrooms'));
this.transitionToRoute('search.results', model);
}.observes('suburb', 'bedrooms')
});
App.SearchRoute = Ember.Route.extend({
});
App.SearchResultsRoute = Ember.Route.extend({
});
App.SearchTextField = Ember.TextField.extend({});
FIDDLE

Ember JS cannot createRecord with new ember-data syntax

I am trying to use the new ember-data syntax like explained here: https://github.com/emberjs/data/blob/master/TRANSITION.md (read from Transaction is Gone: Save Individual Records ).
When I hit the save button I get the error Uncaught TypeError: Cannot call method 'save' of undefined in the console. Also in the network tab, there is no POST request to the api.
The template
<script type="text/x-handlebars" data-template-name="landcode/new">
Code: {{input value=code}}<br />
Image: {{input value=image}}<br />
<button {{action 'saveLandcode'}}>opslaan</button>
The app.js (relevant code)
App.Router.map(function() {
this.resource("landcodes"),
this.resource("landcode", function() {
this.route("new");
});
});
App.LandcodeNewRoute = Ember.Route.extend({
model: function() {
this.store.createRecord('landcode');
},
actions: {
saveLandcode: function(){
this.modelFor('landcode').save(); // does not save
}
}
});
App.ApplicationAdapter = DS.RESTAdapter.extend({
namespace: 'api'
});
App.Store = DS.Store.extend({
adapter: 'App.ApplicationAdapter'
});
App.Landcode = DS.Model.extend({
code: DS.attr('string'),
image: DS.attr('string')
});
You are using this.modelFor('landcode') this will take the returned model from App.LandcodeRoute, but your model is returned from LandcodeNewRoute. Just use this.currentModel, since you want the model of the current route.
App.LandcodeNewRoute = Ember.Route.extend({
model: function() {
return this.store.createRecord('landcode');
},
actions: {
saveLandcode: function(){
this.currentModel.save();
}
}
});
Your model for should include the route name as well
App.LandcodeNewRoute = Ember.Route.extend({
model: function() {
return this.store.createRecord('landcode');
},
actions: {
saveLandcode: function(){
this.modelFor('landcode.new').save(); // the correct model
}
}
});

emberjs: transitionToRoute Error no method 'addArrayObserver

From the 'job' route I am trying to transition to 'careers' route using following code.
<script type="text/x-handlebars" data-template-name="job">
<button {{action 'backToCareers' this}}>Back</button>
</script>
The controller with following gives 'Uncaught TypeError: Object # has no method 'addArrayObserver' ' error.
CareerApp.JobController = Ember.ObjectController.extend({
backToCareers: function(){
this.transitionToRoute('careers');
}
});
If I change the code(see below) to provide model object the error changes to 'Uncaught More context objects were passed than there are dynamic segments for the route: careers '
CareerApp.JobController = Ember.ObjectController.extend({
backToCareers: function(){
var jobs = CareerApp.Job.findAll();
this.transitionToRoute('careers', jobs);
}
});
Following is the code of my Model and the router
CareerApp.Job = Ember.Model.extend({
refNo: '',
title: ''
});
CareerApp.Job.reopenClass({
findAll: function(){
return $.getJSON("http://site/jobs").then(
function(response){
var jobs = Ember.A();
response.forEach(function(child){
jobs.pushObject(CareerApp.Job.create(child));
});
return jobs;
}
);
}
});
Router code
CareerApp.Router.map(function(){
this.resource('careers', {path: '/'});
this.resource('job', {path: '/jobs/:job_id'});
});
CareerApp.CareersRoute = Ember.Route.extend({
model:function(){
return CareerApp.Job.findAll();
}
});
CareerApp.CareersController = Ember.ArrayController.extend({
gradJobCount: function () {
return this.filterProperty('isExp', false).get('length');
}.property('#each.isExp')
});
The model hook is expected to return an array but you return a jQuery promise object. findAll should return an empty array which is filled when the callback is executed.
findAll: function() {
var jobs = [];
$.getJSON("http://site/jobs").then(function(response){
response.forEach(function(child){
jobs.pushObject(CareerApp.Job.create(child));
});
});
return jobs;
}
As you pass jobs to CarreersController, this one needs to be an ArrayController, maybe you have to define it manually

How to disable a text field in emberJS when controller loads data

I have the following controller:
var ProductsController = Ember.ArrayController.extend({
search: function(queryString) {
this.set('model', App.Product.find({query: queryString }));
}
});
and a text field:
var SearchFieldView = Ember.TextField.extend({
insertNewline: function() {
this.get('controller').search(this.get('value'));
}
});
Now I want to disable the text field when the controller loads a new model in the search function. Using something like disabledBinding: 'controller.content.isLoaded' in the view doesn't work.
var ProductsController = Ember.ArrayController.extend({
search: function(queryString) {
this.set('isLoadingData', true);
var products = App.Product.find({query: queryString });
this.set('model', products);
products.then(function() {
this.set('isLoadingData', false);
});
}
});
var SearchFieldView = Ember.TextField.extend({
attributeBindings: ['disabled'],
disabledBinding: 'controller.isLoadingData',
insertNewline: function() {
this.get('controller').search(this.get('value'));
}
});
Explanation:
Before doing a request set isLoadingData to true. ember-data find() uses the Promise API: Set the isLoadingData to false when the request has been completed successfully. You might want to handle the failed case. See RSVP.js for reference. Finally bind the disabled property of Ember.TextField to controller.isLoadingData.
A simpler way, as you have already tried:
var ProductsController = Ember.ArrayController.extend({
search: function(queryString) {
this.set('model', App.Product.find({query: queryString }));
}
});
var SearchFieldView = Ember.TextField.extend({
attributeBindings: ['disabled'],
disabled: function() {
return this.get('controller.model.isLoaded') === false;
}.property('controller.model.isLoaded'),
insertNewline: function() {
this.get('controller').search(this.get('value'));
}
});
If you want all the Ember.TextField to have the disabled property binding:
Ember.TextField.reopen({
attributeBindings: ['disabled']
});

{{action}} link with transitionTo using relationship id

Given a view with a context like { id: 1, form_id: 5}, I want to create an {{action}} link to the form using the form_id.
My view code looks like:
<script type="text/x-handlebars" data-template-name="group">
{{action showForm form_id href=true}}
</script>
And the action in my router looks like:
showForm: function(router, event) {
var form_id = event.context;
router.transitionTo('root.form', { id: form_id });
},
I get an error that reads:
Uncaught Error: assertion failed: You must specify a target state for event 'showForm' in order to link to it in the current state 'root.index'.
I'm guessing that the problem is with the way I'm setting up the context for transitionTo, but I haven't been able to figure out the correct solution.
Here is the full code to reproduce the problem:
<script type="text/x-handlebars" data-template-name="application">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="group">
{{action showForm form_id href=true}}
</script>
MyApp = Ember.Application.create({
autoinit: false
});
MyApp.router = Ember.Router.create({
root: Ember.Route.extend({
index: Ember.Route.extend({
route: '/',
// Throws error:
// You must specify a target state for event 'showForm' in
// order to link to it in the current state 'root.index'
//
showForm: function(router, event) {
var form_id = event.context;
router.transitionTo('root.form', { id: form_id });
},
// Won't work because form deserialize finds id, not form_id
//showForm: Em.Route.transitionTo('root.form'),
// This won't work either
// showForm: Em.Route.transitionTo('root.form', { id: this.form_id }),
connectOutlets: function( router, context ){
var group = Em.Object.create({ id:1, form_id: 5 });
router.get( 'applicationController' ).connectOutlet( 'group', group );
}
}),
form: Ember.Route.extend({
route: '/form/:id',
serialize: function( router, context ){
return { id: context.id }
},
deserialize: function( router, context ){
var form = Em.Object.create({ id: 5, name: 'my form' });
return MyApp.Form.find( context.id );
},
connectOutlets: function( router, context ){
// left out for fiddle example
}
})
})
});
MyApp.ApplicationController = Ember.Controller.extend({});
MyApp.GroupController = Em.ObjectController.extend({});
MyApp.GroupView = Em.View.extend({ templateName: 'group' });
MyApp.initialize(MyApp.router);​
And the cooresponding fiddle:
http://jsfiddle.net/jefflab/LJGCz/
I was able to come up with an ugly solution to this problem using a computed property as the context of my action. The key snippets are:
<script type="text/x-handlebars" data-template-name="group">
<a {{action showForm view.formHash href=true}}>go to form</a>
</script>
MyApp.GroupView = Em.View.extend({
templateName: 'group',
formHash: function() {
return { id: this.get('controller').get('form_id') };
}.property('form_id')
});
And the working fiddle is here:
http://jsfiddle.net/jefflab/pGv8u/
However, after talking to Tom Dale, it is clear that the "right way" to solve to solve this problem is by using materialized objects instead of id values. If you are using Ember data, this is a great use case for the "sideloading" belongsTo feature.