alpinejs / alpine

A rugged, minimal framework for composing JavaScript behavior in your markup.
https://alpinejs.dev
MIT License
28.32k stars 1.23k forks source link

Does Alpine have a concept of components? #414

Closed chopfitzroy closed 4 years ago

chopfitzroy commented 4 years ago

Hey,

From what I can see Alpine does not include any sort of concept of components (in the same way that something like React/Vue would handle them) this makes sense given it's use with Livewire and that it would be used in includes/partials.

I just wanted to confirm this though and if I am correct I wondered if you would be interested in a PR updating the "Read Me" and maybe a reference to some sort of HTML includes library that could be used on static projects for people who are un-familiar with this kind of thing.

Cheers.

ryangjchandler commented 4 years ago

Hi @CrashyBang - there's not a components API currently but there is a similar concept on the roadmap for v3.

Obviously the reason there isn't any templating with JSX or render function is because Alpine is designed to live in the plain DOM. Feel free to make a PR with some links if you think other people would find it useful, or alternatively, make a PR on the alpinejs/awesome-alpine repo with some links too (I think this one might be better).

chopfitzroy commented 4 years ago

Hey @ryangjchandler,

Makes total sense, will do thank you! Also is there a spec/RFC? for the proposed v3 api?

Cheers, Otis.

ryangjchandler commented 4 years ago

Hey @ryangjchandler,

Makes total sense, will do thank you! Also is there a spec/RFC? for the proposed v3 api?

Cheers, Otis.

There's no spec or RFC yet, that's a good idea though. I'll be sure to bring it up with the rest of the team.

pandichef commented 4 years ago

@ryangjchandler Assuming you're rending on the server (e.g., Rails, Django, Laravel), why do you need components? In Django, I think you can just create a file called MyShinyAlpineComponent.html. Then in your main template file, you use pass "props" to the component like so:

{% include "MyShinyAlpineComponent.html" with prop1="Hello" prop2="World" only %}
ryangjchandler commented 4 years ago

@pandichef The proposed component API for Alpine won't be used for templating / markup since that isn't what Alpine was designed for, and as you mentioned, this can all be done on the server.

Instead, it will provide a standardised way of defining re-usable component functions with support for initialiser methods without needing to define x-init and potentially some more developer-experience focused improvements.

pandichef commented 4 years ago

@ryangjchandler Actually, I take back what I said. Imagine you have this in your Django base.html template: {% include "MyShinyAlpineComponent.html" with prop1="Hello" prop2="World" only %} Django will just copy/paste the contents of MyShinyAlpineComponent.html into the top-level template.

Now say base.html and MyShinyAlpineComponent.html BOTH have Alpine.js components. Then the include statement above will create a nested component, which I believe isn't supported.

Do you see what I mean? Is there currently a solution/workaround for this?

(I realize not everyone here is familiar with Django, but I'm sure Rails/Laravel users would face the same issue.)

ryangjchandler commented 4 years ago

You can have nested components in Alpine, that's fine. Those components can't access parent data though.

dector commented 4 years ago

Can we have an issue to subscribe to be informed about components progress? Reusing same template in different parts of the page is crucial sometimes. Thanks!

ryangjchandler commented 4 years ago

@dector as I said above, the proposed x-component directive will NOT have anything to do with templating or the markup for your component. Instead it will provide a way of writing more immediately reusable data sets & functions, whilst reducing the amount of directives you need to define in your markup.

If you need re-usable templates, I would consider using a server-side template engine or a more monolithic front end framework such as Vue or React.

dector commented 4 years ago

Well, yeah. All these options that you described definitely works. It's a bit sad that alpine don't have built-in mechanism for achieving this functionality. Thanks.

ryangjchandler commented 4 years ago

@dector - the functionality you are looking for is far out of the scope of Alpine. It's designed to work alongside your existing markup from the server or static files, not replace / component-ise your markup.

pandichef commented 4 years ago

@ryangjchandler

You can have nested components in Alpine, that's fine. Those components can't access parent data though.

Is this what you mean?

<div x-data='{parent: "I am in the parent", shared: "I am also in the parent"}'>
  <div x-text="parent"></div>  <!-- This works -->
  <div x-text="shared"></div>  <!-- This works -->
  <div x-text="child"></div>  <!-- Uncaught ReferenceError: child is not defined -->
  <!---------------------------------------------------------------->
  <!-- Alpine "component" inserted by server side template engine -->
  <div x-data='{child: "I am in the child", shared: "I am also in the child"}'>
    <div x-text="parent"></div>  <!-- Displays "[object Window]" in the browser -->
    <div x-text="shared"></div>  <!-- This works -->
    <div x-text="child"></div>  <!-- This works -->
    <div x-text="blah"></div>  <!-- Uncaught ReferenceError: blah is not defined -->
  </div>
  <!---------------------------------------------------------------->
</div>

In the browser you get:

I am in the parent
I am also in the parent
[object Window]
I am also in the child
I am in the child

It seems like the child is aware of the parent variable's existence, but it displays [object Window] instead of null or throwing an error. Is this the behavior that you'd expect?

SimoTod commented 4 years ago

That parent is the global variable (https://developer.mozilla.org/en-US/docs/Web/API/Window/parent). If you look at the output, it's a window object, likely your current page. At the moment, there's no direct communication between components, even when nested.

pandichef commented 4 years ago

That parent is the global variable (https://developer.mozilla.org/en-US/docs/Web/API/Window/parent). If you look at the output, it's a window object, likely your current page. At the moment, there's no direct communication between components, even when nested.

@SimoTod Oh. Lol. I should have realized that.

Is there a proposal for a shared state feature? Since Alpine.js doesn't "break the DOM", you can do something like this in the meantime:

<div x-data='{myparent: "I am in the parent"'>
  <div class="sharedStateVarible"></div>
  <!---------------------------------------------------------------->
  <!-- Alpine "component" inserted by server side template engine -->
  <div x-data='{myvar: "Hello World"}'>
    <div class="sharedStateVarible"></div>
    <button type="button" onclick="changeGlobalState(this.innerText,'sharedStateVarible');">
      <div x-text="myvar"></div>
    </button>
  </div>
  <!---------------------------------------------------------------->
</div>
<script>
  function changeGlobalState(x, y) {
    var myClasses = document.getElementsByClassName(y);
    for (var i = 0; i < myClasses.length; i++) {
      myClasses[i].innerHTML = x;
    }
  }
</script>
SimoTod commented 4 years ago

If your goal is to share state between components you can use a third party library called spruce (https://github.com/ryangjchandler/spruce).

ryangjchandler commented 4 years ago

@pandichef Open an issue on the Spruce repo if you need any help 🤙🏼

mmm8955405 commented 2 years ago

All HTML codes will become very large. How to split modules is a problem that every framework will encounter. How does Alpine apply the HTML code of another file?

hickiy commented 1 year ago

All HTML codes will become very large. How to split modules is a problem that every framework will encounter. How does Alpine apply the HTML code of another file?

apply the HTML code in another file is too difficult on an existing basis, try it as a web component for a block code to reuse that maybe easier!