Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplified Ember.RadioButtonGroup implementation #1235

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG
@@ -1,3 +1,7 @@
*Ember 1.0.0-pre.3

* RadioButton and RadioButtonGroup

*Ember 1.0.0-pre.2 (October 25, 2012)*

* Ember.SortableMixin: don't remove and reinsert items when their sort order doesn't change. Fixes #1486.
Expand Down
2 changes: 2 additions & 0 deletions packages/ember-handlebars/lib/controls.js
@@ -1,6 +1,8 @@
require("ember-handlebars/controls/control");
require("ember-handlebars/controls/checkbox");
require("ember-handlebars/controls/text_field");
require("ember-handlebars/controls/button");
require("ember-handlebars/controls/radio_button");
require("ember-handlebars/controls/text_area");
require("ember-handlebars/controls/tabs");
require("ember-handlebars/controls/select");
24 changes: 24 additions & 0 deletions packages/ember-handlebars/lib/controls/control.js
@@ -0,0 +1,24 @@
require("ember-views/views/view");

var get = Ember.get, getPath = Ember.getPath, set = Ember.set;


/**
@class

A control is an Ember.View subclass which behaves slightly differently.

Specifically, a control uses itself as its context and is generally controller-less.

@extends Ember.View
*/
Ember.Control = Ember.View.extend({

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems odd to only subclass Ember.Control in one place. Were you intending for other controls to subclass this as well?

init: function() {
this._super();

// Once #1234 is merged in this can be moved out of init
set(this, '_context', this);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#1234 has been merged.

}

});
159 changes: 159 additions & 0 deletions packages/ember-handlebars/lib/controls/radio_button.js
@@ -0,0 +1,159 @@
require("ember-handlebars/controls/control");

var get = Ember.get, getPath = Ember.getPath, set = Ember.set, fmt = Ember.String.fmt;

/**
@class

The `Ember.RadioButton` view class renders an html radio input, allowing the
user to select a single value from a list of values.

Dealing with multiple radio buttons can be simplified by using an
`Ember.RadioButtonGroup`. See the {@link Ember.RadioButtonGroup} documentation
for more information.

@extends Ember.View
*/
Ember.RadioButton = Ember.Control.extend(
/** @scope Ember.RadioButton.prototype */ {

attributeBindings: ["disabled", "type", "name", "value", "checked"],
classNames: ["ember-radio-button"],

/**
The value of this radio button.

@type Object
*/
value: null,

/**
The selected value in this group of radio buttons.

@type Object
*/
selectedValue: null,

/**
Sets the disabled property on the element.

@default false
@type Boolean
*/
isDisabled: false,

/**
Sets the checked property on the element.

@default false
@type Boolean
*/
checked: false,

tagName: "input",
type: "radio",

selectedValueChanged: Ember.observer(function() {
var selectedValue = get(this, "selectedValue");
if(!Ember.empty(selectedValue) && get(this, "value") === selectedValue) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would need to be updated to use Ember.isEmpty() instead of Ember.empty() for RC1

set(this, "checked", true);
} else {
set(this, "checked", false);
}
}, 'selectedValue'),

checkedChanged: Ember.observer(function() {
this._updateElementValue();
}, 'checked'),

init: function() {
this._super();
this.selectedValueChanged();
},

change: function() {
set(this, 'checked', this.$().prop('checked'));
Ember.run.once(this, this._updateElementValue);
},

_updateElementValue: function() {
if(!get(this, 'checked')) return;
set(this, 'selectedValue', get(this, 'value'));
}

});

/**
@class

The `Ember.RadioButtonGroup` view class provides a simplfied method for dealing
with multiple `Ember.RadioButton` instances.

## Simple Example

```handlebars
{{#view Ember.RadioButtonGroup name="role" valueBinding="content.role"}}
{{view RadioButton value="admin"}}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nested view has to be accessed with {{view view.RadioButton value="..."}} in the latest RC

{{view RadioButton value="owner"}}
{{view RadioButton value="user"}}
{{/view}}
```

Note that the radio buttons are declared as `{{view RadioButton ...}}` as opposed
to `{{view Ember.RadioButton ...}}`. When inside the body of a RadioButtonGroup,
a `RadioButton` view is provided which automatically picks up the same name and value
binding as the containing group.

## More Complex Example

```javascript
App.person = Ember.Object.create({name: 'Gordon', role: 'admin'})
App.PersonController = Ember.Controller.extend({
contentBinding: 'App.person',
roleOptions: ['admin', 'owner', 'user', 'banned']
});
```

```handlebars
{{#view Ember.RadioButtonGroup name="role" valueBinding="content.role"}}
{{#each role in controller.roleOptions}}
<label>
{{view RadioButton valueBinding="role"}}
{{role}}
</label>
{{/each}}
{{/view}}
```

The above controller/template combination will render html containing a
radio input for each item in the `roleOptions` property of the controller.
Initially, the `admin` option will be checked. If the user selects a different
radio, the `role` property of the controller's `content` will be updated
accordingly.

@extends Ember.View
*/
Ember.RadioButtonGroup = Ember.Control.extend(
/** @scope Ember.RadioButtonGroup.prototype */ {

classNames: ['ember-radio-button-group'],
attributeBindings: ['name:data-name'],

name: Ember.required(),

/**
The value of the selected radio button in this group

@type Object
*/
value: null,

RadioButton: Ember.computed(function() {
return Ember.RadioButton.extend({
group: this,
selectedValueBinding: 'group.value',
nameBinding: 'group.name'
});
})

});
41 changes: 41 additions & 0 deletions packages/ember-handlebars/tests/controls/control_test.js
@@ -0,0 +1,41 @@
/*globals TemplateTests:true */

var get = Ember.get, getPath = Ember.getPath, set = Ember.set, group, rb, rb2, application;

var view;

var appendView = function() {
Ember.run(function() { view.appendTo('#qunit-fixture'); });
};

module("Ember.Control", {
setup: function() {
window.TemplateTests = Ember.Namespace.create();
},

teardown: function() {
if (view) {
Ember.run(function() {
view.destroy();
});
view = null;
}
window.TemplateTests = undefined;
}
});

test("a nested Ember.Control should use itself as its context", function() {
TemplateTests.TestControl = Ember.Control.extend({
location: 'Lake Tahoe',
template: Ember.Handlebars.compile("{{location}}")
});

view = Ember.View.create({
location: 'Seattle',
template: Ember.Handlebars.compile("{{view TemplateTests.TestControl}}")
});

appendView();

equal(view.$().text(), 'Lake Tahoe');
});