Git Product home page Git Product logo

alpine-magic-helpers's Introduction

GitHub tag (latest by date) npm bundle size

Magic Helpers

A collection of magic properties and helper functions for use with Alpine.js version 2

⚠️ Notice: This collection of helpers will be tied to version 2 of Alpine and will only receive bug fixes and minor updates as needed.

→ New features and plugins for V3 that will be found in the toolkit repo.

About

Adds the following magic helpers to use with Alpine JS.

Magic Helpers Description
$component/$parent Natively access and update data from other components or the parent component.
$fetch/$get/$post Using Axios, fetch JSON from an external source.
$interval Run a function every n milliseconds. Optionally start and stop the timer.
$range Iterate over a range of values.
$refresh Manually refresh a component.
$screen Detect if the current browser width is equal or greater than a given breakpoint.
$scroll Scroll the page vertically to a specific position.
$truncate Limit a text string to a specific number of characters or words.
$undo Track and undo state changes inside your component.

Adds the following custom directives to use with Alpine JS.

Custom Directives Description
x-unsafe-html like x-html but allowing new javascript scripts to run.

More to come!

🚀 If you have ideas for more magic helpers or custom directives, please open a discussion or join us on the AlpineJS Discord

Known issues

Installation

Include the following <script> tag in the <head> of your document before Alpine:

<script src="https://cdn.jsdelivr.net/gh/alpine-collective/[email protected]/dist/index.min.js" defer></script>

Or you can use the specific magic helpers you need:

<script src="https://cdn.jsdelivr.net/gh/alpine-collective/[email protected]/dist/component.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/gh/alpine-collective/[email protected]/dist/fetch.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/gh/alpine-collective/[email protected]/dist/interval.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/gh/alpine-collective/[email protected]/dist/range.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/gh/alpine-collective/[email protected]/dist/refresh.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/gh/alpine-collective/[email protected]/dist/screen.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/gh/alpine-collective/[email protected]/dist/scroll.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/gh/alpine-collective/[email protected]/dist/truncate.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/gh/alpine-collective/[email protected]/dist/undo.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/gh/alpine-collective/[email protected]/dist/unsafeHTML.min.js" defer></script>

Manual

If you wish to create your own bundle:

npm install alpine-magic-helpers --save

Then add the following to your script:

import 'alpine-magic-helpers'
import 'alpinejs'

Or you can import the specific magic helpers you need like so:

import 'alpine-magic-helpers/dist/component'
import 'alpine-magic-helpers/dist/fetch'
import 'alpinejs'

⚠️ Using Magic Helpers with Livewire

When using magic helpers along with Laravel Livewire, you need to make sure that the library is registered after Livewire to prevent Livewire from overriding the magic helper startup callbacks. This can be done either using the defer attribute on the magic helper script or including the magic helper script at the bottom of your body after @livewireScripts without the defer attribute.


$component

Example:

Arguably more useful, this also adds a $parent magic helper to access parent data

<div x-data="{ color: 'blue' }">
    <p x-data x-text="$parent.color"></p>
    <!-- The text will say blue -->
</div>

Demo

You may watch other components, but you must give them each an id using the 'id' attribute or x-id if you need more flexibility:

<div x-data="{ color: 'blue' }">
    <p
        x-data
        x-text="$component('yellowSquare').color"
        :class="`text-${$parent.color}-700`">
        <!-- This text will have blue background color and the text will say yellow -->
    </p>
</div>

<div x-id="yellowSquare" x-data="{ color: 'yellow' }"></div>

⚠️ Using $component/$parent in x-init

 <!-- This won't populate baz correctly -->
 <div x-data="{ foo: 'bar' }">
   <div x-data="{ baz: null }" x-init="() => baz = $parent.foo">
     <span x-text='baz'></span>
   </div>
 </div>
 <!-- use this instead -->
 <div x-data="{ foo: 'bar' }">
   <div x-data="{ baz: null }" x-init="$nextTick(() => baz = $parent.foo)">
     <span x-text='baz'></span>
   </div>
 </div>
 <!-- or -->
 <div x-data="{ foo: 'bar' }">
   <div x-data="{ baz: null }" x-init="setTimeout(() => baz = $parent.foo)">
     <span x-text='baz'></span>
   </div>
 </div>

When a component is initialised, the observed component may not be ready yet due to the way Alpine starts up. This is always true for $parent and it occurs for $component when the observer is placed before the observed component in the page structure. Previous versions were using a hack to evaluate the missing x-data on the fly but that strategy wasn't allowing to use nested magic properties and it was not syncronising properly in some edge cases. The magic helper since version 1.0 defers the resolution of those properties (resolving temporary to empty strings/noop functions) until the observed component is ready and then refreshes the component: this happens in a few milliseconds and it's not noticable by the final users but refreshing a component won't rerun x-init with the correct values. If developers need to use the magic property inside x-init, they'll need to manually postpone the execution of x-init for one tick either using the Alpine native $nextTick or a setTimeout with no duration (See examples above).


$fetch

Example:

<div x-data="{ url: 'https://jsonplaceholder.typicode.com/todos/1' }"
    x-init="$fetch(url).then(data => console.log(data))">
    <!-- After init, data will be logged to the console -->
</div>

Demo

As a shortcut, you can optionally use $get(url, params) or $post(url, data) to conveniently send a GET or POST request with params or data as the second argument.

Optionally pass in an Axios options object

If you need more control, you may pass in an object to customize the request See all options.

Example:

<div x-data="{ url: 'https://jsonplaceholder.typicode.com/todos/1' }"
    x-init="$fetch({ url: url, method: 'post' }).then(({ data }) => console.log(data))">
</div>

Note that this will return the entire response object, whereas by default $fetch will only return the data


$interval

Example:

<div
    x-data="{
        timer: 500,
        functionToRun: function() {
            console.log('Hello console')
        }
    }"
    x-init="$interval(functionToRun, timer)">
</div>

Demo

Optionally pass in options

By default, $interval will run your function every nth millisecond when browser provides an animation frame (via requestAnimationFrame). This means that the function will not run if the browser tab is not visible. Optionally, you may pass in the following options as the second parameter:

Property Description
timer Timer in milliseconds.
delay Delay the first run. N.B. The first run is also delayed by the timer time.
forceInterval Ignore the browser animation request mechanism. Default is false

⚠️ We also add a hidden property autoIntervalTest that will clear/stop the timer if set to false, and start the timer if then set to true.

Example:

<div
    x-data="{
        timer: 500,
        autoIntervalTest: true, // optional to start/stop the timer
        funtionToRun: function() {
            console.log('Hi again!')
        }
    }"
    x-init="$interval(funtionToRun, { timer: 1000, delay: 5000, forceInterval: true })">
    <button
        @click="autoIntervalTest = !autoIntervalTest"
        x-text="autoIntervalTest ? 'pause' : 'play'"></button>
</div>

Demo


$range

Example:

The $range helper mostly mimics implementations found in other languages $range(start, stop, step = 1)

<div x-data>
    <template x-for="item in $range(1, 5)">
        ...
    </template>
</div>
<!-- This will output 5 iterations [1, 2, 3, 4, 5], modelled after PHP's implimentation of range() -->

Demo

N.B: You may use $range(10) which will compute to [1...10]


$refresh

Example:

<div x-data>
    <button @click="$refresh()">Refresh <code>Date.now()</code></button>
    <span x-text="Date.now()"></span>
</div>

Demo


$screen

Example:

The $screen helper detects if the current browser width is equal or greater than a given breakpoint and returns true or false based on the result.

<div x-data>
    <span x-show="$screen('lg')">This will be visible if the window width is equal or greater than 1024px.</span>
</div>

By default the $screen helper uses the following endpoint borrowed by Tailwind CSS:

  • xs: 0px
  • sm: 640px
  • md: 768px
  • lg: 1024px
  • xl: 1280px
  • 2xl: 1536px

⚠️ NOTE: A single breakpoint is only going to tell you if the browser width is equal or greater than the given breakpoint. If you want to restrict the check to a specific range, you will need to negate the next endpoint as:

<div x-data>
    <span x-show="$screen('md') && !$screen('lg')">This will be visible if screen width is equal or greater than 768px but smaller then 1024px.</span>
</div>

Custom breakpoints

You can pass a numeric value to use an ad-hoc breakpoint.

<div x-data>
    <span x-show="$screen(999)">This will be visible if screen width is equal or greater than 999px.</span>
</div>

