Ember js multi text box form - ember.js

I have started to look at ember js but I am having problems getting my head around the view - template part, if I want one text box to fire a create event this is simple enough I use the insertNewline function on the Ember.TextField view, however most web application require a form to be filled out and submitted when a button is pressed, I can't seen to get the a view working that has multiple text input boxes on it.
I have followed the example on git hub https://github.com/mikecrittenden/tangletube however he seems to be referencing DOM elements directly from the view rather than Binding to properties of the view.
Does anyone have an example of an ember project that uses multi text field forms.
On a side note: There seems to be no standard structure to developing ember applications, every example I have looked at does things completely differently.

Here's a very simple example showing two ways of using multiple text fields in a view: http://jsfiddle.net/tomwhatmore/HEaGm/
The first one binds the textfields to their view using viewName, which lets the view access each of them using this.get('whateverYouPutAsViewName').
The second binds the values of the textfields directly to an Ember object, by using valueBinding. Any changes you make to the fields will automatically update the object.
Both have a button which triggers a simple action which use the values to show how they are accessed in the view. Hopefully the code is pretty self-explanatory.
HTML:
<script type="text/x-handlebars">
{{#view App.HelloView}}
{{view Ember.TextField viewName="firstName" placeholder="first name"}}
{{view Ember.TextField viewName="lastName" placeholder="last name"}}
<button {{action "hello"}}>Say Hello</button>
{{/view}}
{{#view App.BoundView}}
{{#with person}}
{{view Ember.TextField valueBinding="firstName"}}
{{view Ember.TextField valueBinding="lastName"}}
{{/with}}
<button {{action "hello"}}>Say Hello</button>
{{/view}}
</script>
JS:
App = Ember.Application.create()
App.HelloView = Ember.View.extend({
hello: function() {
alert("Hello " + this.get('firstName').get('value') + " " + this.get('lastName').get('value'));
}
});
App.Person = Ember.Object.create({
'firstName': 'John',
'lastName': 'Doe',
fullName: function() {
return this.get('firstName') + ' ' + this.get('lastName');
}.property('firstName', 'lastName')
});
App.BoundView = Ember.View.extend({
personBinding: 'App.Person',
hello: function() {
alert("hello " + this.get('person').get('fullName'));
}
})

The simple bindings example app on the emberjs site (http://emberjs.com/examples/) has multiple text boxes.
You may also want to checkout the sproutcore 2 (ember's previous name) demo app contest. http://blog.sproutcore.com/announcing-the-winners-of-the-demo-apps-contest/.
Generally speaking, to convert sproutcore 2 to emberjs, just change the SC namespace to Ember.

Related

can you run an ember.js app inside an ember.js app

We have a theoretical need to run an ember widget inside of another ember widget. Is this possible to do? Would scoping the nested ember widget to a different dom element than its parent work?
The idea is that we might need to embed our ember widgets on customer websites that also run ember.
Thanks!
I'm not sure to understand you correctly but If your need is to have separate components that you can distribute and reuse across application, you sure can have them. Although I'm not sure I got you right as you seem to speak of 2 different problems (nesting widgets and distributing ember widgets as third parties for other ember applications...)
Here is a fiddle on how to make an external component and reuse it if you want more details. Let me know if this helps.
The component :
var GravatarImageComponent = Ember.Component.extend({
size: 200,
email: '',
gravatarUrl: function () {
var email = this.get('email'),
size = this.get('size');
return 'http://www.gravatar.com/avatar/' + CryptoJS.MD5(email) + '?s=' + size;
}.property('email', 'size')
});
Ember.Application.initializer({
name: "gravatar-image-component",
initialize: function(container, application) {
container.register('component:gravatar-image', GravatarImageComponent);
}
});
The HTML to bring it to life :
<script type="text/x-handlebars">
<ul class="example-gravatar">
<li>{{gravatar-image email="tomster#emberjs.com" size="200"}}</li>
<li>{{gravatar-image size="200"}}</li>
</ul>
</script>
<script type="text/x-handlebars" id="components/gravatar-image">
<img {{bind-attr src=gravatarUrl}}>
<div class="email-input">
{{input type="email" value=email placeholder="tomster#emberjs.com for instance"}}
</div>
</script>

getting back reference to a specific model using Ember's Array Controller

I'm new to Ember and am finding some of their concepts a bit opaque. I have a app that manages inventory for a company. There is a screen that lists the entirety of their inventory and allows them to edit each inventory item. The text fields are disabled by default and I want to have an 'edit item' button that will set disabled / true to disabled / false. I have created the following which renders out correctly:
Inv.InventoryitemsRoute = Ember.Route.extend({
model: function(params) {
return Ember.$.getJSON("/arc/v1/api/inventory_items/" + params.location_id);
}
<script type="text/x-handlebars" data-template-name="inventoryitems">
{{#each}}
<div class='row'>
<p>{{input type="text" value=header disabled="true"}}</p>
<p>{{input type="text" value=detail disabled="true"}}</p>
<button {{action "editInventoryItem" data-id=id}}>edit item</button>
<button {{action "saveInventoryItem" data-id=id}}>save item</button>
</div>
{{/each}}
</script>
So this renders in the UI fine but I am not sure how to access the specific model to change the text input from disabled/true to disabled/false. If I were just doing this as normal jQuery, I would add the id value of that specific model and place an id in the text input so that I could set the textfield. Based upon reading through docs, it seems like I would want a controller - would I want an ArrayController for this model instance or could Ember figure that out on its own?
I'm thinking I want to do something like the following but alerting the id give me undefined:
Inv.InventoryitemsController=Ember.ArrayController.extend({
isEditing: false,
actions: {
editInventoryItem: function(){
var model = this.get('model');
/*
^^^^
should this be a reference to that specific instance of a single model or the list of models provided by the InventoryitemsRoute
*/
alert('you want to edit this:' + model.id); // <-undefined
}
}
});
In the Ember docs, they use a playlist example (here: http://emberjs.com/guides/controllers/representing-multiple-models-with-arraycontroller/) like this:
App.SongsRoute = Ember.Route.extend({
setupController: function(controller, playlist) {
controller.set('model', playlist.get('songs'));
}
});
But this example is a bit confusing (for a couple of reasons) but in this particular case - how would I map their concept of playlist to me trying to edit a single inventory item?
<script type="text/x-handlebars" data-template-name="inventoryitems">
{{#each}}
<div class='row'>
<p>{{input type="text" value=header disabled="true"}}</p>
<p>{{input type="text" value=detail disabled="true"}}</p>
<button {{action "editInventoryItem" this}}>edit item</button>
<button {{action "saveInventoryItem" this}}>save item</button>
</div>
{{/each}}
</script>
and
actions: {
editInventoryItem: function(object){
alert('you want to edit this:' + object.id);
}
}
Is what you need. But let me explain in a bit more detail:
First of all, terminology: Your "model" is the entire object tied to your controller. When you call this.get('model') on an action within an array controller, you will receive the entire model, in this case an array of inventory items.
The {{#each}} handlebars tag iterates through a selected array (by default it uses your entire model as the selected array). While within the {{#each}} block helper, you can reference the specific object you are currently on by saying this. You could also name the iteration object instead of relying on a this declaration by typing {{#each thing in model}}, within which each object would be referenced as thing.
Lastly, your actions are capable of taking inputs. You can declare these inputs simply by giving the variable name after the action name. Above, I demonstrated this with {{action "saveInventoryItem" this}} which will pass this to the action saveInventoryItem. You also need to add an input parameter to that action in order for it to be accepted.
Ok, that's because as you said, you're just starting with Ember. I would probably do this:
<script type="text/x-handlebars" data-template-name="inventoryitems">
{{#each}}
<div class='row'>
<p>{{input type="text" value=header disabled=headerEnabled}}</p>
<p>{{input type="text" value=detail disabled=detailEnabled}}</p>
<button {{action "editInventoryItem"}}>edit item</button>
<button {{action "saveInventoryItem"}}>save item</button>
</div>
{{/each}}
</script>
with this, you need to define a headerEnabled property in the InventoryitemController(Note that it is singular, not the one that contains all the items), and the same for detailEnabled, and the actions, you can define them also either in the same controller or in the route:
App.InventoryitemController = Ember.ObjectController.extend({
headerEnabled: false,
detailEnabled: false,
actions: {
editInventoryItem: function() {
this.set('headerEnabled', true);
this.set('detailEnabled', true);
}
}
});
that's just an example how you can access the data, in case the same property will enable both text fields, then you only need one, instead of the two that I put . In case the 'each' loop doesn't pick up the right controller, just specify itemController.

Access Controller in a View in a Render

I have a view like this:
App.AbilityFilter = Ember.TextField.extend({
classNames: ['span3'],
keyUp: function(evt) {
this.get('controller').send('filterAbilities','text');
},
placeholder:'Search abilities'
});
It's part of a render like this:
<script type="text/x-handlebars" data-template-name="abilities">
{{view App.AbilityFilter}}
<div class="accordion" id="abilities">
{{#each ability in model}}
<div class="accordion-group">
{{ability.name}}
</div>
{{/each}}
</div>
</script>
Which is being rendered in my application like this:
{{render 'abilities'}}
The problem I'm having is with the event or, rather, the action. The keyUp event fires perfectly well, but for some reason it won't go to a controller.
I've tried adding the filterAbilities to the actions hash on both the App.AbilitiesController and the App.IndexRoute according to this. According to this, the view should be part of the abilities controller since that's the context of it's parent, but it's not working.
I've done some testing and it almost seems like this.get('controller') isn't fetching a controller at all. I'm a bit lost as to what's causing the problem. This code worked a few RCs ago, but as soon as I upgraded to 1.0 it broke.
What I'm trying to do here is filter the list of abilities. If this isn't the way to this anymore, please let me know! Any help would be appreciated. Thanks!!
Ember.TextField and Ember.TextArea are no longer simple views but rather subclasses of Ember.Component which means that this.get('controller') does not refer anymore to the views controller.
But there is a different variable which indeed holds a reference to the surrounding controller and this is this.get('targetObject'). Therefore you should send your action to the targetObject:
App.AbilityFilter = Ember.TextField.extend({
classNames: ['span3'],
keyUp: function(evt) {
this.get('targetObject').send('filterAbilities','text');
},
placeholder:'Search abilities'
});
Hope it helps.

Ember.js : How to set context of an action helper?

I added the example application.
http://jsfiddle.net/Sly7/amG56/
Js:
App = Ember.Application.create();
App.ApplicationController = Ember.ArrayController.extend({
selectedBook: null
});
App.ApplicationView = Ember.View.extend({
actions: {
selectBook: function(book) {
this.get('controller').set("selectedBook", book);
},
cancel: function(book) {
alert(book);
}
}
});
App.Book = Em.Object.extend({
name: null
});
Template:
<script type="text/x-handlebars">
{{#each book in books}}
<a {{action "selectBook" book target="view"}} href="#">select {{book.name}}</a><br />
{{/each}}
<hr />
Selected Book: {{selectedBook.name}}
<br />
<a {{action "cancel" selectedBook target="view"}} href="#">cancel selected book</a>
</script>
​
Select one of the books. You will see that name of the book will be displayed. But the "cancel selected book" link does not work.
I think the problem is context of the action helper does not change when a book is selected.
How do I implement an action helper which has a changing context? Or is it a bug?
The answer is in the guides
http://emberjs.com/guides/templates/actions/#toc_action-parameters
And the context is lazily evaluated, so the problem does not occur anymore
DEPRECATED ANSWER BELOW
The problem here is that the action helper is interpreted with the selectedBook context. But at this time, selectedBook is null. So when clicking on the link, even if you previously select a book, it's too late, for the registered action, the context is still null.
As a workaround, you can enclose this with a {{with}} block.
{{#with selectedBook}}
Selected Book: {{name}}
<br />
<a {{action cancel this target="view"}} href="#">cancel selected book</a>
{{/with}}
see: http://jsfiddle.net/x82dr/17/
BTW, you can see the code of the ApplicationView, where I access the application controller, using the controller property. With Ember.js convention, the controller is injected to the view when the application initialize
UPDATE: The use of the {{with}} helper seems to be not mandatory now, see: https://github.com/emberjs/ember.js/issues/1150

How to build a form with access to text input values on submit with ember.js

I'm using the latest pre 1.0 of ember.js and wanted to get away from using the deprecated button for simple forms.
I have something that works but I don't feel like this is the correct way to wire up a view that has both a text input and a button that needs access to that text.
Here is the basic view
{{#view PersonApp.AddPersonView}}
{{view Ember.TextField valueBinding="username"}}
{{#with this as username}}
<input type="submit" value="add" {{action addPerson username}}/>
{{/with}}
{{/view}}
Here is the view
PersonApp.AddPersonView = Ember.View.extend({
username: null,
addPerson: function(event) {
var username = event.context.username;
if (username) {
PersonApp.personController.addPerson(username);
this.set('username', ''); //this does not currently work
}
}
});
The only other issue I'm having is that I don't have access to username the usual way. ie - this.get('username') but in addition I can't clear the textbox value (even though it's shown above).
I'm looking to build a modern version of this gist (previous version of ember) https://gist.github.com/1477225
I see three issues here (perhaps there are more). First, username will not be a field in the event.context, but will actually be the event context. Secondly, I believe you need to specify view.username in the valueBinding, otherwise the controller is the default home of the property (I believe). Then, to set it to initial state you need to set it to null. Third, the target of your action will be the router, so you need to specify the view as the target.
This should work:
{{#view PersonApp.AddPersonView}}
{{view Ember.TextField valueBinding="view.username"}}
{{#with this as username}}
<input type="submit" value="add" {{action addPerson username target="this"}}/>
{{/with}}
{{/view}}
PersonApp.AddPersonView = Ember.View.extend({
username: null
addPerson: function(event) {
var username = event.context;
if (username) {
this.get('controller').addPerson(username);
this.set('username', null);
}
}
});
Also, a better way of creating a new person would be to create a blank person model, bind the controller and view to that, and then save the record, afterwards setting the binding back to null.
You can do the validation and then pass data right now, even with Gidrius' code. The only thing you need to do is write the validation code in the submit handling method. Or, 'cause we`re talking client-side validation anyway, you can do it on field value change or blur, which will give the user almost instant feedback on what he is doing.
I still couldn't get something like this.get('username') to work but I ended up with the following
{{#view PersonApp.AddPersonForm}}
{{view Ember.TextField valueBinding="username"}}
<input type="submit" value="add" {{action addPerson this}}/>
{{/view}}
PersonApp.AddPersonForm = Ember.View.extend({
addPerson: function(event) {
var username = event.context.username;
if (username) {
PersonApp.personController.addPerson(username);
event.context.set('username', '');
}
}
});
probably a bit too late, but might be helpful to someone else.
Usually form field value will be bind to controller or model, so all you need is to have is a submit function in the controller so whenever function will be called you will have access to the fields via bindings.
Here is how it all could look like, assuming you are using latest pre.4 ember
Updated
// DOM part
<form {{action submitForm on="submit"}}>
{{view Ember.TextField valueBinding="username"}}
<button type="submit">add</button>
</form>
And here is a controller
PersonApp.PersonController = Ember.ArrayController({
username: '',
submitForm: function() {
var u = this.get('username'); // saving value to variable
this.set('username',''); // sets username to ''
console.log(u); // will output saved username
}
});