|
| 1 | +## Update record attributes |
| 2 | + |
| 3 | +#### record.attrName = val |
| 4 | + |
| 5 | +Assign record attribute. |
| 6 | + |
| 7 | +1. Assign new attribute value. |
| 8 | +2. If value has changed, |
| 9 | + - trigger `change:attrName` *( record, value )* event. |
| 10 | + - trigger `change` *( record )* event. |
| 11 | + |
| 12 | +#### record.set( { attrName : val, ... }, options? : `options` ) |
| 13 | + |
| 14 | +Bulk assign record's attributes, possibly taking options. |
| 15 | + |
| 16 | +1. Assign new attribut values. |
| 17 | +2. For any changed attribute, trigger `change:attrName` *( record, val, options )* event. |
| 18 | +3. If any attribute has changed: |
| 19 | + - trigger `change` *(record, options)* event. |
| 20 | + |
| 21 | +#### `options` { parse : true } |
| 22 | + |
| 23 | +Transform `record.set` argument with user-defined parse logic. Typically used to process the responce from the server to make user-defined JSON format conversion. |
| 24 | + |
| 25 | +#### `options` { merge : true } |
| 26 | + |
| 27 | +Merge attributes of record and collection types instead of replacement. If the new instance of record or collection is to be assigned, |
| 28 | +update the current instances instead. |
| 29 | + |
| 30 | +#### record.assignFrom( otherRecord ) |
| 31 | + |
| 32 | +Makes the `record` to be the copy of `otherRecord`, recursively assigning all attributes. |
| 33 | + |
| 34 | +Works similar to `record.set( otherRecord.attributes, { merge : true }); |
| 35 | + |
| 36 | +## Listening to events |
| 37 | + |
| 38 | +### Deep changes detection |
| 39 | + |
| 40 | +Records automatically listens to change events of nested records and collections, and triggers the corresponding change events on its attributes. It means that the single attribute change deeply inside of aggregation tree will trigger change events on all the parents in a sequence. |
| 41 | + |
| 42 | +#### `attrDef` attr : Type.has.changeEvents( false ) |
| 43 | + |
| 44 | +Do _not_ listen for the inner changes of the `attr`. |
| 45 | + |
| 46 | +### Custom attribute change watchers |
| 47 | + |
| 48 | +Declaratively attach the watcher in attribute definition. Subscription will be managed automatically. |
| 49 | + |
| 50 | +#### `attrDef` attr : Type.has.watcher( 'methodName' ) |
| 51 | +#### `attrDef` attr : Type.has.watcher( function( value, name ){ ... } ) |
| 52 | + |
| 53 | +Attach `change:attr` event listener to the particular record's attribute. |
| 54 | + |
| 55 | +_Watcher function_ has the signature `( attrValue, attrName ) => void` and is executed in the context of the record. |
| 56 | + |
| 57 | +```javascript |
| 58 | +@define class User extends Record { |
| 59 | + static attributes = { |
| 60 | + name : String.has.watcher( 'onNameChange' ), |
| 61 | + isAdmin : Boolean, |
| 62 | + } |
| 63 | + |
| 64 | + onNameChange(){ |
| 65 | + // Cruel. But we need it for the purpose of the example. |
| 66 | + this.isAdmin = this.name.indexOf( 'Admin' ) >= 0; |
| 67 | + } |
| 68 | +} |
| 69 | +``` |
| 70 | + |
| 71 | +### Generic event API |
| 72 | + |
| 73 | +Generic events API can be used to listen to any record's events. |
| 74 | + |
| 75 | +#### listener.listenTo( record, eventName, handler ) |
| 76 | +#### listener.listenTo( record, { eventName : handler, ... } ) |
| 77 | + |
| 78 | +Subscribe for `eventName` from the record. Subscription will be automatically cancelled when messenger is disposed. |
| 79 | +The handler is executed in the context of the listener. |
| 80 | + |
| 81 | +#### listener.stopListening( record?, eventName?, hander? ) |
| 82 | +#### listener.stopListening( record?, { eventName : handler, ... } ) |
| 83 | + |
| 84 | +`listener.stopListening( record )` stops subscriptions from the record. |
| 85 | + |
| 86 | +`listener.stopListening()` stops _all_ event subscriptions and _is called automatically_ from the `listener.dispose()`. |
| 87 | + |
| 88 | +### Low-level event API |
| 89 | + |
| 90 | +Low level event API is more efficient but requires precise `off()` call to stop the subscription. It must be done |
| 91 | +in overidden `dispose()` method to prevent memory leaks. |
| 92 | + |
| 93 | +#### record.onChanges( handler, context? ) |
| 94 | + |
| 95 | +Subscribe for `change` event from the record. If `context` is passed, handler will be called in this context. |
| 96 | + |
| 97 | +#### record.on( eventName, handler, context? ) |
| 98 | +#### record.on( { eventName : handler, ... }, context? ) |
| 99 | + |
| 100 | +Subscribe for the `eventName` from the record. If `context` is passed, handler will be called in this context. |
| 101 | + |
| 102 | +#### record.off( eventName, handler, context? ) |
| 103 | +#### record.off( { eventName : handler, ... }, context? ) |
| 104 | + |
| 105 | +Cancel event subscription. |
| 106 | + |
| 107 | +## Transactions |
| 108 | + |
| 109 | +All record updates occures in the scope of transactions. Transaction is the sequence of changes which results in a single `change` event. |
| 110 | + |
| 111 | +Transaction can be opened either manually or implicitly with calling `set()` or assigning an attribute. |
| 112 | +Any additional changes made to the record in `change:attr` event handler will be executed in the scope of the original transaction, and won't trigger additional `change` events. |
| 113 | + |
| 114 | +#### record.transaction( fun ) |
| 115 | + |
| 116 | +Execute the all changes made to the record in `fun` as single transaction. |
| 117 | + |
| 118 | +```javascript |
| 119 | +some.record.transaction( record => { |
| 120 | + record.a = 1; // `change:a` is triggered. |
| 121 | + record.b = 2; // `change:b` is triggered. |
| 122 | +}); // `change` is triggered. |
| 123 | +``` |
| 124 | + |
| 125 | +Manual transactions with attribute assignments are superior to `record.set()` in terms of both performance and flexibility. |
| 126 | + |
| 127 | +## Change inspection methods |
| 128 | + |
| 129 | +Following API might be useful in change event listeners. |
| 130 | + |
| 131 | +#### record.changed |
| 132 | + |
| 133 | +The `changed` property is the internal hash containing all the attributes that have changed during its last transaction. |
| 134 | +Please do not update `changed` directly since its state is internally maintained by `set()`. |
| 135 | +A copy of `changed` can be acquired from `changedAttributes()`. |
| 136 | + |
| 137 | +#### record.changedAttributes( attrs? ) |
| 138 | + |
| 139 | +Retrieve a hash of only the record's attributes that have changed during the last transaction, |
| 140 | +or false if there are none. Optionally, an external attributes hash can be passed in, |
| 141 | +returning the attributes in that hash which differ from the record. |
| 142 | +This can be used to figure out which portions of a view should be updated, |
| 143 | +or what calls need to be made to sync the changes to the server. |
| 144 | + |
| 145 | +#### record.previous( attr ) |
| 146 | + |
| 147 | +During a "change" event, this method can be used to get the previous value of a changed attribute. |
| 148 | + |
| 149 | +```javascript |
| 150 | +@define class Person extends Record{ |
| 151 | + static attributes = { |
| 152 | + name: '' |
| 153 | + } |
| 154 | +} |
| 155 | + |
| 156 | +const bill = new Person({ |
| 157 | + name: "Bill Smith" |
| 158 | +}); |
| 159 | + |
| 160 | +bill.on("change:name", ( record, name ) => { |
| 161 | + alert( `Changed name from ${ bill.previous('name') } to ${ name }`); |
| 162 | +}); |
| 163 | + |
| 164 | +bill.name = "Bill Jones"; |
| 165 | +``` |
| 166 | + |
| 167 | +#### record.previousAttributes() |
| 168 | + |
| 169 | +Return a copy of the record's previous attributes. Useful for getting a diff between versions of a record, or getting back to a valid state after an error occurs. |
0 commit comments