Skip to content

Commit

Permalink
add support for object compacting
Browse files Browse the repository at this point in the history
JSCS Errors

Fixes error on Ember 1.13

Use isPresent

one liner

guard for non-arrays

Guard before setting the array

support object compacting

update readme

guard for non-array/non-object values

don't use array computed

be explicit about not supporting changing keys

don't use @each

remove unnecessary observer
  • Loading branch information
spencer516 committed Feb 22, 2016
1 parent 4b1ab63 commit c84fd7f
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 13 deletions.
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ only: ['pipe'] // imports only `pipe`
+ [`compact`](#compact)
* [Object](#object-helpers)
+ [`group-by`](#group-by)
+ [`compact`](#compact)
* [Math](#math-helpers)
+ [`inc`](#inc)
+ [`dec`](#dec)
Expand Down Expand Up @@ -288,17 +289,25 @@ Joins the given array with an optional separator into a string.
**[⬆️ back to top](#available-helpers)**
#### `compact`
Removes blank items from an array.
Removes blank items from an array or blank values from an object.
```hbs
{{#each (compact arrayWithBlanks) as |notBlank|}}
{{notBlank}} is most definitely not blank!
{{/each}}
```
Or:
```hbs
{{#each-in (compacy objectWithBlanks) as |notBlankKey notBlankValue|}}
{{notBlankValue}} is also definitely not blank.
{{/each-in}}
```
**[⬆️ back to top](#available-helpers)**
---
---
### Object helpers
Expand Down
51 changes: 41 additions & 10 deletions addon/helpers/compact.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,60 @@ import Ember from 'ember';

const {
Helper,
isArray,
defineProperty,
get,
set,
observer,
isPresent,
isArray,
A: emberArray,
computed: { filter }
computed,
copy,
typeOf,
A: emberArray
} = Ember;
const keys = Object.keys || Ember.keys;

// For compacting object instances
export function compact(objectInstance) {
return keys(objectInstance).reduce((compactedObject, key) => {
let val = objectInstance[key];
if (isPresent(val)) {
compactedObject[key] = val;
}
return compactedObject;
}, {});
}

export default Helper.extend({
array: null,
arrayOrObject: null,

compute([array]) {
if (!isArray(array)) {
return emberArray([array]);
}
compute([arrayOrObject]) {
set(this, 'arrayOrObject', arrayOrObject);

set(this, 'array', array);
if (isArray(arrayOrObject)) {
this.makeComputedForArray(arrayOrObject);
} else if (typeOf(arrayOrObject) === 'object' || typeOf(arrayOrObject) === 'instance') {
this.makeComputedForObject(arrayOrObject);
} else {
return emberArray([arrayOrObject]);
}

return get(this, 'content');
},

content: filter('array', isPresent),
makeComputedForArray(array) {
defineProperty(this, 'content', computed('arrayOrObject.[]', function() {
return get(this, 'arrayOrObject').filter((item) => isPresent(item));
}));
},

makeComputedForObject(object) {
let currentKeys = keys(object);
let dependentKey = `arrayOrObject.\{${currentKeys.join(',')}\}`;
defineProperty(this, 'content', computed(dependentKey, function() {
return compact(get(this, 'arrayOrObject'));
}));
},

contentDidChange: observer('content', function() {
this.recompute();
Expand Down
86 changes: 85 additions & 1 deletion tests/integration/helpers/compact-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import Ember from 'ember';

const { A: emberArray, run } = Ember;
const { A: emberArray, run, set } = Ember;

moduleForComponent('compact', 'Integration | Helper | {{compact}}', {
integration: true
Expand Down Expand Up @@ -60,3 +60,87 @@ test('It recomputes the filter if an item in the array changes', function(assert

assert.equal(this.$().text().trim(), '1253', 'null is removed');
});

test('Removes empty values from objects', function(assert) {
this.set('obj', { keyA: 1, keyB: 2, keyC: null, keyD: 4 });
this.render(hbs`
{{~#each-in (compact obj) as |key value|~}}
{{key}}{{value}}
{{~/each-in~}}
`);

assert.equal(this.$().text().trim(), 'keyA1keyB2keyD4');
});

test('Recomputes if the object changes', function(assert) {
this.set('obj', { keyA: 1, keyB: 2, keyC: null, keyD: 4 });
this.render(hbs`
{{~#each-in (compact obj) as |key value|~}}
{{key}}{{value}}
{{~/each-in~}}
`);

assert.equal(this.$().text().trim(), 'keyA1keyB2keyD4');

this.set('obj', { keyA: 1, keyB: 2, keyC: 3, keyD: 4 });

assert.equal(this.$().text().trim(), 'keyA1keyB2keyC3keyD4');
});

test('Recomputes if a value on the object changes', function(assert) {
let obj = { keyA: 1, keyB: 2, keyC: null, keyD: 4 };
this.set('obj', obj);
this.render(hbs`
{{~#each-in (compact obj) as |key value|~}}
{{key}}{{value}}
{{~/each-in~}}
`);

assert.equal(this.$().text().trim(), 'keyA1keyB2keyD4');

run(() => set(obj, 'keyC', 3));

assert.equal(this.$().text().trim(), 'keyA1keyB2keyC3keyD4');
});

test('New keys do not cause rerender', function(assert) {
let obj = { keyA: 1, keyB: 2, keyC: null, keyD: 4 };
this.set('obj', obj);
this.render(hbs`
{{~#each-in (compact obj) as |key value|~}}
{{key}}{{value}}
{{~/each-in~}}
`);

assert.equal(this.$().text().trim(), 'keyA1keyB2keyD4');

run(() => set(obj, 'keyE', 5));

assert.equal(this.$().text().trim(), 'keyA1keyB2keyD4');
});

test('Gracefully handles arrays and objects interchangeably', function(assert) {
this.set('value', emberArray([1, 2, null, 3]));
this.set('isArray', true);

this.render(hbs`
{{#with (compact value) as |compactedValue|}}
{{#if isArray}}
{{~#each compactedValue as |value|~}}
{{value}}
{{~/each~}}
{{else}}
{{~#each-in compactedValue as |key value|~}}
{{key}}{{value}}
{{~/each-in~}}
{{/if}}
{{/with}}
`);

assert.equal(this.$().text().trim(), '123', 'null is removed');

this.set('value', { keyA: 1, keyB: 2, keyC: null, keyD: 4 });
this.set('isArray', false);

assert.equal(this.$().text().trim(), 'keyA1keyB2keyD4');
});

0 comments on commit c84fd7f

Please sign in to comment.