anywhichway/tlx

t-forvalues add a row with "undefined" content and loose initial data

pwFoo opened this issue · 15 comments

pwFoo commented

Hi @anywhichway,

back to tlx after some time... But at the moment I have a new project idea where I would like to use tlxjs :)

Tried to update ui by enventsource data. My source code:

var data = {
      events: [{
        session_id: " 123 ",
        date: "init",
        data: " test data ",
        extra: " Additional data ",
    }]
};

const model = tlx.reactor(data);
tlx.view(document.getElementById("tstream"),{model,linkModel:true});

ws.addEventListener('message', function(received) {
    var response = JSON.parse(received.data);
	console.log("EventId:", received.lastEventId);  // = event "id"!
    console.log("data:", response);
   // Array.prototype.push.apply(model.events, [response]);   // need to be an array!?
});

Last line I disabled the updates! So just the initial line from data should be shown and it works as should. Output:

init 123 test data Additional data

Next step activate updates to model.events by activate Array.prototype.push.apply(model.events, [response]); // need to be an array!?

Now the first / initial line should be shown and new lines received from stream should be added. But the first line / initial line is lost! Looks like a bug?
Output:

Sat, 03 Aug 2019 15:59:03 +0000 ds73al3qm7abdpknvkqsrd6fjiJust a text more data
Sat, 03 Aug 2019 15:59:06 +0000 ds73al3qm7abdpknvkqsrd6fjiJust a text more data
Sat, 03 Aug 2019 15:59:09 +0000 ds73al3qm7abdpknvkqsrd6fjiJust a text more data
Sat, 03 Aug 2019 15:59:12 +0000 ds73al3qm7abdpknvkqsrd6fjiJust a text more data
undefinedundefinedundefinedundefined

