AlpineJS: How to pass a x-for variable to an x-data function - alpine.js

could you please help?
I 'm trying to add a count down according to some property in a loop but I could not find any way (not by trying out nor by googling) how I could pass that value in my functions:
<template x-for="item in cartData.items">
[...]
<template x-if="item.product_type == 'test'">
<div x-data="getCountdown()" x-init="init()">
<span x-text="timeLeft(item.timerEnd)"></span>
</div>
<script type="text/javascript">[...]
</script>
</template>
</template>
I was trying to pass item.timerEnd to every functon (getCountdown, init and timeLeft) but I always get the error that item is undefined, wheras if I pass it eg. to
<span x-text="new Date(item.timerEnd).toLocaleString()"></span> this works.
What am I missing?
PS: Thanks fpr the first help here: How to make timer in alpine.js app with time interval

Do this $data.item.timerEnd.
Like this:
<template x-for="item in cartData.items">
[...]
<template x-if="item.product_type == 'test'">
<div x-data="getCountdown($data.item.timerEnd)" x-init="init($data.item.timerEnd)">
<span x-text="timeLeft($data.item.timerEnd)"></span>
</div>
<script type="text/javascript">[...]
</script>
</template>
</template>

Related

Alpine Expression Error: Cannot set properties of null (setting '_x_dataStack')

I created a simple countdown with Alpine JS, but when timer is 0, I have an error.
First declare in x-data the function appTimer.
<div x-data="appTimer()">
<div x-show="active">
<template x-if="countdown > 0">
<div>
<div>Counting down</div>
<div x-text="countdown"></div>
</div>
</template>
<template x-if="countdown === 0">
Countdown completed!
</template>
</div>
</div>
This code JS, here set active, countdown and window.setInterval.
<script>
function appTimer()
{
return {
active: true,
countdown: 5,
init() {
window.setInterval(() => {
if(this.countdown > 0) this.countdown = this.countdown - 1; console.log(this.countdown)}, 1000)
}
}
}
</script>
The issue is that you're putting text in the template instead of html, alpine tries to get the html defined in the template but doesn't find any so it gets confused, to fix the issue you simply need to wrap your message in an html element, such as:
<template x-if="countdown === 0">
<div>Countdown completed!</div>
</template>
see this stackblitz reproduction
I think it depends on your compare. You compare strict but i dont know if countdown is an integer. Try to compare with ==
<template x-if="countdown == 0">

Vue component template not render when used with #if in blade,php

I have created Vue template which works fine when it is not used inside #if , but it return black if used inside #if.
Here is my template
<template>
<div>
<usage-token-button :tokens="usageTokens" v-on:use-token="useComplimentaryToken">
<template v-slot:default="data">
<span class="d-inline-block">
ABC
</span>
</template>
<template v-slot:use-button>ABC</template>
</usage-token-button>
<usage-token-button :tokens="researchPasses" v-on:use-token="useResearchPassToken">
<template v-slot:default="data">
<span class="d-inline-block">
ABC
</span>
</template>
<template v-slot:use-button>ABC</template>
</usage-token-button>
</div>
</template>
and this is how it is called
#if(!isset($showdata))
<usage-token-access
doi="{{ $content->metaInfo()['content']->doi }}"
root="{{ config('app.subdir') ? config('app.subdir') : '/' }}">
</usage-token-access>
#endif
I have confirmed it is going inside if condition , and when I inspect it I can see blank div as show in screenshot

Creating Layouts & Components with AlpineJS

I am wondering -- is there a way to create layouts and/or components with AlpineJS (so that I can keep my code DRY)? If not, are there any solutions that integrate with AlpineJS that add this functionality (without having to resort to a full framework like React or Vue)?
Alpine.js tries to avoid templating as much as possible since it's designed to work in tandem with server-side templating or a static side generator.
The example per the Alpine.js docs to load HTML partials is to use x-init="fetch('/path/to/my/file.html).then(r => r.text()).then(html => $refs.someRef.innerHTML = html)" (x-init is just one spot where this could be done).
You could use x-component with named slot in vimesh ui
<head>
<script src="https://unpkg.com/#vimesh/ui"></script>
<script src="https://unpkg.com/alpinejs" defer></script>
</head>
<body>
<vui-card>
<template slot="title">TITLE</template>
This is content
<template slot="footer">Copyright</template>
</vui-card>
<template x-component="card">
<h1>
<slot name="title"></slot>
</h1>
<p>
<slot></slot>
</p>
<div>
<slot name="footer"></slot>
</div>
</template>
</body>
If you only need small components you can use x-html and x-data:
<div x-data="{ message: '<p>Hello <strong>World!</strong></p>' }">
<span x-html="message"></span>
<span x-html="message"></span>
</div>
This would return:
Hello World!
Hello World!
You can read the docs here