You can also override the default breakpoints including the following <script> tag in the <head> of your document

<!-- this example uses Bulma's breakpoints. -->
<script>
    window.AlpineMagicHelpersConfig = {
        breakpoints: {
            mobile: 0,
            tablet: 769,
            desktop: 1024,
            widescreen: 1216,
            fullhd: 1408
        }
    }
</script>

And using those breakpoints in your page.

<div x-data>
    <span x-show="$screen('tablet')">This will be visible if screen width is equal or greater than 769px.</span>
</div>

Demo


$scroll

Example:

<div x-data>
    <div x-ref="foo">
        ...
    </div>
    <button x-on:click="$scroll($refs.foo)">Scroll to foo</button>
</div>

Demo

Alternatively, you can pass a css selector to scroll to an element at any position.

<div id="foo">
</div>
<div x-data>
    <button x-on:click="$scroll('#foo')">Scroll to #foo</button>
</div>

$scroll also supports integers to scroll to a specific point of the page.

<button x-data x-on:click="$scroll(0)">Scroll to top</button>

Demo (same as above)

$scroll optionally supports a second parameter where it's possible to define the behavior mode, auto|smooth (default smooth):

<div x-data>
    <div x-ref="foo">
        ...
    </div>
    <button x-on:click="$scroll($refs.foo, {behavior: 'auto'})">Jump to foo</button>
</div>
...
<div id="foo">
</div>
<div x-data>
    <button x-on:click="$scroll('#foo', {behavior: 'auto'})">Jump to #foo</button>
</div>
...
<button x-data x-on:click="$scroll(0, {behavior: 'auto'})">Jump to top</button>

With offset:

<div x-data>
    <div x-ref="foo">
        ...
    </div>
    <button x-on:click="$scroll($refs.foo, {offset: 50})">Scroll to 50px before foo</button>
</div>
...
<div id="foo">
</div>
<div x-data>
    <button x-on:click="$scroll('#foo', {offset: 50})">Scroll to 50px before #foo</button>
</div>
...
<button x-data x-on:click="$scroll(0, {offset: 50})">Jump to 50px before top (a bit daft but supported)</button>

With both:

<div x-data>
    <div x-ref="foo">
        ...
    </div>
    <button x-on:click="$scroll($refs.foo, {behavior: 'auto', offset: 50})">Jump to 50px before foo</button>
</div>
...
<div id="foo">
</div>
<div x-data>
    <button x-on:click="$scroll('#foo', {behavior: 'auto', offset: 50})">Jump to 50px before #foo</button>
</div>
...
<button x-data x-on:click="$scroll(0, {behavior: 'auto', offset: 50})">Jump to 50px before top</button>

Demo (same as above)


$truncate

Example:

<div
    x-data="{ characters: 50, string: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'}"
    x-text="$truncate(string, characters)"
    @click="characters = undefined">
    <!-- Text will show 'Lorem ipsum dolor sit amet, consectetur adipiscing…' and will reveal all when clicked-->
</div>

You may also pass a third argument to change the string that will be appended to the end:

<div
    x-data="{ characters: 50, string: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'}"
    x-text="$truncate(string, characters, ' (...)')">
    <!-- Text will show 'Lorem ipsum dolor sit amet, consectetur adipiscing (...)' -->
</div>

Demo

Optionally pass in options

By default, $truncate will return take characters as a parameter. Instead you can pass in an object and trim by words. You may also update the ellipsis.

Example:

<div
    x-data="{ count: 5, string: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'}"
    x-text="$truncate(string, { words: words, ellipsis: ' ...read more' })"
    @click="count = 0">
    <!-- Will start with 5 words, then increase to unlimited when clicked -->
</div>

Demo (same as above)

Behind the scenes, for words, this uses sentence.split(" ").splice(0, words).join(" ") which does not define a word in all languages.


$undo

Example:

<div x-data="{ number: 0 }" x-init="$track()">
    <button @click="number = Math.floor(Math.random() * 10)" x-text="number"></button>
    <button x-show="$history.length" @click="$undo()">undo</button>
</div>

Demo

The $undo helper actually involves three helpers in one. First, add the $track() helper to the x-init directive to start tracking the component state. Next, add a button to $undo() changes as needed. And finally, you can access whether changes have occurred by using $history.length.

Optionally pass in options

