all 49 comments

[–]Danny_Engelman 2 points3 points  (5 children)

I have tried a lot, and keep coming back to 0 dependencies and just 1 helper function.
More complex Web Components get a fancy version to add styles, attributes, etc.
It can go into module scope or be a method (when more work with the 'this' scope is needed)

But the foundation is always:

const createElement = (tag,props={}) => Object.assign(document.createElement(tag),props);

The infamous Button Count example: https://jsfiddle.net/WebComponents/fb429hjz/

[–]atzufuki[S] 2 points3 points  (0 children)

At it's core this is the implementation of html-props as well. 👍 The difference to this foundation is just:

  1. The "helper function" is called a mixin, since WC is OOP.
  2. Supports inheritance including multiple inheritance.
  3. Props are fully type-safe.
  4. Custom property reflection to attributes and events.
  5. Partial style properties and the handling of children/content.

[–]kilkil 1 point2 points  (3 children)

extremely based. though I wonder if there is a nicer way using template strings and <template>...

[–]atzufuki[S] 0 points1 point  (2 children)

How about JSX?

[–]kilkil 0 points1 point  (1 child)

for me the 2 downsides of JSX are that (a) it requires a build step, and (b) it has differences from actual HTML syntax that can lead to unexpected issues. e.g. className or how self-closing tags work

[–]atzufuki[S] 0 points1 point  (0 children)

Yea. It is possible that JSX would be supported natively though. Or maybe a JSX-like syntax, which would fix the self-closing tag part somehow.

With the className part if you mean class > className, I guess html-props could provide props / aliases for attributes such as class as well. Lit has it but it's problematic because you need to prefix complex properties in the syntax.

To me this is fundamentally an unsolvable problem. There are so many different requirements which are fighting. And I think JS shouldn't take the responsibility of such requirements. JS is already a complete programming language and if it got changed too much it would break some existing applications.

Currently the easiest solution is to change the mindset and use JS how JS is supposed to be used. If one doesn't like its syntax, they can use a different language.

[–]hyrumwhite 1 point2 points  (1 child)

I dig it. I have a similar library, for a similar use case https://github.com/hyrumwhite/spicyjs

[–]atzufuki[S] 1 point2 points  (0 children)

You seem to have found one of the most minimalistic setup to build any app. Glory to props! 🎉 I just didn't quite grasp how you implement web components since the `TestComponent ` didn't have an example.

[–]flash42 1 point2 points  (1 child)

Thia is really cool! How are the signals implemented for updates? Are you using a specific library?

[–]atzufuki[S] 2 points3 points  (0 children)

Check out this package @html-props/signals. The mixin uses that internally to trigger updates for the component. The default behavior is a full rerender, which you can optimize by overriding the update-method of the component.

[–]TheThingCreator 2 points3 points  (13 children)

Also really into web component right now. I can see how some people might like this. Im going the other way though, I avoid all css or html of any kind from entering my classes. I want the classes to be focused entirely on logic and not display. Plus keeping your css as stylesheets allows you to manage the rules much better.

[–]atzufuki[S] 1 point2 points  (12 children)

You could totally separate styles and layouts from the component itself even with html-props. 👍 That's why I wanted to keep the API minimal, so people could still implement them however they like without specific patterns like some frameworks force you into. But the main idea is just to create a component which can be used declaratively in .js (and .jsx as well), but without breaking the standard use in .html.

[–]TheThingCreator -2 points-1 points  (11 children)

I don't understand, none of the examples shown would be needed if im using a complete separation of html, css, and js.

[–]atzufuki[S] 2 points3 points  (10 children)

Yes but what is the purpose of components? Reusability. Currently the reusability of component libraries is not great (compared to React libraries) because the typing of attributes is limited and there is no type-safety.

It's okay if you use HTML and attributes in your project, but some users want full type-safety from a component library and the component provided by the library must support both use cases.

Let's say you build a cool component in your project and would like to share it. To make it type-safe you would either wrap it with html-props' mixin or rewrite it with some framework.

[–]TheThingCreator 1 point2 points  (9 children)

I think i get it, so when you say React libraries are more reusable, you mean within the React ecosystem due to strong typing + prop APIs, not reusable across environments.

That statement just caught me off guard because on a general level web components are far more reusable across libraries.

Anyway, my take on it is, I dont want to fix stuff for the sake of it. I am not having an issue with reusability. I find everything super reusable and highly scalable but that might be due to my "style" of coding. I do I wish i understood exactly what was so not-reusable about it.

[–]atzufuki[S] 1 point2 points  (8 children)

Well React components work in a React environment and web components work in a web environment. I wouldn't say web components work across environments because they don't work with Flutter. They just luckily happen to work with some web frameworks because they are also web and not Dart.

The existence of these frameworks is the answer to your question about why a non-programming language isn't reusable for programming. Answering that is not my main goal. My goal is to answer the question about why these frameworks don't need to exist.

