nextapps-de / mikado

Mikado is the webs fastest template library for building user interfaces.
Apache License 2.0
779 stars 35 forks source link

Recommended method of building portable contained reactive components? #83

Open hsnoil opened 9 months ago

hsnoil commented 9 months ago

Many frameworks have methods of building portable components before the webcomponent spec, what is the recommended way to do this with Mikado?

What I came up with is something like this (though it feels like I am hacking around the foreach and I'd have to do an app.render(data) on every change, is there a better way?):

index.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Mikado</title>
    <script type="module" src="js/main.js" async></script>
</head>
<body><!----></body>
</html>

main.js

import Mikado, {register} from "../node_modules/mikado/dist/module-debug/mikado.js";
import {route} from "../node_modules/mikado/dist/module-debug/event.js";
import tpl_app from "../compiled/app.js";
import tpl_form from "../compiled/form.js";
import tpl_textbox from "../compiled/textbox.js";

register(tpl_form)
register(tpl_textbox)

const app = new Mikado(tpl_app, {
    mount: document.body,
    recycle: true,
    cache: true
});

const state = {
    form: { id:"form", items:{ username: 'cookie' } },
    setState: function (s,k,v) {
        s[k] = v
    }
}

const data = {}
route("testrun", function(target, event){
console.log("DATA",data,state)
const id = target.dataset.id
const fn = target.dataset.fn
console.log(id,fn,target)

state[id][fn](state[id]);

});
app.render(data,state);

template/app.html

<main>
  <header>Test Form</header>
  <section include="compiled/form" foreach="state.form"></section>
</main>

template/form.html

<form>
    <script>{{@
        state.setState(data,'items', {
            username: { type:"text", title:"Username", name:"user", value:((data||{}).items||{}).username },
            password: { type:"password", title:"Password", name:"pass", value:'' }
        });
        state.setState(data,'fn_submit', function(data) {
            console.log("got", data)
        });
    }}</script>
    <section include="compiled/textbox" foreach="data.items.username"></section>
    <section include="compiled/textbox" foreach="data.items.password"></section>
<div click="testrun" data-id="{{data.id}}" data-fn="fn_submit">button</div>
</form>

template/textbox.html

<span>
{{@
         state.setState(data,'value', (data||{}).value )
}}
    <input type="{{?data.type}}" placeholder="{{?data.title}}" name="{{?data.name}}" value="{{?data.value}}">
</span>
john5000 commented 9 months ago

It appears that Mikado mounts listeners on the window object (the root of all listeners) and so events won't know what component to go do. (See src/event.js:391).

Mikado needs to be changed to mount the listener on the base of each component.

ts-thomas commented 9 months ago

I don't understand what you are trying to do. For reactive approach Mikado gives you 2 possibilities: 1. using reactive property notation within template expression and also optionally 2. using observable array. For web components there is explanation in the Readme. None of them was used.

Can you explain me what is your goal? You can give me some example of how the final result should look like but without giving the imlementation, just the result is important to me. I don't understand your example above, also I'm pretty sure that this could be solved better, when I'm understand what you need.

ts-thomas commented 9 months ago

Mikado needs to be changed to mount the listener on the base of each component. I don't know what you exactly mean with "component". When it is shadow DOM component, then this makes sense to me. But that is just one usage scenario. When using non-shadow DOM components it can't, because it needs a global event delegation solution and binding routing to every template doesn't provide this. It isn't needed to use the event delegation, when you need non-global event capturing then just assign a native listener. This can also be done within custom callbacks.

ts-thomas commented 9 months ago

@john5000 I will add a new feature binding delegation listeners to the shadow root when used.

ts-thomas commented 9 months ago

@hsnoil Probably I understand your example, the confusing part is the use of state.setState which completely should not be there. Why you use it?

hsnoil commented 9 months ago

@ts-thomas I know one can just do data.items = but my goal in long term was to do some auto structuring of what is passed to the values and get more data about where it is, thus the use of a function. But I can see how in this minimum example it looks weird, sorry about that

Also, a side note did you see my other post about how things come out as compiled/textbox instead of textbox for the naming? or should I open a new issue for it?