Use bind-attr helper with input helper in emberjs - ember.js

I want to use bind-attr and input helpers together so i can assign class to my input field. I have a workaround to do so like creating a parent dom and use bind-attr on that
<td {{bind-attr class="showmsg:alert-msg"}} >{{input type="text" value=rank}}</td>
How can I use bind-attr inside the input helper?

Refer to Views: Customizing a View's Element for general view customisation.
You can use classNameBindings in much of the same sort as you are using in bind-attr above.
In view:
export default Ember.TextField.extend({
classNameBindings: ['showmsg:alert-msg'],
showmsg: true
});
If you want to do something such as dynamic classes, you can pass values to your view through, for example, a controller:
In template:
{{view 'some-td-view' classBinding="typeClass"}}
where typeClass refers to a property on the controller
In Controller:
typeClass: function() {
return this.get('type');
}.property('type')
In View (views/some-td-view.js):
export default Ember.TextField.extend({
tagName: 'td',
classNames: ['some-default-class'],
});
So dependant on the controller's value of type you can have a variable typeClass. For a type of multi would result in a view of:
<td class="some-default-class multi"></td>
whereas a type of single would result in:
<td class="some-default-class single"></td>

Related

How to setup "style" attribute on Embers Handlebars {{input}} helper?

I try to use {{input style="width:100%"}} in my view's template but without any success.
Browser stubbornly renders <input> without style="width:100%;".
How can I achieve it?
For future reference, below is my resolution (for Ember-Cli) based on #damienc answer:
Component class
//app/components/forms/elements/style-input.js
import Ember from "ember";
export default Ember.TextField.extend({
attributeBindings: ['style'],
styleAttrib : null,
style: Ember.computed({
get: function () {
return Ember.String.htmlSafe(this.get('styleAttrib'));
},
set: function (key, newStyle) {
this.set('styleAttrib', newStyle);
return Ember.String.htmlSafe(newStyle);
}
})
});
And the template:
{{!app/templates/components/portlet-datatable/header-cell-filterable.hbs}}
<div class="ember-table-content-container">
<span class="ember-table-content">
{{forms/elements/style-input type="text"
placeholder=view.content.headerCellName style="width: 100%;"}}
</span>
</div>
This is not supported out-of-the-box by the input helper.
You could either add a class parameter that matches a CSS rule, or implement your own input helper to add style to your component's attributeBindings attribute.
This old cookbook entry achieves some specific behavior by extending the Ember.TextField class:
http://guides.emberjs.com/v1.10.0/cookbook/user_interface_and_interaction/focusing_a_textfield_after_its_been_inserted/

replace deprecated Ember.ObjectController used in View in ember.js