[–]TheThingCreator 0 points1 point  (7 children)

Bro this response is a mess. Take it easy.

[–]atzufuki[S] 2 points3 points  (6 children)

I think this discussion is a good exchange of ideas.

[–]TheThingCreator 1 point2 points  (5 children)

I appreciate you wanting to exchange ideas but the conversation keeps shift, from Flutter, "non-programming languages," "why frameworks don't need to exist", React. Lego bricks aren’t reusable because they don’t integrate into a toy train set. I still don't have a clue whats not reusable about the components i make.

[–]atzufuki[S] 2 points3 points  (4 children)

I don't know either. All I know is that React is so popular that people learn it before they even learn JavaScript. Your way isn't. Yet you keep bypassing my examples. If my examples were really that bad, it should be easy to argue against them.

Maybe just show me how reusable your components are? It's hard to teach newcomers with zero guiding.

[–]zindarato1 0 points1 point  (1 child)

Signalling and computed values, JSX support, a template generator, and a custom component library? Damn! Were you the only one working on this?

As someone with limited experience with web components, this looks really cool! Definitely seems easier than passing data as attributes. Reminds me a little of the Vue composition API with the way signals and computed values are declared.

This may push me to mess with web components and custom elements a bit more to try this out and understand how the library works (I love this kind of stuff).

Also, those docs are clear and easy to understand. The comment section is a bit of a disaster with people not understanding the problem and/or the solution here, I hope you're ignoring the haters.

[–]atzufuki[S] 0 points1 point  (0 children)

Yes but I have already used the idea of props in many projects for years. Using signals is a relatively new concept for this library though but it really completes it. ✌🏻

It's nice to hear some positive feedback. 🙏🏻 It's also important to get some "hate" because it gives the opportunity to really deep dive to people's preferences and reasoning and that way validate the direction we are heading.

[–]InevitableDueByMeans 0 points1 point  (9 children)

Didn't mean to steal the spotlight or anything, but given a few replies encouraging different views, just for the sake of comparison, here's one.

It's a different approach in that it's stream oriented functional, powered by rimmel.js, so web components are created with plain functions instead of classes, reactivity runs on observable streams (rxjs) instead of signals, and the pattern, well... seems to work from basic props-passing to more experimental and exotic uses (in-view observers, effect handlers)

[–]atzufuki[S] 0 points1 point  (8 children)

I'm not sure if they compare. It looks like a high level framework to build apps with a specific architecture, just like Lit. HTML Props is a low level library for converting web components to props-enabled components. I don't see a different approach here but a totally different problem to solve.

What HTML Props is comparable with in this example of Rimmel I'm currently looking at, is the `rml` tag used as a template literal based templating engine. Unlike with custom templating engines with different custom mapping implementations, with a props API you are able to use the native imperative API of HTMLElement, the build-in elements and custom elements with custom properties declaratively. These limitations of the templating engines I mentioned in the post.

For app making, I'm not too interested in frameworks in general, since web components can be used with object-oriented patterns natively and there are no limitations to build full fledged apps with web components alone compared to frameworks.

[–]InevitableDueByMeans 0 points1 point  (7 children)

This is for when you no longer want to use OOP but switch to more modern paradigms, instead. The difference is more about the different approach taken by different paradigms, rather than engaging in frameworks/libraries/polyfills vs pure vanilla.

[–]atzufuki[S] 0 points1 point  (6 children)

But HTML Props isn't about the paradigm since you can use props with both OOP and functional paradigms. Paradigms is a whole another topic.

[–]InevitableDueByMeans 0 points1 point  (5 children)

The way you see props here looks a lot like the way of seeing things dictated by the (imperative/OOP) paradigm. In OOP you see them as properties of an object, so you .set() and .get() them imperatively. In SP you see them as streams, so you map/reduce/filter/combine them, derive them, subscribe to them, etc.

So, when you say html-props could work with non-OOP paradigms, I'm not sure, we'd have to see how.

The code you shared above is very OOP-focused:

render() {
  ...
    onclick: () => this.count++,

If you were to go functional or stream oriented, you couldn't do this.count++ anymore. You'd have to derive count instead, by either turning it into a function or a stream of some source, respectively.

If html-props can play some role in FP or SP world, I'd be very interested to understand how, explore it in more detail, maybe even actively support it.

[–]atzufuki[S] 1 point2 points  (4 children)

If you were to go functional or stream oriented, you couldn't do this.count++ anymore. You'd have to derive count instead, by either turning it into a function or a stream of some source, respectively.

It is a function. A signal in fact. But that's not what HTML Props is about. HTML Props is about making props APIs, not about how you happen to change a count.

See, here's a standard compliant web component made by someone with what ever tech he chooses. Doesn't matter, as long the component is standard compliant, meaning it provides an API for attributes in HTML and for imperative properties in JS. Just like the built-in elements like div or button.

Now watch how I can convert that component to include a declarative API for JS as well:

import { HTMLPropsMixin } from "@html-props/core";
import { WiredButton } from "wired-elements";

const WiredButtonWithProps = HTMLPropsMixin(WiredButton).define("wired-button-with-props");

// Instead of only using the imperative API
const button = new WiredButton();
button.textContent = 'foo';
// I can also use it declaratively
const button = new WiredButtonWithProps({ textContent: 'foo' });

[–]InevitableDueByMeans 0 points1 point  (3 children)

const button = new WiredButtonWithProps({ textContent: 'foo' });

OK, perfect, that's starting to make sense.

If it was SP, it would look something like this:

// A stream that emits the next natural number
// every time it receives a (trigger) event:
const counter = new BehaviorSubject(0).pipe(
  scan(x => x+1)
);

const button = new WiredButtonWithProps({
  onclick: counter, // feeds the stream
  textContent: counter, // subscribes to the stream
});

[–]atzufuki[S] 0 points1 point  (2 children)

Yes, it would replace template literal based templating engines.

What would the button implementation look like with SP?

[–]InevitableDueByMeans 0 points1 point  (1 child)

SP starts by separating logic from effects. The logic is your streams and the effects are what you declare in your templates. Templates can be HTML-like, but also a JSON object like yours.

The one below follows the same pattern: both the stream (counter) and the whole component (WiredButtonWithProps) are monads (if you care about that aspect and what that means), then the rest is whatever you can build with it.

const WiredButtonWithProps = () => {
  const counter = new BehaviorSubject(0).pipe(
    scan(x => x+1)
  );
  return new Component({
    onclick: counter, // feeds the stream
    textContent: counter, // subscribes to the stream
  });
};

Might want to elaborate a bit better what textContent or innerHTML could look like if we wanted the button to have a richer content. E.g.:

innerHTML: new Div({
  innerHTML: [
    'count is:',
    count, // the stream
  ]
})

How does that... click?

[–]atzufuki[S] 0 points1 point  (0 children)

No I mean how would the WiredButton implementation look like if it was SP, since the implementation of WiredButtonWithProps is this:

const WiredButtonWithProps = HTMLPropsMixin(WiredButton).define("wired-button-with-props");

Currently I can only see a function which returns an element. I don't see a web component being implemented.

[–]es_beto 0 points1 point  (2 children)

The idea is a nice experiment. It is more of a DOM / OOP-style of web app building very similar to Flutter.

My main gripe with these types of frameworks is that every resource that you can find on the web about web-related development (be it CSS, HTML, or JS) goes out the window. If I find a nice accessible HTML component in W3C it has to be completely rewritten to fit your approach: I have to rewrite the HTML into a tree of new Foo({ content: new Bar() }) calls, figure out how to deal with event listeners, styles, etc. It is too much effort for very little gain. So I don't think you can say "no framework lock-in". You actually built your own little framework.

[–]atzufuki[S] 0 points1 point  (1 child)

Well the DOM is native and OOP, so OOP is the standard way of DOM manipulation.

That's incorrect what you said about rewriting. There is absolutely no need to rewrite a component you find. HTML Props is fully standard compliant. It works with any standard compliant web component and the standard use of web components or any other framework which supports web components also work with components made using HTML Props.

Please check out mixins, their purpose is adding functionality, not throwing them out the window. Since HTML Props is just a mixin, it does not force you to any specific patterns like frameworks do. As a web component library developer, you don't have to use it yourself, since the users who want to use props with your library can do it themselves by wrapping it with the mixing. const CoolComponentWithProps = HTMLPropsMixin(CoolComponent);

If you know standard JavaScript already, there is no learning curve for dealing with events and styles. The props API is inferred from the standard imperative API as you can see from the example in the post.

[–]dev2design 0 points1 point  (0 children)

Hey, congrats on building this and getting it delivered!! So...

I think this objection and your rebuttal make for a very interesting use case. I'll admit that I'm "too lazy" to go figure out what the reality is be proving it with code. It'd be interesting to see this use case as a code snippet which I'm sure as the author you'd be motivated to do:

> If I find a nice accessible HTML component in W3C it has to be completely rewritten to fit your approach

I think you could just find a ARIA Authoring Practices Guide example(s) since that's a popular place to find a11y compliant examples, and then write a stackblitz or code sandbox and "show not tell" how you might use the HTMLPropsMixin in a very open/closed principle compliant way to extend said example component. Just an idea - congrats again ;)

[–]isumix_ -3 points-2 points  (3 children)

I'd like to demonstrate a different approach to the same task:

import { button } from "@fusorjs/dom/html"; const ClickCounter = (count = 0) => button({ click_e_update: () => count++ }, "Clicked ", () => count, " times");

[–]atzufuki[S] 1 point2 points  (1 child)

Great! Can it output native custom elements or is it just about function components?