Skip to main content
Version: 1.x.x

Defining a Record Class

Developers can configure their quip.apps.Record subclasses by defining these static methods/fields on their class. One in particular, getProperties(), is required, as it describes the property schema for their Record and is used both to validate the property values that are being stored for the Record object as well as to construct the right types of Record and RecordList objects when setting property values.

The API is relatively strict about setting new property values on Record objects; if the property name has not been defined by getProperties() or if the value does not match the data type defined in getProperties(), the set() call will fail. In addition to the properties that the developer defines in getProperties(), subclasses inheriting from Quip's special Record types (e.g. quip.apps.RichTextRecord or quip.apps.ImageRecord can also inherit type-specific properties. These properties will be listed for each of the subclasses below, but the property names will be prefixed by the Record type (e.g. RichText_defaultText). Developers will also not be allowed to define property names with these prefixes to avoid collisions.

note

It's important to note that when using typescript, properties are untyped, which means all calls to .get will have the type any. To avoid losing type safety, we recommend that you write getters and setters for your properties to enforce type safety at compile time. The example below demonstrates what this looks like in practice.

Special Record types:

Example


interface MyRecordProps {
title: string;
count: number;
}

interface MyRecordData {
title: string;
count: number;
}

class MyRecord extends quip.apps.Record {
static getProperties = () => ({
title: "string",
count: "number",
})

getTitle = () => this.get("title") as string;
getCount = () => this.get("count") as number;

setTitle = (title: string) => this.set("title", string)
setCount = (count: number) => this.set("count", count)

getData(): MyRecordData {
return {
title: this.getTitle(),
count: this.getCount()
}
}
}

quip.apps.registerClass(MyRecord, "my-record");

interface AppData {
title?: string,
items: string[],
editable: boolean,
text: quip.apps.RichTextRecord,
subRecord?: MyRecordData
}

class RootRecord extends quip.apps.RootRecord {
static getProperties = () => ({
title: "string",
items: "array",
editable: "boolean",
text: quip.apps.RichTextRecord,
subRecord: MyRecord,
})

static getDefaultProperties = () => ({
items: [],
editable: true,
text: { RichText_placeholderText: "Type In Here!" },
})

getTitle = () => this.get("title") as string
getItems = () => this.get("items") as string[]
getEditable = () => this.get("editable") as boolean
getText = () => this.get("text") as quip.apps.RichTextRecord
getSubRecord = () => this.get("subRecord").getData()

setTitle = (title: string) => this.set("title", title)
setItems = (items: string[]) => this.set("items", items)
setEditable = (editable: boolean) => this.set("editable", editable)
setText = (text: quip.apps.RichTextRecord) => this.set("text", text)
setSubRecord = (props: MyRecordProps) => this.set("subRecord", props)

getData(): AppData {
return {
title: this.getTitle(),
items: this.getItems(),
editable: this.getEditable(),
text: this.getText(),
subRecord: this.getSubRecord()
}
}
}

quip.apps.registerClass(RootRecord, "root-record");

class Root extends React.Component {

addItem = () => {
const { record } = this.props;
const { items } = record.getData();
record.setItems(items.concat(`new item ${current.length + 1}`));
}

addRecordData = () => {
const { record } = this.props;
record.setSubRecord({ title: "title", count: 5 });
}

render() {
const { record } = this.props;
const {title, items, editable, text, subRecord} = record.getData();
return (
<div>
<button onClick={() => record.setTitle("new title")}>
Set title
</button>
<button onClick={this.addItem}>Add item</button>
<button onClick={() => record.setEditable(!record.get("editable"))}>
Toggle editable
</button>
<button onClick={this.addRecordData}>Add record data</button>
<br />
{title || "No title set"}
<br />
{items.length ? items.join(", ") : "No items"}
<br />
{record.get("editable") ? "editable" : "not editable"}
<br />
{subRecord
? JSON.stringify(subRecord.getData())
: "no subrecord data"
}
<br />
<quip.apps.ui.RichTextBox record={text} />
</div>
);
}
}

quip.apps.initialize({
initializationCallback: (root, params) => {
const root = quip.apps.getRootRecord() as RootRecord;
ReactDOM.render(<Root record={root} />, root);
},
});