By default, $undo will track all properties. Optionally you may limit the properties by passing in a string with the property name, or an array of property names.

Example:

<div x-data="{ number: 0; another: 0 }" x-init="$track('number')">
    <button @click="number = number + 1" x-text="number"></button>
    <button @click="another = another + 1" x-text="another"></button>
    <button x-show="$history.length" @click="$undo()">undo number only</button>
</div>

Use $track(['prop1', 'prop2']) to track multiple properties

Demo


x-unsafe-html

Example:

<div x-data="{ foo: bar }">
    <div x-unsafe-html="foo"></div>
    <button @click="foo = '<p>bar</p><script>alert(1)</script>'">test</button>
</div>

⚠️ Only use on trusted content. ⚠️

Dynamically rendering HTML from third parties can easily lead to XSS vulnerabilities.

Demo


License

Copyright (c) 2020 Alpine Collective

Licensed under the MIT license, see LICENSE.md for details.

alpine-magic-helpers's People

Contributors

atomgiant avatar basepack avatar dependabot[bot] avatar hugodf avatar kevinbatdorf avatar markfirmware avatar muzafferdede avatar pascalandy avatar pomartel avatar ryangjchandler avatar simotod avatar usernotnull avatar ypk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

alpine-magic-helpers's Issues

Using $refresh in a function?

Is it possible to use $refresh to force a component to update?

I have this function in one component:

selected: function(item) {
    return this.$store.stats.includes(item.value)
}

which is used by:

<template x-for="(item, index) in items" :key="index">
    <div :class="{ 'font-medium bg-primary': selected(item)  }">...</div>
</template>

It basically checks if that item's value is in the Spruce store array of stats.

The value of this.$store.stats gets changed after this is called (I can't change the loading order either) but this function doesn't then seem to reflect this change.

I appreciate that I'm probably pushing Alpine to/past the limit, it's on a project that started off quite simple and keeps evolving, and a rewrite into something like Vue isn't feasible.

Any help would be really appreciated!

Child element needs to fire event 2 times to mutate parent data

Issue:

When you try to mutate parent data from a child element on an event, you need to fire it 2 times. I have also tried to pre-bind data-last-refresh but it does not give me any luck.

Source code:

<div x-data="{num: 16}">
  <!-- Outside -->
  <p x-text='num'></p>
  <button @click="num = Math.random()">Random a Number (Outside)</button>
  <div x-data>
    <!-- Inside -->
    <button @click="$parent.num = Math.random()">Random a Number (Inside)</button>
  </div>
</div>

Is it possible to use multi-level $parent.$parent

Hello.

Thanks for your awesome helpers

Just a quick question. Is it possible to use $parent.$parent.property ?

I have data property at the body but a two-level component. I want to update the body data property from 2nd child.

What's the solution to this?

Thank you.

Test failing with latest Alpine version

If we run npm update and we pull the latest version of Alpine, $undo > component can keep track after component is replaced with a clone starts failing. Not sure if it's a red herring but I think a bug fix/BC went into the latest version around how alpine behaves in regards to a cloned element and it might affect $undo. cc @KevinBatdorf

[V3] Installation suggestion incorrect?

When running the suggested install command I get these errors:

npm install @alpine-collective/toolkit --save
npm ERR! code E404
npm ERR! 404 Not Found - GET https://registry.npmjs.org/@alpine-collective%2ftoolkit - Not found

When searching npm I see the following:

npm search alpine-collective
NAME                      | DESCRIPTION          | AUTHOR          | DATE       | VERSION  | KEYWORDS
@alpine-collective/toolki | A $scroll helper…    | =simotod        | 2021-07-25 | 1.0.0    |
t-scroll                  |                      |                 |            |          |
@alpine-collective/toolki | Alpine magic helper… | =simotod        | 2021-07-25 | 1.0.0    |
t-screen                  |                      |                 |            |          |
@alpine-collective/toolki | Truncate a string…   | =simotod        | 2021-07-25 | 1.0.0    |
t-truncate                |                      |                 |            |          |
@alpine-collective/toolki | A magic function to… | =simotod        | 2021-07-25 | 1.0.0    |
t-dbg                     |                      |                 |            |          |
@alpine-collective/toolki | Generate an array…   | =simotod        | 2021-07-25 | 1.0.0    |
t-range                   |                      |                 |            |          |

autoIntervalTest in $interval doesn't resume/restart properly

Hello 👋

First of all, thank you for the good work with these helpers.

I'm trying to build an image slider using $interval.
I want it to start automatically on init and pause/resume on @mouseenter and @mouseleave.

Here is my code:

<div x-data="{
timer: 7000,
autoIntervalTest: true,
autoplaySlider: function() {
document.getElementById('next_btn').click();
}}"
x-init="$interval(autoplaySlider, { timer, forceInterval: true})"
@mouseenter="autoIntervalTest = false"
@mouseleave="autoIntervalTest = true">

