I have a project that uses Vue components and collection design pattern for list manipulation. Inside of the collection, I have an arr.add function that adds a new object to the list when a button is pressed. This works correctly the first time called but then shows an error stating it is not a function if it is called a second time. This is happening for all my function calls.
If I manually enter the data into the list and avoid using the collection call it works fine but if I use the collection for a second time I get the error: Uncaught TypeError: this.exerciseList.add is not a function
This is the collection model:
function ExerciseCollection(arr = []){
arr.add = function (exercise, date){
if (date){
exercise.date = date;
}
exercise.id = genId(date);
this.push(exercise);
return this;
}
arr.delete = function (exercise){
this.splice(this.indexOf(exercise), 1);
return this;
}
arr.deleteSet = function (exercise, set){
let editExercise = this.indexOf(exercise);
editExercise.weight.splice(set, 1);
editExercise.reps.splice(set, 1);
editExercise.sets--;
return this;
}
arr.addSet = function (exercise){
this.indexOf(exercise).sets++;
return this;
}
return arr;
}
This is where they are called:
const app = Vue.createApp({
// All data for the app, must return an object
data() {
return {
exerciseList: new ExerciseCollection().add({
id: 0, title: 'Bench Press', date: '2024-04-03',
sets: 2, reps: [10, 10], weight: [185, 185]
}),
dayList: new DayExerciseCollection(),
reviewList: new ReviewCollection(),
daysReview:{},
selectedEditExercise: {},
currentDay: ''
};
},
methods: {
addExercise(exercise, date){
this.exerciseList.add(exercise, date);
this.dayList.add(exercise, date);
},
...(rest of the file works properly but is pretty long wont put it in here)
2
Answers
It appears that the issue is likely with how the add function modifies the
exerciseList
causing it to lose its methods, including add itself. The context (this) within the add method refers to the original array, but when you reassignexerciseList
using newExerciseCollection().add(...)
, you are replacingexerciseList
with just thereturned array
from the add function, not the entire modifiedExerciseCollection
object.Remember that in JavaScript, functions are first-class objects, and when you assign the returned value of arr.add, you are not assigning the function but the result of the function.
Here is a modified approach that preserves the methods:
It took your initialization of
exerciseList
out of the data object and placed it in the created lifecycle hook of the Vue component. In the created hook, It calledaddExercise
to add the initial workout.It’s a not a good idea to add custom methods to an array, nothing requires that.
The problem here is that Vue reactivity makes an arrays reactive in
data
and discards custom methods because they aren’t expected. Vue 3 supports reactive classes with several known pitfalls but these are ES6 classes with prototype methods that won’t cause problems.It can even support method chaining but
arr
state needs to be a field and not an instance itself: