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
});
});
});
}
}
});
Related
I have some actions that take some time and I wan't to indicate that to the user by showing a loading spinner. I know realized that sometimes the longer running action is directly triggered before the loading spinner is shown:
this.set('betRound.isLoading', true);
var _this = this;
Ember.run.sync();
Ember.run.later(function(){
_this.transitionToRoute('betround.edit', _this.get('betRound.content'));
}, 50);
I tried to achieve this by delaying the transition with 50ms, but on some slower mobile devices, the loading spinner that depends on "isLoading" is not shown.
I would go with some kind of callback mechanism, where you'd put your loading spinner in a view or component and notify the controller when DOM is ready. Here's an example of a Mixin you can attach to any view or component to make it send a notice after it loads.
App.LoadAware = Ember.Mixin.create({
didInsertElement: function () {
this._super();
var target = this.get("loadedNoticeTarget");
Ember.assert("We must have loadedNoticeTarget in LoadAware component or view", target);
target.send("loadAwareLoaded");
}
});
You would then apply it like this:
App.LoadingSpinnerComponent = Ember.Component.extend(App.LoadAware);
In your template:
{{#if isLoading}}
{{loading-spinner loadedNoticeTarget=this}}
{{/if}}
And then, in your controller:
App.IndexController = Ember.Controller.extend({
actions: {
goToSlowRoute: function () {
this.set("_waitingForSpinner", true);
this.set("isLoading", true);
},
loadAwareLoaded: function () {
Ember.assert("We must be waiting on spinner at this point", this.get("_waitingForSpinner"));
this.set("_waitingForSpinner", false);
this.transitionToRoute("slowRoute");
}
},
isLoading: false
});
In this example, you would initiate transition by sending goToSlowRoute message to the controller. Full JSBin here.
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')
//...
});
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'm evaluating Emberjs in the context of whether we could port some of our code to it but maintain the same api so my first day really looking at it.
I'm using the Tom Dale tutorial but WITHOUT ember-data. I think I have kinda figured out how to get data into the app (thx #kingping2k). I just need to get this to save / update.
I have a doneEditing action which gets called when I click on it as seen by console but how do I get a reference to the model. Looking at the controller docs (http://emberjs.com/api/classes/Ember.Controller.html), I don't see a really obvious property like model or something. How would I tell the PostController to save the post that it is getting back in its route? Also, do people normally use jQuery promises to do something else after the save has completed here(I'm assuming yes)?
I've included the relevant code with the doneEditing action at the bottom where I'm looking for help:
thx for any help
Model:
Hex.Post = Ember.Object.extend({
id: null,
body: null,
isEnabled: null,
createdAt: null,
save: function(data){
console.log("you want to save this item");
$.post( "api/post", data, function( data ) {
// something here
});
}
});
View:
<script type="text/x-handlebars" id="post">
{{#if isEditing}}
{{partial 'post/edit'}}
<button {{action 'doneEditing'}}>Done</button>
{{else}}
<button {{action 'edit'}}>Edit</button>
{{/if}}
<h1>{{id}}</h1>
{{outlet}}
</script>
Route:
Hex.PostRoute = Ember.Route.extend({
model: function(params) {
console.log('called with: ' + params.post_id);
return Hex.Post.findById(params.post_id);
}
});
Controller:
Hex.PostController = Ember.ObjectController.extend({
isEditing: false,
actions:{
edit: function() {
this.set('isEditing', true);
},
doneEditing: function() {
this.set('isEditing', false);
console.log("this gets called");
//this.get('content').save();
//this.save();
//console.log("here: " + this.model.id);
//this.model.save(); //doesn't work ???
// this.post.save(); //doesn't work ???
//this.get('store').commit(); // prob no
}
}
});
when you return a model from the model hook it's then passed to the setupController in the route. The default implementation of setupController does this, controller.set('model', model)
setupController:function(controller, model){
controller.set('model', model');
}
so to get the model within the context of the controller just get that property.
var model = this.get('model')
I would return the promise, then you can trigger something on save/failure etc
save: function(){
console.log("you want to save this item");
return Ember.$.post( "api/post", JSON.stringify(this));
}
doneEditing: function() {
this.set('isEditing', false);
var model = this.get('model');
model.save().then(function(){alert('saved');},
function(){alert('failure');});
}
And generally you'll put save in the reopen
Hex.Post.reopen({
save: function(){
console.log("you want to save this item");
return Ember.$.post( "api/post", JSON.stringify(this));
}
});
hi i have the following route:
MB3.PlaylistRoute = Ember.Route.extend({
model: function(params) {
return MB3.Playlist.find(params.playlist_id);
}
});
The playlist has a hasMany realtion with tracks. in the playlist view i want do do some logic with an attribute of the first track of the playlist.
so i added this code:
MB3.PlaylistView = Ember.View.extend({
didInsertElement: function() {
console.log(this.get("controller.tracks").objectAt(0).get("title"));
}
});
The problem is title is undefined (i think because it is not yet loaded. the second thing i tried is waiting for the didLoad event:
MB3.PlaylistView = Ember.View.extend({
didInsertElement: function() {
var self=this;
this.get("controller.tracks").on("didLoad", function() {
console.log(self.get("controller.tracks").objectAt(0).get("title"));
});
}
});
but this logges null as well. How do i accomplish that?
Like Adrien said in the comments, it seems you are running into issue 587. That said, I don't think you actually need the "didLoad" callback in this case. Instead, try using a computed property to get the video_id or track title. For example:
MB3.PlaylistView = Ember.View.extend({
firstTrackTitle: function() {
return this.get('controller.tracks.firstObject.title');
}.property('controller.tracks.firstObject.title')
});
Then in your template, embed the player if this property is defined:
{{#if view.firstTrackTitle}}
embed code here
{{/if}}
FWIW I would put this logic in controller instead of view, but same idea.