From 12628fea3cd62a2095b4f2736bf9458bb89c019a Mon Sep 17 00:00:00 2001 From: Dan Knutsen Date: Sat, 23 Mar 2019 14:56:47 -0500 Subject: [PATCH] More helpers - add peek-record helper - add peek-all helper - add query helper - add async-all helper - add async-hash helper --- README.md | 29 +++++++-- addon/helpers/async-all.js | 11 ++++ addon/helpers/async-hash.js | 11 ++++ addon/helpers/peek-all.js | 13 ++++ addon/helpers/peek-record.js | 14 ++++ addon/helpers/query.js | 13 ++++ app/helpers/async-all.js | 1 + app/helpers/async-hash.js | 1 + app/helpers/peek-all.js | 1 + app/helpers/peek-record.js | 1 + app/helpers/query.js | 1 + tests/dummy/app/controllers/application.js | 16 ----- tests/dummy/app/controllers/index.js | 20 ++++++ tests/dummy/app/models/user.js | 1 + tests/dummy/app/router.js | 1 + tests/dummy/app/routes/index.js | 4 ++ tests/dummy/app/templates/application.hbs | 59 ++--------------- tests/dummy/app/templates/index.hbs | 64 +++++++++++++++++++ tests/dummy/app/templates/sandbox.hbs | 51 +++++++++++++++ tests/dummy/mirage/factories/user.js | 1 + tests/integration/helpers/async-all-test.js | 17 +++++ tests/integration/helpers/async-hash-test.js | 17 +++++ tests/integration/helpers/peek-all-test.js | 17 +++++ tests/integration/helpers/peek-record-test.js | 17 +++++ tests/integration/helpers/query-test.js | 17 +++++ 25 files changed, 320 insertions(+), 78 deletions(-) create mode 100644 addon/helpers/async-all.js create mode 100644 addon/helpers/async-hash.js create mode 100644 addon/helpers/peek-all.js create mode 100644 addon/helpers/peek-record.js create mode 100644 addon/helpers/query.js create mode 100644 app/helpers/async-all.js create mode 100644 app/helpers/async-hash.js create mode 100644 app/helpers/peek-all.js create mode 100644 app/helpers/peek-record.js create mode 100644 app/helpers/query.js create mode 100644 tests/dummy/app/controllers/index.js create mode 100644 tests/dummy/app/routes/index.js create mode 100644 tests/dummy/app/templates/index.hbs create mode 100644 tests/dummy/app/templates/sandbox.hbs create mode 100644 tests/integration/helpers/async-all-test.js create mode 100644 tests/integration/helpers/async-hash-test.js create mode 100644 tests/integration/helpers/peek-all-test.js create mode 100644 tests/integration/helpers/peek-record-test.js create mode 100644 tests/integration/helpers/query-test.js diff --git a/README.md b/README.md index ae73b14..1aef2c7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,28 @@ ember-needs-async ============================================================================== -[Short description of the addon.] +Lightweight provider component and helpers that allow declarative, composable async data fetching and async-aware rendering in just a template. The component waits for an async task and yields the results as well as loading and error states. It can also be used with any ember-concurrency task. The helpers provide shorthand concurrency task wrappers around common Ember Data operations. Example: + +``` +
+ + {{#states.loading}} + + {{/states.loading}} + + {{#states.error as |error|}} + There was an error loading the user +
{{error}}
+ {{/states.error}} + + {{#states.loaded as |user|}} + {{user.fullName}} profile picture +
{{user.firstName}} {{user.lastName}}
+
{{user.jobTitle}} at {{user.company}}
+ {{/states.loaded}} +
+
+``` Compatibility @@ -19,12 +40,6 @@ ember install ember-needs-async ``` -Usage ------------------------------------------------------------------------------- - -[Longer description of how to use the addon in apps.] - - License ------------------------------------------------------------------------------ diff --git a/addon/helpers/async-all.js b/addon/helpers/async-all.js new file mode 100644 index 0000000..1eda651 --- /dev/null +++ b/addon/helpers/async-all.js @@ -0,0 +1,11 @@ +import Helper from '@ember/component/helper'; +import { all, task } from 'ember-concurrency'; + +export default Helper.extend({ + asyncAllTask: task(function * (tasks) { + return yield all(tasks); + }), + compute([tasks/*, ...rest*/]/*, hash*/) { + return this.asyncAllTask.perform(tasks); + } +}); diff --git a/addon/helpers/async-hash.js b/addon/helpers/async-hash.js new file mode 100644 index 0000000..967ec0e --- /dev/null +++ b/addon/helpers/async-hash.js @@ -0,0 +1,11 @@ +import Helper from '@ember/component/helper'; +import { hash, task } from 'ember-concurrency'; + +export default Helper.extend({ + asyncHashTask: task(function * (tasks) { + return yield hash(tasks); + }), + compute([tasks/*, ...rest*/]/*, hash*/) { + return this.asyncHashTask.perform(tasks); + } +}); diff --git a/addon/helpers/peek-all.js b/addon/helpers/peek-all.js new file mode 100644 index 0000000..897cedd --- /dev/null +++ b/addon/helpers/peek-all.js @@ -0,0 +1,13 @@ +import Helper from '@ember/component/helper'; +import { inject as service } from '@ember/service'; +import { task } from 'ember-concurrency'; + +export default Helper.extend({ + store: service(), + peekAllTask: task(function * (modelType) { + return yield this.store.peekAll(modelType); + }), + compute([modelType/*, ...rest*/]/*, hash*/) { + return this.peekAllTask.perform(modelType); + } +}); diff --git a/addon/helpers/peek-record.js b/addon/helpers/peek-record.js new file mode 100644 index 0000000..b94292b --- /dev/null +++ b/addon/helpers/peek-record.js @@ -0,0 +1,14 @@ +import Helper from '@ember/component/helper'; +import { inject as service } from '@ember/service'; +import { task } from 'ember-concurrency'; + +export default Helper.extend({ + store: service(), + peekRecordTask: task(function * (modelType, id) { + return yield this.store.peekRecord(modelType, id); + }), + compute([modelType, id/*, ...rest*/]/*, hash*/) { + if(!id) return null; + return this.peekRecordTask.perform(modelType, id); + } +}); diff --git a/addon/helpers/query.js b/addon/helpers/query.js new file mode 100644 index 0000000..7d690d2 --- /dev/null +++ b/addon/helpers/query.js @@ -0,0 +1,13 @@ +import Helper from '@ember/component/helper'; +import { inject as service } from '@ember/service'; +import { task } from 'ember-concurrency'; + +export default Helper.extend({ + store: service(), + queryTask: task(function * (modelType, hash) { + return yield this.store.query(modelType, hash); + }), + compute([modelType, hash/*, ...rest*/]/*, hash*/) { + return this.queryTask.perform(modelType, hash); + } +}); diff --git a/app/helpers/async-all.js b/app/helpers/async-all.js new file mode 100644 index 0000000..809ea44 --- /dev/null +++ b/app/helpers/async-all.js @@ -0,0 +1 @@ +export { default, asyncAll } from 'ember-needs-async/helpers/async-all'; diff --git a/app/helpers/async-hash.js b/app/helpers/async-hash.js new file mode 100644 index 0000000..3cc3440 --- /dev/null +++ b/app/helpers/async-hash.js @@ -0,0 +1 @@ +export { default, asyncHash } from 'ember-needs-async/helpers/async-hash'; diff --git a/app/helpers/peek-all.js b/app/helpers/peek-all.js new file mode 100644 index 0000000..8fa4eb9 --- /dev/null +++ b/app/helpers/peek-all.js @@ -0,0 +1 @@ +export { default, peekAll } from 'ember-needs-async/helpers/peek-all'; diff --git a/app/helpers/peek-record.js b/app/helpers/peek-record.js new file mode 100644 index 0000000..dcc879c --- /dev/null +++ b/app/helpers/peek-record.js @@ -0,0 +1 @@ +export { default, peekRecord } from 'ember-needs-async/helpers/peek-record'; diff --git a/app/helpers/query.js b/app/helpers/query.js new file mode 100644 index 0000000..961ae83 --- /dev/null +++ b/app/helpers/query.js @@ -0,0 +1 @@ +export { default, query } from 'ember-needs-async/helpers/query'; diff --git a/tests/dummy/app/controllers/application.js b/tests/dummy/app/controllers/application.js index 5a1ba9a..d630f31 100644 --- a/tests/dummy/app/controllers/application.js +++ b/tests/dummy/app/controllers/application.js @@ -1,20 +1,4 @@ import Controller from '@ember/controller'; -import { task, timeout } from 'ember-concurrency'; export default Controller.extend({ - askQuestion: task(function * () { - yield timeout(1000); - this.set('result', Math.random()); - return this.result; - }).drop(), - - result: null, - - taskInstance: null, - - actions: { - onClick() { - this.set('taskInstance', this.askQuestion.perform()); - } - } }); diff --git a/tests/dummy/app/controllers/index.js b/tests/dummy/app/controllers/index.js new file mode 100644 index 0000000..5a1ba9a --- /dev/null +++ b/tests/dummy/app/controllers/index.js @@ -0,0 +1,20 @@ +import Controller from '@ember/controller'; +import { task, timeout } from 'ember-concurrency'; + +export default Controller.extend({ + askQuestion: task(function * () { + yield timeout(1000); + this.set('result', Math.random()); + return this.result; + }).drop(), + + result: null, + + taskInstance: null, + + actions: { + onClick() { + this.set('taskInstance', this.askQuestion.perform()); + } + } +}); diff --git a/tests/dummy/app/models/user.js b/tests/dummy/app/models/user.js index c93c575..055e98f 100644 --- a/tests/dummy/app/models/user.js +++ b/tests/dummy/app/models/user.js @@ -4,6 +4,7 @@ import { computed } from '@ember/object'; export default DS.Model.extend({ firstName: DS.attr(), lastName: DS.attr(), + avatar: DS.attr(), friends: DS.hasMany('user'), diff --git a/tests/dummy/app/router.js b/tests/dummy/app/router.js index d0bb009..b694355 100644 --- a/tests/dummy/app/router.js +++ b/tests/dummy/app/router.js @@ -7,6 +7,7 @@ const Router = EmberRouter.extend({ }); Router.map(function() { + this.route('sandbox'); }); export default Router; diff --git a/tests/dummy/app/routes/index.js b/tests/dummy/app/routes/index.js new file mode 100644 index 0000000..6c74252 --- /dev/null +++ b/tests/dummy/app/routes/index.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default Route.extend({ +}); diff --git a/tests/dummy/app/templates/application.hbs b/tests/dummy/app/templates/application.hbs index 5d5ce24..0bd29ab 100644 --- a/tests/dummy/app/templates/application.hbs +++ b/tests/dummy/app/templates/application.hbs @@ -1,57 +1,6 @@ -

Welcome to Ember

-{{!-- - - -
-
- {{input value=userid}} -
- -
- - {{#states.loading}}LOADING THE USER{{/states.loading}} - - {{#states.loaded as |user|}} - {{user.lastName}}, {{user.firstName}} -
- - {{#states.loading}}LOADING COMPANY{{/states.loading}} - {{#states.loaded as |company|}} -
-
{{company.name}} - {{company.motto}}
-
{{company.street}} {{company.city}} {{company.state}} {{company.zip}}
-
- {{/states.loaded}} -
- - {{#states.loading}}LOADING FRIENDS{{/states.loading}} - {{#states.loaded as |friends|}} -
- {{#each friends as |friend|}} - {{friend.fullName}}, - {{/each}} -
- {{/states.loaded}} -
-
- {{/states.loaded}} - - {{#states.error as |error|}} - There was an error: {{error}} - {{/states.error}} -
-
- - -
+ {{outlet}} diff --git a/tests/dummy/app/templates/index.hbs b/tests/dummy/app/templates/index.hbs new file mode 100644 index 0000000..96ad856 --- /dev/null +++ b/tests/dummy/app/templates/index.hbs @@ -0,0 +1,64 @@ +

Main

+ + + +
+
+ {{input value=userid}} +
+ +
+ + {{#states.loading}}LOADING THE USER{{/states.loading}} + + {{#states.loaded as |user|}} + {{user.fullName}} profile picture + {{user.lastName}}, {{user.firstName}} +
+ + {{#states.loading}}LOADING COMPANY{{/states.loading}} + {{#states.loaded as |company|}} +
+
{{company.name}} - {{company.motto}}
+
{{company.street}} {{company.city}} {{company.state}} {{company.zip}}
+
+ {{/states.loaded}} +
+ + {{#states.loading}}LOADING FRIENDS{{/states.loading}} + {{#states.loaded as |friends|}} +
+ {{#each friends as |friend|}} + {{friend.fullName}}, + {{/each}} +
+ {{/states.loaded}} +
+
+ {{/states.loaded}} + + {{#states.error as |error|}} + There was an error: {{error}} + {{/states.error}} +
+
+ + <#States.loaded as |users|> + Loaded users: {{users.length}} + + +
+
+ + +
+ + +{{outlet}} diff --git a/tests/dummy/app/templates/sandbox.hbs b/tests/dummy/app/templates/sandbox.hbs new file mode 100644 index 0000000..942a88f --- /dev/null +++ b/tests/dummy/app/templates/sandbox.hbs @@ -0,0 +1,51 @@ +

Sandbox

+ + + <#States.loading> + Loading... + + + <#States.loaded as |users|> + {{#each users as |user|}} + {{user.fullName}}, + {{/each}} + + + + + + <#States.loading> + Loading... + + + <#States.loaded as |users|> + {{#each-in users as |name user|}} + {{name}}:{{user.fullName}}, + {{/each-in}} + + + +
+ QUERY: + + <#States.loading> + Loading... + + + <#States.loaded as |users|> + {{#each users as |user|}} + {{user.fullName}}, + {{/each}} + + +
+ +{{outlet}} diff --git a/tests/dummy/mirage/factories/user.js b/tests/dummy/mirage/factories/user.js index be361f0..1cc5c8f 100644 --- a/tests/dummy/mirage/factories/user.js +++ b/tests/dummy/mirage/factories/user.js @@ -3,6 +3,7 @@ import { Factory, faker, trait } from 'ember-cli-mirage'; export default Factory.extend({ firstName: faker.name.firstName, lastName: faker.name.lastName, + avatar: faker.image.avatar, withCompany: trait({ afterCreate(user, server) { diff --git a/tests/integration/helpers/async-all-test.js b/tests/integration/helpers/async-all-test.js new file mode 100644 index 0000000..d8aa5c8 --- /dev/null +++ b/tests/integration/helpers/async-all-test.js @@ -0,0 +1,17 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import hbs from 'htmlbars-inline-precompile'; + +module('Integration | Helper | async-all', function(hooks) { + setupRenderingTest(hooks); + + // Replace this with your real tests. + test('it renders', async function(assert) { + this.set('inputValue', '1234'); + + await render(hbs`{{async-all inputValue}}`); + + assert.equal(this.element.textContent.trim(), '1234'); + }); +}); diff --git a/tests/integration/helpers/async-hash-test.js b/tests/integration/helpers/async-hash-test.js new file mode 100644 index 0000000..726d432 --- /dev/null +++ b/tests/integration/helpers/async-hash-test.js @@ -0,0 +1,17 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import hbs from 'htmlbars-inline-precompile'; + +module('Integration | Helper | async-hash', function(hooks) { + setupRenderingTest(hooks); + + // Replace this with your real tests. + test('it renders', async function(assert) { + this.set('inputValue', '1234'); + + await render(hbs`{{async-hash inputValue}}`); + + assert.equal(this.element.textContent.trim(), '1234'); + }); +}); diff --git a/tests/integration/helpers/peek-all-test.js b/tests/integration/helpers/peek-all-test.js new file mode 100644 index 0000000..f1b4346 --- /dev/null +++ b/tests/integration/helpers/peek-all-test.js @@ -0,0 +1,17 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import hbs from 'htmlbars-inline-precompile'; + +module('Integration | Helper | peek-all', function(hooks) { + setupRenderingTest(hooks); + + // Replace this with your real tests. + test('it renders', async function(assert) { + this.set('inputValue', '1234'); + + await render(hbs`{{peek-all inputValue}}`); + + assert.equal(this.element.textContent.trim(), '1234'); + }); +}); diff --git a/tests/integration/helpers/peek-record-test.js b/tests/integration/helpers/peek-record-test.js new file mode 100644 index 0000000..2b78dae --- /dev/null +++ b/tests/integration/helpers/peek-record-test.js @@ -0,0 +1,17 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import hbs from 'htmlbars-inline-precompile'; + +module('Integration | Helper | peek-record', function(hooks) { + setupRenderingTest(hooks); + + // Replace this with your real tests. + test('it renders', async function(assert) { + this.set('inputValue', '1234'); + + await render(hbs`{{peek-record inputValue}}`); + + assert.equal(this.element.textContent.trim(), '1234'); + }); +}); diff --git a/tests/integration/helpers/query-test.js b/tests/integration/helpers/query-test.js new file mode 100644 index 0000000..8c96574 --- /dev/null +++ b/tests/integration/helpers/query-test.js @@ -0,0 +1,17 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import hbs from 'htmlbars-inline-precompile'; + +module('Integration | Helper | query', function(hooks) { + setupRenderingTest(hooks); + + // Replace this with your real tests. + test('it renders', async function(assert) { + this.set('inputValue', '1234'); + + await render(hbs`{{query inputValue}}`); + + assert.equal(this.element.textContent.trim(), '1234'); + }); +});