-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
Copy pathattr.js
165 lines (141 loc) · 4.71 KB
/
attr.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import { computed } from '@ember/object';
import { assert } from '@ember/debug';
import { DEBUG } from '@glimmer/env';
import { recordDataFor } from '@ember-data/store/-private';
import { RECORD_DATA_ERRORS } from '@ember-data/canary-features';
import { computedMacroWithOptionalParams } from './util';
/**
@module @ember-data/model
*/
function getDefaultValue(record, options, key) {
if (typeof options.defaultValue === 'function') {
return options.defaultValue.apply(null, arguments);
} else {
let defaultValue = options.defaultValue;
assert(
`Non primitive defaultValues are not supported because they are shared between all instances. If you would like to use a complex object as a default value please provide a function that returns the complex object.`,
typeof defaultValue !== 'object' || defaultValue === null
);
return defaultValue;
}
}
function hasValue(internalModel, key) {
return recordDataFor(internalModel).hasAttr(key);
}
/**
`attr` defines an attribute on a [Model](/api/data/classes/DS.Model.html).
By default, attributes are passed through as-is, however you can specify an
optional type to have the value automatically transformed.
Ember Data ships with four basic transform types: `string`, `number`,
`boolean` and `date`. You can define your own transforms by subclassing
[Transform](/api/data/classes/DS.Transform.html).
Note that you cannot use `attr` to define an attribute of `id`.
`attr` takes an optional hash as a second parameter, currently
supported options are:
- `defaultValue`: Pass a string or a function to be called to set the attribute
to a default value if none is supplied.
Example
```app/models/user.js
import Model, { attr } from '@ember-data/model';
export default Model.extend({
username: attr('string'),
email: attr('string'),
verified: attr('boolean', { defaultValue: false })
});
```
Default value can also be a function. This is useful it you want to return
a new object for each attribute.
```app/models/user.js
import Model, { attr } from '@ember-data/model';
export default Model.extend({
username: attr('string'),
email: attr('string'),
settings: attr({
defaultValue() {
return {};
}
})
});
```
The `options` hash is passed as second argument to a transforms'
`serialize` and `deserialize` method. This allows to configure a
transformation and adapt the corresponding value, based on the config:
```app/models/post.js
import Model, { attr } from '@ember-data/model';
export default Model.extend({
text: attr('text', {
uppercase: true
})
});
```
```app/transforms/text.js
import Transform from '@ember-data/serializer/transform';
export default Transform.extend({
serialize(value, options) {
if (options.uppercase) {
return value.toUpperCase();
}
return value;
},
deserialize(value) {
return value;
}
})
```
@method attr
@param {String|Object} type the attribute type
@param {Object} options a hash of options
@return {Attribute}
*/
function attr(type, options) {
if (typeof type === 'object') {
options = type;
type = undefined;
} else {
options = options || {};
}
let meta = {
type: type,
isAttribute: true,
kind: 'attribute',
options: options,
};
return computed({
get(key) {
if (DEBUG) {
if (['_internalModel', 'recordData', 'currentState'].indexOf(key) !== -1) {
throw new Error(
`'${key}' is a reserved property name on instances of classes extending Model. Please choose a different property name for your attr on ${this.constructor.toString()}`
);
}
}
let internalModel = this._internalModel;
if (hasValue(internalModel, key)) {
return internalModel.getAttributeValue(key);
} else {
return getDefaultValue(this, options, key);
}
},
set(key, value) {
if (DEBUG) {
if (['_internalModel', 'recordData', 'currentState'].indexOf(key) !== -1) {
throw new Error(
`'${key}' is a reserved property name on instances of classes extending Model. Please choose a different property name for your attr on ${this.constructor.toString()}`
);
}
}
if (RECORD_DATA_ERRORS) {
let oldValue = this._internalModel._recordData.getAttr(key);
if (oldValue !== value) {
let errors = this.get('errors');
if (errors.get(key)) {
errors.remove(key);
}
this._markInvalidRequestAsClean();
}
}
return this._internalModel.setDirtyAttribute(key, value);
},
}).meta(meta);
}
export default computedMacroWithOptionalParams(attr);