I am attempting to create a basic CRUD setup for managing 'User' objects in Ember. I think I have my models and routes in order. I'm struggling with managing:
A) The proper controller setup for the (all) users page. I think that I should be creating an ArrayController, but it seems to work fine automatically. Does my Ember App know to make an array of individual 'user' objects? if so, how?
B) Passing data from InputFields. If you click 'Add User' in my example, I have made a form to create a user. When you click 'create', I'm not sure how to get the textField values, nor do I understand what to do with those values once I have them. How would I update my controller?
Again, here a jsbin of my code. Any guidance would be greatly appreciated.
Regarding A):
I assume you refer to the following route of your App. This model function returns an Array. Therefore Ember knows that it should use an ArrayController to render your UsersRoute.
App.UsersIndexRoute = Ember.Route.extend({
model: function() {
return App.User.find();
}
});
Regarding B): I have updated your code -> http://jsbin.com/ozilam/15/edit
I needed to update some of your names (controller and view) to match the naming conventions of Ember.
With Ember you do not have to use forms and manually read those values. Instead you create a new records, when you enter your Route:
App.UsersNewRoute = Ember.Route.extend({
setupController : function(controller){
controller.set("content", App.User.createRecord({}));
}
});
Inside your View you are binding on the properties of your record. As you see i also updated your button with an action helper.
<script type="text/x-handlebars" data-template-name="users/new">
<div class="row">
<div class="six columns">
<h3>New User Information</h3>
<form>
<label>First Name</label>
{{view Ember.TextField valueBinding='name_first'}}<br />
<label>Last Name</label>
{{view Ember.TextField valueBinding='name_last'}}<br />
<label>Email Address</label>
{{view Ember.TextField valueBinding='email_address'}}<br />
<label>SSN</label>
{{view Ember.TextField valueBinding='ssn'}}<br />
<button {{action create target="view"}} class="button">Create</button>
{{#linkTo users}}Cancel{{/linkTo}}
</form>
</div>
</div>
</script>
As those changes in the form get automatically propagated to your controller, you can just access the object in the controller:
App.UsersNewView = Ember.View.extend({
didInsertElement: function() {
this.$('input:first').focus();
},
create: function(){
console.log('view submitted');
var newUser = this.get("controller.content");
console.log(newUser.get("name_first"));
console.log(newUser.get("name_last"));
// HOW DO I PROCESS THIS FORM
}
});
Note: As i am writing this i am realizing that it would be probably better, if you would handle this event in your Controller instead of the View, since its an data modification task.
Related
I've been trying to get this button to take me to a route called 'manage'. But somehow the data I'm passing with the {{#link-to}} does not seem to work.
router.js
this.route('manage', {path: '/:stack_id/manage'});
main.hbs
<div class="row">
<div class="pull-right">
{{#link-to "manage" list}}
<button class="btn-gray"> Manage People {{fa-icon "coffee"}}</button>
{{/link-to}}
</div>
</div>
main.js where {path: '/:stack_id'}
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params) {
return this.store.find('list', params.stack_id);
},
I've tried replacing {{#link-to "manage" list}} with list.id and model.id, both of these brings the message that
"This link-to is in an inactive loading state because at least one of its parameters presently has a null/undefined value, or the provided route name is invalid.
You need to pass model.id to link-to helper but model can't be null. Make sure param is defined when you call store.find method and API returns correct response/record is found. You can use {{log model}} to check if model is defined in your template.
I've only just started using Ember and am struggling with a couple of concepts. I used Knockout for a previous application which was great, and I can already see how Ember will help me be more structured and write less code. Although I could go back to Knockout and get this up and running pretty quickly, I really want to persevere with Ember.
The bit I'm struggling with is the right way and place to load related records. My app has a list of systems, and a list of suppliers related to each system. Each system may have one or more suppliers, and vice-versa.
So far I have a route called Systems with a corresponding controller and template. The model hook on the route gets the list of systems from an API (I'm using Ember Data). The systems are displayed as a list via the template, and when the user selects one (via a radio button) I need to load and display the related suppliers. That's the bit I'm not sure about.
I'm not changing the URL here, just displaying an additional list of suppliers underneath the list of systems so adding a new route doesn't sound quite right. Conceptually what would be the best way to do this?
Should I use an action in the controller to load and display the data? That doesn't sound quite right either as the model hooks are all in the routes.
Should I just load the data all up front and filter it (and toggle display) using the controller?
Is there a right, or better, way?
Here's the current code.
System Model
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
suppliers: DS.hasMany('supplier')
});
Supplier Model
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
systems: DS.hasMany('system')
});
Systems Route
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.find('system');
}
});
Systems Controller
import Ember from 'ember';
export default Ember.ObjectController.extend({
systemSelected: false,
actions: {
selectSystem: function(id) {
this.set('systemSelected', true);
console.log(id);
},
selectSupplier: function(supplier) {
console.log(supplier);
}
}
});
Systems Template
<h1>systems</h1>
<form id="systems">
<fieldset>
<div class="form-group">
<label for="system" class="control-label">Select the system</label>
{{#each}}
<div class="radio">
<label>
<input type="radio" name="system" value="{{id}}" {{action 'selectSystem' id}}>
<span>{{name}}</span>
</label>
</div>
{{/each}}
</div>
{{#if systemSelected}}
<div class="form-group">
<label for="supplier" class="control-label">Select the supplier</label>
{{#each suppliers}}
<div class="radio">
<label>
<input type="radio" name="supplier" value="{{id}}" {{action 'selectSupplier' supplier}}>
<span>{{name}}</span>
</label>
</div>
{{/each}}
</div>
{{/if}}
</fieldset>
</form>
The way to handle this is pretty simple. You just load both related models in your route. You do that using RSVP.
First of all, we'll need a Suppliers Controller, but just a really basic one to store all the Suppliers:
App.SuppliersController = Ember.ArrayController.extend();
In your Systems Route, fetch the models like this and set them to the correct model properties:
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return Ember.RSVP.hash({
systems: store.find('system'),
suppliers: store.find('suppliers')
});
},
setupController(controller,models) {
this.controllerFor('systems').set('model',models.systems);
this.controllerFor('suppliers').set('model',models.suppliers);
}
});
Then in your Systems Controller, add the Suppliers Controller to the needs:
import Ember from 'ember';
export default Ember.ObjectController.extend({
needs:['systems'],
systemSelected: false,
actions: {
selectSystem: function(id) {
this.set('systemSelected', true);
console.log(id);
},
selectSupplier: function(supplier) {
console.log(supplier);
}
}
});
You might want to use an ArrayController instead of an ObjectController for Systems Controller since it stores more than one object.
Then in your Systems Template, you can access the Systems Controller as a property of your main controller, like this:
<h1>systems</h1>
<form id="systems">
<fieldset>
<div class="form-group">
<label for="system" class="control-label">Select the system</label>
{{#each system in controllers.systems}}
<div class="radio">
<label>
<input type="radio" name="system" value="{{id}}" {{action 'selectSystem' id}}>
<span>{{name}}</span>
</label>
</div>
{{/each}}
</div>
{{#if systemSelected}}
<div class="form-group">
<label for="supplier" class="control-label">Select the supplier</label>
{{#each suppliers}}
<div class="radio">
<label>
<input type="radio" name="supplier" value="{{id}}" {{action 'selectSupplier' supplier}}>
<span>{{name}}</span>
</label>
</div>
{{/each}}
</div>
{{/if}}
</fieldset>
</form>
There ya go. Hope that helps. There are other ways to do this, but this is the most Emberific way to do it that I know.
I am trying to find whether the checkbox is checked or not in a controller.
Here's my template:
<script type="text/x-handlebars">
{{view Ember.TextField valueBinding="firstname" placeholder="First Name"}}
<input type="checkbox" name="remember_me"> Remember me </input>
<button {{action save }}>Save</button>
</script>
Here's my controller:
App = Ember.Application.create();
App.ApplicationController = Ember.Controller.extend({
save: function(){
//need to get the value of "remember_me" here
alert(this.get("firstname"));
}
});
How can I get the value of "remember_me" (whether it's checked or not) in the controller. Can I do valueBinding on the check box. If so , can you please give me an example syntax.
jsfiddle:
http://jsfiddle.net/Rtd4d/
You should probably use the input helper that ember provides (see the docs).
{{input type="checkbox" checked=remember_me}}
To get the model that is set on a controller, use this.get('model').
So, to get the remember_me attribute from the model, it's simply
this.get('model').get('remember_me')
Assuming remember_me is a boolean attribute, this should return true or false.
See the jsbin.
EDIT
I didn't realize that by default the controller will delegate to it's model, so
this.get('remember_me')
should work.
My authentication system uses lightboxes, so that when a user clicks "Sign In" or "Sign Up", a lightbox pops up for them to input their credentials. The page they were on remains rendered behind the lightbox, and when they're done signing in, the lightbox disappears and the view returns to the way it was. I can get this to work when I deviate from the conventional Ember route flow by using a lot of Jquery, but I'd prefer to integrate this more tightly into the rest of my Ember app.
The problem is, the conventional Ember route flow expects views and templates to be handled in a particular way. Specifically, a route such as /sign-in will render the sign-in template within the application template, erasing whatever was there before. Since I want to preserve the view that was there before, this approach doesn't work.
Is there a way to tell an Ember view not to erase the current view, but instead to render an independent view such as a lightbox?
You can use named outlets and render a template into the outlet, in my aplication template I has an outlet called modal, and two actions in the ApplicationRoute, openModal and closeModal. The open one receives a template name and uses the route method render to set the outlet content, the close one renders an empty template.
App.ApplicationRoute = Ember.Route.extend({
actions: {
openModal: function(modal) {
this.render(modal, {into:'application', outlet: 'modal'});
},
closeModal: function() {
this.render('empty', {into: 'application', outlet: 'modal'});
},
}
});
Html handelbars
<script type="text/x-handlebars" data-template-name="application">
{{! Other application template code}}
<button {{action openModal 'hellow.modal'}}>Open it!</button>
{{outlet modal}}
</script>
<script type="text/x-handlebars" data-template-name="empty"></script>
<script type="text/x-handlebars" data-template-name="hellow/modal">
<div class="modal">
<div class="modal-header">
Hellow
</div>
<div class="modal-footer">
<button {{action closeModal}}>Close</button>
</div>
</div>
</script>
This is adapted from http://nerdyworm.com/blog/2013/04/20/ember-modal-example/
I'm trying to use the the currentView feature of an Ember.ContainerView in the context of a #each helper but it fails when currentView property is changed to another view.
My aim here is to allow editing an item of a list, by changing the regular view to an edit view when the user click a link.
Main template:
<ul>
{{#each itemController="person"}}
<li>{{view Ember.ContainerView currentViewBinding="cv"}}</li>
{{/each}}
</ul>
Template 'name' used to display a person :
{{firstName}} {{lastName}} <a {{action edit}}>edit</a>
Controller for the currentViewBinding property ('cv') and handling for the edit action.
App.PersonController = Ember.ObjectController.extend({
cv: Ember.View.extend({
templateName: 'name'
}),
edit: function() {
this.set('cv', Ember.View.extend({
templateName: 'nameEdit'
}));
}
})
'nameEdit' template corresponding to the view that needs to be displayed to edit the person object.
{{input type='text' value=firstName}} {{input type='text' value=lastName}}
The api guide says that:
When the currentView property is set to a view instance, it will be added to the ContainerView. If the currentView property is later changed to a different view, the new view will replace the old view.
But it's worse if I replace the cv property with a view instance (by using create() instead of extend()) as a re-render error is yield. See this question of mine.
Here is the jsFiddle to play with http://jsfiddle.net/fblanvil/tD3Ph/3/
I ended up not using ContainerView at all and using a simple if. But it doesn't explain why it's not possible to use a ContainerView this way in an #each helper. If someone thinks it's worth a Jira, put a comment and I'll do it.
<ul>
{{#each itemController="person"}}
<li>
{{#if editing}}
{{view templateName='nameEdit'}}
{{else}}
{{view templateName='name'}}
{{/if}}
</li>
{{/each}}
</ul>
Simple and effective after all...
App.PersonController = Ember.ObjectController.extend({
editing: false,
edit: function() {
this.set('editing', true);
}
})