1

I want to create a list of actions (each of which is a component) conditionally if the object variable store.plan is not empty, I have tried v-if which works well for rendering but not for creating the component.

I get an error:

Uncaught (in promise) TypeError: action is undefined

The full code of this component can be found here.

Can you please tell me how can I handle this problem? thanks in advance.

Err

<template>
    <div class="planlist" v-if="parse">
        <ul id="planOl">
        <Action
        v-for="action in store.plan"
            :action_id="action.act_id"
            :actor="action.actor"
            :color="action.color"
            :size="action.size"
            :lego_name="action.lego"
            :pick_pos="action.pick"
            :place_pos="action.place"
            :blocked="action.blocked"
            :status="action.status"
            :key="action.act_id"
        />
        </ul>
    </div>
</template>

<script>
import Action from '../components/Action.vue';
import { store } from '../js/store.js'

export default {
    name: 'Plan', 
    data() {
        return {           
            store,
        }
    },
    computed: {
        parse() { 
            if (store.plan.length > 0) { 
                return true;
            }
            return false;
        }
    },
    components: {Action}
}
</script>
3
  • Can you reproduce your problem in stackblitz.com ? Commented Dec 13, 2022 at 7:12
  • @Duannx I'm not sure how to use stackblitz with ROS, but the full code is available here, to reproduce the problem you can run the code with rosbridge and this test ros publisher Commented Dec 13, 2022 at 7:35
  • Too many efforts to reproduce it myself. You can easily mock your data and provide a simple version of your code with only one component that causes the error. Maybe this step will help you find out what is the root cause of the problem Commented Dec 13, 2022 at 7:38

3 Answers 3

1
+50

Did you try with optional chaining:

parse() { 
  return store?.plan?.length > 0 ? true : false
}

and don't mix v-if and v-for. Try to create wrapper div with v-if around your component:

<ul id="planOl">
  <div v-if="parse">
    <Action
    v-for="action in store.plan"
        :action_id="action.act_id"
        :actor="action.actor"
        :color="action.color"
        :size="action.size"
        :lego_name="action.lego"
        :pick_pos="action.pick"
        :place_pos="action.place"
        :blocked="action.blocked"
        :status="action.status"
        :key="action.act_id"
    />
  </div>
</ul>
Sign up to request clarification or add additional context in comments.

4 Comments

still I'm facing the same error, I have updated the question with the precise message error, if I understand well v-if just affects rendering, and not creation.
I have created a div wrapper with v-if outside, and still this issue unresolved.
can you provide how the store looks like when you have error?
"the store.plan looks like when you have error", actually it is an array (Not Empty) as you can see here
1

It is recommended not to use v-if and v-for directives together on the same element due to the syntax ambiguity.

As per your code, Computed property parse is used to check the length of an array. You can move the v-if to a container element (e.g. ul).

In template :

<ul id="planOl" v-if="parse">
 <Action v-for="action in store.plan">...</Action>
</ul>

Script :

computed: {
    parse() { 
        return store.plan.length > 0 ? true : false;
    }
}

6 Comments

I have created a div wrapper withv-if outside, and still this issue unresolved, I have updated the question accordingly.
@Bilal As you have a v-if condition on top of v-for, Ideally you should not get action undefined error. as action will always be available if v-if condition passed.
@Bilal - I saw the code and did not find anything wrong in that as you are just assigning the objects in the plan store. So it should work as you are using computed property for v-if
the problem was caused by assigning a wrong index to the store.plan[idx] when updating the plan.
@Bilal That's great. I am glad that you find the root cause of the problem.
|
1
  1. To make the process easy, we can move the store.plan to a computed property to use inside the template and parse property.
  2. Simply return store.plan.length from the computed property will do the job too instead of returning true and false based on condition.
  3. If you want to use v-if just outside the Action component, you can use template to do this. No need for an extra element.

So, below changes can help fixing the issues-

<template>
    <div class="planlist">
        <ul id="planOl">
            <template v-if="parse">
                <Action
                    v-for="action in plan"
                    :key="action.act_id"
                    :action_id="action.act_id"
                    :actor="action.actor"
                    :color="action.color"
                    :size="action.size"
                    :lego_name="action.lego"
                    :pick_pos="action.pick"
                    :place_pos="action.place"
                    :blocked="action.blocked"
                    :status="action.status"
                />
            </template>
        </ul>
    </div>
</template>

<script>
import Action from "../components/Action.vue";
import { store } from "../js/store.js";

export default {
    name: "Plan",

    components: {
        Action,
    },

    computed: {
        // A computed property to access the plan ()
        plan() {
            return store.plan;
        },

        parse() {
            /**
             * 1. The plan should be available (not null or empty or undefined)
             * 2. The plan should be an array so length property can be applied
             * 3. If its an array then it should have data (length in other words)
             */
            return this.plan && Array.isArray(this.plan) && this.plan.length;
        },
    },
};
</script>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.