I have a route that displays a list of parcels, and an Ember.Select that allows the user to select which state's parcels to show.
Model
App.Parcel = DS.Model.extend({
addresses: DS.attr('array')
});
Route
App.ParcelsRoute = Ember.Route.extend({
state: null,
renderTemplate: function () {
this.render({ outlet: 'parcels' });
},
model: function (params) {
state = params.state;
App.ParcelAdapter.state = state;
App.ImageAdapter.state = state;
return Ember.RSVP.hash({
props: this.store.findAll('parcel'),
states: this.store.findAll('state'),
});
},
setupController: function (controller, model) {
controller.set('states', model.states);
controller.set('props', model.props);
controller.set('selectedState', state);
}
});
Controller
App.ParcelsController = Ember.ObjectController.extend({
selectedState: null,
props: null,
states: null,
first: true,
modelReloadNeeded: function () {
if (this.get('selectedState') != undefined && !this.get('first')) {
this.transitionToRoute('/parcels/' + this.get('selectedState'));
}else{
this.set('first', false);
}
}.observes('selectedState')
});
Handlebars
<script type="text/x-handlebars" id="parcels">
{{view Ember.Select content=states optionValuePath="content.id" optionLabelPath="content.id" value=selectedState}}
<input class="search" placeholder="Search"/>
<ul class="list nav">
{{#each props}}
<li>{{#link-to 'parcel' this}}<h3 class="name">{{addresses.0.street_address}}</h3>{{/link-to}}</li>
{{/each}}
</ul>
</script>
When the select transitions to the new route, both the old routes data and new routes are in the model, but if I reload the page, only the current routes data is loaded. Is there a way to clear the DS.RecordArray for props in the controller without a location.reload() call?
Related
I am creating universal grid for some entities. For that I added this in routes:
this.route('record', { path: '/record' },function() {
this.route('index', {path: '/:entity'});
this.route('view', {path: '/:entity/:record_id'});
});
and created new "index" route:
export default Ember.Route.extend({
entity: '',
queryParams: {
sort: {
refreshModel: true
}
},
beforeModel: function(transition) {
var entity = transition.params[this.get('routeName')].entity;
this.set('entity', entity);
},
model: function(params) {
delete params.entity;
return this.store.findQuery(this.get('entity'), params);
},
}
my controller
export default Ember.ArrayController.extend({
queryParams: ['sort'],
sort: ''
}
how can I set default value for the "sort" based on dynamic segment?
For example in my settings I store sort values for all entites:
'settings.user.sort': 'email ASC',
'settings.company.sort': 'name ASC',
I tried to define "sort" as computed property, but its "get" method is called in time when I can't get a value of dynamic segment from currentHadlerInfo or from route.
Also defining of the property "sort" as computed property has strange effect, for example, when I define it as
sort: 'email ASC'
in my template it is displayed via {{sort}} as expected (email ASC).
but when I return a value from computed property, I see empty value in my template and this affects on a work of components (I can't get current sorted column)
What can I do?..
Here is a rough implementation of setting the sort properties based on dynamic segment values. The code will look like
<script type="text/x-handlebars" data-template-name="index">
{{#link-to 'sort' model.settings.user.sort}}Sort{{/link-to}}
</script>
<script type="text/x-handlebars" data-template-name="sort">
<ul>
{{#each arrangedContent as |item|}}
<li>{{item.val}}</li>
{{/each}}
</ul>
</script>
var settings = Em.Object.create({
settings: {
user: {
sort: 'val ASC'
}
}
});
App.Router.map(function() {
this.route('sort', {path: '/:sortParams'});
});
App.SortRoute = Ember.Route.extend({
params: null,
model: function(params) {
this.set('params', params);
return [{val:1}, {val:5}, {val:0}];
},
setupController: function(controller, model) {
this._super(controller, model);
var props = this.get('params').sortParams.split(' ');
var property = [props[0]];
var order = props[1] === 'ASC'? true : false;
controller.set('sortProperties', property);
controller.set('sortAscending', order);
}
});
The working demo can be found here..
As you can see I access the params object in the model hook and store it on the route. In the setupController hook I access the params and set the required values on the controller.
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'));
}
}
Caveat: This is part of my first ember app.
I have an Ember.MutableArray on a controller. The corresponding view has an observer that attempts to rerender the template when the array changes. All the changes on the array (via user interaction) work fine. The template is just never updated. What am I doing wrong?
I'm using Ember 1.2.0 and Ember Data 1.0.0-beta.4+canary.7af6fcb0, though I guess the latter shouldn't matter for this.
Here is the code:
var ApplicationRoute = Ember.Route.extend({
renderTemplate: function() {
this._super();
var topicsController = this.controllerFor('topics');
var topicFilterController = this.controllerFor('topic_filter');
this.render('topics', {outlet: 'topics', controller: topicsController, into: 'application'});
this.render('topic_filter', {outlet: 'topic_filter', controller: topicFilterController, into: 'application'});
},
});
module.exports = ApplicationRoute;
var TopicFilterController = Ember.Controller.extend({
topicFilters: Ember.A([ ]),
areTopicFilters: function() {
console.log('topicFilters.length -> ' + this.topicFilters.length);
return this.topicFilters.length > 0;
}.property('topicFilters'),
getTopicFilters: function() {
console.log('getTopicFilters....');
return this.store.findByIds('topic', this.topicFilters);
}.property('topicFilters'),
actions: {
addTopicFilter: function(t) {
if(this.topicFilters.indexOf(parseInt(t)) == -1) {
this.topicFilters.pushObject(parseInt(t));
}
// this.topicFilters.add(parseInt(t));
console.log('topicFilters -> ' + JSON.stringify(this.topicFilters));
},
removeTopicFilter: function(t) {
this.topicFilters.removeObject(parseInt(t));
console.log('topicFilters -> ' + JSON.stringify(this.topicFilters));
}
}
});
module.exports = TopicFilterController;
var TopicFilterView = Ember.View.extend({
topicFiltersObserver: function() {
console.log('from view.... topicFilters has changed');
this.rerender();
}.observes('this.controller.topicFilters.[]')
});
module.exports = TopicFilterView;
// topic_filter.hbs
{{#if areTopicFilters}}
<strong>Topic filters:</strong>
{{#each getTopicFilters}}
<a {{bind-attr href='#'}} {{action 'removeTopicFilter' id}}>{{topic}}</a>
{{/each}}
{{/if}}
var TopicsController = Ember.ArrayController.extend({
needs: ['topicFilter'],
all_topics: function() {
return this.store.find('topic');
}.property('model', 'App.Topic.#each'),
actions: {
addTopicFilter: function(t) {
App.__container__.lookup('controller:topicFilter').send('addTopicFilter', t);
}
}
});
module.exports = TopicsController;
// topics.hbs
<ul class="list-group list-unstyled">
{{#each all_topics}}
<li class="clear list-group-item">
<span class="badge">{{entryCount}}</span>
<a {{bind-attr href="#"}} {{action 'addTopicFilter' id}}>{{topic}}</a>
</li>
{{/each}}
</ul>
your observes should just be controller.topicFilters.[]
And honestly this is a very inefficient way of doing this, rerendering your entire view because a single item changed on the array. If you show your template I can give you a much better way of handling this.
Here's an example, I've changed quite a few things, and guessed on some others since I don't know exactly how your app is.
http://emberjs.jsbin.com/uFIMekOJ/1/edit
I will try to bind model,controller and template in ember
Here is my js
App = Ember.Application.create({});
App.Person = Ember.Object.extend({
firstName: "r",
lastName: "issa"
});
App.TestingRoute = Ember.Route.extend({
model: function () {
return App.Person.create();
},
setupController: function (controller, model) {
controller.set("model", model);
}
});
App.TestingController = Ember.ObjectController.extend({
submitAction: function () {
alert("My model is :" + this.get("model"));
}
});
my template is :
<script type="text/x-handlebars" data-template-name="application">
{{render testing}}
</script>
<script type="text/x-handlebars" data-template-name="testing">
{{input valueBinding="model.firstName"}}
{{input valueBinding="model.lastName"}}
<button {{action submitAction target="controller"}} class="btn btn-success btn-lg">Pseudo Submit</button>
<p>{{model.firstName}} - {{model.lastName}}</p>
</script>
what s wrong why model not binding in template and alert retunn model is null
Your setupController and model methods from TestingRoute aren't being called. Because your controller is created by the render view helper, not by a transition.
For example using the following works:
template
<script type="text/x-handlebars" data-template-name="testing">
{{input valueBinding="model.firstName"}}
{{input valueBinding="model.lastName"}}
<button {{action submitAction target="controller"}} class="btn btn-success btn-lg">Pseudo Submit</button>
<p>{{model.firstName}} - {{model.lastName}}</p>
</script>
javascript
App = Ember.Application.create({});
App.Router.map(function() {
this.route('testing', { path: '/' })
});
App.Person = Ember.Object.extend({
firstName: "r",
lastName: "issa"
});
App.TestingRoute = Ember.Route.extend({
model: function () {
return App.Person.create();
},
setupController: function (controller, model) {
debugger
controller.set("model", model);
}
});
App.TestingController = Ember.ObjectController.extend({
actions: {
submitAction: function () {
alert("My model is :" + this.get("model"));
}
}
});
Here is the fiddle http://jsfiddle.net/marciojunior/8DaE9/
Also, the use of actions in controllers is deprecated in favor of using the actions: { ... } object
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.