Skip to content

Commit

Permalink
[FEATURE array-helper] Implement array helper RFC #318
Browse files Browse the repository at this point in the history
  • Loading branch information
josemarluedke committed Oct 10, 2018
1 parent 7116215 commit d6d9526
Show file tree
Hide file tree
Showing 3 changed files with 278 additions and 0 deletions.
40 changes: 40 additions & 0 deletions packages/@ember/-internals/glimmer/lib/helpers/array.ts
@@ -0,0 +1,40 @@
import { PathReference } from '@glimmer/reference';
import { Arguments, VM } from '@glimmer/runtime';
import { Opaque } from '@glimmer/util';

/**
@module ember
*/

/**
Use the `{{array}}` helper to create an array to pass as an option to your
components.
```handlebars
{{my-component people=(array
'Tom Dade'
'Yehuda Katz'
this.myOtherPerson)
}}
```
Would result in an object such as:
```js
['Tom Date', 'Yehuda Katz', this.get('myOtherPerson')]
```
Where the 3rd item in the array is bound to updates of the `myOtherPerson` property.
@method array
@for Ember.Templates.helpers
@param {Array} options
@return {Array} Array
@category array-helper
@since 3.7.0
@public
*/

export default function(_vm: VM, args: Arguments): PathReference<Opaque[]> {
return args.positional.capture();
}
2 changes: 2 additions & 0 deletions packages/@ember/-internals/glimmer/lib/resolver.ts
Expand Up @@ -23,6 +23,7 @@ import { default as htmlSafeHelper } from './helpers/-html-safe';
import { default as inputTypeHelper } from './helpers/-input-type';
import { default as normalizeClassHelper } from './helpers/-normalize-class';
import { default as action } from './helpers/action';
import { default as array } from './helpers/array';
import { default as concat } from './helpers/concat';
import { default as eachIn } from './helpers/each-in';
import { default as get } from './helpers/get';
Expand Down Expand Up @@ -58,6 +59,7 @@ const BUILTINS_HELPERS = {
concat,
get,
hash,
array,
log,
mut,
'query-params': queryParams,
Expand Down
@@ -0,0 +1,236 @@
import { RenderingTest, moduleFor } from '../../utils/test-case';
import { strip } from '../../utils/abstract-test-case';
import { Component } from '../../utils/helpers';
import { set } from '@ember/-internals/metal';

moduleFor(
'Helpers test: {{array}}',
class extends RenderingTest {
['@test returns an array']() {
this.render(strip`{{#with (array "Sergio") as |people|}}
{{#each people as |personName|}}
{{personName}}
{{/each}}
{{/with}}`);

this.assertText('Sergio');

this.assertStableRerender();
}

['@test can have more than one value']() {
this.render(strip`{{#with (array "Sergio" "Robert") as |people|}}
{{#each people as |personName|}}
{{personName}},
{{/each}}
{{/with}}`);

this.assertText('Sergio,Robert,');

this.assertStableRerender();
}

['@test binds values when variables are used']() {
this.render(
strip`{{#with (array personOne) as |people|}}
{{#each people as |personName|}}
{{personName}}
{{/each}}
{{/with}}`,
{
personOne: 'Tom',
}
);

this.assertText('Tom');

this.assertStableRerender();

this.runTask(() => set(this.context, 'personOne', 'Yehuda'));
this.assertText('Yehuda');
}

['@test binds multiple values when variables are used']() {
this.render(
strip`{{#with (array personOne personTwo) as |people|}}
{{#each people as |personName|}}
{{personName}},
{{/each}}
{{/with}}`,
{
personOne: 'Tom',
personTwo: 'Yehuda',
}
);

this.assertText('Tom,Yehuda,');

this.assertStableRerender();

this.runTask(() => set(this.context, 'personOne', 'Sergio'));

this.assertText('Sergio,Yehuda,');

this.runTask(() => set(this.context, 'personTwo', 'Tom'));

this.assertText('Sergio,Tom,');
}

['@test array helpers can be nested']() {
this.render(
strip`{{#with (array (array personOne personTwo)) as |listOfPeople|}}
{{#each listOfPeople as |people|}}
List:
{{#each people as |personName|}}
{{personName}},
{{/each}}
{{/each}}
{{/with}}`,
{
personOne: 'Tom',
personTwo: 'Yehuda',
}
);

this.assertText('List:Tom,Yehuda,');

this.assertStableRerender();

this.runTask(() => set(this.context, 'personOne', 'Chad'));

this.assertText('List:Chad,Yehuda,');

this.runTask(() => set(this.context, 'personTwo', 'Balint'));

this.assertText('List:Chad,Balint,');
}

['@test should yield hash of an array of internal properties']() {
let fooBarInstance;
let FooBarComponent = Component.extend({
init() {
this._super();
fooBarInstance = this;
this.model = { personOne: 'Chad' };
},
});

this.registerComponent('foo-bar', {
ComponentClass: FooBarComponent,
template: `{{yield (hash people=(array model.personOne))}}`,
});

this.render(strip`{{#foo-bar as |values|}}
{{#each values.people as |personName|}}
{{personName}}
{{/each}}
{{/foo-bar}}`);

this.assertText('Chad');

this.assertStableRerender();

this.runTask(() => set(fooBarInstance, 'model.personOne', 'Godfrey'));

this.assertText('Godfrey');

this.runTask(() => set(fooBarInstance, 'model', { personOne: 'Chad' }));

this.assertText('Chad');
}

['@test should yield hash of an array of internal and external properties']() {
let fooBarInstance;
let FooBarComponent = Component.extend({
init() {
this._super();
fooBarInstance = this;
this.model = { personOne: 'Chad' };
},
});

this.registerComponent('foo-bar', {
ComponentClass: FooBarComponent,
template: `{{yield (hash people=(array model.personOne personTwo))}}`,
});

this.render(
strip`{{#foo-bar personTwo=model.personTwo as |values|}}
{{#each values.people as |personName|}}
{{personName}},
{{/each}}
{{/foo-bar}}`,
{
model: { personTwo: 'Tom' },
}
);

this.assertText('Chad,Tom,');

this.assertStableRerender();

this.runTask(() => {
set(fooBarInstance, 'model.personOne', 'Godfrey');
set(this.context, 'model.personTwo', 'Yehuda');
});

this.assertText('Godfrey,Yehuda,');

this.runTask(() => {
set(fooBarInstance, 'model', { personOne: 'Chad' });
set(this.context, 'model', { personTwo: 'Tom' });
});

this.assertText('Chad,Tom,');
}

['@test should render when passing as argument to a component invocation']() {
let FooBarComponent = Component.extend({});

this.registerComponent('foo-bar', {
ComponentClass: FooBarComponent,
template: strip`{{#each people as |personName|}}
{{personName}},
{{/each}}`,
});

this.render(strip`{{foo-bar people=(array "Tom" personTwo)}}`, { personTwo: 'Chad' });

this.assertText('Tom,Chad,');

this.assertStableRerender();

this.runTask(() => set(this.context, 'personTwo', 'Godfrey'));

this.assertText('Tom,Godfrey,');
}

['@test should return an entirely new array when any argument change']() {
let fooBarInstance;
let FooBarComponent = Component.extend({
init() {
this._super();
fooBarInstance = this;
},
});

this.registerComponent('foo-bar', {
ComponentClass: FooBarComponent,
template: strip`{{#each people as |personName|}}
{{personName}},
{{/each}}`,
});

this.render(strip`{{foo-bar people=(array "Tom" personTwo)}}`, { personTwo: 'Chad' });

let firstArray = fooBarInstance.people;

this.runTask(() => set(this.context, 'personTwo', 'Godfrey'));

this.assert.ok(
firstArray !== fooBarInstance.people,
'should have created an entirely new array'
);
}
}
);

0 comments on commit d6d9526

Please sign in to comment.