Ember bidirectional value bind to object property - ember.js

I want to achieve a very simple thing with a contenteditable div. Let us take this simple example:
<script type="text/x-handlebars">
<div contenteditable>{{MyApp.president.fullName}}</div>
</script>
I want the MyApp.president.fullname's value to change when I edit the content of the div. Does Ember have a way to do that? Or I have to observe the changes of the div's content and set the property with Ember.set?
I need all this to build a WYSIWYG editor.

You should use Ember.TextField, with which you can bind values both ways:
{{view Ember.TextField valueBinding="MyApp.president.fullName"}}
Using a textfield, when you change MyApp.president.fullName will update the textfield. When you change the value of the textfield it will update MyApp.president.fullName.
If you want to be able to edit any element you can do it like this:
Template:
{{#if MyApp.isEditing}}
{{view App.InlineTextField valueBinding="MyApp.president.name"}}
{{else}}
{{#view App.TextView}}
{{MyApp.president.name}}
{{/view}}
{{/if}}
Views:
App.InlineTextField = Ember.TextField.extend({
focusOut: function() {
MyApp.set('isEditing', false);
}
});
App.TextView = Ember.View.extend({
doubleClick: function() {
MyApp.set('isEditing', true);
}
});

Related

Different layouts depending on sub resources

Sorry if this is a really obvious questions but I have the following routes:
Web.Router.map(function () {
this.resource('orders', function(){
this.resource("order", {path:":order_id"});
});
});
And for my orders template I have something like:
<div class="someclass">
{{outlet}}
</div>
And what I want todo is:
{{#if onOrderRoute}}
<div class="someclass">
{{outlet}}
{{else}}
<div class="someotherclass">
{{/if}}
</div>
I was wondering what the best way of doing this is, or am I mising something?
There are multiple ways to accomplish this. The view has a layoutName property you can use to specify your layout. Another option is to specify a property on your child view, and then your template can bind to that by using the view property.
For example:
Web.OrderView = Ember.View.extend({
childView: true
);
Then, in your template you bind to view.childView
{{#if view.childView}}
<!-- code goes here -->
{{/if}}
Further, you can even create a mixin and then just inject that mixin into every view.
Web.ChildViewMixin = Ember.Mixin.create({
childView: true
});
Web.ChildView = Ember.View.extend(ChildViewMixin, {
});

How do I render a collection of models each with variable view and controller?

I have a collection of models in my Ember.js app, which I would like to render. The catch is that I want to be able to specify a specialized view and controller for each of the models.
The controller part seems to be easy: I would just wrap the array in an ArrayController and implement itemController method. The view part is where it gets tricky. I don't see an obvious idiomatic way of doing this.
The best way we came up with is the combination of ArrayController and CollectionView with an overridden createChildView. For instance:
createChildView: function(viewClass, attrs) {
var viewInstance,
widgetType = attrs.content.get('type');
// lookup view, if found, use it, if not, pass empty view
var viewDefined = this.container.lookup('view:' + widgetType);
var createWidgetType = viewDefined ? widgetType : 'empty';
// create view instance from widgetType name
// it causes lookup in controller
viewInstance = this._super(createWidgetType, attrs);
// if `attrs.content` is controller (item of `ComponentsController`)
// set it as controller of newly created view
if(attrs.content.get('isController')) {
viewInstance.set('controller', attrs.content);
}
return viewInstance;
}
This feels unnecessarily convoluted, I don't like that I have to connect the view with the controller manually like that. Is there a cleaner way?
You can create a component, which will act as controller and have a view associated with it:
App.XItemComponent = Ember.Component.extend({
controllerProperty: '!',
tagName: 'li'
});
Then, you can just do:
<script type="text/x-handlebars" data-template-name="index">
<ul>
{{#each model }}
{{ x-item item=this }}
{{/each}}
</ul>
</script>
http://emberjs.jsbin.com/wehevixolu/1/edit?html,js,output
I'd use the {{render}} helper. It'll create a view and controller for each instance.
{{#each item in model}}
{{render "item" item}}
{{/each}}
Example: http://emberjs.jsbin.com/vuwimu/2/edit?html,js,output
Render helper guide: http://emberjs.com/guides/templates/rendering-with-helpers/#toc_the-code-render-code-helper
Additionally:
In your comment you mentioned you want different controller/view types for particular model types. This could be done like this:
{{#each item in model}}
{{#if item.typeX}}
{{render "itemX" item}}
{{/if}}
{{#if item.typeY}}
{{render "itemY" item}}
{{/if}}
{{/each}}
or if you'd choose to go with components:
{{#each item in model}}
{{#if item.typeX}}
{{component-x item=item}}
{{/if}}
{{#if item.typeY}}
{{component-y item=item}}
{{/if}}
{{/each}}
Without knowing what you are trying to accomplish in more detail it’s hard to tell what the best solution is.

Using Ember, how do I dynamically bind a property in a view helper contained in an each block?

I have a template with code similar to:
{{#each item in items}}
<label>{{item.name}}</label>
{{view Ember.Select content=selectableValues value={{item.name}}}}
{{/each}}
I understand the code above won't work, but it illustrates my problem and what I want to achieve. I don't want to bind what's selected in the Ember.Select to the property of the iterated item, but rather to a controller property of the name of the iterated item's name property.
How might I do this?
You're going to have to dynamically create a computed property, since you are binding the value of the value, unfortunately, this presents problems if you then decide to change the value of the value, but, I'll leave that up to you to play with.
<script type="text/x-handlebars" data-template-name="index">
{{input value=foo}}
{{view App.BlahView host=controller propName=chacha}}
</script>
<script type="text/x-handlebars" data-template-name="blah">
{{view.propValue}}
</script>
Code
App.IndexController = Em.Controller.extend({
chacha:'foo',
foo:'bar'
});
App.BlahView = Em.View.extend({
templateName:'blah',
setupDyna: function(){
var propName = this.get('propName');
Ember.defineProperty(this, 'propValue', Ember.computed(function() {
return this.get('host').get(propName);
}).property("host." + propName));
}.on('init')
});
http://emberjs.jsbin.com/vunureno/1/edit

Ember checkbox and value

I have checkboxes created in a loop. I set checked to a property name, and want to reach a dynamic value from the checkbox on click. In the property (for key, value) I'm getting the wrong thing - the label of my property instead of the value. Is there a simple way in Ember to get the value out of the checkbox?
Any help is much appreciated.
In HTML:
{{#each url in controllers.application.env.urls}}
<div>
{{view Ember.Checkbox checked=updateServerList valueBinding="url"}}{{url}}
</div>
{{/each}}
In javascript:
updateServerList:function(key,value)
{
if(value!=undefined)
{
console.log("----1 ", key, value);
}
}.property(''),
Per your comment, I started actually playing around with this and realized that it was not as simple as I thought. What I ended up doing was just building a custom component rather than working with Ember.Checkbox.
In your template:
{{#each}}
{{check-box url=url}}
{{/each}}
Component Template:
<script type='text/x-handlebars' id="components/check-box">
{{input type="checkbox" checked=toggleURL}}{{url}}
</script>
The component code:
App.CheckBoxComponent = Ember.Component.extend({
toggleURL: false,
logURL: function() {
console.log(this.url);
// Do something with the URL
}.observes('toggleURL')
});

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.