Derived Observable: cannot read property


#1

Purpose: Create dynamically UI TextInputs.

Given the following code, I’m trying to create some TextInput fields based on a JSON object. The JSON holds the names and the captions/placeholders which should be used to render the TextInputs. The code does render as wished but I can’t create the Observables described in the JSON name -> ‘serial’ and ‘model’. Fuse reports cannot read property ‘serial’ when I try to get the value of database.dbFields.serial.value

<App>
    <JavaScript>
        var Observable = require('FuseJS/Observable');

        var fields = Observable();
        var database  = Observable();

        
        var json = {
            "settings": [
                { 
                    dBfields: [
                        {"name": "serial", "caption": "SERIAL"},
                        {"name": "model", "caption": "MODEL"},
                    ]
                }   
            ]   
        };

        fields.replaceAll(json.settings);


        database = fields.flatMap(function(val){
            return val;
        });
        
        module.exports = {
            fields: fields,
            database: database,
            saveNewItem: saveNewItem
        };

        function saveNewItem(arg) {
            // Here goes the call to a API (PHP) wich saves 'name' and 'model' to a dB
            // console.log reports: cannot read property...                
           console.log(database.dbFields.serial.value);
        }

    </JavaScript>

    <ClientPanel>
        <StackPanel Margin="24,24,24,24">
            <Each Items="{database.dBfields}">
                <Text Value="{caption}" />
                <MyTextInput Value="{name}" PlaceholderText="{caption}" />
            </Each>
        </StackPanel>
    </ClientPanel>

    <Panel Alignment="Bottom" Margin="0,0,0,50">
        <MyButton Text="Salva" Clicked="{saveNewItem}" />
    </Panel>

    <!-- ========================================================================== -->
    <Panel ux:Class="MyButton" HitTestMode="LocalBounds" Color="#25a" >
        <string ux:Property="Text" />
        <Text Value="{ReadProperty Text}" Color="#fff" Alignment="Center" Margin="30,15,30,15" />
        <WhilePressed>
            <Change this.Color="#138" Duration="0.05" DurationBack=".2" />
        </WhilePressed>
    </Panel>

    <TextInput ux:Class="MyTextInput" FontSize="20" PlaceholderColor="#ccc" Padding="5" Height="32">
        <Rectangle Layer="Background" CornerRadius="3">
            <Stroke Width="1" Color="#CCCCCC" />
            <SolidColor Color="White" />
        </Rectangle>
    </TextInput>
    
</App>

Expanding the Each the expected result shoud be;

<MyTextInput Value="{serial}" PlaceholderText="SERIAL" />
<MyTextInput Value="{model}" PlaceholderText=MODEL" />

where serial and model are the values of the JSON keys name.
In this way I may read/write the two TextInputs and create dynamically as many TextInputs as the dB fields has (as I would like to ‘bulid’ the UX interface dynamically, not with a number of fix Input fields).


#2

Hi. I think that database.dbFields has two property. name, caption.

try console.log(JSON.stringify(database.dbFields));


#3

Hallo,

console.log(JSON.stringify(database.dbFields)); 

returns null.


#4

I think database is array. So you should use like database.getAt(0).dbFields.

If you want to make fields and database the same, use database = fields.

And you should use like {“name”: Observable(“serial”), “captuon”: Observable(“Serial”)}.

Property vriable in Observable is not Observable.

I will test your code tomorrow at the office and give you the correct answer. :slight_smile:


#5

I try next code.

    function saveNewItem(arg) {
        // Here goes the call to a API (PHP) wich saves 'name' and 'model' to a dB
        // console.log reports: cannot read property...                
       console.log("DB: " + JSON.stringify(database));
       console.log("fields: " + JSON.stringify(fields));
       console.log(database.getAt(0).dBfields[0].name);
    }

And got next message.

DB: {"_origin":-30,"_subscribers":[{"version":2,"active":true}],"_isProxy":true,"_values":[{"dBfields":[{"name":"serial","caption":"SERIAL"},{"name":"model","caption":"MODEL"}]}],"_subscriptionWatchers":[{"watcherId":14}],"_beganSubscriptions":true}
fields: {"_origin":-27,"_subscribers":[{"version":2,"active":true},{"version":2,"active":true}],"_isProxy":false,"_values":[{"dBfields":[{"name":"serial","caption":"SERIAL"},{"name":"model","caption":"MODEL"}]}],"_beganSubscriptions":true}
serial

settings is Observable. So I use Observable.getAt(position number). And dBfields isn’t Observable. Just array. So I use dBfields[0].


#6

Thank you for taking time to help me. Now I understand.

But if my purpose is to create dynamically a number of UI TextInputs based on the JSON, how I can get the value of serial? When the user enters some data into the TextInput how I get its value?

database.getAt(0).dBfields[0].name.value

returns null.

