Alpine: $refs is not defined or undefined

Created on 7 Apr 2020  路  11Comments  路  Source: alpinejs/alpine

Is there any possibility for getting $refs working in this example? Is there a reason it's not that I'm missing? I'm on version 2.0.2.

I'm either getting an undefined error OR $refs is not defined error (if I use $refs.panel and not this.$refs.panel.

<a
    href="<?php echo $c->url; ?>"
    x-data="contentPanel()"
    x-init="init()"
    x-on:click.prevent
>
    Link
</a>
<div x-ref="panel"></div>
<script>
    function contentPanel() {
        return {
            init: function() {
                this.$el.addEventListener('click', (ev) => {

                    let request = new Request(this.$el.href, {
                        headers: new Headers({
                            'X-Requested-With': 'XMLHttpRequest'
                        })
                    });

                    fetch(request)
                    .then((response) => response.text())
                    .then((html) => {
                        console.log(this.$refs.panel);
                    })
                    .catch((error) => {
                        console.error('Error:', error);
                    });
                });
            }
        }
    }
</script>

All 11 comments

You x-ref element is outside of your Alpine component. I've taken your code and moved it over to a CodePen example with the correct fixes (and obviously replaced the PHP with a fixed JSON endpoint but the same applies to HTML files / AJAX routes in your app).

See the pen here: https://codepen.io/ryangjchandler/pen/PoqMXgW?editors=1010

I would instead add an x-ref to the <a> tag, or use an @click.prevent handler on it. You can see in the example, I've tried to keep most of your code structure the same. The only big changes are in the part that adds the event listener, and the HTML itself.

If you look inside of the console, you'll see the <div x-ref="panel"> gets logged correctly. Each Alpine component is only aware of it's nested scope, so any x-ref attributes outside of the x-data root element will not be picked up correctly.

Hope this helps.

@ryangjchandler Thanks for the swift reply 馃挴this makes perfect sense and much appreciated!

Don't stress it. There's a few of us always around on here so ask any questions you've got. Hope that solution makes sense and helps you out.

@ryangjchandler And if I wanted to have multiple links (multiple x-refs) then could the refs be dynamic or would it require a forEach?

Let me be more clear. The panel only exists once but the links are multiple so clicking each would update the panel. I could just use querySelectorAll but fancied using x-ref 馃憤

<div x-data="contentPanel()" x-init="init()">
    <a href="https://jsonplaceholder.typicode.com/todos/1" x-ref="link">
        Link
    </a>
   <a href="https://jsonplaceholder.typicode.com/todos/2" x-ref="link">
        Link
    </a>
   <a href="https://jsonplaceholder.typicode.com/todos/3" x-ref="link">
        Link
    </a>
   <a href="https://jsonplaceholder.typicode.com/todos/3" x-ref="link">
        Link
    </a>
    <div x-ref="panel"></div>
</div>

@ryangjchandler And if I wanted to have multiple links (multiple x-refs) then could the refs be dynamic or would it require a forEach?

Yeah you could do it that way, but the refs would need to be different remember. There's multiple ways it would work, using @click.prevent="handleClick" for example instead of attaching the event listeners inside of the init hook.

Let me know how you get on.

Let me know how you get on.

Thanks! Is it possible to pass this into the on:click directive? So if I passed a function x-on:click.prevent="aFunction()" in aFunction() it knows what this is i.e the element clicked?

Sorry for all the questions.

If you did x-on:click.prevent="aFunction" the callback will receive an event variable, call it e for example. You could then do e.target to get the element clicked like a normal event listener.

If you did x-on:click.prevent="aFunction" the callback will receive an event variable, call it e for example. You could then do e.target to get the element clicked like a normal event listener.

Hmm, this is what I thought but this doesn't seem to work?

https://codepen.io/johnthepainter/pen/JjdgQpO?editors=1011

Apologies if I am being super dense here by not being able to get the simplest of things working...

Here you go. Alpine has a special $event variable that is available inside of your handlers. You can pass it through to the function like in the pen.

If you're using a global function, you will need to manually pass the $event variable through as seen in the first link. If the callback is in your x-data object then the callback with receive it automatically, the same as a regular event listener as seen with link number 2.

https://codepen.io/ryangjchandler/pen/ExVYYWq?editors=1010

I'm happy to help you out on Twitter if you prefer, https://twitter.com/ryangjchandler

Was this page helpful?
0 / 5 - 0 ratings