Button that shows modal for each b-table row

Im using laravel, vue, and bootstrap-vue.
I have created a vue component that displays a table of elements (subnets in this example).
For each of them I show a component (modal_edit-subnet) thats should open a modal that allows to edit the data of the element of the related row.
The problem is that it shows modals for all of the table elements. For example, if the table has 3 rows, it shows 3 modals (after closing one it shows the next). Each of the modals with the data of each of the rows.
I've tried to add "key"s but no success.
What am i doing wrong?
Thanks!
Component that shows the table
<template>
<div>
<b-card class="text-center">
<b-table small striped hover :items="data_subnets" :fields="fields" :tbody-tr-class="rowClass">
<template slot="[ip_address]" slot-scope="data_subnets">
<b>{{ long2ip(data_subnets.item.ip_address) }}</b>
</template>
<template slot="[actions]" slot-scope="data_subnets">
v-on:deleteSubnet="deleteSubnet"></modal_delete-subnet>
<modal_edit-subnet :key="'modal_edit_subnet' + data_subnets.item.id" :subnet="data_subnets.item" v-on:editSubnet="editSubnet"></modal_edit-subnet>
</template>
</b-table>
</b-card>
</div>
</template>
Modal modal_edit-subnet
<template>
<div>
<b-button size="sm" v-b-modal.modal-edit-subnet>Edit</b-button>
<b-modal
id="modal-edit-subnet"
ref="modal"
title="Edit subnet"
#ok="handleOk"
>
This is subnet {{data_subnet.id}}
</b-modal>
</div>
</template>
The problem is that:
You're rendering a modal for each row of the table and;
Reading the docs, it seems like the modal is triggered by the id, and your b-modal id is not dynamic depending on the row.
How to fix it:
Use just one modal on the b-table level
Dynamically inject id into your modal_edit-subnet component:
<template>
<div>
<b-button size="sm" v-b-modal[id]>Edit</b-button>
<b-modal
:id="id"
ref="modal"
title="Edit subnet"
#ok="handleOk"
>
This is subnet {{data_subnet.id}}
</b-modal>
</div>
</template>
<script>
export default {
props: {
id: {
type: String | Number
}
}
}
</script>
Use v-model (this is the way I would do it)
<template>
<div>
<b-button size="sm" #click="show = true">Edit</b-button>
<b-modal
v-model="show"
ref="modal"
title="Edit subnet"
#ok="handleOk"
>
This is subnet {{data_subnet.id}}
</b-modal>
</div>
</template>
<script>
export default {
data() {
return {
show: false
}
}
}
</script>

Dynamically-generated paper-dropdown-menu initial selection issue

So I'm trying to dynamically generate a paper-dropdown-menu populated from an AJAX data source, which is working great using the code below:
<polymer-element name="my-element" attributes="selected">
<template>
<core-ajax auto url="/api/items/" response="{{items}}" handleAs="json"></core-ajax>
<paper-dropdown-menu selected="{{selected}}">
<template repeat="{{items}}">
<paper-item name="{{id}}" label="{{name}}"></paper-item>
</template>
</paper-dropdown-menu>
</template>
But if I set the initial selected item to be either the value of the published attribute, or a value that I set in the 'ready' callback, the dropdown menu item does not get selected when the items are loaded:
<script>
Polymer({
publish: {
selected: null
}
});
</script>
</polymer-element>
I understand that this is happening because the 'selected' property is being set before the template in the dropdown gets bound, but my question is whether there is a way to either 1) defer the 'selected' property change until after the template is bound or 2) otherwise reliably set an initially selected value for the dropdown menu?
One option would be to not render the dropdown until the data is available.
ex: http://jsbin.com/piyogo/13/edit
<polymer-element name="foo-drop">
<template>
<core-ajax auto
url="http://www.json-generator.com/api/json/get/bJMeMASvTm?indent=2"
response="{{items}}"
handleas="json">
</core-ajax>
<template if="{{items}}">
<paper-dropdown-menu selected="{{selected}}">
<template repeat="{{item in items}}">
<paper-item label="{{item.name}}"></paper-item>
</template>
</paper-dropdown-menu>
</template>
</template>
<script>
Polymer({
publish: {
selected: null
}
});
</script>
</polymer-element>
<foo-drop selected="2"></foo-drop>
For Polymer 1.0 (and later), the same can be achieved with the following code:
<iron-ajax auto url={{url}} handle-as="json" last-response="{{items}}"></iron-ajax>
<template is="dom-if" if="{{items}}">
<paper-dropdown-menu-light label="[[label]]" no-animations selected-item="{{selected}}">
<paper-listbox class="dropdown-content">
<template is="dom-repeat" items="[[items]]">
<paper-item>[[item.name]]</paper-item>
</template>
</paper-listbox>
</paper-dropdown-menu-light>
</template>

Resources