realm-js
realm-js copied to clipboard
Class-based Models do not Support Embedded Objects in Typescript
How frequently does the bug occur?
Seen once
Description
Currently, it is not possible to add an new embedded object to a model without casting a plain javascript object as the class based model. This is due to not being able to call new on an embedded object, even when setting it directly to its parent class. For example:
realm.write(() => {
item.child = new Child(realm, {param1: "foo"});
});
This will throw an error that it's not possible to create an embedded object. Also:
realm.write(() => {
item.child = { param1: "foo"};
});
Will be successful in runtime, but it will show a typescript error.
Currently the only work around is:
realm.write(() => {
item.child = { param1: "foo" } as Child;
});
Where Child is the linked object of child on item and extends Realm.Object<Child>.
Stacktrace & log output
No response
Can you reproduce the bug?
Yes, always
Reproduction Steps
No response
Version
11.0.0
What SDK flavour are you using?
Local Database only
Are you using encryption?
No, not using encryption
Platform OS and version(s)
All
Build environment
Which debugger for React Native: ..
Cocoapods version
No response
any updates?
Any update on this ?
Seeing this on 11.8.0.
const createParent = (): void => {
realm.write(() => {
//Type '{ label: string; }' is missing the following properties from type 'Child': keys, entries, toJSON, isValid, and 8 more.
realm.create<Parent>(Parent, { subSchema: { label: 'label' } }); // <== this is the problem
});
};
Shemas :
export class Parent extends Realm.Object<Parent> {
static schema: Realm.ObjectSchema = {
name: 'Parent',
primaryKey: '_id',
properties: {
_id: { type: 'objectId', default: () => new Realm.BSON.ObjectId() },
subSchema: 'Child'
}
};
_id!: Realm.BSON.ObjectId;
subSchema!: Child;
}
export class Child extends Realm.Object<Child, 'label'> {
static schema: Realm.ObjectSchema = {
name: 'Child',
embedded: true,
properties: {
label: 'string'
}
};
label!: string;
}
Thanks !
Any updates or advice from the team on this? I am also getting the same Type error of ...keys, entries, toJSON, isValid, and 8 more... and am wondering what the recommended approach is for typing embedded objects before writing them into a new object assuming there is a way other than using an as assertion? Thanks!
Still an issue in version 12.3
Any updates on this? Same problem here. This is a pretty common scenario. Spring would like to use this feature
Hey, adding a little bit more context here. Not being able to use class based models to add embedded objects also kills the use of setter and getters. We are trying to implement something like this in our data models:
class Model extends Realm.object {
myField: string
setMyField(value: string) {
this.myField = value;
// ... other validations or updates
}
}
On a normal class this pattern of having a setter that will validate the value and might also set other fields work quite well.
But if we know expand this to embedded objects or list we have this scenario:
class Model extends Realm.object {
_myField: string; // this is mapped as a realm field on the static schema
_embModel: EmbeddedModel | null;
set myField(value: string) {
this.myField = value;
// ... other validations or updates
}
}
class EmbeddedModel extends Realm.object {
_embField: number // this is mapped as a realm field on the static schema
set embField(value: number) {
this._embField = value;
// ... other validations or updates
}
}
The setter embField will never get triggered because when we are creating the embedded field we don't create the class instance itself, we sent a dummy wrong object
realm.write(() => {
const model = new Model(realm)
model.embField = {
// we cannot create this object with new constructor
}
});
So we basically need the ability to user our own constructor, setter and getters on this kind of objects
@kneth sorry to tag you, but we are really desperate about this issue. A lot of places where we could have setter validations are being duplicated.
Is there any workaround to be able to use setter to create the realm elements?
@gfrancischini Sorry for the late reply.
Currently it is not possible to use embedded objects together with class-based models. The constructor of our class-based models takes a Realm instance as argument, which means that the constructor will create a managed/Realm object. For embedded objects, creating the object without a parent it by definition not possible, and this issue is about that.
A workaround is to not use classes which extends Realm.Object. A small example is:
import Realm from "realm";
class Contact {
name: string;
address?: Address;
constructor(name: string, address?: Address) {
this.name = name;
if (address) {
this.address = address;
}
}
static schema: Realm.ObjectSchema = {
name: "Contact",
properties: {
name: "string",
address: "Address",
},
};
}
class Address {
street: string;
city: string;
constructor(street: string, city: string) {
this.street = street;
this.city = city;
}
static schema: Realm.ObjectSchema = {
name: "Address",
embedded: true,
properties: {
street: "string",
city: "string",
},
};
}
Realm.deleteFile({});
let realm = new Realm({ schema: [Contact.schema, Address.schema] });
realm.write(() => {
const parkvej = new Address("Grøndals Parkvej 2A", "Vanløse");
const alice = new Contact("Alice", parkvej);
realm.create(Contact.schema.name, alice);
const griffensfeldsgade = new Address("Griffenfeldsgade 14B", "København N");
const bob = new Contact("Bob", griffensfeldsgade);
realm.create(Contact.schema.name, bob);
});
realm.objects<Contact>(Contact.schema.name).forEach((c) => {
console.log(`${c.name} at ${c.address?.street}, ${c.address?.city}`);
});
Hey thanks, when using that way what are the downsides of not extending from Realm.object?
is purely the Contact.schema.name instead of just Contact in the realm.create? And also not having ts visibility on exposed methods from the realm.object?