I've got a working "launch modal from an event" using the ApplicationRoute but I'd like to track the modal changes in the url if possible.
App.ApplicationRoute = Ember.Route.extend({
actions: {
modal: function() {
view = Ember.View.create({
templateName: "modal",
controller: this.controller,
content: []
}).appendTo('body');
}
}
});
If I change the url, how can I trigger a modal to show w/ a given context (using the url params to build it)?
Depending on how centralized you want this behavior to be, you could add a path observer to the application controller:
App.ApplicationController = Ember.Controller.extend({
//...
currentPathDidChange: function () {
switch(this.get('currentPath')){
case 'foo-route.index':
//trigger modal change
break;
}
}.observes('currentPath')
//...
});
Related
Given a application route like this:
App.ApplicationRoute = Ember.Route.extend({
model: function() {
return new Ember.RSVP.Promise(function(resolve) {
setTimeout(function() {
resolve();
}, 3000);
});
}
});
How do I show a loading template while this model hook is waiting?
I tried something like this:
<script type="text/x-handlebars" id="loading">
<h3>Loading...</h3>
</script>
But this only displays when a sub route of the application is loading. How do I show a loading template when the application itself is still loading?
Thank you.
You could make use of some sort of loading overlay (which could be some static html/css) and the afterModel route hook:
App.ApplicationRoute = Ember.Route.extend({
model: function() {
return new Ember.RSVP.Promise(function(resolve) {
setTimeout(resolve, 3000);
});
},
afterModel: function (model, transition) {
$('.loading-overlay').fadeOut();
}
});
You would have to determine the best place to put your overlay, but this should work.
Working example: http://jsbin.com/rixukune/3
Take a look at this hook's details here in the docs: http://emberjs.com/api/classes/Ember.Route.html#method_afterModel
I want to set some state variable which can be used to add a css class to my header template only when I am in the admin section of my app (i.e. any url beginning with /admin). I have the following routes:
this.resource('admin', { path: '/admin' }, function() {
this.route('dashboard', { path: '/' });
// more admin routes...
}
this.route('user')
// more routes...
Here is the setupController in my ApplicationRoute:
App.ApplicationRoute = App.Route.extend({
setupController: function(){
this.controllerFor('header').set('isInAdmin', false);
}
}
And the same in AdminRoute:
App.AdminRoute = App.Route.extend({
setupController: function(){
this.controllerFor('header').set('isInAdmin', true);
}
});
This works fine but once I have navigated into an admin route, and navigate back to a non-admin route, I'm not sure how to reset the isInAdmin variable.
The application controller have a computed property called currentPath. That property is equal the current path, for example, transitioning to admin/dashboard will return admin.dashboard in currentPath, going to /foo return foo etc.
So you can use the following code to know when a admin route is entered:
App.ApplicationController = Ember.Controller.extend({
isInAdmin: function() {
var currentPath = this.get('currentPath');
// if the first hierarchy is admin, so is a admin route
return currentPath.split('.')[0] === 'admin';
}.property('currentPath')
});
And in your application template isInAdmin will be avaliable:
<script type="text/x-handlebars" data-template-name="application">
...
<div {{bind-attr class=isInAdmin}}>
...
</div>
...
</script>
Give a look in that fiddle to see this in action http://jsfiddle.net/marciojunior/j5PRe/
I just read about the deactivate hook on Ember.Route:
http://emberjs.com/api/classes/Ember.Route.html#method_deactivate
This hook is executed when the router completely exits this route. It
is not executed when the model for the route changes.
So I added:
App.AdminRoute = App.Route.extend({
deactivate: function(){
this.controllerFor('header').set('isInAdmin', false);
}
});
which also works, however Márcio's answer is probably a little neater
I have defined an application view that handles click events. When someone clicks outside of a view, the application click function fires and sets all child views 'active' property to false. I am selecting specific views by assigning 'viewName'. This works great for a couple of my views, but I have hit a snag with a view that is instantiated after the page renders (it is in a conditional that can become true in the template). I would expect the applicationView to pick this up, but it doesn't. How can I get a list of all of the views instantiated in the app (preferably without using the internal API)?
App.ApplicationView = Ember.View.extend({
templateName: 'application',
click: function () {
this.get('childViews').filterProperty('viewName','applicationViewClickChild').setEach('active', false);
}
});
App.PersonView = Ember.View.extend({
templateName: 'views/person',
active: false,
viewName: 'applicationViewClickChild',
click: function () {
if (this.get('active')) {
this.set('active', false);
} else {
this.set('active', true);
}
return false;
}
});
Get list of all instantiated views from application view in ember
You can get to a list of instantiated views via the Global views hash Ember.View.views. For example:
App.ApplicationView = Ember.View.extend({
templateName: 'application',
click: function () {
var keys = Em.keys(Ember.View.views);
var views = keys.map( function(key) {
return Ember.View.views[key]
});
views.filterProperty('viewName','applicationViewClickChild').setEach('active', false);
}
});
I am trying to implement router events and using the send function of the router to trigger the events on the router. But couldn't get any documentation on this.
What I am trying to implement is that I am raising an event from controller/view to get the data from the server. And the events asynchronously fetches the data from the server and when the data has been successfully fetched I wanted to initialize the child view of the view from where I called the event i.e. I need to know when the data has been fetched. But I don't think the events on router returns anything such that I can know when the call has been over.
Something like:
View:
Em.View.extend({
click: function(){
var recordsPromise = this.get('controller.target').send('getRecords');
recordsPromise.done(this.initiateProtocol);
},
showChild: false,
initiateProtocol: function(){
//this showChild is used as a condition in the template to show/hide
// the childview. And is being set after the call completed
// successfully
//here childOneView is present some other js file and is fetched using requirejs
require(['childOneView'], $.proxy(function(childOneView){
this.set('childOne', childOneView.extend({
templateName: 'childOne'
});
this.set('showChild', true);
}, this));
}
}
Router
Em.Route.extend({
events: {
getRecords: function(){
//make an ajax call to fetch the records and return the ajax as a
// promise
}
}
});
Template
{{#if view.showChild}}
{{view view.childOne}}
{{/if}}
I think the idiomatic Ember approach would be a bit different. Send the action up to the controller and let it bubble to the route, and then set properties that your view will respond to via bindings:
View
App.RecordsView = Em.View.extend(Ember.ViewTargetActionSupport, {
click: function(){
this.triggerAction({action: 'getRecords'})
}
});
Controller
App.RecordsController = Em.ArrayController.extend({
content: [],
isLoaded: false
});
Template
<!-- records.handlebars -->
{{#if isLoaded}}
render stuff here... perhaps {{#each this}}{{someRecordProperty}}{{/each}}
{{/if}}
Router
App.RecordsRoute = Em.Route.extend({
events: {
getRecords: function(){
var controller = this.controllerFor('records');
$.ajax(...).then(function(data){
Em.run(function(){
controller.setProperties({
content: data.foo,
isLoaded: true
});
});
});
}
}
});
I got problem with initialization of application.
I create jsfiddle which simply works on my desktop but not on jsfiddle.
http://jsfiddle.net/zDSnm/
I hope you will catch the idea.
On the beginining od my aplication I have to get some values from rest and values to Ember.Select.
Depends on what is choosen all my connectOutlets functions use this value.
Here I get some data from REST
$.ajax({
url: 'https://api.github.com/repos/emberjs/ember.js/contributors',
dataType: 'jsonp',
context: this,
success: function(response){
[{login: 'a'},{login: 'b'}].forEach(function(c){
this.allContributors.addObject(App.Contributor.create(c))
},this);
}
})
and put it to my Select View:
{{view Ember.Select
contentBinding="App.Contributor.allContributors"
selectionBinding="App.Contributor.selectedContributor"
optionLabelPath="content.login"
optionValuePath="content.id" }}
{{outlet}}
And in every of my route I need to use this value, which is selected in this selection box
index : Ember.Route.extend({
route: '/',
connectOutlets: function(router){
router.get('applicationController').connectOutlet('oneContributor',App.Contributor.selectedContributor);
}
})
I'd also add observer to this selectedContributor value which calls connectOutlets of currentState (I know I shouldn't do this but I don't know why and how should I do this in properly way)
App.Contributor.reopenClass({
//...
refresh : function(){
App.router.currentState.connectOutlets(App.router);
}.observes('selectedContributor'),
//...
I hope there is some good way to solve such problem.
If there is something not clear please leave comment.
If I understand correctly you want to show the currently selected contributor. One way to do it is to listen for a change in the selected contributor and send a transitionTo action to the Router.
First the router:
index : Ember.Route.extend({
route: '/',
showContibutor: Ember.Route.transitionTo('show'),
showNoneSelected: Ember.Route.transitionTo('noneSelected'),
connectOutlets: function(router){
router.applicationController.connectOutlet({ name: 'contributors', context: App.Contributor.find() });
},
// if no contributor is selected, the router navigates here
// for example, when a default option "Make a selection" is selected.
noneSelected: Ember.Route.extend({
route: '/'
}),
show: Ember.Route.extend({
route: '/:contributor_id'
connectOutlets: function(router, context){
router.applicationController.connectOutlet({name: 'contributor', context: context})
},
exit: function(router) {
// This will remove the App.ContributorView from the template.
router.applicationController.disconnectOutlet('view');
}
})
})
with a template for App.ContributorsView:
{{view Ember.Select
contentBinding="controller"
selectionBinding="controller.selectedContributor"
optionLabelPath="content.login"
optionValuePath="content.id"}}
{{outlet}}
and an ArrayController to manage contributors:
App.ContributorsController = Ember.ArrayController.extend({
onSelectedContributorChange: function() {
var selectedContributor = this.get('selectedContributor');
if (selectedContributor) {
App.router.send('showContributor', selectedContributor);
} else {
App.router.send('showNoneSelected');
}
}.observes('selectedContributor')
});
The user a selects a contributor and the contributorsController tells the router to show the view for the contributor (i.e, show the view App.ContributorView with context selectedContributor).
App.ContributorView = Ember.View.extend({
templateName: 'contributor'
});
controller for selected contributor:
App.ContributorController = Ember.ObjectController.extend({
// define events and data manipulation methods
// specific to the currently selected contributor.
});
Hope this helps.
UPDATE: If you need to show the first record by default, the noneSelected route should look like this:
noneSelected: Ember.Route.extend({
route: '/',
connectOutlets: function(router){
var context = router.contributorsController.get('content.firstRecord');
router.applicationController.connectOutlet({name: 'contributor', context: context})
}
})
And define firstRecord in ContributorsController:
App.ContributorsController = Ember.ArrayController.extend({
firstRecord: function() {
return this.get('content') && this.get('content').objectAt(0)
}.property('content.[]')
});
Haven't tested it, but it should work.