Page 1 of 2

Define multiple model subclass type via crud manager for a single store

Posted: Thu Jan 05, 2023 4:26 pm
by jarmen

Our resource store contains two different types of resources that are represented by different subclasses of ResourceModel. e.g. TypeAResourceModel and TypeBResourceModel. The incoming data has a discriminator defining which type of model should be created. Is there a specific field this can be mapped to so that the correct type of model is constructed?

We are familiar with the modelClass field for a store, however that only allows us to specify a single model class.

This would be similar to how the column store provides a 'type' specification allowing configuration of different column types that utilize different Column classes derived from Model


Re: Define multiple model subclass type via crud manager for a single store

Posted: Fri Jan 06, 2023 3:30 am
by alex.l

Store cannot contain records with different model classes. By design, a store contains a set of records with the one data model class, same as database table.
If you want to have "typeA" and "typeB" records, you can add a new field "type" to your data model and specify the type in there. After that check that flag in your code and fill-in fields that required for this type.


Re: Define multiple model subclass type via crud manager for a single store

Posted: Fri Jan 06, 2023 10:13 am
by mats

We actually should support this by using a custom createRecord function on the Store, see below:

store : {
        // Default model is a Gate
        modelClass : Gate,
        readUrl    : 'data/kastrup-airport.json',
        autoLoad   : true,
        createRecord(data, skipExpose = false, rawData = false) {
            let modelClass = this.modelClass;
            if (data.type === 'terminal') {
                modelClass = Terminal;
            }
            return new modelClass(data, this, null, skipExpose, false, rawData);
        }
    },

Re: Define multiple model subclass type via crud manager for a single store

Posted: Fri Jan 06, 2023 10:49 am
by jarmen

Thank you Mats, this should work for us.

It appears the typescript exports may be incorrect. This is the signature we see:

createRecord(data: object, skipExpose?: boolean): void;

Re: Define multiple model subclass type via crud manager for a single store

Posted: Fri Jan 06, 2023 11:47 am
by mats

Ignore rawData, shouldn't be used (private).


Re: Define multiple model subclass type via crud manager for a single store

Posted: Sat Jan 14, 2023 6:48 am
by jarmen

This is working great for our flat data, however if we have nested data, the types of nested data are ignored, and instead use the parent constructor.

For example, if we have the following:

{
  type: 'terminal',
  name: 'Terminal,
  children: [
    { type: 'gate', name: 'Gate A' }
  ]

Both types would use the TerminalResourceModel class. We can get around this by wrapping our children in new TerminalResourceModel, however it would be nice if we didn't have to do this extra step.


Re: Define multiple model subclass type via crud manager for a single store

Posted: Sat Jan 14, 2023 11:45 am
by mats

How can I reproduce this? Tests pass just fine here:

 t.iit('Initialize with tree data', t => {
        const store = new MixedStore({
            tree : true,
            data : [
                {
                    type     : 'a',
                    name     : 'Terminal',
                    children : [
                        { type : 'b', name : 'Gate A' }
                    ]
                }
            ]
        });

    t.isInstanceOf(store.rootNode.firstChild, ModelA);
    t.isInstanceOf(store.rootNode.firstChild.firstChild, ModelB);
});

Re: Define multiple model subclass type via crud manager for a single store

Posted: Sat Jan 14, 2023 7:54 pm
by jarmen

I'll see if I can get an example setup for you. The use case where this seems to be an issue is when we would say

record.appendChild({ type: 'b', children: [ { type: 'c'] }) 

Re: Define multiple model subclass type via crud manager for a single store

Posted: Mon Jan 16, 2023 10:28 am
by alex.l

Waiting for your testcase. meanwhile I tested with your code and it still passed

t.iit('Initialize with tree data', t => {
        const store = new MixedStore({
            tree : true,
            data : [
                {
                    id : 1,
                    type     : 'a',
                    name     : 'Terminal',
                    children : [
                        { id : 2, type : 'b', name : 'Gate A' }
                    ]
                }
            ]
        });

store.first.appendChild({id : 3, type : 'a', children : [ { id : 4, type : 'b'} ] })

t.isInstanceOf(store.rootNode.firstChild, ModelA);
t.isInstanceOf(store.rootNode.firstChild.firstChild, ModelB);
t.isInstanceOf(store.getById(3), ModelA);
t.isInstanceOf(store.getById(4), ModelB);
});

Re: Define multiple model subclass type via crud manager for a single store

Posted: Fri Jan 27, 2023 11:29 pm
by jarmen

Here's the code I was able to use to reproduce. I see that we should be passing in a reference to 'this' in the model constructor, however the exported typescript model's constructor only accepts a single argument for data. Is there a way to inform the model which store it belongs to during construction in typescript?

Thanks, Jon

Tree demo
 Code editor
Download code

app.module.js
Auto apply

import { SchedulerPro, ResourceModel, StringHelper } from '../../build/schedulerpro.module.js';
import shared from '../_shared/shared.module.js';

class Gate extends ResourceModel {
    get doWork() { return 'gate model'; }
}

class Terminal extends ResourceModel {
    get doWork() { return 'terminal model'; }
}

new SchedulerPro({
    appendTo   : 'container',
    eventColor : null,
    eventStyle : null,

resourceStore: {
  modelClass: Gate,
  createRecord(data, skipExpose = false, rawData = false) {
        let modelClass = this.modelClass;
        if (data.type === 'terminal') {
            modelClass = Terminal;
        }

        // Works
        //return new modelClass(data, this);

        // Does not Work
        return new modelClass(data);
  }
},

features : {
    tree         : true,
},

rowHeight : 45,
barMargin : 5,

columns : [
    {
        type  : 'tree',
        text  : 'Name',
        width : 220,
        field : 'name'
    }, {
        text  : 'Model Type',
        width : 90,
        field : 'doWork'
    }
],


viewPreset : 'hourAndDay',

resources: [{
  name: 'Terminal A',
  type: 'terminal',
  children: [{
    name: 'Gate A1',
    type: 'gate'
   }]
}]


});
Idle