I am a starter of Ember and I try to use Ember.js(1.0.0.pre) in my app.
I am trying to set title for my Ember.Select's options to show tips when mouseover.
But, I can't find any information about the option's title in API.
Do I have to write a function myself to populate the "title" attribute?
Is there any way like "optionLabelPath" to bind "title" attribute for options?
To achieve this we need to reopen the Ember.SelectOption
here is the fiddle for the following example
MyApp = Ember.Application.create();
Ember.SelectOption.reopen({
attributeBindings: ['title'],
title: function() {
var titlePath = this.getPath('parentView.optionTitlePath');
return this.getPath(titlePath);
}.property('parentView.optionTitlePath')
});
MyApp.selectArray = [{
label: "A",
id: "1",
title: "for Apple"
},
{
label: "B",
id: "2",
title: "for Ball"
}];
Handlebars
<script type="text/x-handlebars" >
{{view Ember.Select
contentBinding="MyApp.selectArray"
optionLabelPath="content.label"
optionValuePath="content.id"
optionClassPath="content.title"
}}
</script>
Here is the simplest I could come up with:
http://jsfiddle.net/aK8JH/1/
Template:
{{view MyApp.Select contentBinding="content"}}
View:
MyApp.Select = Ember.Select.extend({
attributeBindings: ['title'],
title: 'myTitle'
});
Read this: http://emberjs.com/documentation/#toc_attribute-bindings-on-a-view
Below is what I've got after observing source code in Ember:
Ember.SelectOption.reopen({
attributeBindings: ['title'],
init: function() {
this.titlePathDidChange();
this._super();
},
titlePathDidChange: function() {
var titlePath = this.get('parentView.optionTitlePath');
if (!titlePath) { return; }
Ember.defineProperty(this, 'title', Ember.computed(function() {
return this.get(titlePath);
}).property(titlePath));
}.observes('parentView.optionTitlePath')
});
Related
I have two models, MyModel and MyOptions.
MyModel has a myValue property belongsTo('myOption), and myName('string').
In the view, I have an input for myName and a select with possible values of the model MyOptions.
When I select a new related row, I expect myModel to be 'dirty'. If I change myName, myModel gets 'dirty' (correctly).
What I'm doing wrong?
Thanks,
See this jsfiddle for the code
window.App = Ember.Application.create();
App.ApplicationAdapter = DS.FixtureAdapter.extend();
App.IndexController = Ember.ObjectController.extend({
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return Ember.RSVP.hash({
myModel: this.store.find('myModel', 1),
myOptions: this.store.find('myOption')
});
},
});
App.MyOption = DS.Model.extend({
name: DS.attr('name')
});
App.MyOption.FIXTURES = [
{ name: 'User a', id: 1 },
{ name: 'User b', id: 2 },
{ name: 'User c', id: 3 },
];
App.MyModel = DS.Model.extend({
myValue: DS.belongsTo('myOption'),
myName: DS.attr('string')
});
App.MyModel.FIXTURES = [
{
id: 1,
myValue: 2
}
];
<script type="text/x-handlebars" data-template-name="index">
<h1>Test</h1>
<lablel>My Value</label>{{input value=myModel.myValue.id}}
<lablel>My Name</label>{{input value=myModel.myName}}
{{view "select"
content=myOptions
selectionBinding=myModel.myValue
optionLabelPath="content.name"}}
{{myModel.isDirty}}
</script>
ember-data doesn't handle dirtyness for relationships, yet. You would need to implement it in user-space.
This is the relevant issue: https://github.com/emberjs/data/issues/2122
This old issue specifically discusses belongsTo: https://github.com/emberjs/data/issues/1367
Instead of using selectionBinding, you should use value:
{{view "select"
content=myOptions
value=myModel.myValue
optionLabelPath="content.name"}}
I have a router that returns data from the data model.
I want to use this data to bind it to a widget in a view.
Model:
myApp.Unit = DS.Model.extend({
active: DS.attr('boolean'),
allowdecimals: DS.attr('boolean'),
name: DS.attr('string'),
count: DS.attr('number'),
});
Router:
myApp.EunitsRoute = Ember.Route.extend({
model: function() {
return this.store.find('unit');
},
setupController: function(controller, model) {
this._super(controller, model);
controller.set('units', model);
},
actions: { ...
In the view I expect an object formated as follows:
[ {"id": 1,"active": true,"allowdecimals": true,"name": "Pint","count": 8},
{"id": 2,"active": true,"allowdecimals": true,"name": "Each","count": 8},
...]
What I am getting now in the view is an object:<DS.RecordArray:ember400>
View:
var source10 = {
datatype: "array",
datafields: [
{ name: 'id' },
{ name: 'name' },
{ name: 'allowdecimals' },
{ name: 'active' },
{ name: 'count' }
],
localdata: controller.get('units')
};
var dataAdapter10 = new $.jqx.dataAdapter(source10);
$("#eunits_grid").jqxGrid({
pageable: true,
theme: 'energyblue',
autorowheight : true,
rowsheight : 50,
pagesize: 10,
source: dataAdapter10,
....
Template:
<script type="text/x-handlebars" data-template-name="eunits">
<div class="col-md-12">
<h3 class="page-header">Edit Units</h3>
{{#if adding}}
{{view AddnewunitView}}
{{/if}}
{{view UnitsView id='eunits_grid'}}
</div>
</script>
Given a view with a context like { id: 1, form_id: 5}, I want to create an {{action}} link to the form using the form_id.
My view code looks like:
<script type="text/x-handlebars" data-template-name="group">
{{action showForm form_id href=true}}
</script>
And the action in my router looks like:
showForm: function(router, event) {
var form_id = event.context;
router.transitionTo('root.form', { id: form_id });
},
I get an error that reads:
Uncaught Error: assertion failed: You must specify a target state for event 'showForm' in order to link to it in the current state 'root.index'.
I'm guessing that the problem is with the way I'm setting up the context for transitionTo, but I haven't been able to figure out the correct solution.
Here is the full code to reproduce the problem:
<script type="text/x-handlebars" data-template-name="application">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="group">
{{action showForm form_id href=true}}
</script>
MyApp = Ember.Application.create({
autoinit: false
});
MyApp.router = Ember.Router.create({
root: Ember.Route.extend({
index: Ember.Route.extend({
route: '/',
// Throws error:
// You must specify a target state for event 'showForm' in
// order to link to it in the current state 'root.index'
//
showForm: function(router, event) {
var form_id = event.context;
router.transitionTo('root.form', { id: form_id });
},
// Won't work because form deserialize finds id, not form_id
//showForm: Em.Route.transitionTo('root.form'),
// This won't work either
// showForm: Em.Route.transitionTo('root.form', { id: this.form_id }),
connectOutlets: function( router, context ){
var group = Em.Object.create({ id:1, form_id: 5 });
router.get( 'applicationController' ).connectOutlet( 'group', group );
}
}),
form: Ember.Route.extend({
route: '/form/:id',
serialize: function( router, context ){
return { id: context.id }
},
deserialize: function( router, context ){
var form = Em.Object.create({ id: 5, name: 'my form' });
return MyApp.Form.find( context.id );
},
connectOutlets: function( router, context ){
// left out for fiddle example
}
})
})
});
MyApp.ApplicationController = Ember.Controller.extend({});
MyApp.GroupController = Em.ObjectController.extend({});
MyApp.GroupView = Em.View.extend({ templateName: 'group' });
MyApp.initialize(MyApp.router);
And the cooresponding fiddle:
http://jsfiddle.net/jefflab/LJGCz/
I was able to come up with an ugly solution to this problem using a computed property as the context of my action. The key snippets are:
<script type="text/x-handlebars" data-template-name="group">
<a {{action showForm view.formHash href=true}}>go to form</a>
</script>
MyApp.GroupView = Em.View.extend({
templateName: 'group',
formHash: function() {
return { id: this.get('controller').get('form_id') };
}.property('form_id')
});
And the working fiddle is here:
http://jsfiddle.net/jefflab/pGv8u/
However, after talking to Tom Dale, it is clear that the "right way" to solve to solve this problem is by using materialized objects instead of id values. If you are using Ember data, this is a great use case for the "sideloading" belongsTo feature.
i have drop downlist in emberjs and i declare it like that below
{{view Ember.Select contentBinding="ResAdmin.adminController.serviceAreaList" selectionBinding="ResAdmin.adminController.serviceAreaSelection" optionLabelPath="content.ZipCode" optionValuePath="content.ServiceAreaID"}}
but i want to use kendo ui's dropdownlist which i can use like below
<input id="dropDownList" />
$(document).ready(function() {
$("#dropDownList").kendoDropDownList({
dataTextField: "text",
dataValueField: "value",
dataSource: [
{ text: "Item1", value: "1" },
{ text: "Item2", value: "2" }
]
});
});
i want to use kendoui dropownlist with ember
Try it like wycats' examples for jqueryUI
App.KendoSelectView = Em.Select.extend({
didInsertElement: function () {
this.$().kendoDropDownList({
dataTextField: ....
});
}
});
You can probably pick up the attributes for the DDL from the select properties.
I try to bind value from input select to attribute "selectedValue" in controller.
This is app.js
Food = Ember.Application.create();
Food.appsController = Ember.Object.create({
selectedValue: ""
});
Food.Todo = Ember.Object.extend({
title: null,
value: null
});
Food.FoodController = Ember.ArrayProxy.create({
content: []
});
Food.FoodController.pushObject(Food.Todo.create({title:"a", value:"1"}));
Food.FoodController.pushObject(Food.Todo.create({title:"b", value:"2"}));
Food.FoodController.pushObject(Food.Todo.create({title:"c", value:"3"}));
This is index.html
{{#collection
contentBinding="Todos.todosController"
tagName="select"
itemClassBinding="content.isDone"}}
{{content.title}}
{{/collection}}
Output look like this
<select id="ember180" class="ember-view">
<option id="ember192" class="ember-view">
<script id="metamorph-0-start" type="text/x-placeholder"></script>
a
<script id="metamorph-0-end" type="text/x-placeholder"></script>
</option>
<option id="ember196" class="ember-view">
<script id="metamorph-1-start" type="text/x-placeholder"></script>
b
<script id="metamorph-1-end" type="text/x-placeholder"></script>
</option>
<option id="ember200" class="ember-view">
<script id="metamorph-2-start" type="text/x-placeholder"></script>
c
<script id="metamorph-2-end" type="text/x-placeholder"></script>
</option>
</select>
I have no idea how to add value to option and how to binding selected value back to controller.
Is this possible to do in Emberjs?
Ember now has a built-in Select view.
You can find it in the latest Ember.js build here: http://cloud.github.com/downloads/emberjs/ember.js/ember-latest.js
Here's an example usage:
var App = Ember.Application.create();
App.Person = Ember.Object.extend({
id: null,
firstName: null,
lastName: null,
fullName: function() {
return this.get('firstName') + " " + this.get('lastName');
}.property('firstName', 'lastName').cacheable()
});
App.selectedPersonController = Ember.Object.create({
person: null
});
App.peopleController = Ember.ArrayController.create({
content: [
App.Person.create({id: 1, firstName: 'Yehuda', lastName: 'Katz'}),
App.Person.create({id: 2, firstName: 'Tom', lastName: 'Dale'}),
App.Person.create({id: 3, firstName: 'Peter', lastName: 'Wagenet'}),
App.Person.create({id: 4, firstName: 'Erik', lastName: 'Bryn'})
]
});
Your template would look like:
{{view Ember.Select
contentBinding="App.peopleController"
selectionBinding="App.selectedPersonController.person"
optionLabelPath="content.fullName"
optionValuePath="content.id"}}
Again, here's a jsFiddle example: http://jsfiddle.net/ebryn/zgLCr/
Jumping off from the solution for #pangrantz, this Fiddle example (http://jsfiddle.net/bsyjr/) illustrates some improvements: The Handlebars code is cleaner through the use of tagName. When tagName is set to "select", the child views automatically become "option" elements. See the Ember.CollectionView.CONTAINER_MAP in https://github.com/emberjs/ember.js/blob/master/packages/ember-views/lib/views/collection_view.js to understand why. On the Javascript side, by specifying an itemViewClass, we can add the value attribute to the option element.
<script type="text/x-handlebars" >
{{#collection Food.SelectView tagName="select" contentBinding="Food.foodController"
valueBinding="Food.appsController.selectedValue"}}
{{content.title}}
{{/collection}}
selected: {{view Ember.TextField valueBinding="Food.appsController.selectedValue"}}{{Food.appsController.selectedValue}}
</script>
Food = Ember.Application.create();
Food.SelectView = Ember.CollectionView.extend({
value: null,
itemViewClass: SC.View.extend({
attributeBindings:['value'],
valueBinding: 'content.value'
}),
valueChanged: function(){
this.$().val( this.get('value') );
}.observes('value'),
didInsertElement: function(){
var self = this;
this.$().change(function(){
var val = $('select option:selected').val();
self.set('value', val);
});
}
});
Food.appsController = Ember.Object.create({
selectedValue: ""
});
Food.Todo = Ember.Object.extend({
title: null,
value: null
});
Food.foodController = Ember.ArrayProxy.create({
content: []
});
Food.foodController.pushObject(Food.Todo.create({title:"a", value:"1"}));
Food.foodController.pushObject(Food.Todo.create({title:"b", value:"2"}));
Food.foodController.pushObject(Food.Todo.create({title:"c", value:"3"}));
There is still room for improvement in the event handling, which is not using Ember's event framework, and it would make a lot of sense to use a custom written SelectView that doesn't leverage Handlebars, since IMO, it is dubious how much value Handlebars adds in this case.
Using a custom Ember.View works for me, but I think there is a better solution...
See working example is this fiddle http://jsfiddle.net/pangratz/hcxrJ/
Handlebars:
{{#view Food.SelectView contentBinding="Food.foodController"
valueBinding="Food.appsController.selectedValue"}}
<select>
{{#each content}}
<option {{bindAttr value="value"}} >{{title}}</option>
{{/each}}
</select>
{{/view}}
app.js:
Food = Ember.Application.create();
Food.SelectView = Ember.View.extend({
value: null,
valueChanged: function(){
this.$('select').val( this.get('value') );
}.observes('value'),
didInsertElement: function(){
var self = this;
this.$('select').change(function(){
var val = $('select option:selected').val();
self.set('value', val);
});
}
});
Food.appsController = Ember.Object.create({
selectedValue: ""
});
Food.Todo = Ember.Object.extend({
title: null,
value: null
});
Food.foodController = Ember.ArrayProxy.create({
content: []
});
Food.foodController.pushObject(Food.Todo.create({title:"a", value:"1"}));
Food.foodController.pushObject(Food.Todo.create({title:"b", value:"2"}));
Food.foodController.pushObject(Food.Todo.create({title:"c", value:"3"}));
I'm not sure if this is of use to others, but I've been doing something similar based on the answers here, and have made this SelectView that should work in this context too. It binds to 'change', works out the view that is currently selected and then does something with its content.
Food.SelectView = Ember.CollectionView.extend({
change: function(e) {
var selected = this.$().find(":selected").index();
var content = this.get('childViews')[selected].content;
// Do something with the result
Food.appsController.set('selectedValue', content.title);
}
});
This way you're able to pass around an object rather than the index of the select.