this
inside the handler may cause troubles
data: {
value: function (prop) {
prop.listenTo("dataPromise", function (ev, newPromise) {
newPromise.then((response) => {
if (response) {
prop.resolve(response);
}
});
});
}
}
this.data
prop.listenTo(prop.lastSet, prop.resolve);
sets a default value
listenTo
.data: {
value: function (prop) {
prop.resolve([]); // set a default value
prop.listenTo("dataPromise", function (ev, newPromise) {
newPromise.then((response) => {
if (response) {
prop.resolve(response);
}
});
});
}
}
prop.listenTo(prop.lastSet, prop.resolve);
as allowing someone to set the property to a value, it doesn't really mean "default"
myProp: {
value({resolve}) {
resolve(3) //-> 3 is the default value
}
}
resolve()
this.data
creates extra levels of miss direction
plate_code
as the primary key. In the car model I defined the QueryLogic's identity to plate_code
. The car's plate code is filled in a form by the user and when I call save
it always call the updateData, because plate_code is allready defined, but not created yet. How to solve this? I can imagine a lot of API endpoint that could use a natural pk, for example: invoice -> invoice_number (some countries use pre-printed invoices, with number already assigned, where the user must match the pre-printed number), product -> product_code where for example the company has his own nomenclature for product codes. How to handle this cases where the identity values are filled by the user in the app before they exists and should fire the createData method instead of updateData?
resolve
the data will not be set. you told me, that resolve do not set ... resolve is like "what flows out"
resolve
returns what is read ... how is that changing things for you?
isNew()
function
value()
functions don't use lastSet
@justinbmeyer Interesting, is there a property that only exists if the instance was retrieve from the API? In may case I am using realtimeRestModel
I thought about changing the isNew() function but i also saw that the save funtion in can-connect/constructor/constructor.js
is evaluating if id is undefined or not, instead of isNew() and I was not sure if I changed the isNew function it will affect the save:
save: function(instance){
var serialized = this.serializeInstance(instance);
var id = this.id(instance);
var self = this;
if(id === undefined) {
// If `id` is undefined, we are creating this instance.
// It should be given a local id and temporarily added to the cidStore
// so other hooks can get back the instance that's being created.
var cid = this._cid++;
// cid is really a token to be able to reference this transaction.
this.cidStore.addReference(cid, instance);
// Call the data layer.
// If the data returned is undefined, don't call `createdInstance`
return this.createData(serialized, cid).then(function(data){
// if undefined is returned, this can't be created, or someone has taken care of it
if(data !== undefined) {
self.createdInstance(instance, data);
}
self.cidStore.deleteReference(cid, instance);
return instance;
});
} else {
return this.updateData(serialized).then(function(data){
if(data !== undefined) {
self.updatedInstance(instance, data);
}
return instance;
});
}
},
How can I extend or overwrite the isNew
function without modifying the original one?
parseInstanceData(data){
data.createdKey = data.license;
return data;
}
@justinbmeyer Ok, thanks. I also saw that addInstanceReference
uses if (id===undefined)
:
addInstanceReference: function(instance, id) {
var ID = id || this.id(instance);
if(ID === undefined) {
// save in the newInstanceStore store temporarily.
this.newInstanceStore.addReference(instance);
} else {
this.instanceStore.addReference( ID, instance );
}
},
and many other functions inside can-connect/constructor/store
use this same logic. This affects me because im using realtimeRestModel
.
id
to each endpoint. But I really want to avoid this. I think is not a good practice for the database to maintain a extra index that really doesn't has any meaning
createdKey
as the identity, right?
Ok, but ill need to modify each url, because createdKey
is not the real Identity
url: {
getListData: "GET /api/todos/find",
getData: "GET /api/todo/get/{id}",
createData: "POST /api/todo/create",
updateData: "POST /api/todo/update?id={id}",
destroyData: "POST /api/todo/delete?id={id}"
}
the updateData would set createdKey
value as the id. I could specify a function returning the Promise.
I already did that, but in my case updateData is a generic function that reads the identity value and creates dynamically the url endpoint. I created a custom myRestModel module, that takes 2 parameters: the DefineMap and the QueryLogic. My model actually looks like this:
import { DefineMap, DefineList, QueryLogic } from 'can';
import myRestModel from '~/models/my-rest-model';
const Invoice = DefineMap.extend("Invoice",{
seal: false
}, {
'invoice_number': {
type: 'string',
default: '001-001'
}
});
Invoice.List = DefineList.extend({
'#': Invoice
});
Invoice.connection = myRestModel(
Invoice,
new QueryLogic({
identity: ['invoice_number']
})
);
export default Invoice;
And myRestModel looks like this:
import {realtimeRestModel,namespace,ajax} from 'can';
const myRestModel = function(Model,queryLogic,options) {
var url = '/api/'+Model.shortName.toLowerCase();
var connection = realtimeRestModel({
url: {
updateData: function(param) {
var ids = queryLogic.identityKeys();
var idQuery = [];
for (const i in ids) {
idQuery.push(`${ids[i]}=eq.${param[ids[i]]}`);
}
idQuery = idQuery.join('&');
return new Promise(function(resolve, reject){
$.ajax({
url: `${url}?${idQuery}`,
type: 'PATCH',
dataType: 'json',
data: param,
beforeSend: function(xhr,settings) {
xhr.setRequestHeader('Prefer', 'return=representation');
xhr.setRequestHeader('Accept', 'application/vnd.pgrst.object+json');
return xhr;
}
}).then(resolve, reject);
});
}
},
Map: Model,
updateInstanceWithAssignDeep: true,
List: Model.List,
queryLogic: queryLogic
});
return connection
}
export default myRestModel;
That way, each model url endpoint are build dynamically. Now, if I change the Identity property I have to write each Url Promise function for each model.
myRestModel
that reads the identityKeys and builds the url and adds this custom headers. If I change the identity keys to createdKey
this logic will have no sense anymore. I could pass a third parameter options
that contains the "real identity" to build dynamically the url endpoints.
For exmaple:
Invoice.connection = myRestModel(
Invoice,
new QueryLogic({
identity: ['createdKey']
}),
ids: ['invoice_number']
);
and then in myRestModel instead of reading identityKeys like this: var ids = queryLogic.identityKeys();
I could read them from options, like this: var ids = options.ids;
can-query-logic.js:103 you probably can get the identity keys some other way
class="{{menu}}"
or el:className:from="menu"