I am using Ember.js to create a one page map editing software.
In my app, I use a model to represent the layer's state of the map and to associate it with an actual openlayers' layer.
There is a summary of my work:
in my entry point map.hbs, I call the mapLayers view:
{{view "mapLayers"}}
Here is the mapLayers view definition:
export default Ember.View.extend({
templateName: "mapLayers",
classNames: ["map-layers"]
});
The mapLayers template :
<ul>
{{#each layer in tileLayers itemController="mapLayer"}}
<li {{bind-attr id=layer.identifier}}>
<a>
<label class="hint--top" {{bind-attr data-hint=layer.title}}>
{{str-sub layer.title 20}}
</label>
{{input class="range" type="range" name="range" min="0" max="100" value=layer.opacity}}
</a>
</li>
{{/each}}
</ul>
And the mapLayer controller:
export default Ember.ObjectController.extend({
opacity: function(key, value){
var model = this.get('model');
if (value === undefined) {
// property being used as a getter
console.log("get layer opacity: " + model.get('opacity'));
return model.get('opacity') * 100;
} else {
// property being used as a setter
model.set('opacity', value / 100);
model.get('layer').setOpacity(value / 100);
model.save();
return value;
}
}.property('model.opacity')
});
As you see, I am using the proxy ObjectController to modify on the fly the values set and get in the view.
I'm trying to understand how to remove the ObjectController without success.
I tried to change to Ember.Controller but how can I proxy my model properties then??
I read this without help:
OBJECTCONTROLLER
Experienced Ember users have enjoyed the use of proxying behavior in
the Ember.ObjectController class since 1.0. However, this behavior
will be removed in Ember 2.0 as the framework migrates to routable
components.
New users hit three roadbumps when learning about the object
controller pattern.
Given a certain model, which of the three controller options should I
be using? Which controller is generated by the framework if I do not
specify one? When using an object controller, why should the this
context not be passed to actions if it has the properties of my model?
For these reasons, the Road to Ember 2.0 RFC listed object controllers
as a concept to be removed from the framework.
To migrate from an explicitly defined object controller, first convert
the class definition to inherit from Ember.Controller. For example:
import Ember from "ember";
// Change: export default Ember.ObjectController.extend({ // To:
export default Ember.Controller.extend({
// ...
Next update any use of {{modelPropertyName}} in templates with
{{model.modelPropertyName}}. You should also review any computed
property dependent keys, observer keys, and get and set statements on
the route and controller.
Instead of proxying you just have to fully qualify you are getting the property off of the model, instead of the controller, which is what is in scope in your template and controller.
Template
<ul>
{{#each layer in tileLayers itemController="mapLayer"}}
<li id={{layer.model.identifier}}>
<a>
<label class="hint--top" data-hint={{layer.model.title}}>
{{str-sub layer.model.title 20}}
</label>
{{input class="range" type="range" name="range" min="0" max="100" value=layer.opacity}}
</a>
</li>
{{/each}}
</ul>
Controller
export default Ember.Controller.extend({
opacity: function(key, value){
var model = this.get('model');
if (value === undefined) {
// property being used as a getter
console.log("get layer opacity: " + model.get('opacity'));
return model.get('opacity') * 100;
} else {
// property being used as a setter
model.set('opacity', value / 100);
model.get('layer').setOpacity(value / 100);
model.save();
return value;
}
}.property('model.opacity')
});

Change class on tr element after action on tr

With Ember 2.0 coming down the pipeline and the move away from itemControllers & views, what is the best way to apply the selected class to the currently selected tr element?
Originally there was just the each loop within the controller template that set itemControllers on each tr element. The itemController would then hold the isSelected property as well as hoisting it into the parentController upon select.
Selection currently is working without issues with the bindings of pumpSelected to a property passed into the component.
Although the code is a bit cleaner after the refactor, it's just pushed the need for the itemController lower to me. Any help appreciated.
Component .hbs snippet:
{{#each pump in results}}
<tr {{action 'select' pump}} {{bind-attr class='isSelected:selected'}}>
<td>{{pump.modelNumber}}</td>
</tr>
{{/each}}
Component Definition:
PumpResultComponent = Ember.Component.extend
tagName: 'table'
classNames: ['table', 'table-striped']
actions:
select: (selectedPump)->
#set 'pumpSelected', selectedPump
The way I'm doing this right now is by wrapping the content in a list and defining the selected state in each item. In your template you can than loop over this wrapper list, so in your case:
PumpResultComponent = Ember.Component.extend({
pumpList: function() {
var selected = this.get('pumpSelected');
return this.get('results').map(function(pump) {
return {
isSelected: selected && pump.get('id') === selected.get('id'), // or some other equality property
pump: pump
};
});
}.property('pumpSelected','results')
});
{{#each item in pumpList}}
<tr {{action 'select' item.pump}} {{bind-attr class='item.isSelected:selected'}}>
<td>{{item.pump.modelNumber}}</td>
</tr>
{{/each}}

Using Ember.js, how do I get a template to show dynamically all of the properties of a model? [duplicate]

Is there a way to iterate over a view's context's attributes in EmberJS? I am using Ember-Data (https://github.com/emberjs/data) for ORM.
Lets say I use connectOutlets to register a UserView with a user that has attributes such as email, name, etc. In the connected Handlebars template, is there anyway that I can iterate over those attributes?
I basically need to build a generic view that can be reused with different models...
Ryan is right about the attributes, but it takes some doing to actually get where you're going. My examples here are using the latest RC1 Ember.
Here is an editor template that is model agnostic:
<script type="text/x-handlebars" data-template-name="edit_monster">
{{#if clientId}}
<h1>Edit Monster: {{name}}</h1>
<div>
{{#each metadata}}
<span class="edit-label">{{name}}</span>
<span class="edit-field">
{{view App.AutoTextField typeBinding="type" nameBinding="name" }}
</span>
{{/each}}
</div>
{{else}}
No monster selected.
{{/if}}
</script>
To make that work, we need a couple of pieces of magic-magic. This controller is a good start:
App.EditMonsterController = Em.ObjectController.extend({
metadata: function() {
var vals = [];
var attributeMap = this.get('content.constructor.attributes');
attributeMap.forEach(function(name, value) {
vals.push(value);
});
return vals;
}.property('content')
});
That uses that "attributes" property that Ryan mentioned to provide the metadata that we are feeding into our #each up there in the template!
Now, here is a view that we can use to provide the text input. There's an outer container view that is needed to feed the valueBinding in to the actual textfield.
App.AutoTextField = Ember.ContainerView.extend({
type: null,
name: null,
init: function() {
this._super();
this.createChildView();
},
createChildView: function() {
this.set('currentView', Ember.TextField.create({
valueBinding: 'controller.' + this.get('name'),
type: this.get('type')
}));
}.observes('name', 'type')
});
Here is a fiddle demonstrating the whole crazy thing: http://jsfiddle.net/Malkyne/m4bu6/
The Ember Data objects that represent your models have an attributes property that contains all of the attributes for the given model. This is what Ember Data's toJSON uses to convert your models into Javascript objects.
You can use this attributes property to read a models attributes and then pull those specific attributes out of an instance. Here is an example.
http://jsfiddle.net/BdUyU/1/
Just to reiterate what's going on here. We are reading the attributes from App.User and then pulling the values out of App.ryan and App.steve. Hope this makes sense.

Iterating over a model's attributes in EmberJS handlebars template

Is there a way to iterate over a view's context's attributes in EmberJS? I am using Ember-Data (https://github.com/emberjs/data) for ORM.
Lets say I use connectOutlets to register a UserView with a user that has attributes such as email, name, etc. In the connected Handlebars template, is there anyway that I can iterate over those attributes?
I basically need to build a generic view that can be reused with different models...
Ryan is right about the attributes, but it takes some doing to actually get where you're going. My examples here are using the latest RC1 Ember.
Here is an editor template that is model agnostic:
<script type="text/x-handlebars" data-template-name="edit_monster">
{{#if clientId}}
<h1>Edit Monster: {{name}}</h1>
<div>
{{#each metadata}}
<span class="edit-label">{{name}}</span>
<span class="edit-field">
{{view App.AutoTextField typeBinding="type" nameBinding="name" }}
</span>
{{/each}}
</div>
{{else}}
No monster selected.
{{/if}}
</script>
To make that work, we need a couple of pieces of magic-magic. This controller is a good start:
App.EditMonsterController = Em.ObjectController.extend({
metadata: function() {
var vals = [];
var attributeMap = this.get('content.constructor.attributes');
attributeMap.forEach(function(name, value) {
vals.push(value);
});
return vals;
}.property('content')
});
That uses that "attributes" property that Ryan mentioned to provide the metadata that we are feeding into our #each up there in the template!
Now, here is a view that we can use to provide the text input. There's an outer container view that is needed to feed the valueBinding in to the actual textfield.
App.AutoTextField = Ember.ContainerView.extend({
type: null,
name: null,
init: function() {
this._super();
this.createChildView();
},
createChildView: function() {
this.set('currentView', Ember.TextField.create({
valueBinding: 'controller.' + this.get('name'),
type: this.get('type')
}));
}.observes('name', 'type')
});
Here is a fiddle demonstrating the whole crazy thing: http://jsfiddle.net/Malkyne/m4bu6/
The Ember Data objects that represent your models have an attributes property that contains all of the attributes for the given model. This is what Ember Data's toJSON uses to convert your models into Javascript objects.
You can use this attributes property to read a models attributes and then pull those specific attributes out of an instance. Here is an example.
http://jsfiddle.net/BdUyU/1/
Just to reiterate what's going on here. We are reading the attributes from App.User and then pulling the values out of App.ryan and App.steve. Hope this makes sense.