Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 5 years ago.
Improve this question
I have a drop-down.vue component like this:
<template>
<div #mouseenter="handleEnter" #mouseleave="handleLeave" #click="handleClick" v-click-outside="hide">
<slot name="dropdownBtn" :visible="visible" ></slot>
<slot v-if="visible" name="dropdownWrap" :visible="visible"></slot>
</div>
</template>
...
it's a drop down component, then click or hover the dropdownBtn the dropdownWrap will apear/disapear.
now i have another component need the dropdown, so i can use the dropdown in two way:
first: use nested component
<template>
<ui-dropdown>
<div name="dropdownBtn">...</div>
<div name="dropdownWrap">...</div>
</ui-dropdown>
</template>
...
second: use extend
<template>
<!-- the handleEnter and handleLeave are extend from the dropdown component -->
<div>
<div class="btn" #mouseenter="handleEnter" #mouseleave="handleLeave">...</div>
<div class="popup" #mouseenter="handleEnter" #mouseleave="handleLeave">...</div>
</div>
<template>
<script>
import dropdown from '../dropdown';
export default {
extend: dropdown,
...
}
</script>
i think the first is better. but the second has one less component than the first, the performance will be better?
who can tell me which is better?
Both ways work, and I can't really say that one way is correct and the other isn't, it depends on your requirements.
Extend
Extending a base component can get hairy if you want to modify the base component's template in some way (you can't really modify part of the base component's template, you either keep the base template or completely redefine it).
You need to make sure that the new template adheres to any requirements that the base component needs for it to work correctly (such as the presence of certain refs, using correct CSS class names, etc.
The base and extended components are tightly coupled.
Composition
I'd say this is more the "Vue way".
You can take advantage of slots.
There's no tight coupling between the child and parent components; they're completely separate. You can modify the child component's code as much as you like as long as its interface (props, events, slots, etc) are unchanged.
A disadvantage could be that the parent component is a completely separate component from the child; it won't inherit any props, events, slots (anything) from the child component.
You might want to have a look at functional components if you're concerned about the overhead of having an extra component with the composition approach.
I rarely extend components, but when I do, it's because I want to share a common blueprint for a set of related components (the base component isn't usually functional on its own, I've just extracted the common code out).
Related
I'm new to Vue and have looked at some answers regarding my problem in which, I want to pass an id (for example 0099) from my child component to the parent via v-model. Is this possible, to pass an ID this way?
Here's my child code:
<template>
<v-dialog v-model="show" width="unset">
<v-card class="px-15 py-8" elevation="0">
<div class="px-6 py-8 mb-n4 text-center">
<span class="lucky-point--text text-h6">
Are you sure you want to access this employee?
</span>
</div>
</v-card>
</v-dialog>
</template>
What I want is that in v-model="show" I want to also include some id of an employee. Example v-model="show[0099] to the parent.
my parent code:
<ClaimDialog
:id="item.employeeid"
/>
although I used some props.
props: {
id: Number
}
Once I click from parent dialog it gets stacked on top of one another. I have found one answer but on my side its a bit tricky, since I'm new to Vue, and this approach is different from mine, since mine has a child component. Here's that question: Why are my dialogs stacked on top of one another? (Vuetify)
If you need to pass data from a child to a parent, then you must use $emit
Here is an example of a question and answer
Here is a link to the official documentation
If these links do not help you, then update the question: what event should the data be transmitted on, and how your components are connected (more code from your components)
Yes, this old chestnut.
I'm making an editor for a large object tree, and it seemed sensible to make components for each of the property types at a vertex in the tree in order to remove clutter from the main component. However, this has created the problem that the main JSON object isn't being updated beause the editor is implemented in child components (#Input).
Using #Output and EventEmitter seems infeasible because:
how on earth do you get a component to call the emitter very time a change is made to the data it's editing, and
it seems really stupid to have to write an update function to handle the event because the UI should be just automatically bound to the JSON object it's editing -- that's the point of this observable malarkey.
The only solution I can think of is to not use components and just wirte a massive monolithic editor.
Is there a better way?
The way you describe is pretty standard. An alternative though would be to use a template # variable to access your child properties.
<hello #childcomponent name="{{ name }}"></hello>
<div>
{{ childcomponent.name }} - Parent component
</div>
You can use this to access your child component through the typescript as well.
#ViewChild('childcomponent') childcomponent;
Stackblitz
Sorry for the beginner question (I'm fairly sure this will be a duplicate, but I actually can't figure out what terms to use in order to find it). I just started with Vue.
I am just getting started with Vue, and following this course (https://www.vuemastery.com/courses/intro-to-vue-js/communicating-events). In this problem, there is (and here I don't know the term, so I'm going to go with...) app-level data parameter called cart. cart is an array which holds the id of each item a user has added to cart.
The problem tells us to add a button to remove items from the cart.
I ran into problems trying to create a computed property, which would allow me to hide the "remove" button in the event the selected item is not in the cart (eg. `
Communicating data from the app-level (the cart array), to the component-level (to a computed property in the product component), so that I could use something like :hidden="!inCart" on the "Remove from Cart" button, which is itself defined in the component. inCart would be a computed value here.
Communicating the selected product from the component to the app-level, computing inCart at the app level, then using the computed value at the component-level.
Either way, I can't seem to figure out how to do this in the way I would want to, which would look something like how v-bind operates. Namely, I think I may be able to hack together a solution using methods (which I believe have to be triggered by certain events), but I don't understand how I might go about this using built-in functionality such that the value of inCart is dynamically auto-computed.
Maybe there would be an answer to this in the next few courses, but I don't see us covering that in the intro material. Sorry for the neophyte question. Thank you in advance.
In Vue the way you communicate "state" from higher-level objects to lower-level objects is through props.
So, assuming your app looks something like...
<MyApp>
<MyShoppingPageWithItems>
<MyItem></MyItem>
<MyItem></MyItem>
<MyShoppingPageWithItems>
</MyApp>
You need to pass the cart object down as a prop.
So in your MyShoppingPageWithItems template, you'll have something like...
<template>
<div>
<MyItem v-for="item in items" :item=item :cart="cart"?
</MyItem>
</div>
</template>
And in your item template...
<template>
<div>
<div>
{{item.name}}
</div>
<div v-if="cart.includes(item.id)">
Remove button or whatever
</div>
</div>
</template>
Not that the .includes() method is a native JavaScript method, which you can read more about here.
Edit
To reference a prop in a computed property (or anywhere else in a Vue component), just refer to this.propName, as demonstrated here in the Vue docs.
So, if you want to create a computed property, you can do the following:
<template>
<div>
<div>
{{item.name}}
</div>
<div v-if="isInCart">
Remove button or whatever
</div>
</div>
</template>
<script>
export default {
props: ['cart', 'item'],
computed: {
isInCart() {
return this.cart.includes(this.item.id)
}
}
}
</script>
Note that the formula is the exact same as above, but just includes this. for cart and item. In templates, the this. is implied when referring to props, data, and computed properties.
I have a Vue component for a generic dialog window. It has no model, and a handful of slots for the title, the center, and the buttons.
Now I want to populate this dialog with a form. Obviously the details of the model, and the content, are specific to the form contained in, as well as the general behavior (e.g. validation etc).
What is the preferred Vue approach to handle this requirement?
Since your dialog component is just a template with slots, it seems like it would be easiest to use both the generic dialog component and the form component in your template and then simply put the form component tag in the dialog component's center slot:
<template>
<my-generic-dialog>
<template slot="center">
<my-form></my-form>
</template>
</my-generic-dialog>
</template>
As Bert Evans mentioned in his comment, if you were looking for a way to separate out general functionality of a vue component, you could either use Vue.extend(), or Vue mixins.
I'm trying to wrap my head around how "inner components" can adjust the content of "outer components". Let's say I have an application template that looks something like this:
<template>
<div class="sidebar">
<div>Some app-wide content</div>
<div>
<!-- I want to put some view-specific content here -->
</div>
</div>
<div class="main-body">
<router-view></router-view>
</div>
</template>
Each subview wants to render different content to the sidebar. Obviously this would be easy if the subview included the sidebar area itself, but let's say it is important to preserve the structure and we don't want to replicate the boilerplate of the sidebar across every view.
Is there any way for a child view to declare "export this extra component for display in another place?" I imagine something like injecting the parent view and calling a method on it, but I can't figure it out from the documentation.
Simple demo:
It's fairly simple, actually. Just import and inject your sidebar or any other viewmodel and call a method or update a property.
https://gist.run/?id=745b6792a07d92cbe7e9937020063260
Solution with Compose:
If you wanted to get more elaborate, you could set a compose view.bind variable to that your sidebar would pull in a different view/viewmodel based on the set value.
https://gist.run/?id=ac765dde74a30e009f4aba0f1acadcc5
Alternate approach:
If you don't want to import, you could also use the eventAggregator to publish an event from the router view and subscribe to listen to that event from your sidebar and act accordingly. This would work well for a large app setting where you didn't want to tie them too closely together but wanted the sidebar to react correctly to unpredictable routing patterns by always responding when triggers were published.
https://gist.run/?id=28447bcb4b0c67cff472aae397fd66c0
#LStarkey's <compose> solution is what I was looking for, but to help others I think it's worth mentioning two other viable solutions that were suggested to me in other forums:
View ports. You can specify multiple named router views in a template and then populate them by passing in a viewPorts object to the router instead of specifying a single moduleId. The best source of documentation is a brief blurb in the "Cheat Sheet" of the Aurelia docs.
Custom elements. It's a little more "inside-out" but you could define most of the outer content as a custom element that has slots for the sidebar and the main body; each child view would define this custom element and pass in the appropriate pieces.