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

Serialize multi-select values as an array #101

Open
wants to merge 8 commits into
base: minor
Choose a base branch
from
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
sudo: false
language: node_js
before_install: npm install -g grunt-cli
node_js:
Expand Down
5 changes: 4 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ By default, a checkbox will return a boolean value signifying whether or not it
<form>
<input type="checkbox" name="a">
<input type="checkbox" name="b" checked>
<input type="checkbox" name="c" indeterminate>
</form>
```

Expand All @@ -133,7 +134,8 @@ Backbone.Syphon.serialize(view);

{
a: false,
b: true
b: true,
c: null
}
```

Expand Down Expand Up @@ -437,6 +439,7 @@ For more information on Key Assignment Validators, see the full
There some known limitations in Backbone.Syphon, partially by design and partially implemented as default behaviors.

* An input of type `checkbox` will return a boolean value. This can be overriden by replacing the Input Reader for checkboxes.
* Yo avoid circular references, care should be taken when using Backbone.Relational. See (#33)[https://github.com/marionettejs/backbone.syphon/issues/33].

## Building Backbone.Syphon

Expand Down
43 changes: 43 additions & 0 deletions spec/javascripts/deserialize.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,34 @@ describe('deserializing an object into a form', function() {
});
});

describe('when deserializing into a multi-select box', function() {
beforeEach(function() {
this.View = Backbone.View.extend({
render: function() {
this.$el.html(
'<form>' +
'<select name="foo[]" multiple="multiple">' +
'<option value="bar1">Bar 1</option>' +
'<option value="bar2">Bar 2</option>' +
'<option value="bar3">Bar 3</option>' +
'</select>' +
'</form>'
);
}
});

this.view = new this.View();
this.view.render();

Backbone.Syphon.deserialize(this.view, {foo: ['bar1', 'bar2']});
this.result = this.view.$('select').val();
});

it('should select the options corresponding to the values in the given object', function() {
expect(this.result).to.deep.equal(['bar1', 'bar2']);
});
});

describe('when deserializing into a checkbox', function() {
beforeEach(function() {
this.View = Backbone.View.extend({
Expand Down Expand Up @@ -137,6 +165,21 @@ describe('deserializing an object into a form', function() {
expect(this.result).to.be.false;
});
});

describe('and the corresponding value in the given object is null', function() {
beforeEach(function() {
this.view = new this.View();
this.view.render();
this.view.$('#the-checkbox').prop('checked', false);

Backbone.Syphon.deserialize(this.view, {chk: null});
this.result = this.view.$('#the-checkbox').prop('indeterminate');
});

it('should add an indeterminate attribute', function() {
expect(this.result).to.be.true;
});
});
});

describe('when deserializing into a button', function() {
Expand Down
43 changes: 42 additions & 1 deletion spec/javascripts/serialize.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,38 @@ describe('serializing a form', function() {
this.result = Backbone.Syphon.serialize(this.view);
});

it('should have the textarea\'s value', function() {
it('should return the selected option value', function() {
expect(this.result.foo).to.equal('bar');
});
});

describe('when serializing a multi-select box', function() {
beforeEach(function() {
this.View = Backbone.View.extend({
render: function() {
this.$el.html(
'<form>' +
'<select name="foo[]" multiple="multiple">' +
'<option value="bar1" selected="selected">Bar 1</option>' +
'<option value="bar2">Bar 2</option>' +
'<option value="bar3" selected="selected">Bar 3</option>' +
'</select>' +
'</form>'
);
}
});

this.view = new this.View();
this.view.render();

this.result = Backbone.Syphon.serialize(this.view);
});

it('should return the selected options values', function() {
expect(this.result.foo).to.deep.equal(['bar1', 'bar3']);
});
});

describe('when serializing a checkbox', function() {
beforeEach(function() {
this.View = Backbone.View.extend({
Expand Down Expand Up @@ -158,6 +185,20 @@ describe('serializing a form', function() {
expect(this.result.chk).to.be.false;
});
});

describe('and the checkbox is indeterminate', function() {
beforeEach(function() {
this.view = new this.View();
this.view.render();
this.view.$('#the-checkbox').prop('indeterminate', true);

this.result = Backbone.Syphon.serialize(this.view);
});

it('should return an object with a value of null', function() {
expect(this.result.chk).to.be.null;
});
});
});

describe('when serializing a button', function() {
Expand Down
2 changes: 1 addition & 1 deletion src/backbone.syphon.inputreaders.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ InputReaders.registerDefault(function($el) {
// Checkbox reader, returning a boolean value for
// whether or not the checkbox is checked.
InputReaders.register('checkbox', function($el) {
return $el.prop('checked');
return ($el.prop('indeterminate')) ? null : $el.prop('checked');
});
6 changes: 5 additions & 1 deletion src/backbone.syphon.inputwriters.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ InputWriters.registerDefault(function($el, value) {
// Checkbox writer, set whether or not the checkbox is checked
// depending on the boolean value.
InputWriters.register('checkbox', function($el, value) {
$el.prop('checked', value);
if (value === null) {
$el.prop('indeterminate', true);
} else {
$el.prop('checked', value);
}
});

// Radio button writer, set whether or not the radio button is
Expand Down
14 changes: 9 additions & 5 deletions src/backbone.syphon.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,15 @@ var assignKeyValue = function(obj, keychain, value) {

// if it's the last key in the chain, assign the value directly
if (keychain.length === 0) {
if (_.isArray(obj[key])) {
obj[key].push(value);
} else {
obj[key] = value;
}
value = _.isArray(value) ? value : [value];

_.each(value, function(v) {
if (_.isArray(obj[key])) {
obj[key].push(v);
} else {
obj[key] = v;
}
});
}

// recursive parsing of the array, depth-first
Expand Down