I have a multi-step layout managed with a dynamic component
<keep-alive>
<component :is="actualStep" :status.sync="status"></component>
</keep-alive>
<button #click.prevent="prevStep">Prev</button>
<button #click.prevent="nextStep" :disabled="!status">Next</button>
status value is used to monitor the actual component status, updating its value to true or false depending on component's internal validation process, and allowing (or not) to move to next step. As an example, a component will change status to true only if all required fields are filled.
status is also changed to an initial value when component is initiated. Some components change it to true (as they don't make any validation) and some change it to the validation process result.
mounted() {
this.$emit('update:status', this.isValid); // isValid is a computed
}
However this is only happening once when the component is mounted (I guess because of keep-alive). I would need to do this every time the dynamic component changes to the actualStep. How can this be done? or is it a better way to manage dynamic components' status?
Related
I Have component A and Component B
in component A i have an API call.
when i passing info to my component b:
<B :structuresMetaData="structureTree"></B>
Inside mounted the variable structuresMetaData the length is 0
and inside the watch the length is 1.
my issue that mounted appear before the watch.
is it would be right to put all my code in watch ? if not what is the solution ?
It looks like structureTree is being changed after <B> is created/mounted.
You have two options depending on your use case:
The first option is to defer the creation of <B> until your data is loaded. This way you don't need <B> to watch for changes to the prop and the prop will be fully populated in the created/mounted hook. This is the simpler approach.
Assuming structureTree is initially an empty array:
<B
v-if="structureTree.length > 0"
:structuresMetaData="structureTree"
/>
created() {
// this.structuresMetaData is a non-empty array here
}
I usually initialize my data with null so that I can distinguish between "not loaded" and "loaded" (where "loaded" can actually be an empty array if the data I fetched from the API was empty).
The second way is using a watcher in <B> to react to changes to the prop. You will need to do this method if the bound prop might change multiple times and you need <B> to update in some way to that change.
watch: {
structuresMetaData(value) {
// React to the change, load secondary data, etc
}
}
What exactly are you doing in <B> that requires a watcher? It's best to avoid explicit watchers if you can avoid them because it usually means you are copying data around and it gets messy when you don't have a single source of truth. Use pure computed properties whenever you can if you just want to transform the data in some way.
I have a list of items. Each item has a "complete" flag. If this flag is true, the item shows in view A. If it is false, it shows in view B. The items are retrieved via an API request, with a client-side filter applied through a computed method on the base dataset. So for view A it will return items with complete=true, for view B, it will return items with complete=false.
The complete flag is editable (via a checkbox). I have a watcher function on the complete field that results in a PATCH being made to the item to toggle the complete field on the server.
Here's the issue. Since the vue app is filtering items based on the complete flag, any time that field changes, the item is removed from the view, so the watcher never gets triggered and the PATCH is never made.
It makes sense that the item's removal would cause the filter function to be re-run, however it seems odd that the watcher is killed before it can run.
So my question is, how do I achieve this - a list of filtered objects where the filter criteria can be changed, and have that change recognized via a watcher or similar?
You're probably better off using an #input event on the checkbox, and tying that to the api call:
<input type="checkbox" #input="onCheckChange">
...
methods: {
onCheckChange() {
// Perform API call here.
}
}
I have an Order form component which uses a OrderTasklistBuilder component. Note: there are nested components here. The hierarchy is as follows: OrderSingle > CreateOrderForm > OrderTasklistBuilder
The initial order is fetched when the OrderSingle is mounted and then passed down through child components.
The tasklist builder component is used like this:
<order-tasklist-builder v-if="form.tasks.length" :initial-tasks="form.tasks" #taskAdded="handleTaskAdded" class="mb-2" />
This works fine, but if there aren't any tasks in the listbuilder, the component will not display. This is a problem because someone might remove all of the tasks.
Now, if I remove the length check on form tasks the tasklist builder component will not display any tasks even after adding new tasks.
<order-tasklist-builder :initial-tasks="form.tasks" #taskAdded="handleTaskAdded" class="mb-2" />
The components are fairly large ( > 300 lines ) so if I can elaborate on something specific, let me know.
Solved by moving logic previously in mounted function inside of CreateOrderForm component to created function.
I'm having a scoping issues with Vuex mapState. I have a requirement to output the users name locally on the page without it being reactively updated by v-model in my input field (for the same user's name data).
The following code works fine, however if the user refreshes the page the state is lost and returns as undefined.
computed: {
...mapState(["userProfile"]),
},
beforeMount() {
this.localUserProfile.name = this.userProfile.name;
console.log("localUserProfile: " + this.localUserProfile.name);
},
I think what might be happening is that your userProfile data in Vuex is set only after the component has mounted when you directly refresh the page? In that case userProfile.name might still be undefined when you assign it. If this is what happens, you could add a watcher in this component that checks for changes in the userProfile and updates the localProfile if it hasn't been set during the mount.
Also: have you looked at the v-once directive? https://v2.vuejs.org/v2/api/#v-once
This should prevent the username from being updated when the value changes without having to reference a local copy.
I'm having a hard time figuring out how to get a component to reload after a sibling is updated. For instance, when I make a selection in the first component, I want the second component to "refresh" to account for the newly selected "state" data:
<c-select
dataEndpoint="/states.json"
errorMessage="Some error message..."
id="state"
message="Some message"
v-model="form.state"
:v="$v.form.state" />
Has the following dependency, so to speak:
<c-select
:dataEndpoint="`/${form.state}.json`"
errorMessage="Some other error message..."
id="county"
message="This field uses a local data source and **is required**"
v-model="form.county"
:v="$v.form.county" />
Once a state is selected or changed, I need to "dynamically" reload the appropriate endpoint to show the counties for that state in a second component. Right now, the only way I can make this work is with a v-if="form.state hack. But, if a user attempts to change the state again, the changes do not take effect in the "county" component. I would appreciate any help or advice on how to best solve this issue.
Here is a link to my vue codebase in Code Sanbox
Ok. Here is the result:
mounted is executed only ONCE, so after dataEndpoint has changed NO UPDATE action performed, that's why you should add watch to your c-list component and check if the entry has changed - update drop-down list:
watch: {
dataEndpoint() {
this.fetchAndSetJsonEndpoint();
}
},
Here is a working version of your code: https://codesandbox.io/s/64mj19r6zw