alpinejs / alpine

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

[Feature Request] x-watch #79

Closed TomS- closed 4 years ago

TomS- commented 4 years ago

In this example; there is a slider (let's say flickity), we want to create a custom UI that will track the active slide. AlpineJS would be good to do this because we can minipulate the UI based on the active slide.

Let's say for example on each slide we have a UI that has the title of the slide, we can use <div @click="activeSlide = 1; activeTitle = 'Projects'">Projects</div>

Then we can use x-watch to watch the activeSlide and run a function when it changes, I'm unsure of the best implementation for this x-watch:activeSlide="function" or x-watch="{ activeSlide: function } so for example we could do x-watch="{ activeSlide: $el.select(value) }" value would be the value of the variable. I know the syntax is completely wrong, but it's the best way I can think of demostrating my thinking.

TomS- commented 4 years ago

Noticed @pfrlv responded with a confused emoji. Here is Vue's implementation - https://vuejs.org/v2/guide/computed.html#ad

calebporzio commented 4 years ago

Yeah, I could see the use for this. I'd probably rather do it like Vue does:

this.$watch('activeSlide', funcion(value) {...})

Thinking this doesn't need to be a directive though right? (x-watch)

TomS- commented 4 years ago

@calebporzio correct me if I'm wrong, but wouldn't this rely on taking the function approach (https://github.com/alpinejs/alpine#x-data)? I don't see any harm in that, it could be seen as advanced functionality. I just thought x-watch or x-computed would keep the inline/TailwindCSS nature.

SimoTod commented 4 years ago

@TomS- I like the idea of having a x-watch but only allowed on the same tag where you define x-data (like x-init does). I'm not sure if we need 'x-computed, though. Wouldn't it be the same as calling a function directly when you use the variable? (https://codepen.io/SimoTod/pen/RwNBeJr)

TomS- commented 4 years ago

@SimoTod I also don't believe we need x-computed for this and I do believe it would be called where you define x-data I'm just unsure of the syntax but I was thinking for example: x-data="activeText: 'Hello'" x-watch="{ activeText: function(value) { this.$refs.text.innerText = value } }" so you can later define @click="activeText = 'Goodbye'" and it will auto-update the text refs innerText.

EDIT: To avoid confusion I know you can use x-text, this will be used for more advanced stuff that needs to be reactive to variable updates. Such as a slider and keeping track of the active slide in my OP.

SimoTod commented 4 years ago

@TomS- Yeah, I got that. In my opinion, to keep it consistent with the core, it should be something like x-watch="{ activeText: '$refs.text.innerText = activeText' }" When the component is initialised, alpine would deserialise the string and store the object somewhere. When a variable gets updated, the code would do a lookup on that object. if it found a match, it would pass the value to the built-in eval function. That would automatically resolve $refs and activeText from the proxy object. It should be easier to implement.

SimoTod commented 4 years ago

@TomS- are you working at this feature? If you are not, I'd like to give it a go if @calebporzio is open to a PR. The approach i would take is the one I described in the post above.

TomS- commented 4 years ago

@SimoTod I started working on it but I realised I wasn't quite ready to understand it all. I might give it a go as a personal project, but please by all means work on this. I appreciate it thank you.

SimoTod commented 4 years ago

The only question, more for Caleb, is: which one of the following syntax we want to use:

1) <div x-data="{foo: 'bar', baz: ''}" x-watch="{foo: 'baz = foo +|+ foo'}"> Similar to the other x-* directives Cons: It's a bit weird when you have a string since you need to use ` as a delimiter, we don't have access to the old value like Vue does. Also, what the watcher executes is totally disconnected from the variable and it could be anything.

2) ,<div x-data="{foo: 'bar', baz: ''}" x-watch="{foo: function (newValue, oldValue) { this.baz = newValue + '|' + oldValue }"> Closer to VueJS, I'm reevaluating it but I'm still torn. Cons: A bit more verbose, other variables need to be prefixed by this which is different from what we do in other directives.

TomS- commented 4 years ago

Both look good to me, I like 1 with the posibility to have it call a function inside x-data like this: https://github.com/alpinejs/alpine#x-data what do you think @calebporzio

SimoTod commented 4 years ago

@TomS-

I think it should be something like this -> https://codepen.io/SimoTod/pen/VwYEXdj I've also made two magic properties ($oldValue and new $newValue) available in the context so they can be used.

This example is deliberately verbose to show that we can use strings, access variables defined in x-data without using "this." and access the old/new values using the magic properties.

TomS- commented 4 years ago

@SimoTod That looks amazing, great work! I look forward to the PR. Hopefully @calebporzio is keen to add this to the core. I can definitely see a big use case for it.

jaymakes11 commented 4 years ago

Should https://github.com/alpinejs/alpine/releases/tag/v2.2.0 have closed this?

HugoDF commented 4 years ago

Technically 2.2.0 introduced a $watch magic value not an "x-watch" directive. I do believe this should be closed because with the magic value there's no reason to have a directive.

mikaeljorhult commented 4 years ago

Personally I would've liked to see a x-watch directive with a syntax like x-watch:[property]="[expression]". This could also make for modifiers like the proposed debounce one x-watch:[property].debounce.250ms="[expression].

Just a personal preference that I feel would look a bit cleaner but with that said, the magic property is great!

ryangjchandler commented 4 years ago

I'm going to close this issue since the $watch magic variable has been added. Great discussion though! If you're still interested in an x-watch directive, it might be worth opening another issue for that discussion (and mention this issue for a paper trail).