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

Implement named args RFC #15968

Merged
merged 2 commits into from
Dec 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,4 @@ env:
- TEST_SUITE=blueprints
- TEST_SUITE=travis-browsers DISABLE_JSCS=true DISABLE_JSHINT=true
- TEST_SUITE=sauce
- TEST_SUITE=allow-backtracking-rerender ALLOW_BACKTRACKING=true
- TEST_SUITE=each-package-tests BUILD_TYPE=alpha PUBLISH=true
5 changes: 5 additions & 0 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ for a detailed explanation.
Adds an ability to for developers to integrate their own custom component managers
into Ember Applications per [RFC](https://github.com/emberjs/rfcs/blob/custom-components/text/0000-custom-components.md).

* `ember-glimmer-named-arguments`

Add `{{@foo}}` syntax to access named arguments in component templates per
[RFC](https://github.com/emberjs/rfcs/pull/276).

* `ember-module-unification`

Introduces support for Module Unification
Expand Down
9 changes: 1 addition & 8 deletions broccoli/features.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,6 @@ function getFeatures(isDebug) {
}
}

features['ember-glimmer-allow-backtracking-rerender'] = false;

if (process.env.ALLOW_BACKTRACKING) {
features['ember-glimmer-allow-backtracking-rerender'] = true;
features['ember-glimmer-detect-backtracking-rerender'] = false;
}

features['mandatory-setter'] = isDebug;
features['ember-glimmer-detect-backtracking-rerender'] = isDebug;

Expand All @@ -46,4 +39,4 @@ function toConst(features) {

module.exports.toConst = toConst;
module.exports.RELEASE = getFeatures(false);
module.exports.DEBUG = getFeatures(true);
module.exports.DEBUG = getFeatures(true);
2 changes: 1 addition & 1 deletion features.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"features-stripped-test": null,
"ember-libraries-isregistered": null,
"ember-improved-instrumentation": null,
"ember-glimmer-allow-backtracking-rerender": null,
"ember-glimmer-named-arguments": null,
"ember-routing-router-service": true,
"ember-engines-mount-params": true,
"ember-module-unification": null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,33 @@ moduleFor('Components test: curly components', class extends RenderingTest {
assert.deepEqual(fooBarInstance.childViews, [fooBarBazInstance]);
}

['@test it renders passed named arguments']() {
['@feature(ember-glimmer-named-arguments) it renders passed named arguments']() {
this.registerComponent('foo-bar', {
template: '{{@foo}}'
});

this.render('{{foo-bar foo=model.bar}}', {
model: {
bar: 'Hola'
}
});

this.assertText('Hola');

this.runTask(() => this.rerender());

this.assertText('Hola');

this.runTask(() => this.context.set('model.bar', 'Hello'));

this.assertText('Hello');

this.runTask(() => this.context.set('model', { bar: 'Hola' }));

this.assertText('Hola');
}

['@test it reflects named arguments as properties']() {
this.registerComponent('foo-bar', {
template: '{{foo}}'
});
Expand Down Expand Up @@ -1063,6 +1089,30 @@ moduleFor('Components test: curly components', class extends RenderingTest {
this.assertText('In layout - someProp: something here');
}

['@feature(ember-glimmer-named-arguments) non-block with named argument']() {
this.registerComponent('non-block', {
template: 'In layout - someProp: {{@someProp}}'
});

this.render('{{non-block someProp=prop}}', {
prop: 'something here'
});

this.assertText('In layout - someProp: something here');

this.runTask(() => this.rerender());

this.assertText('In layout - someProp: something here');

this.runTask(() => this.context.set('prop', 'other thing there'));

this.assertText('In layout - someProp: other thing there');

this.runTask(() => this.context.set('prop', 'something here'));

this.assertText('In layout - someProp: something here');
}

['@test non-block with properties overridden in init']() {
let instance;
this.registerComponent('non-block', {
Expand Down Expand Up @@ -1171,7 +1221,7 @@ moduleFor('Components test: curly components', class extends RenderingTest {
this.assertText('In layout - someProp: wycats');
}

['@test this.attrs.foo === attrs.foo === foo']() {
['@feature(!ember-glimmer-named-arguments) this.attrs.foo === attrs.foo === foo']() {
this.registerComponent('foo-bar', {
template: strip`
Args: {{this.attrs.value}} | {{attrs.value}} | {{value}}
Expand Down Expand Up @@ -1208,6 +1258,46 @@ moduleFor('Components test: curly components', class extends RenderingTest {
this.assertText('Args: wat | wat | wat123123123');
}

['@feature(ember-glimmer-named-arguments) this.attrs.foo === attrs.foo === @foo === foo']() {
this.registerComponent('foo-bar', {
template: strip`
Args: {{this.attrs.value}} | {{attrs.value}} | {{@value}} | {{value}}
{{#each this.attrs.items as |item|}}
{{item}}
{{/each}}
{{#each attrs.items as |item|}}
{{item}}
{{/each}}
{{#each @items as |item|}}
{{item}}
{{/each}}
{{#each items as |item|}}
{{item}}
{{/each}}
`
});

this.render('{{foo-bar value=model.value items=model.items}}', {
model: {
value: 'wat',
items: [1, 2, 3]
}
});

this.assertStableRerender();

this.runTask(() => {
this.context.set('model.value', 'lul');
this.context.set('model.items', [1]);
});

this.assertText(strip`Args: lul | lul | lul | lul1111`);

this.runTask(() => this.context.set('model', { value: 'wat', items: [1, 2, 3] }));

this.assertText('Args: wat | wat | wat | wat123123123123');
}

['@test non-block with properties on self']() {
this.registerComponent('non-block', {
template: 'In layout - someProp: {{someProp}}'
Expand Down Expand Up @@ -1288,6 +1378,34 @@ moduleFor('Components test: curly components', class extends RenderingTest {
this.assertText('In layout - someProp: something here - In template');
}

['@feature(ember-glimmer-named-arguments) block with named argument']() {
this.registerComponent('with-block', {
template: 'In layout - someProp: {{@someProp}} - {{yield}}'
});

this.render(strip`
{{#with-block someProp=prop}}
In template
{{/with-block}}`, {
prop: 'something here'
}
);

this.assertText('In layout - someProp: something here - In template');

this.runTask(() => this.rerender());

this.assertText('In layout - someProp: something here - In template');

this.runTask(() => this.context.set('prop', 'something else'));

this.assertText('In layout - someProp: something else - In template');

this.runTask(() => this.context.set('prop', 'something here'));

this.assertText('In layout - someProp: something here - In template');
}

['@test static arbitrary number of positional parameters'](assert) {
this.registerComponent('sample-component', {
ComponentClass: Component.extend().reopenClass({
Expand Down Expand Up @@ -2966,6 +3084,21 @@ moduleFor('Components test: curly components', class extends RenderingTest {
this.assertText('MyVar1: 1 1 MyVar2: 2 2');
}

['@feature(ember-glimmer-named-arguments) using named arguments for positional params'](assert) {
let MyComponent = Component.extend();

this.registerComponent('foo-bar', {
ComponentClass: MyComponent.reopenClass({
positionalParams: ['myVar']
}),
template: 'MyVar1: {{@myVar}} {{myVar}} MyVar2: {{myVar2}} {{@myVar2}}'
});

this.render('{{foo-bar 1 myVar2=2}}');

this.assertText('MyVar1: 1 1 MyVar2: 2 2');
}

['@test can use `{{this}}` to emit the component\'s toString value [GH#14581]'](assert) {
this.registerComponent('foo-bar', {
ComponentClass: Component.extend({
Expand Down Expand Up @@ -3035,7 +3168,7 @@ moduleFor('Components test: curly components', class extends RenderingTest {
this.assertText('Hi!');
}

['@test can access properties off of rest style positionalParams array'](assert) {
['@feature(!ember-glimmer-named-arguments) can access properties off of rest style positionalParams array'](assert) {
this.registerComponent('foo-bar', {
ComponentClass: Component.extend().reopenClass({ positionalParams: 'things' }),
// using `attrs` here to simulate `@things.length`
Expand All @@ -3046,4 +3179,15 @@ moduleFor('Components test: curly components', class extends RenderingTest {

this.assertText('3');
}

['@feature(ember-glimmer-named-arguments) can access properties off of rest style positionalParams array'](assert) {
this.registerComponent('foo-bar', {
ComponentClass: Component.extend().reopenClass({ positionalParams: 'things' }),
template: `{{@things.length}}`
});

this.render('{{foo-bar "foo" "bar" "baz"}}');

this.assertText('3');
}
});
1 change: 0 additions & 1 deletion packages/ember-metal/lib/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
let runInTransaction, didRender, assertNotRendered;

// detect-backtracking-rerender by default is debug build only
// detect-glimmer-allow-backtracking-rerender can be enabled in custom builds
if (EMBER_GLIMMER_DETECT_BACKTRACKING_RERENDER) {

// there are 2 states
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
import { assert } from 'ember-debug';
import { EMBER_GLIMMER_NAMED_ARGUMENTS } from 'ember/features';
import calculateLocationDisplay from '../system/calculate-location-display';

const RESERVED = ['@arguments', '@args'];

let isReserved, assertMessage;

export default function assertReservedNamedArguments(env) {
let { moduleName } = env.meta;

return {
name: 'assert-reserved-named-arguments',

visitors: {
PathExpression(node) {
if (node.original[0] === '@') {
assert(assertMessage(moduleName, node));
PathExpression({ original, loc }) {
if (isReserved(original)) {
assert(`${assertMessage(original)} ${calculateLocationDisplay(moduleName, loc)}`);
}
}
}
};
}

function assertMessage(moduleName, node) {
let path = node.original;
let source = calculateLocationDisplay(moduleName, node.loc);

return `'${path}' is not a valid path. ${source}`;
if (EMBER_GLIMMER_NAMED_ARGUMENTS) {
isReserved = name => RESERVED.indexOf(name) !== -1 || name.match(/^@[^a-z]/);
assertMessage = name => `'${name}' is reserved.`;
} else {
isReserved = name => name[0] === '@';
assertMessage = name => `'${name}' is not a valid path.`;
}
Loading