0

I am currently trying to have a child component $emit data into an object of a parent, and the parent object is included in an array that is created and added to when clicking a button.

one iteration of create component button one iteration of create component button

clicking the button twice, adds 2 more objects into the array clicking the button twice, adds 2 more objects into the array

I do not understand how I can add numbers into amountbudgeted via props and emits while also creating new components.

New to Vue so sorry if it is not the best description

array of objects array of objects

each object in the area has an amountbudgeted, uniqueid, remaining, input budget each object in the area has an amountbudgeted, uniqueid, remaining, input budget

Parent Component

<div class="budgetItemContainer">
        <div class="budgetItemRow">
            <!--component being created via button click-->
            <div v-for="(input, index) in budgetRows" :key="index">

            <!--<component :is="$options.components.budgetItemRowContent" v-bind="budgetRows"></component>-->
                <budgetItemRowContent v-bind:="budgetRows" ></budgetItemRowContent>
            <progress data-min="0" data-max="100" data-value="20"></progress>
            </div>
        </div>
    </div>

export default {
name: 'budgetGroup',
components: {
    budgetItemRowContent,
    BudgetItemButton,
},
data: () => {
    return {
        budgetItemHeading: 'Housing',
        budgetRows: [
            {
                inputbudget: '',
                amountbudgeted: 0,
                remaining: 0,
                id: uniqId(),
            },
        ],
    };
},
methods: {
    //creates new budgetRow when button is clicked
    createNewContent() {
        this.budgetRows.push({inputbudget: '', amountbudgeted: 0, remaining: 0, id: uniqId() });
    },
},
}


Child Component

 <!--input that will hopefully update amoundbudgeted in parent object-->


       <div class="budgetItemRow-Column">
        <div class="budgetItemLabel">
            <input v-model="blabel" type="text" maxlength="32" placeholder="Label" class="input-Budget-Inline-Small budgetItemRow-Input">
        </div>
    </div>

    <!--input that will hopefully update amoundbudgeted in parent object-->
    <div class="budgetItemRow-Column">
        <div class="amountBudgetedInputContainer">
            <input v-model.number="amount" class="amountBudgetedNumber budgetItemRow-Input input-Budget-Inline-Small" type="number" placeholder="$">
        </div>
    </div>

     export default {
    props: ['value', 'label'],
    computed: {
        amount: {
            set(newVal) {
                this.$emit('input', newVal);
            },
            get() {
                return this.value;
            },
        },
        blabel : {
            set(newLab) {
                this.$emit('labels', newLab);
            },
            get() {
                return this.label;
            }
        }
    },
};

1 Answer 1

2

If you're just concerned with amountBudgeted then here's how you would lay out your code.

First, convert the component line to the following:

//<component :is="$options.components.budgetItemRowContent" v-bind="budgetRows"></component>
<budgetItemRowContent v-model="budgetRows[index].amountBudgeted"></budgetItemRowContent>

This basically just makes your code cleaner and readable. Note that I've also attached a v-model to the component, this is basically the same as a) passing a value prop and b) watching an input event and then updating the binding (in this case, budgetRows[index].amountBudgeted) with the payload of the event.

Next, change your child component to the following:

...
<input v-model.number="amount" ...
...

<script>
export default {
  // Name isn't 100% necessary here as it's inferred from the parent scope
  props: ['value'], //Implicitly passed from parent
  computed: {
    amount: {
      set(newVal){
        this.$emit('input', newVal)
      },
      get(){
        return this.value
      },
    },
  }
}
</script>

This should get you on the right track, let me know if you have any issues.

Sign up to request clarification or add additional context in comments.

5 Comments

Hey Andrew, thanks your code definitely helped with my data being reset when a new item was created as well as overall emitting of input data. In your last comment you mention setting up a watcher for value. That is the next step, i am also needing the input to update the amountbudgeted in the object making it as you said a two way data binding. How would i go about doing that?
You can achieve two-way binding rather elegantly by making a settable computed rather than a copied data item.
@LandonHarvey I just updated my code with that new watcher. In hindsight, I should've added this from the get-go as it eliminates the need for any created logic. @Roy J Can you elaborate? What would you bind to the child component's input element? Or would you just add @input to the input?
@AndrewPeters Thank you both for helping me understand this communication better. Roy J Thanks you as well for guiding us both to learning something new.
@AndrewPeters Hey Sorry to bother. I have implemented a new input for the label feature in the child component and need a two way binding between this input as well as the amount. I understand that v-model="budgetRows[index].amountBudgeted" is only passing amountBudgeted as a prop and only retrieving that info as well. I need to pass multiple items in the object now and return multiple values through the input. I have tried v-bind, and create new computed properties for the other input but have not been able to make it work. I have updated my code above to reflect this change. Thanks.

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.