The app I am working on has an Event-page where users see Events from themselves and friends as well as being able to use an inline event-creator (to create events, on that very same page/route).
To be a bit more precise, the events get all loaded and displayed in a newsfeed style, which works perfectly fine but the problem now is when trying to save a new event-model. I think some code will make this easier to understand.
The routes:
this.resource('newsfeed', function() {
this.route('personal');
this.route('whatever');
});
then in NewsfeedIndexRoute the app has
model: function() {
return App.Event.find();
}
for displaying all Events with an ArrayController at /newsfeed. That works fine.
Furthermroe the app has a NewsfeedRoute and Controller as well so the event-creator is accessible on all sub-routes and for saving an Event we have the following code:
App.NewsfeedRoute = Ember.Route.extend({
setupController: function(controller){
controller.newRecord();
}
});
App.NewsfeedController = Em.ObjectController.extend({
newRecord: function() {
//The following line works but makes the API 'dirty' and an extra model needs to be created
this.set('content', App.Newsfeed.createRecord({title: "new title"}));
//The next line would be preferred, but overrides the displayed models at /newsfeed
//this.set('content', App.Event.createRecord({title: "new title"}));
},
save: function() {
this.get('model').save();
}
});
So the problem now is, when I go to /newsfeed and use the line this.set('content', App.Event.createRecord({title: "new title"})); it overrides everything that gets displayed in the newsfeed/index.hbs template with that one model (so just displaying 'new title'). And when you type in more into the even-creator that gets displayed as well. This is obviously not the behaviour we want. Ideally it should just be separated somehow, then get saved to the Server.
The other line you can see with the Newsfeed model is a work-around and it works fine, but as mentioned in the comment it feels really like a hack and also makes the API kinda dirty, because using the /events endpoint with a POST request would be much more RESTful.
So does anyone have any idea, if there is any way to achieve that right now with ember-data?
There are many ways to accomplish this in ember. Seems like you are pretty close to a good solution but what's missing in this case is an EventController. It should look a lot like what you'd had in App.NewsfeedController.
App.EventController = Em.ObjectController.extend({
newRecord: function() {
this.set('content', App.Event.createRecord({title: "new title"}));
},
save: function() {
this.get('model').save();
}
});
Now in your template, use the {{render}} helper to add the
{{render event}}
And define a event.hbs template.
Related
I am just starting with ember and trying to do a simple test.
Which, also very simple, got me stuck for some reason and I cant find the answer anywhere.
So I need load data from the server without transition to another route and do it from within a submit action (or any other action for that matter).
I have a simple input form where I type in manually an object ID and
I want it to be loaded say right underneath. Simple enough. Seams to be a three minutes job in angular. Here, I just cant get the hang of communication between route and controller.
So given this little emblem
form submit="submit"
= input type="text" value=oid
button type="submit" Submit
#display
= person
And this route
import Ember from 'ember';
export default Ember.Route.extend({
model: {
person: null
},
actions: {
submit: function() {
var oid = this.controllerFor('application').get('oid');
var person = this.store.find('person', oid);
this.modelFor('application').set('person', person);
}
}
});
This is as far as I could think. I want to click submit with ID of an object and I want that object loaded and displayed in the div#display.
So what am I doing wrong? What is the right way to do it?
First, I don't even know where to put such an action? Controller or route?
If I put it in controller, I don't know how to refresh the model. If I put it in route, I am stuck with the above. Would be also nice to see how to do it if action was placed in the controller.
For simplicity I just do it all in application route, template, controller ...
Thank you
The best place to put your code is on Controller given it responds to UI, so doing that on your controller the code is much more simple.
On this jsfiddle I have put some dummy code which tries to do something what you want to achieve.
//Index Route
App.IndexRoute = Ember.Route.extend({
model: function () {
return ['red', 'yellow', 'blue'];
}
});
//Here my dummy controller.
App.IndexController = Ember.Controller.extend({
oid: 1,
actions: {
submitAction() {
//Here your logic to find record given the input and attach
//the response to the model object as UI is binding to model
//if you add/remove new records they will show up.
//On this example I have added a new object.
this.get('model').addObject('green');
}
}
})
Enjoy!
I'm getting some curious behaviour that I can't figure out the reason for.
This is my router:
App.Router.map(function() {
this.resource('mapPieceSets', { path: '/map-pieces' }, function () {
this.resource('mapPieceSet', { path: '/:mapPieceSet_id' }, function () {
this.resource('mapPiece', { path: '/:mapPiece_id' });
});
});
});
I reload the app from the home page #/ then navigate down to the mapPiece route, I get these URLs requested:
[Domain]/api/mapPieceSets/
[Domain]/api/mapPieces/1/
[Domain]/api/mapPieces/2/
And it all works fine (mapPieceSets returns a list of mapPieceSet which have a list of mapPiece against them)
However, if I reload the whilst on a mapPiece routed page, then I get this URL:
[Domain]/api/mapPieceSets/
[Domain]/api/mapPieceSets/?mapPieceSet_id=1
[Domain]/api/mapPieces/?mapPiece_id=1
So switching from /:id= to ?id=, which isn't working on my end points (that's a side issue which I need to resolve), but just wondering why the URLs changed what they're requesting, and why we get a request to mapPieceSets/?mapPieceSet_id=1 when the whole of that object is returned within the response from mapPieceSets/
(If you need any other snippets of code from my app, let me know and I can put them in)
This is a fairly common confusion. When you're in your app navigating around you're often using a link-to which is then telling ember to use the specified model when visiting the route. When you're refreshing the page, Ember has to divine the models using the url /apimapPieceSets/3/2. At that point it will go to each route MapPieceSetsRoute, MapPieceSetRoute, and MapPieceRoute and hit each model hook passing in any associated params. So what you need to tell Ember how to do, is how to load a mapPieceSet, and mapPiece properly. You'll need to setup a model hook for both of those.
App.MapPieceSetsRoute = Em.Route.extend({
// I don't know if you're using Ember Data, but I'm going to assume you are
model: function(params){
return this.store.find('mapPieceSet', params.mapPieceSet_id);
}
});
From what you said, it sounds like the model is already available client side from the mapPieceSets. In that case, you can use the modelFor method to get a parent's route's model and get your model.
App.MapPieceSetsRoute = Em.Route.extend({
// I don't know if you're using Ember Data, but I'm going to assume you are
model: function(params){
return this.modelFor('mapPieceSets').get('properyWithMapPieces').findBy('id', params.mapPieceSet_id);
}
});
Ember - v1.7.0
Ember Data - v1.0.0-beta.10
I created a modal component using zurb foundation 5 CSS framework reveal features, though all works well, am unable to save data captured from the form in controller save action.
Controller which handles on save button execution
App.PersonModalController = Ember.ObjectController.extend({
actions: {
close: function() {
return this.send( 'closeModal' );
},
save:function() {
this.get('model').save();
}
}
});
The issue am facing is that the this.get('model').save() is not working and data is not been posted to restful backend.
Am not sure exactly how to go about storing the data captured from the form, when I console.log( this.get('model') ); it appears to be a proper model object with all the bells and whistles.
I tried obtaining the store to add model to it but that doesn't work too.
A. Addendum
After searching around I came across a number of Stack Overflow questions relating to this.get('model').save() it appears it doesn't quite work as expect, perhaps based on context.
difference-between-model-save-versus-model-getstore-commit
ember-js-how-to-save-a-model
save-record-of-model-is-not-working-in-ember-data-1-0-0-beta-3
When I change code to the following:
App.PersonModalController = Ember.ObjectController.extend({
actions: {
close: function() {
return this.send( 'closeModal' );
},
save:function() {
var person = this.store.createRecord('person',{firstName:firstName,lastName:lastName});
person.save();
}
}
});
It POSTs data correctly to back-end and saves, I however believe there must be a better way, cause if you have a form with say 50 fields, you won't want to manually set each attribute.
After careful inspection, though posting occurs, the data posted is empty.
I would try to continue your save method as in Bart's answer to the ember-js-how-to-save-a-model question.
person.save().then(function() {
// SUCCESS
}, function() {
// FAILURE
});
and in those methods I would console.log() the results.
I would imagine it has something to do with the promise aspect of the save functionality.
I have simple Ember app with Ember Data (powered by Rails). I have created these routes:
App.Router.map(function () {
this.resource('bands', function () {
this.route('new');
this.resource('band', {path: ':band_id'}, function() {
this.route('edit');
});
});
});
Then, when I go to http://localhost:3000/#/bands/23/edit, I'd like to see the values of the object with ID 23, like this:
<div>name: {{name}}</div>
{{! should output "name: Pavel" }}
However, when I put that code into band/edit.hbs, the application displays only "name: ", without any value. I have figured out it's because the content is not passed to the inner view. When I put the same piece of code directly to band.hbs, everything works as expected. Where am I doing a mistake? How can I pass the content object to the controller? (If possible, I'd like to have generated as much controllers as I can).
You will have to at least define the the route for BandEdit:
App.BandEditRoute = Ember.Route.extend({
model: function(params) {
return this.modelFor('band'));
}
});
If you need to you could always add the model to a transaction before returning it.
You could also do this by referencing the parentController from within the BandEdit Controller, but that seems like it would be a less elegant way to solve this.
It's because the context has been changed by your inner view (the context will now be the view itself), but the view will have access to the BandEditController and its associated model, so if you always reference the model first then Ember will know what you're after: {{model.name}}/{{model.genre}}.
See JSFiddle: http://jsfiddle.net/JZ84L/
(Use in conjunction with Andre's answer, because if you're not even passing through the model to your outlet then it's unusable.)
I can't figure out the correct way to handle modal states/views with the new Ember router. More generally, how do you handle states that you can enter and exit without affecting the "main" state (the URL)?
For example, a "New Message" button that is always available regardless of the current leaf state. Clicking "New Message" should open the new message modal over the current view, without affecting the URL.
Currently, I'm using an approach like this:
Routes:
App.Router.map(function() {
this.route('inbox');
this.route('archive');
});
App.IndexRoute = Em.Route.extend({
...
events: {
newMessage: function() {
this.render('new_message', { into: 'application', outlet: 'modalView' });
},
// Clicking 'Save' or 'Cancel' in the new message modal triggers this event to remove the view:
hideModal: function() {
// BAD - using private API
this.router._lookupActiveView('application').disconnectOutlet('modalView');
}
}
});
App.InboxRoute = Em.Route.extend({
...
renderTemplate: function(controller, model) {
// BAD - need to specify the application template, instead of using default implementation
this.render('inbox', { into: 'application' });
}
});
App.ArchiveRoute = ... // basically the same as InboxRoute
application.handlebars:
<button {{action newMessage}}>New Message</button>
{{outlet}}
{{outlet modalView}}
I've obviously left out some code for brevity.
This approach 'works' but has the two problems identified above:
I'm using a private API to remove the modal view in the hideModal event handler.
I need to specify the application template in all of my subroutes, because if I don't, the default implementation of renderTemplate will attempt to render into the modal's template instead of into application if you open the modal, close it, and then navigate between the inbox and archive states (because the modal's template has become the lastRenderedTemplate for the IndexRoute).
Obviously, neither of these problems are dealbreakers but it would be nice to know if there is a better approach that I'm missing or if this is just a gap in the current router API.
We do kind of the same thing but without accessing the private API.
I don't know if our solution is a best practice, but it works.
In the events of our RootRoute I have an event (same as your newMessage), where we create the view we need to render, and then append it.
events: {
showNewSomething: function(){
var newSomethingView = app.NewSomethingView.create({
controller: this.controllerFor('newSomething')
});
newSomethingView.append();
}
}
This appends the modal view into our app.
On cancel or save in the newSomethingView we call this.remove() to destroy the view and removing it from the app again.
Again, this doesn't feel like a best practice, but it works. Feel free to comment on this if someone have a better solution.
Don't know if you are using the Bootstrap Modal script or which one, but if you are, this question has a proposed solution. Haven't figured out all the pieces myself yet, but is looking for a similar type of solution myself to be able to use Colorbox in an "Ember best practices"-compliant way.