When I enter the picture, the timer is correctly stopped and starts again when leaving.

The problem is that when you quickly enter and leave the slider, the function will run multiple times in a row without respecting the initial timer. It's like leaving the picture fires the function every time and stacks these functions instead of resetting the initial one.

Another example with this

If you increase the timer value a little bit (2000 for example) and try clicking the counter multiple times in a row, the counter is now increasing much faster than the initial 2 seconds set by the timer.

Is there a way to resume/restart the same function instead of launching a new one with autoIntervalTest?

Hope my explanation is clear enough and thanks in advance for your help!

Regards,

Joachim

Installation suggestion is incorrect

When running the suggested install command I get errors:

npm install @alpine-collective/toolkit --save
npm ERR! code E404
npm ERR! 404 Not Found - GET https://registry.npmjs.org/@alpine-collective%2ftoolkit - Not found

When searching npm I see the following:

npm search alpine-collective
NAME                      | DESCRIPTION          | AUTHOR          | DATE       | VERSION  | KEYWORDS
@alpine-collective/toolki | A $scroll helper…    | =simotod        | 2021-07-25 | 1.0.0    |
t-scroll                  |                      |                 |            |          |
@alpine-collective/toolki | Alpine magic helper… | =simotod        | 2021-07-25 | 1.0.0    |
t-screen                  |                      |                 |            |          |
@alpine-collective/toolki | Truncate a string…   | =simotod        | 2021-07-25 | 1.0.0    |
t-truncate                |                      |                 |            |          |
@alpine-collective/toolki | A magic function to… | =simotod        | 2021-07-25 | 1.0.0    |
t-dbg                     |                      |                 |            |          |
@alpine-collective/toolki | Generate an array…   | =simotod        | 2021-07-25 | 1.0.0    |
t-range                   |                      |                 |            |          |

Clean up `$truncate` params

#7 (comment)

