skip to Main Content

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


  1. 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 reassign exerciseList using new ExerciseCollection().add(...), you are replacing exerciseList with just the returned array from the add function, not the entire modified ExerciseCollection 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:

    const app = Vue.createApp({
        // All data for the app, must return an object
        data() {
            return {
                exerciseList: new ExerciseCollection(),
                dayList: new DayExerciseCollection(),
                reviewList: new ReviewCollection(),
                daysReview: {},
                selectedEditExercise: {},
                currentDay: ''
            };
        },
        created() {
            // Add initial exercise when the instance is created
            this.addExercise({
                id: 0, title: 'Bench Press', date: '2024-04-03',
                sets: 2, reps: [10, 10], weight: [185, 185]
            });
        },
        methods: {
            
            addExercise(exercise, date){
                this.exerciseList.add(exercise, date);
                this.dayList.add(exercise, date);
            },
            // ... rest of your 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 called addExercise to add the initial workout.

    Login or Signup to reply.
  2. 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:

    class ExerciseCollection {
      constructor(arr = []) {
        this.list = arr;
      }
    
      add(exercise, date){
        ...
        return this;
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search