I use the following Todo module with AlpineJS in order to have a central logic/schema for data handling. It works in general. However, when I add multiple entries, they all point to the same instance; so when I add a new to do, all the todo's in the list adapt the new name.
To avoid this I tried using a class instead of just a module, however, this does not seem to work at all with AlpineJS.
Any idea how to best tackle this approach?
Notes Using Class Export:
x-data="new todo()"
. Then I get the error: todo is not a constructorAlpine.data('todo', new Todo());
. Then I get the error: Uncaught TypeError: callback.bind is not a functionNotes Using Function Export:
item.name
into the list, instead of (the whole) item
, probably because it gets converted to a simple string, but I need the whole object unfortunatetly.todos.push(structuredClone(item))
I got the error: Uncaught DOMException: Failed to execute 'structuredClone' on 'Window': # could not be cloned.// <todo.js>
/* CLASS EXPORT: Does not work at all */
export default class {
constructor(name, completed = false){
this.item = {
name: name,
completed: completed
}
}
};
/* FUNCTION EXPORT: Works, but all items point to the same instance */
export default() => ({
item: {
name: '',
completed: false
}
});
<script type="module">
import Alpine from './modules/alpine.js';
import Todo from './modules/todo.js';
Alpine.data('todo', Todo);
Alpine.start();
</script>
<article x-data="{ todos: [] }">
<form x-data="todo" @submit.prevent="todos.push(item)">
<input x-model="item.name"><button>Add</button>
</form>
<ul>
<template x-for="todo in todos">
<li x-text="todo.name"></li>
</template>
</ul>
</article>
In todo.js
you can first create the Todo
class, then export it:
class Todo {
constructor(name, completed = false) {
this.name = name
this.completed = completed
}
}
export default Todo
Note that I removed item
property from the Todo
class.
In the HTML file you also need to set Todo as a global class (window.Todo
). You need to remove the Alpine.data('todo', Todo)
and x-data="todo"
parts since Todo
is not an Alpine.js component, so don't try to use it as one. I also created a new property new_todo_name
that holds the new todo's label until it is created with todos.push(new Todo(new_todo_name))
.
<script type="module">
import Alpine from './modules/alpine.js'
import Todo from './modules/todo.js'
window.Todo = Todo
Alpine.start()
</script>
<article x-data="{ todos: [], new_todo_name: '' }">
<form @submit.prevent="todos.push(new Todo(new_todo_name)); new_todo_name = ''">
<input x-model="new_todo_name"><button>Add</button>
</form>
<ul>
<template x-for="todo in todos">
<li x-text="todo.name"></li>
</template>
</ul>
</article>