Alpine.addMagicProperty('truncate', function () {
  return (...parameters) => {

the ...parameters isn't necessary at this point, could be converted to (string, options) to clean up that function.

Idea: Axios API smaller dependency

Someone shared this library on work chat, so I'm sharing it here in case you are interested.

https://github.com/developit/redaxios
The Axios API, as an 800 byte Fetch wrapper.

Extract from the README
Axios has a great API that developers love. Redaxios provides that API in 800 bytes, using native fetch().

For those searching for ways to shave a few kilobytes off of their bundles, that's less than 1/5th of the size.

Unsupported $scroll target

This is really a very helpful package and I am trying to use it inside laravel app.
I have imported both import 'alpine-magic-helpers'; import 'alpinejs';
I am trying to use the $scroll helper and I have tried both $refs and using #id but when I click on the button to scroll to that specific section, It is not working and it shows the following error in the console.
Uncaught (in promise) Error: Unsupported $scroll target: at Proxy.eval (index.js?d52a:2334) at eval (eval at saferEvalNoReturn (alpine.js?df24:1900), <anonymous>:3:21) at saferEvalNoReturn (alpine.js?df24:143) at Component.evaluateCommandExpression (alpine.js?df24:1720) at runListenerHandler (alpine.js?df24:895) at HTMLButtonElement.handler (alpine.js?df24:871) eval @ index.js?d52a:2334 eval @ VM1370:3 saferEvalNoReturn @ alpine.js?df24:143 evaluateCommandExpression @ alpine.js?df24:1720 runListenerHandler @ alpine.js?df24:895 handler @ alpine.js?df24:871 Promise.then (async) handler @ alpine.js?df24:872

Just as a remider the $scroll(0) is working fine.

Thanks in advance

Installing doesn't work in V3 – callback is not a function

Hey everyone,

I'm upgrading a project from a colleague to make use of compiling a single JS file with npm modules instead of using several script tags.

I noticed that we're using the $screen helper (and perhaps more :) ), so I updated our app.js file to the following:

import Alpine from 'alpinejs'
import Toolkit from '@alpine-collective/toolkit'

Alpine.plugin(Toolkit)

window.Alpine = Alpine
window.Alpine.start()

Unfortunately every time I try I to use this in the browser, I get the following error in the console:

Schermafbeelding 2022-02-08 om 17 38 32

I tried different Alpine versions, but I'm not sure what could be causing this. Am I importing this wrong?

Thanks!

PS: I also tried opening an issue in the v3 repo, but was redirected here. Don't know if that's on purpose or if you forgot to update the link.

$component of self has trouble

Try this:

<div x-id='main' x-data="{ x: 1 }">
    <span x-text="$component('main').x">
    </span>
</div>

A variant of this structure in my actual program produces undefined for the text instead of becoming non-responsive.

Add `defer` attribute to `script` tags under Installation header in README

The demonstrated Install instructions on the AlpineJS repository use the defer attribute on the script tag. Perhaps the README and the Installation script tag demonstrations here should be updated accordingly as well?

NB: I have not read the source code for these helpers, so I don't know if there's a reason for the helpers to block the parsing of the page while loading and then executing. My assumption is that since these helpers are dependent upon the base Alpine framework, their utility comes following the load and execution of the base framework.

$scroll target scroll offset not always an Integer

Scroll offset calculation sometimes gives us a value with a fraction ex: 8245.125
target = target.getBoundingClientRect().top + window.pageYOffset

then, it fails this check:
if (Number.isInteger(target)) {

continues to:
if (typeof target !== 'object')

and throws Unsupported $scroll target error

$screen seems to be broken in combination with Livewire

I have a fairly complex setup and I first thought that it was the reason that it did not work anymore.

However see the simple code below that currently does not work with $screen('sm'), and gives this error: Uncaught ReferenceError: $screen is not defined

<script src="https://cdn.jsdelivr.net/gh/alpine-collective/[email protected]/dist/index.min.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/alpinejs/[email protected]/dist/alpine.min.js"></script>
 
<div x-data="">
    <div x-show="$screen('sm')">
        SHOW ON > SM ONLY (DOES NOT WORK)
    </div>
</div>

{{-- remove this line and it works... --}}
@livewireScripts

You can toy around here where I also added my usecase with an x-data function:
https://laravelplayground.com/#/snippets/81b039fe-6a65-479b-aa40-d17bc37d7b75 (go to livewire.blade.php)

Note:

  • I included alpine-magic-helpers first, then AlpineJS
  • there is even no Livewire component in the playground active

I think there is something wrong with the registration of the $screen helper

RFC: Adding a $select magic helper

Would adding a DOM select magic helper add value? Make the Alpine developers happier and more productive?

I'm my case, YES!

Quite often I use a vanilla JS DOM selectors in my code (sometimes one, sometimes more):

getElementsByTagName()
getElementsByClassName()
getElementById()
querySelector()
querySelectorAll()

I'm thinking of porting [select-dom](https://www.npmjs.com/package/select-dom) as a magic helper $select or even implement it!

The idea is to have a unified method to query DOM elements no matter what we're looking for (id, tag, class, data attribute):

This:

element = select('.foo');
element = select('#foo');
element = select('.foo a[href=bar]');
element = select('[data-foo]');

Instead of:

element = document.getElementById('foo')
element = document.getElementsByClassName('foo')
element = document.querySelector('.foo a[href=bar]');
element = document.querySelector('[data-foo]');

Comments?

Unsupported $scroll target

This is really a very helpful package and I am trying to use it inside laravel app.
I have imported both import 'alpine-magic-helpers'; import 'alpinejs';
I am trying to use the $scroll helper and I have tried both $refs and using #id but when I click on the button to scroll to that specific section, It is not working and it shows the following error in the console.
Uncaught (in promise) Error: Unsupported $scroll target: at Proxy.eval (index.js?d52a:2334) at eval (eval at saferEvalNoReturn (alpine.js?df24:1900), <anonymous>:3:21) at saferEvalNoReturn (alpine.js?df24:143) at Component.evaluateCommandExpression (alpine.js?df24:1720) at runListenerHandler (alpine.js?df24:895) at HTMLButtonElement.handler (alpine.js?df24:871) eval @ index.js?d52a:2334 eval @ VM1370:3 saferEvalNoReturn @ alpine.js?df24:143 evaluateCommandExpression @ alpine.js?df24:1720 runListenerHandler @ alpine.js?df24:895 handler @ alpine.js?df24:871 Promise.then (async) handler @ alpine.js?df24:872

Thanks in advance

$get and $post are not defined

Hi, simple string of code not working:

x-on:submit.prevent="$post($el.dataset.action, new FormData($el))"

and i have exception like:

Alpine Error: "ReferenceError: $post is not defined"
Expression: "$post($el.dataset.action, new FormData($el))"

scroll not working in alpine.js v3

Hello,
I am new to livewire/alpine and trying to use alpine-magic-helpers Scroll
i want use apline.js lasted version
<script defer src="https://unpkg.com/[email protected]/dist/cdn.min.js"></script>
not working and show me this error in console
VM8167:3 Uncaught (in promise) ReferenceError: $scroll is not defined at eval (eval at qr (cdn.min.js:1), <anonymous>:3:16) at cdn.min.js:1 at Br (cdn.min.js:1) at cdn.min.js:5 at o (cdn.min.js:5) at cdn.min.js:5 at HTMLButtonElement.<anonymous> (cdn.min.js:5)

Cannot install

Hi

npm install @alpine-collective/toolkit --save

Fail with this error:


npm ERR! code E404
npm ERR! 404 Not Found - GET https://registry.npmjs.org/@alpine-collective%2ftoolkit - Not found
npm ERR! 404 
npm ERR! 404  '@alpine-collective/toolkit@*' is not in this registry.
npm ERR! 404 You should bug the author to publish it (or use the name yourself!)
npm ERR! 404 
npm ERR! 404 Note that you can also install from a
npm ERR! 404 tarball, folder, http url, or git url.

Not expected behavior of $component/$parent helper

Hi,

I have an unexpected behaviour of $component/$parent, when I have multiple nested components.
I've created some simple tests. The examples are not quite realistic, but I hope they show the problem.

Or in a x-for loop, the $parent ist not defined after the component mount phase ?
I'm not sure if I don't understand the helper correctly or if this is a bug.

<!DOCTYPE html>
<html lang="de">

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <script src="https://cdn.jsdelivr.net/gh/kevinbatdorf/[email protected]/dist/component.js"></script>
  <script src="https://cdn.jsdelivr.net/gh/alpinejs/[email protected]/dist/alpine.min.js"></script>
</head>

<body>
    <h1 style="color:green">Works as expected</h1>
    <div x-data="{ colors: ['blue','red','green','yellow'] }">
        <div :data-color-index="0">
            <div x-data x-text="$parent.colors[$el.parentNode.dataset.colorIndex]" x-on:click="alert($parent.colors)"></div>
        </div>
    </div>
    
    <hr />
    <h1 style="color:red">Click handler does not show expected result</h1>
    <p>
        <b>expected result for click on a color:</b> 'blue,red,green,yellow'<br />
        <b>current result:</b> undefined
    </p>
    <div x-data="{ colors: ['blue','red','green','yellow'] }">
        <div :data-color-index="0">
            <div x-data x-text="$parent.colors[$el.parentNode.dataset.colorIndex]" x-on:click="alert($parent.colors)"></div>
        </div>
        <div :data-color-index="1">
            <div x-data x-text="$parent.colors[$el.parentNode.dataset.colorIndex]" x-on:click="alert($parent.colors)"></div>
        </div>
        <div :data-color-index="2">
            <div x-data x-text="$parent.colors[$el.parentNode.dataset.colorIndex]" x-on:click="alert($parent.colors)"></div>
        </div>
        <div :data-color-index="3">
            <div x-data x-text="$parent.colors[$el.parentNode.dataset.colorIndex]" x-on:click="alert($parent.colors)"></div>
        </div>
    </div>
    <hr />
    <h1 style="color:red">Click handler does not show expected result and $parent.colors error in the console log</h1>
    <p>
        <b>expected result for click on a color:</b> 'blue,red,green,yellow'<br />
        <b>current result:</b> undefined<br /><br />
    </p>
    <div x-data="{ colors: ['blue','red','green','yellow'] }">
        <template x-for="(color, index) in colors">
            <div :data-color-index="index">
                <div x-data x-text="$parent.colors[$el.parentNode.dataset.colorIndex]" x-on:click="alert($parent.colors)"></div>
            </div>
        </template>
    </div>
<script> 

</script>
</body>
</html>

Seems like $scroll smooth is not smooth anymore?

It just snaps to the top of the page if I use this code:

$scroll(0, {behavior: 'smooth'})

Or this:

$scroll(0)

And that is on a very long page with a fixed back-to-top button, after clicking on it you snap back to top in an instant.

Scroll not working when loaded from another livewire component

Hello,
I am new to livewire/alpine and trying to debug an error in the console.

I have a livewire component, that renders a form step by step. When a radio input option is selected, the next radio input is loaded and available for selection. On click, the next radio input group is loaded and it scrolls to it. It works well until I load another livewire component inside current livewire component.

Inside first livewire component, I apply the scroll code x-data x-on:click="$nextTick(() => { $scroll('#calendarScroll', {behavior: 'smooth'}) })"
It looks for the id="calendarScroll" inside second livewire component. Here I have another list of radio inputs, and after selecting one, the script turns back to the first livewire component to render remaining data.

When I click on a radio input from the second component, I get following error
scroll.js:526 Uncaught TypeError: Cannot convert undefined or null to object at Function.assign (<anonymous>) at Proxy.<anonymous> (scroll.js:526) at eval (eval at tryCatch.el.el (app.js:182), <anonymous>:3:39) at app.js:1658
If I click twice on the same radio input, it scrolls to given id, that is inside first livewire component, as intended.

What may be the problem? Why it throws the error on first click?

$scroll produces promise error

While using the following setup:

<div wire:click="setBankType({{ $bankType }})"
     x-data x-on:click="$scroll('#scrolltopaymentbutton')">Bank 1</div>
<div wire:click="setBankType({{ $bankType }})"
     x-data x-on:click="$scroll('#scrolltopaymentbutton')">Bank 2</div>

<div id="scrolltopaymentbutton">Pay now</div>

I got the following error: Uncaught (in promise) TypeError: Cannot convert undefined or null to object

Full error:
image

Referenced line:
image

[Question] Finding unused translations

How would you go about finding unused translations? I'm thinking of the possibility of wrapping the $t method with a proxy so its possible to memorize which translations keys have been used. Then there could be a helper function that allows you to see unused translations at that point in time. The only alternative I can think of is a regex based approach on the code. Advise appreciated.

edit: Sorry, this was meant for https://github.com/rehhouari/alpinejs-i18n @rehhouari

Problem using $scroll with $watch

Although not achieving behaviour expected the following is loading without error
<div x-data="chat()" x-init="() => { listen(); $watch('messages', () => $scroll($refs.anchor) ); }">

However, when I try and add additional parameters such as
<div x-data="chat()" x-init="() => { listen(); $watch('messages', () => $scroll($refs.anchor, {behavior: auto}) ); }">

Then I'm getting the following error

Uncaught (in promise) ReferenceError: auto is not defined

Automate Changelog update

Currently the [Unreleased] header isn't updated when making a release.

If someone wants to take this on please do or I can look into it on Sunday

Using $parent in for loop data that has been fetched (to be eventually used in an interval)

Howdy, somewhat new to the Alpine js world, don't know if this is a bug or just a situation of operator error.

The goal is to query some data, get back an array of json items then:

x-for="item in items"

which works, but when i try to insert your demo using my for loop data:

<div x-data="{ baz: null }" x-init="$nextTick(() => baz = $parent.item.minutes)">
         <span x-text='baz'></span>
</div>

I get nothing :/ All the demos work when not using for loop data. And the for loop data works when not nesting it.

I think this is main issue, not understanding how to properly access this parent data, or maybe it's a bug? Appreciate it. If it matters, the goal with this data is to use it with the iterator demo you have created:

<div x-data="{
        timer: 1000,
	count: parseInt($nextTick($parent.item.minutes)),
        addOne: function() {
            this.count++
        }
    }"
        x-init="$nextTick($interval(addOne, timer))"
        x-text="count.toString()">
</div>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.