Premium support for our pure JavaScript UI components


Post 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


Post 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.

All the best,
Alex


Post 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);
        }
    },

Post 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;

Post by mats »

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


Post 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.


Post 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);
});

Post 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'] }) 

Post 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);
});

All the best,
Alex


Post 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

Post Reply