Skip to content

Commit 8c59713

Browse files
authored
Modernize readme examples (#691)
1 parent 3e62ab7 commit 8c59713

File tree

1 file changed

+90
-68
lines changed

1 file changed

+90
-68
lines changed

README.md

Lines changed: 90 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,28 @@ ember install ember-changeset
2424
2525
## Updates
2626

27-
We have released `v3.0.0`. See the CHANGELOG [here](https://github.com/adopted-ember-addons/ember-changeset/blob/master/CHANGELOG.md). This requires Ember >= 3.13 as the use of `@tracked` will help us monitor and propagate changes to the UI layer. If your app is < 3.13 or you need to support IE11, then you can install the 2.0 series `ember install [email protected]`.
27+
We have released `v3.0.0`. See the CHANGELOG [here](https://github.com/adopted-ember-addons/ember-changeset/blob/master/CHANGELOG.md).
28+
This requires Ember >= 3.13 as the use of `@tracked` will help us monitor and propagate changes to the UI layer.
29+
If your app is < 3.13 or you need to support IE11, then you can install the 2.0 series `ember install [email protected]`.
2830

2931
Support for IE11 was dropped with the `v3.0.0` release given our ubiquitous use of Proxy.
3032

31-
The base library for this addon is [validated-changeset](https://github.com/validated-changeset/validated-changeset/). As a result, this functionality is available outside of Ember as well!
33+
The base library for this addon is [validated-changeset](https://github.com/adopted-ember-addons/validated-changeset/).
34+
As a result, this functionality is available outside of Ember as well!
3235

3336
## Philosophy
3437

35-
The idea behind a changeset is simple: it represents a set of valid changes to be applied onto any Object (`Ember.Object`, `DS.Model`, POJOs, etc). Each change is tested against an optional validation, and if valid, the change is stored and applied when executed.
38+
The idea behind a changeset is simple: it represents a set of valid changes to be applied onto any Object (`Ember.Object`, `DS.Model`, POJOs, etc).
39+
Each change is tested against an optional validation, and if valid, the change is stored and applied when executed.
3640

37-
Assuming a Data Down, Actions Up (DDAU) approach, a changeset is more appropriate compared to implicit 2 way bindings. Other validation libraries only validate a property _after_ it is set on an Object, which means that your Object can enter an invalid state.
41+
Assuming a Data Down, Actions Up (DDAU) approach, a changeset is more appropriate compared to implicit 2 way bindings.
42+
Other validation libraries only validate a property _after_ it is set on an Object, which means that your Object can enter an invalid state.
3843

39-
`ember-changeset` only allows valid changes to be set, so your Objects will never become invalid (assuming you have 100% validation coverage). Additionally, this addon is designed to be un-opinionated about your choice of form and/or validation library, so you can easily integrate it into an existing solution.
44+
`ember-changeset` only allows valid changes to be set, so your Objects will never become invalid (assuming you have 100% validation coverage).
45+
Additionally, this addon is designed to be un-opinionated about your choice of form and/or validation library, so you can easily integrate it into an existing solution.
4046

41-
The simplest way to incorporate validations is to use [`ember-changeset-validations`](https://github.com/adopted-ember-addons/ember-changeset-validations/), a companion addon to this one. It has a simple mental model, and there are no Observers or CPs involved – just pure functions.
47+
The simplest way to incorporate validations is to use [`ember-changeset-validations`](https://github.com/adopted-ember-addons/ember-changeset-validations/), a companion addon to this one.
48+
It has a simple mental model, and there are no Observers or CPs involved – just pure functions.
4249

4350
See also the [plugins](#plugins) section for addons that extend `ember-changeset`.
4451

@@ -62,22 +69,22 @@ function validatorFn({ key, newValue, oldValue, changes, content }) {
6269
}
6370

6471
let changeset = Changeset(user, validatorFn);
65-
user.get('firstName'); // "Michael"
66-
user.get('lastName'); // "Bolton"
72+
user.firstName; // "Michael"
73+
user.lastName; // "Bolton"
6774

6875
changeset.set('firstName', 'Jim');
6976
changeset.set('lastName', 'B');
70-
changeset.get('isInvalid'); // true
77+
changeset.isInvalid; // true
7178
changeset.get('errors'); // [{ key: 'lastName', validation: 'too short', value: 'B' }]
7279
changeset.set('lastName', 'Bob');
73-
changeset.get('isValid'); // true
80+
changeset.isValid; // true
7481

75-
user.get('firstName'); // "Michael"
76-
user.get('lastName'); // "Bolton"
82+
user.firstName; // "Michael"
83+
user.lastName; // "Bolton"
7784

7885
changeset.save(); // sets and saves valid changes on the user
79-
user.get('firstName'); // "Jim"
80-
user.get('lastName'); // "Bob"
86+
user.firstName; // "Jim"
87+
user.lastName; // "Bob"
8188
```
8289

8390
## Usage
@@ -86,29 +93,31 @@ First, create a new `Changeset` using the `changeset` helper or through JavaScri
8693

8794
```hbs
8895
{{! application/template.hbs}}
89-
{{#let (changeset model this.validate) as |changesetObj|}}
96+
{{#let (changeset this.model this.validate) as |changesetObj|}}
9097
<DummyForm
91-
@changeset={{changesetObj}}
92-
@submit={{this.submit}}
93-
@rollback={{this.rollback}} />
98+
@changeset={{changesetObj}}
99+
@submit={{this.submit}}
100+
@rollback={{this.rollback}}
101+
/>
94102
{{/let}}
95103
```
96104

97105
```js
98-
import Component from '@ember/component';
106+
import Component from '@glimmer/component';
107+
import { cached } from '@glimmer/tracking';
99108
import { Changeset } from 'ember-changeset';
100109

101110
export default FormComponent extends Component {
102-
init(...args) {
103-
super.init(...args)
104-
111+
@cached
112+
get changeset() {
105113
let validator = this.validate;
106-
this.changeset = Changeset(this.model, validator);
114+
return Changeset(this.model, validator);
107115
}
108116
}
109117
```
110118

111-
The helper receives any Object (including `DS.Model`, `Ember.Object`, or even POJOs) and an optional `validator` action. If a `validator` is passed into the helper, the changeset will attempt to call that function when a value changes.
119+
The helper receives any Object (including `DS.Model`, `Ember.Object`, or even POJOs) and an optional `validator` action.
120+
If a `validator` is passed into the helper, the changeset will attempt to call that function when a value changes.
112121

113122
```js
114123
// application/controller.js
@@ -126,6 +135,11 @@ export default class FormController extends Controller {
126135
return changeset.rollback();
127136
}
128137

138+
@action
139+
setChangesetProperty(changeset, path, evt) {
140+
return changeset.set(path, evt.target.value);
141+
}
142+
129143
@action
130144
validate({ key, newValue, oldValue, changes, content }) {
131145
// lookup a validator function on your favorite validation library
@@ -139,15 +153,16 @@ Then, in your favorite form library, simply pass in the `changeset` in place of
139153
```hbs
140154
{{! dummy-form/template.hbs}}
141155
<form>
142-
<Input @value={{changeset.firstName}} />
143-
<Input @value={{changeset.lastName}} />
156+
<input value={{this.changeset.firstName}} {{on "change" (fn this.setChangesetProperty this.changeset "firstName")}} />
157+
<input value={{this.changeset.lastName}} {{on "change" (fn this.setChangesetProperty this.changeset "lastName")}} />
144158
145-
<button {{on "click" this.submit changeset}}>Submit</button>
146-
<button {{on "click" this.rollback changeset}}>Cancel</button>
159+
<button {{on "click" this.submit this.changeset}}>Submit</button>
160+
<button {{on "click" this.rollback this.changeset}}>Cancel</button>
147161
</form>
148162
```
149163

150-
In the above example, when the input changes, only the changeset's internal values are updated. When the submit button is clicked, the changes are only executed if _all changes_ are valid.
164+
In the above example, when the input changes, only the changeset's internal values are updated.
165+
When the submit button is clicked, the changes are only executed if _all changes_ are valid.
151166

152167
On rollback, all changes are dropped and the underlying Object is left untouched.
153168

@@ -168,17 +183,21 @@ let changeset = Changeset(user, validatorFn, validationMap, { changeset: MyChang
168183

169184
## Changeset template helpers
170185

171-
`ember-changeset` overrides `set` and `get` in order to handle deeply nested setters. `mut` is simply an alias for `Ember.set(changeset, ...)`, thus we provide a `changeset-set` template helper if you are dealing with nested setters.
186+
`ember-changeset` overrides `set` and `get` in order to handle deeply nested setters.
187+
`mut` is simply an alias for `Ember.set(changeset, ...)`, thus we provide a `changeset-set` template helper if you are dealing with nested setters.
172188

173-
`changeset-get` is necessary for nested getters to easily retrieve leaf keys without error. Ember's templating layer will ask us for the first key it comes across as it traverses down the object (`user.firstName`). We keep track of the changes, but to also keep track of unchanged values and properly merge them in the changeset is difficult. If you are only accessing keys in an object that is only one level deep, you do not need this helper.
189+
`changeset-get` is necessary for nested getters to easily retrieve leaf keys without error.
190+
Ember's templating layer will ask us for the first key it comes across as it traverses down the object (`user.firstName`).
191+
We keep track of the changes, but to also keep track of unchanged values and properly merge them in the changeset is difficult.
192+
If you are only accessing keys in an object that is only one level deep, you do not need this helper.
174193

175194
```hbs
176195
<form>
177196
<input
178197
id="first-name"
179198
type="text"
180-
value={{changeset-get changeset "person.firstName"}}
181-
{{on "change" (fn this.updateFirstName changeset)}}>
199+
value={{changeset-get this.changeset "person.firstName"}}
200+
{{on "change" (fn this.updateFirstName this.changeset)}}>
182201
</form>
183202
```
184203

@@ -192,14 +211,15 @@ let changeset = Changeset(model, validatorFn, validationMap, { changesetKeys: ['
192211

193212
## Disabling Automatic Validation
194213

195-
The default behavior of `Changeset` is to automatically validate a field when it is set. Automatic validation can be disabled by passing `skipValidate` as an option when creating a changeset.
214+
The default behavior of `Changeset` is to automatically validate a field when it is set.
215+
Automatic validation can be disabled by passing `skipValidate` as an option when creating a changeset.
196216

197217
```js
198218
let changeset = Changeset(model, validatorFn, validationMap, { skipValidate: true });
199219
```
200220

201221
```hbs
202-
{{#let (changeset model this.validate skipValidate=true) as |changesetObj|}}
222+
{{#let (changeset this.model this.validate skipValidate=true) as |changesetObj|}}
203223
...
204224
{{/let}}
205225
```
@@ -210,7 +230,7 @@ Be sure to call `validate()` on the `changeset` before saving or committing chan
210230

211231
```ts
212232
import Component from '@glimmer/component';
213-
import { BufferedChangeset } from 'ember-changeset/types';
233+
import type { BufferedChangeset } from 'ember-changeset/types';
214234
import { Changeset } from 'ember-changeset';
215235

216236
interface Args {
@@ -233,20 +253,21 @@ export default class Foo extends Component<Args> {
233253
Other available types include the following. Please put in a PR if you need more types or access directly in `validated-changeset`!
234254

235255
```js
236-
import { ValidationResult, ValidatorMapFunc, ValidatorAction } from 'ember-changeset/types';
256+
import type { ValidationResult, ValidatorMapFunc, ValidatorAction } from 'ember-changeset/types';
237257
```
238258

239259
## Alternative Changeset
240260

241261
Enabled in 4.1.0. Experimental and subject to changes until 5.0.
242262

243-
We now ship a ValidatedChangeset that is a proposed new API we would like to introduce and see if it jives with users. The goal of this new feature is to remove confusing APIs and externalize validations.
263+
We now ship a `ValidatedChangeset` that is a proposed new API we would like to introduce and see if it jives with users.
264+
The goal of this new feature is to remove confusing APIs and externalize validations.
244265

245266
- ✂️ `save`
246267
- ✂️ `cast`
247268
- ✂️ `merge`
248269
- `errors` are required to be added to the Changeset manually after `validate`
249-
- `validate` takes a callback with the sum of changes and original content to be applied to your externalized validation. In user land you will call `changeset.validate((changes) => yupSchema.validate(changes))`
270+
- `validate` takes a callback with the sum of changes and original content to be applied to your externalized validation. In user land you will call `changeset.validate((changes) => yupSchema.validate(changes))`
250271

251272
```js
252273
import Component from '@glimmer/component';
@@ -363,12 +384,12 @@ Note that keys can be arbitrarily nested:
363384
You can use this property to locate a single error:
364385

365386
```hbs
366-
{{#if changeset.error.firstName}}
367-
<p>{{changeset.error.firstName.validation}}</p>
387+
{{#if this.changeset.error.firstName}}
388+
<p>{{this.changeset.error.firstName.validation}}</p>
368389
{{/if}}
369390
370-
{{#if changeset.error.address.zipCode}}
371-
<p>{{changeset.error.address.zipCode.validation}}</p>
391+
{{#if this.changeset.error.address.zipCode}}
392+
<p>{{this.changeset.error.address.zipCode.validation}}</p>
372393
{{/if}}
373394
```
374395

@@ -397,8 +418,8 @@ Note that keys can be arbitrarily nested:
397418
You can use this property to locate a single change:
398419

399420
```hbs
400-
{{changeset.change.firstName}}
401-
{{changeset.change.address.zipCode}}
421+
{{this.changeset.change.firstName}}
422+
{{this.changeset.change.address.zipCode}}
402423
```
403424

404425
**[⬆️ back to top](#api)**
@@ -425,10 +446,10 @@ Returns an array of errors. If your `validate` function returns a non-boolean va
425446
You can use this property to render a list of errors:
426447

427448
```hbs
428-
{{#if changeset.isInvalid}}
449+
{{#if this.changeset.isInvalid}}
429450
<p>There were errors in your form:</p>
430451
<ul>
431-
{{#each changeset.errors as |error|}}
452+
{{#each this.changeset.errors as |error|}}
432453
<li>{{error.key}}: {{error.validation}}</li>
433454
{{/each}}
434455
</ul>
@@ -458,7 +479,7 @@ You can use this property to render a list of changes:
458479

459480
```hbs
460481
<ul>
461-
{{#each changeset.changes as |change|}}
482+
{{#each this.changeset.changes as |change|}}
462483
<li>{{change.key}}: {{change.value}}</li>
463484
{{/each}}
464485
</ul>
@@ -502,13 +523,13 @@ changeset.get('pendingData'); // { name: 'Zoe', age: 21, address: { zipCode: '10
502523
Returns a Boolean value of the changeset's validity.
503524

504525
```js
505-
changeset.get('isValid'); // true
526+
changeset.isValid; // true
506527
```
507528

508529
You can use this property in the template:
509530

510531
```hbs
511-
{{#if changeset.isValid}}
532+
{{#if this.changeset.isValid}}
512533
<p>Good job!</p>
513534
{{/if}}
514535
```
@@ -520,13 +541,13 @@ You can use this property in the template:
520541
Returns a Boolean value of the changeset's (in)validity.
521542

522543
```js
523-
changeset.get('isInvalid'); // true
544+
changeset.isInvalid; // true
524545
```
525546

526547
You can use this property in the template:
527548

528549
```hbs
529-
{{#if changeset.isInvalid}}
550+
{{#if this.changeset.isInvalid}}
530551
<p>There were one or more errors in your form</p>
531552
{{/if}}
532553
```
@@ -538,7 +559,7 @@ You can use this property in the template:
538559
Returns a Boolean value of the changeset's state. A pristine changeset is one with no changes.
539560

540561
```js
541-
changeset.get('isPristine'); // true
562+
changeset.isPristine; // true
542563
```
543564

544565
If changes present on the changeset are equal to the content's, this will return `true`. However, note that key/value pairs in the list of changes must all be present and equal on the content, but not necessarily vice versa:
@@ -547,13 +568,13 @@ If changes present on the changeset are equal to the content's, this will return
547568
let user = { name: 'Bobby', age: 21, address: { zipCode: '10001' } };
548569

549570
changeset.set('name', 'Bobby');
550-
changeset.get('isPristine'); // true
571+
changeset.isPristine; // true
551572

552573
changeset.set('address.zipCode', '10001');
553-
changeset.get('isPristine'); // true
574+
changeset.isPristine; // true
554575

555576
changeset.set('foo', 'bar');
556-
changeset.get('isPristine'); // false
577+
changeset.isPristine; // false
557578
```
558579

559580
**[⬆️ back to top](#api)**
@@ -563,7 +584,7 @@ changeset.get('isPristine'); // false
563584
Returns a Boolean value of the changeset's state. A dirty changeset is one with changes.
564585

565586
```js
566-
changeset.get('isDirty'); // true
587+
changeset.isDirty; // true
567588
```
568589

569590
**[⬆️ back to top](#api)**
@@ -585,7 +606,7 @@ changeset.get('address.zipCode'); // "94016"
585606
You can use and bind this property in the template:
586607

587608
```hbs
588-
{{input value=changeset.firstName}}
609+
<input value={{this.changeset.firstName}}>
589610
```
590611

591612
Note that using `Ember.get` **will not necessarily work if you're expecting an Object**. On the other hand, using `changeset.get` will work just fine:
@@ -617,8 +638,8 @@ changeset.set('address.zipCode', '10001'); // "10001"
617638
You can use and bind this property in the template:
618639

619640
```hbs
620-
{{input value=changeset.firstName}}
621-
{{input value=changeset.address.country}}
641+
<input value={{this.changeset.firstName}}>
642+
<input value={{this.changeset.address.country}}>
622643
```
623644

624645
Any updates on this value will only store the change on the changeset, even with 2 way binding.
@@ -886,7 +907,8 @@ changeset.get('address.country'); // "United States"
886907
changeset.get('another.unwantedProp'); // undefined
887908
```
888909

889-
For example, this method can be used to only allow specified changes through prior to saving. This is especially useful if you also setup a `schema` object for your model (using Ember Data), which can then be exported and used as a list of allowed keys:
910+
For example, this method can be used to only allow specified changes through prior to saving.
911+
This is especially useful if you also setup a `schema` object for your model (using Ember Data), which can then be exported and used as a list of allowed keys:
890912

891913
```js
892914
// models/user.js
@@ -1006,7 +1028,7 @@ export default class FormController extends Controller {
10061028

10071029
```hbs
10081030
{{! application/template.hbs}}
1009-
<DummyForm @changeset={{changeset model this.validate}} />
1031+
<DummyForm @changeset={{this.changeset this.model this.validate}} />
10101032
```
10111033

10121034
Your action will receive a single POJO containing the `key`, `newValue`, `oldValue`, a one way reference to `changes`, and the original object `content`.
@@ -1106,12 +1128,12 @@ export default Component.extend({
11061128

11071129
```hbs
11081130
<input
1109-
type={{type}}
1110-
value={{get model valuePath}}
1111-
{{on "input" (fn this.checkValidity changeset)}}
1112-
{{on "blur" (fn this.validateProperty changeset valuePath)}}
1113-
disabled={{disabled}}
1114-
placeholder={{placeholder}}>
1131+
type={{this.type}}
1132+
value={{get this.model this.valuePath}}
1133+
{{on "input" (fn this.checkValidity this.changeset)}}
1134+
{{on "blur" (fn this.validateProperty this.changeset this.valuePath)}}
1135+
disabled={{this.disabled}}
1136+
placeholder={{this.placeholder}}>
11151137
```
11161138

11171139
## Contributors

0 commit comments

Comments
 (0)