dataflux 1.7.3 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -20,7 +20,7 @@ npm install dataflux
20
20
 
21
21
  Using jsDelivr CDN:
22
22
  ```html
23
- <script src="https://cdn.jsdelivr.net/npm/dataflux/dist/index.js"></script>
23
+ <script src="https://cdn.jsdelivr.net/npm/dataflux/dist/dataflux.min.js"></script>
24
24
  ```
25
25
 
26
26
  ## Examples
@@ -31,7 +31,7 @@ Consider the following hypothetical store/model declaration common to all the ex
31
31
 
32
32
  ```js
33
33
  // Content of your store.js
34
- import {Store, Model} from "dataflux";
34
+ const {Store, Model} = require("dataflux");
35
35
 
36
36
  // We create a new Store
37
37
  const store = new Store();
@@ -182,7 +182,7 @@ const callback = ({book, author}) => {
182
182
  // Objects are ready
183
183
  };
184
184
 
185
- const subKey = store.multipleSubscribe(requests, callback); // Subscribe
185
+ const subKey = store.multipleSubscribe(subscriptions, callback); // Subscribe
186
186
 
187
187
  store.unsubscribe(subKey); // Unsubscribe
188
188
  ```
@@ -205,8 +205,8 @@ class MyComponent extends React.Component {
205
205
  // Get all books with a price < 20
206
206
  store.findAll("book", "books", this, ({price}) => price < 20);
207
207
  // An attribute "books" will be added/updated in the
208
- // state (the rest of the state remains unchanged) every time
209
- // a book in our selection is inserted/deleted/edited.
208
+ // state every time a book in our selection is inserted/deleted/edited,
209
+ // the rest of the state remains unchanged.
210
210
 
211
211
  // findAll is a syntactic sugar for:
212
212
  // const callback = (books) => {this.setState({...this.state, books})};
@@ -234,16 +234,36 @@ The method `findAll` returns always an array. The method `findOne` returns a sin
234
234
 
235
235
  When the component will unmount, the `findAll` subscription will be automatically terminated without the need to unsubscribe. Be aware, `store.findAll()` injects the unsubscribe call inside `componentWillUnmount()`. If your component already implements `componentWillUnmount()`, then you will have to use `store.subscribe()` and `store.unsubscribe()` instead of `store.findAll()`, to avoid side effects when the component is unmounted.
236
236
 
237
+ In case you prefer React hooks:
238
+
239
+ ```js
240
+ function MyComponent() {
241
+ const [books, setBooks] = useState([]);
242
+
243
+ useEffect(() => {
244
+ const subKey = store.subscribe("books", setBooks, ({price}) => price < 20);
245
+
246
+ return () => {
247
+ store.unsubscribe(subKey); // Remember to unsubscribe
248
+ };
249
+ }, []);
250
+
251
+ return books.map(book => <Book onTitleChange={(title) => book.set("title", title)}/>);
252
+ }
253
+ ```
254
+
255
+
237
256
  ## Configuration
238
257
 
239
258
  The store can be configured with the following options:
240
259
 
241
260
 
242
- | Option | Description | Default |
243
- |-----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|
244
- | autoSave | It can be `true`, `false`, or an amount of milliseconds (integer). If `false`, you will have to perform `store.save()` manually. If `true`, the store will automatically perform `save()` when objects change. If an amount of milliseconds is provided, the objects are saved periodically AND when a change is detected. See [Editing objects](#editing-objects) for more information. | true |
245
- | saveDelay | An amount of milliseconds used to defer synching operations with the server. It triggers `store.save()` milliseconds after the last change on the store's objects is detedect. This allows to bundle together multiple changes operated by an interacting user. See [Editing objects](#editing-objects) for more information. | 1000 |
246
- | lazyLoad | A boolean. If set to `false`, the store is pre-populated with all the models' objects. If set to `true`, models' objects are loaded only on first usage (e.g., 'find', 'subscribe', 'getRelation'). LazyLoad operates per model, only the objects of the used models are loaded. | false |
261
+ | Option | Description | Default |
262
+ |-------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|
263
+ | autoSave | It can be `true`, `false`, or an amount of milliseconds (integer). If `false`, you will have to perform `store.save()` manually. If `true`, the store will automatically perform `store.save()` when objects change. If an amount of milliseconds is provided, the objects are saved periodically AND when a change is detected. See [Editing objects](#editing-objects) for more information. | true |
264
+ | saveDelay | An amount of milliseconds used to defer synching operations with the server. It triggers `store.save()` milliseconds after the last change on the store's objects is detedect. This allows to bundle together multiple changes operated by an interacting user. See [Editing objects](#editing-objects) for more information. | 1000 |
265
+ | lazyLoad | A boolean. If set to `false`, the store is pre-populated with all the models' objects. If set to `true`, models' objects are loaded only on first usage (e.g., 'find', 'subscribe', 'getRelation'). LazyLoad operates per model, only the objects of the used models are loaded. | false |
266
+ | autoRefresh | It can be `true`, `false`, or an amount of milliseconds (integer). If `false`, you will have to perform `store.refresh()` manually. If `true`, the store will automatically perform `store.refresh()` every 2 minutes. If an amount of milliseconds is provided, the `store.refresh()` is performed periodically. See [store methods](#store-methods) for more information. | false |
247
267
 
248
268
 
249
269
 
@@ -277,6 +297,7 @@ All the possible options for a model creation are (they are all optional):
277
297
  | hiddenFields | An array of attribute names that will never be sent back to the API. E.g., if you set `hiddenFields: ["pages"]`, a book object can contain an attribute `pages` locally, but this will be stripped out in PUT/POST requests. |
278
298
  | deep | A boolean defining if nested objects should be enriched with the object methods. | true |
279
299
  | lazyLoad | A boolean defining if the model should be lazy loaded on the first use. This takes precedence over the lazyLoad declared during store initialization. | false |
300
+ | validate | A dictionary containing functions to validate the objects of this model. See [objects validation](#objects-validation) | no validation |
280
301
 
281
302
  ### Operations
282
303
  As described in the table above, there are four possible operations: **retrieve, insert, update,** and **delete**. An operation can be defined as an operation object or a function.
@@ -289,10 +310,23 @@ An operation object is an object like follows:
289
310
  {
290
311
  "method": "get",
291
312
  "url": "https://api.example.com",
292
- "headers": {"Authorization": "bearer XXXX"}
313
+ "headers": {
314
+ "Authorization": "bearer XXXX"
315
+ },
316
+ "batch": false
293
317
  }
294
318
  ```
295
319
 
320
+ Possible parameters are:
321
+
322
+ | Parameter | Description | Default |
323
+ |-----------|--------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|
324
+ | method | HTTP method, accepted are get,post,put, and delete | "get" for retrieve, "post" for insert, "put" for update, and "delete" for delete |
325
+ | url | The url of the api | |
326
+ | headers | Headers for the HTTP request ([list](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields)). |
327
+ | batch | A boolean declaring if the API is able to receive an array of objects or an object at a time. This is not applicable for `retrieve`. | false |
328
+
329
+
296
330
  Usage example:
297
331
 
298
332
  ```js
@@ -521,41 +555,73 @@ The store has the following method.
521
555
  | find(type, filterFunction) | The promise-oriented method to access objects given a type and a filter function. If the filter function is missing, all the objects are returned. See [example 1](#example-1). |
522
556
  | delete(objects) | It deletes an array of objects. See [example 1](#example-3). |
523
557
  | delete(type, filterFunction) | It deleted objects given an array and a filter function. See [example 1](#example-3). |
524
- | insert(type, object) | It creates a new object of a given type and inserts it in the store. |
558
+ | insert(type, object) | It creates a new object of a given type and inserts it in the store. The object inserted MUST be ready to be persisted, read `mock()` below. |
559
+ | mock(type, object) | It creates a mock object of a given type and inserts it in the store. The mock object behaves exactly like a real object, except that it is not persisted (sent to the API) as long as you don't call `object.insert()`. This is useful when you want to create an object but you need to be able to change some properties before to send it to the API. Read [insert vs. mock](#insert-vs-mock). |
525
560
  | subscribe(type, callback, filterFunction) | The callback-oriented method to access objects given a type and a filter function. It returns the key of the subscription, needed to unsubscribe. If the filter function is missing, all the objects are returned. **DataFlux remembers your query and calls the callback every time any change is affecting the result of your query.** See [example 5](#example-5---observability). |
526
561
  | multipleSubscribe(subscriptions, callback) | A method to subscribe to multiple models. The first parameter is an array of models' names and filterFunctions, the second parameter is the callback to be called when the cumulative dataset is ready. E.g., `multipleSubscribe([["book", filterFunction1], ["author", filterFunction2]], callback)`. It returns the key of the subscription. See [example 5](#example-5---observability). |
527
562
  | unsubscribe(key) | Method to terminate a subscription given a subscription key. See [example 5](#example-5---observability). |
528
563
  | findOne(type, stateAttribute, context, filterFunction) | This method automatically injects and updates the React state with the requested data. If multiple objects satisfy the query, only the first is selected. The `stateAttribute` is the name of the attribute that will be added/updated in the state, the `context` is the React.Component. It automatically unsubscribe when the React.Component will unmount. See [example 6](#example-6---observability--react). |
529
564
  | findAll(type, stateAttribute, context, filterFunction) | This method automatically injects and updates the React state with the requested data. The `stateAttribute` is the name of the attribute that will be added/updated in the state, the `context` is the React.Component. It automatically unsubscribe when the React.Component will unmount. If the filter function is missing, all the objects are returned. See [example 6](#example-6---observability--react). |
530
565
  | preload(type) | This method allows to preLoad all objects of a given model. If you initialize the store with `lazyLoad:true`, the objects of a model are retrieved from the API at the first query performed on that model (e.g., at the first `.find()`). However, sometimes you may want to speed up the first query by pre loading the objects of a specific model while keeping `lazyLoad:true` on the store; in such a case you can use `store.preload(type)`. |
566
+ | save() | Persist the changes. Edited local objects will be sent to the REST APIs (insert/update/delete). See also [editing objects](#editing-objects). |
567
+ | save() | Persist the changes (`local -> remote`). Edited local objects will be sent to the REST APIs (insert/update/delete). See also [editing objects](#editing-objects). |
568
+ | refresh() | This method syncs all the objects in the store with the remote version offered by the REST APIs (`remote -> local`). Remote changes are applied locally, including adding/removing objects. Objects edited locally but not yet persisted are preserved locally (tip: you can also create a store with the `autoRefresh` option). |
569
+ ### Insert vs. Mock
570
+
571
+ If you do:
572
+ ```js
573
+ store.insert("book", {title: "The little prince"});
574
+ ```
575
+ when the store will try to save, the object `{title: "The little prince"}` will be sent to the API in a post request. However, the API may require an attribute "price" for each book object, hence the post request will fail.
576
+ Of course, you can add the attribute directly in the `.insert()` call; however, the value of this attribute may not be known at that time. For example, the user may need to input the price in a text field.
577
+
578
+ You may think to delay the `.insert()` up to when all the required attributes are available; however, **this is a bad idea**, because as long as the object is not in the store, you will not benefit from the observability provided by DataFlux. _E.g., your React state will not update automatically and you will need to handle the changes triggered by the input fields yourself_ ([reinventing the wheel](https://en.wikipedia.org/wiki/Reinventing_the_wheel)).
579
+
580
+ Solution, use `.mock()` to create a mock object:
581
+
582
+ ```js
583
+ store.mock("book", {title: "The little prince"});
584
+ ```
585
+
586
+ The mock object behaves exactly like a normal object: you can retrieve it with `.find`/`.findAll`/`.findOne`/`.subscribe`.
587
+
588
+ However, the mock object is not sent to the API as long as you don't call `.insert()` on the object itself. When you call `.insert()`, the mock object is promoted to real object.
589
+
590
+ > Warning: to promote a mock object, you need to call `object.insert()` on the object itself (you must first retrieve it) and NOT `store.insert()`.
591
+
531
592
 
532
593
  ## Store events
533
594
  The store emits the following events:
534
595
 
535
- | Name | Description |
536
- |---------|---------------------------------------------------------------------------------------------------------------------------------------|
537
- | error | To listen the errors emitted by the store. |
538
- | save | Possible emitted values are `start` and `end`. They are emitted when the store starts/finishes to persist the data (API interaction). |
539
- | loading | The event is emitted while a new model is loaded. The value contains something like `{status: "start", model: "book"}` |
596
+ | Name | Description |
597
+ |------------|---------------------------------------------------------------------------------------------------------------------------------------|
598
+ | error | To listen the errors emitted by the store. |
599
+ | save | Possible emitted values are `start` and `end`. They are emitted when the store starts/finishes to persist the data (API interaction). |
600
+ | loading | The event is emitted while a new model is loaded. The value contains something like `{status: "start", model: "book"}` |
601
+ | refreshing | The event is emitted while a model is refreshed. The value contains something like `{status: "start", model: "book"}` |
540
602
 
541
603
  ## Objects methods
542
604
  Each object created is enriched with the following methods.
543
605
 
544
606
 
545
- | Method | Description |
546
- |------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
547
- | getId() | It returns a unique ID used by the store to identify the object. The ID is unique inside a single model. Be aware, `object.id` and `objet.getId()` may return different values, since store's IDs can be different from the one of the REST API. |
548
- | set(attribute, value, hidden) | A method to set an attribute to the object. It provides some advantages compared to doing `object.attribute = value`, these are discussed in [below](#editing-objects). The third parameter is optional, and when set to true will set the attribute as hidden (see [hiddenFields](#models-creation)). |
549
- | setConstant(attribute, value) | A method to set an unmodifiable hidden attribute on the object. Setting the attribute as a constant will not propagate an update. |
607
+ | Method | Description |
608
+ |------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
609
+ | getId() | It returns a unique ID used by the store to identify the object. The ID is unique inside a single model. Be aware, `object.id` and `objet.getId()` may return different values, since store's IDs can be different from the one of the REST API. |
610
+ | set(attribute, value, hidden) | A method to set an attribute to the object. It provides some advantages compared to doing `object.attribute = value`, these are discussed in [below](#editing-objects). The third parameter is optional, and when set to true will set the attribute as hidden (see [hiddenFields](#models-creation)). |
611
+ | setConstant(attribute, value) | A method to set an unmodifiable hidden attribute on the object. Setting the attribute as a constant will not propagate an update. |
550
612
  | get(attribute, defaultValue) | Method to retrieve the value of an attribute. It does not provide any advantage compared to accessing directly the attribute (e.g., `author.name`); except for hidden fields and constants, which can be retrieved only with the `.get` method. Additionally, you can provide a default value as a second parameter in case the object doesn't have that attribute. |
551
- | getRelation(model, filterFunction) | To get all the objects respecting a specific relation with this object (see [model relations](#model-relations)). |
552
- | save() | Method to save the object. You can do `store.save()` instead. |
553
- | destroy() | Method to delete the object. You can do `store.delete()` instead. |
554
- | toJSON() | It returns a pure JSON representation of the object. |
555
- | toString() | It returns a string representation of the object. |
556
- | getFingerprint() | It returns a hash of the object. The hash changes at every change of the object or of any nested object. Useful to detect object changes. |
557
- | getModel() | It returns the model of this object. Mostly useful to do `object.getModel().getType()` and obtain a string defining the type of the object. |
558
- ### Deeper Objects
613
+ | getRelation(model, filterFunction) | To get all the objects respecting a specific relation with this object (see [model relations](#model-relations)). |
614
+ | save() | Method to save the object. You can do `store.save()` instead. |
615
+ | destroy() | Method to delete the object. You can do `store.delete()` instead. |
616
+ | toJSON() | It returns a pure JSON representation of the object. |
617
+ | toString() | It returns a string representation of the object. |
618
+ | getFingerprint() | It returns a hash of the object. The hash changes at every change of the object or of any nested object. Useful to detect object changes. |
619
+ | getModel() | It returns the model of this object. Mostly useful to do `object.getModel().getType()` and obtain a string defining the type of the object. |
620
+ | getError() | If an operation on an object triggers an error, this error can be retrieved with `getError()`. This allows to observe specific objects' errors, instead of the generic `store.on("error", ...)`. |
621
+ | getError(attributeName) | This method allows you to check if the specificed attribute generated any error according to the validation property specified in the model. See [objects validation](#objects-validation). |
622
+ | setError(error) | Additionally to DataFlux's errors, you can trigger your own errors with this method. Other components observing this objet's error will be notified. |
623
+
624
+ ### Deep Objects
559
625
  When a model is declared with the option `deep: true` (default, see [model creation](#models-creation)), all the sub objects will also offer many of the methods above.
560
626
 
561
627
  Imagine the API returns:
@@ -592,8 +658,75 @@ store.find("book")
592
658
  });
593
659
  ```
594
660
 
661
+ ### Objects validation
662
+
663
+ DataFlux supports automatic validation of the objects. This is important for two reasons:
664
+ * Objects that contain invalid attributes' values are not sent back to the API;
665
+ * Validation errors can be used to automatically suggest errors in the UI.
666
+
667
+ To specify the validation of the objects for a specific model, you need to add a `validate` dictionary during model creation.
668
+
669
+ ```js
670
+ const book = new Model("book", {
671
+ retrieve: {
672
+ url: "https://rest.example.net/api/v1/books/"
673
+ },
674
+ validate: {
675
+ isbn: ({isbn}) => {
676
+ if (typeof(isbn) !== "number") {
677
+ throw new Error("The isbn must be a number");
678
+ }
679
+ },
680
+ title: ({title}) => {
681
+ if (!title) {
682
+ throw new Error("The title is mandatory");
683
+ }
684
+ }
685
+ }
686
+ });
687
+ ```
688
+
689
+ Each key of the `validate` dictionary is an attribute of the object (a field name), each value is a function receiving in input the object and throwing an error in case the field is not valid.
690
+
691
+ > Be aware: A validation function cannot return a boolean, you have to throw an error.
692
+
693
+ To validate a specific attribute of an object, you can do `object.getError(attributeName)` (e.g., `book.getError(isbn)`). In case of error, a string describing the error is returned, `false` otherwise.
694
+
695
+ Example of usage in a React component.
696
+
697
+ ```js
698
+ class MyComponent extends React.Component {
699
+ constructor(props) {
700
+ super(props);
701
+ }
702
+
703
+ render(){
704
+ const {book} = this.state;
705
+
706
+ // A textfield to edit the title of a book
707
+ return <TextField
708
+ value={book.title}
709
+ onChange={store.handleChange(book, "title")}
710
+ error={object.getError("title")}
711
+ // E.g., in material UI the test field will be red in case of errors
712
+ />;
713
+ }
714
+ }
715
+ ```
716
+
717
+ > Be aware: if you do `object.getError()` without specifying any attribute, you will receive object errors not associated with any field, such as API errors. This is similar to `store.on("error")`
718
+
595
719
 
596
720
  ## Editing objects
721
+
722
+ The preferred method to edit objects is using the `.set()` method that each object has, instead of editing directly the attributes. However, there are a few notions to keep in mind.
723
+
724
+ #### Client-side object validaion will not work when editing attributes directly.
725
+
726
+ You will be able to validate objects only if you use `.set()`. If you edit directly the attribute (e.g., `book.title = "test"`) errors will be discovered only by the API.
727
+
728
+ #### Auto save may not work when editing attributes directly.
729
+
597
730
  The option `autoSave` can be `true`, `false`, or a number (milliseconds).
598
731
 
599
732
  * When `autoSave` is set to `false`, the following operations are equivalent:
package/dist/BasicObj.js CHANGED
@@ -24,20 +24,21 @@ function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedec
24
24
 
25
25
  function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
26
26
 
27
- function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "set"); _classApplyDescriptorSet(receiver, descriptor, value); return value; }
27
+ function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
28
28
 
29
- function _classApplyDescriptorSet(receiver, descriptor, value) { if (descriptor.set) { descriptor.set.call(receiver, value); } else { if (!descriptor.writable) { throw new TypeError("attempted to set read only private field"); } descriptor.value = value; } }
29
+ function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }
30
30
 
31
- function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
31
+ function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "set"); _classApplyDescriptorSet(receiver, descriptor, value); return value; }
32
32
 
33
33
  function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); }
34
34
 
35
- function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }
35
+ function _classApplyDescriptorSet(receiver, descriptor, value) { if (descriptor.set) { descriptor.set.call(receiver, value); } else { if (!descriptor.writable) { throw new TypeError("attempted to set read only private field"); } descriptor.value = value; } }
36
36
 
37
37
  function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
38
38
 
39
39
  var dateRegex = new RegExp("^[0-9][0-9][0-9][0-9]-[0-9].*T[0-9].*Z$");
40
40
  exports.dateRegex = dateRegex;
41
+ var globalError = "_object";
41
42
 
42
43
  function setValues(values, model, SubObj, parent, context) {
43
44
  Object.keys(values).forEach(function (key) {
@@ -64,6 +65,10 @@ var _setHidden = /*#__PURE__*/new WeakMap();
64
65
 
65
66
  var _id = /*#__PURE__*/new WeakMap();
66
67
 
68
+ var _error = /*#__PURE__*/new WeakMap();
69
+
70
+ var _model = /*#__PURE__*/new WeakMap();
71
+
67
72
  var BasicObj = /*#__PURE__*/_createClass(function BasicObj(values, model) {
68
73
  var _this = this;
69
74
 
@@ -79,10 +84,26 @@ var BasicObj = /*#__PURE__*/_createClass(function BasicObj(values, model) {
79
84
  value: null
80
85
  });
81
86
 
87
+ _classPrivateFieldInitSpec(this, _error, {
88
+ writable: true,
89
+ value: {}
90
+ });
91
+
92
+ _classPrivateFieldInitSpec(this, _model, {
93
+ writable: true,
94
+ value: void 0
95
+ });
96
+
97
+ _defineProperty(this, "setId", function (id) {
98
+ _classPrivateFieldSet(_this, _id, id);
99
+ });
100
+
82
101
  _defineProperty(this, "getId", function () {
83
102
  if (!_classPrivateFieldGet(_this, _id)) {
84
103
  if (_this.id && (typeof _this.id === "string" || typeof _this.id === "number")) {
85
104
  _classPrivateFieldSet(_this, _id, _this.id.toString());
105
+
106
+ delete _this.setId;
86
107
  } else {
87
108
  _classPrivateFieldSet(_this, _id, (0, _uuid.v4)());
88
109
  }
@@ -106,15 +127,39 @@ var BasicObj = /*#__PURE__*/_createClass(function BasicObj(values, model) {
106
127
  }
107
128
 
108
129
  _this[attribute] = value;
130
+
131
+ _classPrivateFieldGet(_this, _model).validateObjectAttribute(_this, attribute);
109
132
  }
110
133
 
111
134
  return _this.update();
112
135
  });
113
136
 
114
- _defineProperty(this, "setConstant", function (attribute, value) {
137
+ _defineProperty(this, "getError", function () {
115
138
  var _classPrivateFieldGet3;
116
139
 
117
- _classPrivateFieldGet(_this, _setHidden)[attribute] = (_classPrivateFieldGet3 = _classPrivateFieldGet(_this, _setHidden)[attribute]) !== null && _classPrivateFieldGet3 !== void 0 ? _classPrivateFieldGet3 : value;
140
+ var attribute = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
141
+ var key = attribute ? attribute : globalError;
142
+ return (_classPrivateFieldGet3 = _classPrivateFieldGet(_this, _error)[key]) !== null && _classPrivateFieldGet3 !== void 0 ? _classPrivateFieldGet3 : false;
143
+ });
144
+
145
+ _defineProperty(this, "setError", function (error) {
146
+ var attribute = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
147
+
148
+ if (error && attribute) {
149
+ _classPrivateFieldGet(_this, _error)[attribute] = error;
150
+ } else if (error && !attribute) {
151
+ _classPrivateFieldGet(_this, _error)[globalError] = error;
152
+ } else if (!error && attribute) {
153
+ delete _classPrivateFieldGet(_this, _error)[attribute];
154
+ } else {
155
+ _classPrivateFieldSet(_this, _error, {});
156
+ }
157
+ });
158
+
159
+ _defineProperty(this, "setConstant", function (attribute, value) {
160
+ var _classPrivateFieldGet4;
161
+
162
+ _classPrivateFieldGet(_this, _setHidden)[attribute] = (_classPrivateFieldGet4 = _classPrivateFieldGet(_this, _setHidden)[attribute]) !== null && _classPrivateFieldGet4 !== void 0 ? _classPrivateFieldGet4 : value;
118
163
  });
119
164
 
120
165
  _defineProperty(this, "toJSON", function () {
@@ -153,6 +198,8 @@ var BasicObj = /*#__PURE__*/_createClass(function BasicObj(values, model) {
153
198
  _defineProperty(this, "update", function () {
154
199
  return Promise.resolve();
155
200
  });
201
+
202
+ _classPrivateFieldSet(this, _model, model);
156
203
  });
157
204
 
158
205
  exports.BasicObj = BasicObj;