serial should be an Observable (var serial = Observable()) and it shoud be exported to be reactive in the UX. How I can set a JSON value as it is serial to be an Observable?

Thank you.


#7

Hi.

If you don’t want to increase number of textinput playing app, it doesn’t need to use Obsevable.

try this code:

var database = {dBfields:[{name: Observable("serial"), caption: Observable("SERIAL"}]};
console.log(database.dBfields[0].name.value;

If it doesn’t change playing app, it doesn’t need to use Observable.


#8

Hallo! I get:

TypeError: Cannot read property '0' of undefined

And YES, the number of TextInput may increase: its number is set by the JSON which should ‘describe’ the fields that should be rendered in the UX. This is why I’m trying to set a ‘unlimited’ number of Observables based on the values of the JSON (in this case serial and model). As said, the purpose is to display ‘dynamically’ a number of TextInputs according to the fields contained a the database which is not known.


#9

Check Observable API
https://fuseopen.com/docs/fusejs/observable-api.html

In your code, fields is Observable.
In this case, you should use fields.add(something).
and access using fields.getAt(index).

I think that caption doesn’t need Observable.
So, try this code:

<App>
<JavaScript>
    var Observable = require('FuseJS/Observable');

    var fields = Observable();
    // var database  = Observable();

    
    var json = {
        "settings": [
            { 
                dBfields: [
                    {"name": "serial", "caption": "SERIAL"},
                    {"name": "model", "caption": "MODEL"},
                ]
            }   
        ]   
    };

    fields.replaceAll(json.settings);


    var database = Observable();
    database.add({dBfields: Observable()});
    database.getAt(0).dBfields.add({name: Observable("serial"), caption: "SERIAL"});
    database.getAt(0).dBfields.add({name: Observable("model"), caption: "MODEL"});
    console.log(JSON.stringify(database.getAt(0).dBfields));
    
    module.exports = {
        fields: fields,
        database: database,
        saveNewItem: saveNewItem
    };

    function saveNewItem(arg) {
        // Here goes the call to a API (PHP) wich saves 'name' and 'model' to a dB
        // console.log reports: cannot read property...                
       console.log(JSON.stringify(database.getAt(0).dBfields.getAt(0).name.value));
    }

</JavaScript>

<ClientPanel>
    <StackPanel Margin="24,24,24,24">
        <Each Items="{database.dBfields}">
            <Text Value="{caption}" />
            <MyTextInput Value="{name}" PlaceholderText="{caption}" />
        </Each>
    </StackPanel>
</ClientPanel>

<Panel Alignment="Bottom" Margin="0,0,0,50">
    <MyButton Text="Salva" Clicked="{saveNewItem}" />
</Panel>

<!-- ========================================================================== -->
<Panel ux:Class="MyButton" HitTestMode="LocalBounds" Color="#25a" >
    <string ux:Property="Text" />
    <Text Value="{ReadProperty Text}" Color="#fff" Alignment="Center" Margin="30,15,30,15" />
    <WhilePressed>
        <Change this.Color="#138" Duration="0.05" DurationBack=".2" />
    </WhilePressed>
</Panel>

<TextInput ux:Class="MyTextInput" FontSize="20" PlaceholderColor="#ccc" Padding="5" Height="32">
    <Rectangle Layer="Background" CornerRadius="3">
        <Stroke Width="1" Color="#CCCCCC" />
        <SolidColor Color="White" />
    </Rectangle>
</TextInput>

#10

Thank you very much for taking your time to help me, but I’m thinking to give up. Thanks to your suggestion this is partially working:

var json = {
    dBfields: [
        {"name": "serial", "caption": "SERIAL"},
        {"name": "model", "caption": "MODEL"},
    ]
};

fields.replaceAll(json.dBfields);

database.add({dBfields: Observable()});

fields.forEach(function(items) {
    database.getAt(0).dBfields.add({name: Observable(items.name), caption: items.caption});
});

module.exports = {
    fields: fields,
    database: database,
    saveNewItem: saveNewItem
};

function saveNewItem(arg) {
    database.forEach(function(items) {
        console.log(JSON.stringify(items.dBfields.getAt(0).name.value));
    });
}

The matter now is that I’m not able to read the values of the TextInputs as

items.dBfields.getAt(0).name.value

is getting only the first one (due to getAt(0)) but if I change it to:

items.dBfields.getAt(i).name.value
i++;

it will still return only the first one and it won’t loop (i = 0 -> i = 1 -> etc.). I really don’t understand.

But a part all the above posts, as said, my purpose is to add to the UI a undetermined number of TextInputs which are described by a JSON or whatever kind of data that can be loaded by the app before the UI is drawn. These TextInputs must be reactive as they will be populated by data fetched from a dB and will be used to write data to a dB. Any idea how to create TextInputs dynamically?

Thank very much.