Looks like

  1. inital line is at bottom instead of top / first line and changed to "undefined" values?
  2. new rows inserted more or less correct at the bottom (but before the wrong "undefined" row
  3. If I remove the initial data the output starts empty, but also add the undefinedundefinedundefinedundefined line.

So it looks like a (proxy) bug?

pwFoo commented

Hi @anywhichway ,
I do not want to push you, but I'm just starting a project and would like to use tlx as a ui framework and work with eventsource stream where this problem occurs.
Can you reproduce that problem?

pwFoo commented

Hi, many thanks. I like tlx and would prefer it over other solutions (Vue,...).

Take the time you need. I was just a little bit nervous because if you still support tlx ... 😅

pwFoo commented

Hi @anywhichway any progress / idea with that problem?

pwFoo commented

Hi,
for the moment I'll test / move back to https://github.com/blikblum/tinybind until tlx works with the needed features. So you can take the time you need to take a look into the problem.

pwFoo commented

Another commented test without stream updates. Tested "simple" array of objects. Don't works with forvalues so I moved to foreach which looks better...
Take a look at comments in script part.

<!DOCTYPE html>
<html lang="de">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Titel der Seite | Name der Website</title>
    <script src="https://unpkg.com/tlx/browser/tlx.min.js"></script>
  </head>
  <body id="body">
    <div>
        <a href="/tlx/test/1">ID1</a> | <a href="./test/2">ID2</a>
    </div>
    <div class="tlx" id="field1">Hello ${model.user.firstname}</div>
    <div>First Name: <input class="tlx-input" id="username" name="firstname" value="${model.user.firstname}"></div>

    <table class="tlx" id="foreach" t-foreach="${model.persons}">
        <tr>
            <td>Name: ${value.name}</td><td>Alter: ${value.age}</td>
        </tr>
    </table>

    <div class="tlx" id="if">
        <div class="fields" t-if="${model.part.show}">Will be shown</div>
        <div class="fields" t-if="${model.part.notHide}">Will not be shown</div>
    </div>

    <script type="text/javascript">
        var model = {
            // persons foreach only works if NOT reactor(), but can't be updated that way!
            // maybe a problem with array of objects structure?!
            // initially tested with forvalues, but fails at all?!
            persons: [
                { name: "Friend 1", age: 19 },
                { name: "Friend 2 ", age: 21 }
            ],
            part: tlx.reactor({
                show: true,
                notHide: true
            }),
            user: tlx.reactor({
                firstname: "Its",
                lastname: "Me"
            })
        };
        // bind by class
        tlx.view(document.getElementsByClassName("tlx"),{model:model});
        tlx.view(document.getElementsByClassName("tlx-input"),{model:model, linkModel:true});   // not updating on input?!
        //tlx.view(document.getElementById("username"),{model, linkModel:'input'});     // tested linkModel "input"... Same...

        // bind to global document?! Bot how to handle / bind input fields...?
        //tlx.view(document.getElementById("body"),{model, linkModel:true});
    </script>
  </body>
</html>
pwFoo commented

Some more tests I found out forvalues works with an object of objects

            persons: tlx.reactor({
                1: { name: "Friend 1", age: 19 },
                2: { name: "Friend 2 ", age: 21 }
            }),

But it's more difficult to maintain instead of a array of objects I would need a object "key" instead of just another array item (array.push())...

So would be great reactor() would support object with value as array?

            persons: [
                { name: "Friend 1", age: 19 },
                { name: "Friend 2 ", age: 21 }
            ],
pwFoo commented

I played around with different code and now can reproduce my initial error without stream data.

<!DOCTYPE html>
<html lang="de">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Titel der Seite | Name der Website</title>
    <script src="https://unpkg.com/tlx/browser/tlx.min.js"></script>
  </head>
  <body id="body">
    <div>
        <a href="/tlx/test/1">ID1</a> | <a href="./test/2">ID2</a>
    </div>
    <div class="tlx" id="field1">Hello ${model.user.firstname}</div>
    <div>First Name: <input class="tlx-input" id="username" name="user.firstname" value="${model.user.firstname}"></div>

    <table class="tlx" id="foreach" t-forvalues="${model.persons}">
        <tr>
            <td>Name: ${value.name}</td><td>Alter: ${value.age}</td>
        </tr>
    </table>

    <div class="tlx" id="if">
        <div class="fields" t-if="${model.part.show}">Will be shown</div>
        <div class="fields" t-if="${model.part.notHide}">Will not be shown</div>
    </div>

    <script type="text/javascript">
        var model = {
            persons: tlx.reactor([
                { name: "Friend 1", age: 19 },
                { name: "Friend 2 ", age: 21 }
            ]),
/*
            persons: tlx.reactor({
                1: { name: "Friend 1", age: 19 },
                2: { name: "Friend 2 ", age: 21 }
            }),
*/
            part: tlx.reactor({
                show: true,
                notHide: true
            }),
            user: tlx.reactor({
                firstname: "Its",
                lastname: "Me"
            })
        };
        // bind by class
        tlx.view(document.getElementsByClassName("tlx"),{model:model, linkModel:true});
        tlx.view(document.getElementsByClassName("tlx-input"),{model:model, linkModel:true});   // not updating on input?!
        //tlx.view(document.getElementById("username"),{model, linkModel:'input'});     // tested linkModel "input"... Same...

        // bind to global document?! Bot how to handle / bind input fields...?
        //tlx.view(document.getElementById("body"),{model, linkModel:true});
    </script>
  </body>
</html>

Do reproduce append / prepend lines by:

Array.prototype.unshift.apply(model.persons, [{name: "test", age: 99}])
Array.prototype.push.apply(model.persons, [{name: "test", age: 99}])

@pwFoo Sorry it took so long to address this. There was an issue with Arrays and reactor Proxies as you surmised. A new version has been pushed that should address your concerns. This also means you can use simpler code, i.e.

model.events.push({
    session_id: " 124 ",
    date: "init",
    data: "issue 20 test data ",
    extra: "Additional data ",
});

instead of

Array.prototype.push.apply(model,events,[{
    session_id: " 124 ",
    date: "init",
    data: "issue 20 test data ",
    extra: "Additional data ",
}]);

I will leave the issue open until you respond.

pwFoo commented

I try with initial data like model.events, but data need to be an array? So I would have to use model[0].events as below:

var data = [{
      events: [{
        session_id: " 123 ",
        date: "init",
        data: " test data ",
        extra: " Additional data ",
    }]
}];

const model = tlx.reactor(data);
tlx.view(document.getElementById("tstream"),{model, linkModel:true});

How to change it to object instead of array to use as model.events?
If I remove the array around the data object it fails with:

tlx.min.js:1 Uncaught TypeError: (intermediate value)(intermediate value)(intermediate value).forEach is not a function
    at t-forvalues (tlx.min.js:1)
    at tlx.min.js:1
    at Array.forEach (<anonymous>)
    at r (tlx.min.js:1)
    at O (tlx.min.js:1)
    at Object.T [as view] (tlx.min.js:1)
    at index-old.php:44

@pwFoo let's make sure you have the right version. Did you use NPM to get the most recent build or just copy from GitHub? Although NPM was correct, we just found an issue with the browser files on GitHub. It has been corrected.

Also use the non-minified version to reproduce the same error so I can see the line numbers.

pwFoo commented

I used unpkg.com before and maybe it was cached...
Now I moved to GitCDN master:
https://gitcdn.xyz/repo/anywhichway/tlx/master/browser/tlx.js

Tested again and error:

tlx.js:692 Uncaught TypeError: (intermediate value)(intermediate value)(intermediate value).forEach is not a function
    at t-forvalues (tlx.js:692)
    at tlx.js:114
    at Array.forEach (<anonymous>)
    at updateDOM (tlx.js:77)
    at render (tlx.js:529)
    at Object.view (tlx.js:536)
    at index-old.php:44

If I change data to an array by [data] the error is gone, but push() doesn't work anymore ():

var data = {
      events: [{
        session_id: " 123 ",
        date: "init",
        data: " test data ",
        extra: " Additional data ",
    }]
};

// DATA AS ARRAY to prevent error, but should work as model.events instead of model[0].events?
const model = tlx.reactor([data]);   
index-old.php:52 Uncaught TypeError: Cannot read property 'push' of undefined
    at EventSource.<anonymous> (index-old.php:52)

@pwFoo try this code after changing the script src to where you are storing tlx. This works in my environment:

<html>
<head>
<script src="../index.js" type="text/javascript"></script>
<!-- script src="../browser/tlx.min.js" type="text/javascript"></script -->
</head>
<body>
<div id="tstream" t-foreach="${model.events}">
	<p>${value.data}</p>
</div>
<script>
var data = {
	      events: [{
	        session_id: " 123 ",
	        date: "init",
	        data: " test data ",
	        extra: " Additional data ",
	    }]
	};
const model = tlx.reactor(data);
model.events.push({
    session_id: " 124 ",
    date: "init",
    data: "issue 20 test data ",
    extra: "Additional data ",
});

tlx.view(document.getElementById("tstream"),{model,linkModel:true});

setTimeout(() => {
	model.events.push({
    session_id: " 124 ",
    date: "init",
    data: "more issue 20 test data ",
    extra: "Additional data ",
})},2000);
</script>
</body>
</html>
pwFoo commented

Found my mistake... I changed from t-foreach to 't-forvaluesto see if there is a way around... Changed back tot-foreach` and with the latest version it works fine now. Sorry wasting your time with that...
So it's fixed with the latest change 👍