Change v-navigation-drawer content on click - javascript

I have 1 v-navigation-drawer and 2 buttons.
The first button opens the 1st drawer, the 2nd button is supposed to change the drawer's content.
What I would like is to be able to change the content of the drawer (once opened), as soon as I click on the button without having it closed. I don't want it to close when its content changes. I would just like its content to change without the "closing animation".
The issue I'm facing at the moment is that the content changes (I can see the new text for half a second) but then it closes.
Here's what my code looks like :
<template>
<div>
<v-btn #click="drawerTest = !drawerTest">TESTING 1</v-btn><br><br>
<v-btn #click="changeContent">TESTING 2</v-btn>
<v-navigation-drawer
v-model="drawerTest"
height="100vh"
width="360px"
absolute
temporary
hide-overlay
right
>
<p>
{{this.content1}}
</p>
</v-navigation-drawer>
</div>
</template>
data() {
let content1 = "This is the first test";
let content2 = "HELLO";
},
methods: {
changeContent() {
this.content1 = this.content2;
},
}

You should remove the temporary prop and instead add a stateless prop.

Related

Vuetify.js: how to cancel v-stepper-step header click when active form is invalid

I'm using a v-stepper from Vuetify.
What I want is:
when the user click on a stepper step (in the header) I block the click event if the active form isn't valid
I've managed to do so but by setting the click listener on the span element:
<v-stepper-step :key="step.id" :step="step.id" :rules="[() => !!step.valid]" :editable="currentStepId !== step.id && currentStep.valid">
<span #click="headerClick" v-html="step.name"></span>
</v-stepper-step>
This works well, the click is cancelled and that's exactly what I want.
Here is a live demo you can interact with.
My issue is:
It only works on the label, not the full clickable area.
I would like the entire stepper-step button to call this method.
But if I attach the #click listener on the v-stepper-step then it's too late, I can't cancel the click event (by doing e.stopPropagation).
Is there a way for me to do so?
demo code
Thanks!
It's a little bit unclear what you want to do exactly, but taking #click to the whole makes it so the headerCLick function behave exatly how it does when the click is on the label, yet you can click anywhere on the header (which I take for granted is what you mean by "the full clickable area").
<v-stepper-header #click="headerClick">
<template v-for="step in steps">
<v-stepper-step :key="step.id" :step="step.id" :rules="[() => !!step.valid]" edit-icon="$complete" :complete="currentStepId > step.id" :editable="currentStepId !== step.id && currentStep.valid">
<span class="v-stepper-step-clickable" v-html="step.name"></span>
</v-stepper-step>
<v-divider :key="`divider-${step.id}`" v-if="step.id < lastStep"></v-divider>
</template>
</v-stepper-header>

Error and wrong behaviour on close component from parent using Vuetify v-dialog

I've a component with a simple v-dialog to show a message to user and a v-btn to close it. The scenario is:
User click on the button that show v-dialog's component.
User click on the v-btn to close the component
A error is triggered on console: Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "show"
If click again on the button to open dialog, the dialog is not re-open, because the data() show not change the value from the component's v-btn.
The dialog component BasicMessageDialog.vue
<template>
<div class="text-center">
<v-dialog v-if="showDialog" width="500">
<v-card>
<v-card-title primary-title class="title">Ops...</v-card-title>
<v-card-text class="body-1">{{message}}</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn text color="primary" #click="show = false" class="body-1">Beleza!</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script>
export default {
name: "BasicMessageDialog",
computed: {
showDialog() {
return this.show;
}
},
props: {
show: Boolean,
message: String
}
};
</script>
<style>
</style>
The main component Login.vue
<template>
...
<BasicMessageDialog :message="messageBasicDialog" :show="showBasicMessageDialog">
...
</BasicMessageDialog>
</template>
<script>
import BasicMessageDialog from "#/components/BasicMessageDialog";
export default {
name: "Login",
components: {
BasicMessageDialog
},
data: () => ({
showBasicMessageDialog: false,
messageBasicDialog: "",
)},
methods: {
forgetPassword() {
console.log("forgetPassword");
if (this.email == "") {
this.messageBasicDialog = "Digite o e-mail no campo!";
this.showBasicMessageDialog = true;
}
}
}
</script>
It's because your updating the show prop in your dialog component wherein your show data was came from your parent. That's why it is returning a warning of Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders.
To solve your issue, here are some ways to prevent that warning.
First when you clicked the dialog button or clicked on the outside of your dialog, you must emit an event in your dialog component. like this one.
in your V-dialog component. when user clicked on a button
<v-btn text color="primary" #click="this.$emit('hideModal')" class="body-1">Beleza!</v-btn>
Now in your parent component should received this event. Parent Component like this
<BasicMessageDialog :message="messageBasicDialog" :show="showBasicMessageDialog" #hideModal='showBasicMessageDialog = false'>
...
</BasicMessageDialog>
Now, the issue again is what if the user clicked the outside part of dialog not the button ? To solve this you must watch the value of show prop. like this one. In your v-dialog component put this.
watch: {
show(val) {
if(!val) {
this.$emit('hideModal')
}
}
}
And everything will work fine now.
Second is to use vue .sync modifier
For convenience, Vue js offer a shorthand for this pattern with the
.sync modifier. Please read the docs here sync modifier. This approach will let you avoid emitting event. Unfortunately, true two-way binding can create maintenance issues.
Last is to use state management, vuex
It serves as a centralized store for all the components in an
application, with rules ensuring that the state can only be mutated in. docs here vuex
a predictable fashion.
<v-btn text color="primary" #click="show = false" class="body-1">Beleza!</v-btn>
You can't change prop as said in error instead of you have to add function, send it as prop, and call when you need change your prop value , parent have to handle the function and change data.
<template>
...
<BasicMessageDialog :message="messageBasicDialog" :show="showBasicMessageDialog" :hide="showBasicMessageDialog=!showBasicMessageDialog">
...
</BasicMessageDialog>
</template>
and
<v-btn text color="primary" #click="hide" class="body-1">Beleza!</v-btn>
https://v2.vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow

Agm direction panel: Show panel in different component

I have two components that display on one page (first is 70% of the page, the other 30%). One in which the map is and another where I want to show the panel (info on how to get to the destination: turn left, then right, etc.)
I used agm-directions to get the panel - https://robby570.tw/Agm-Direction-Docs/source/featured/direction.html (look at Update Panel With New Query ).
I managed to create it well and, when you click a button the panel will appear with the text, but on the map component, under the map (see picture 2). This is because I put <div #myPanel></div> under the map in the first component.
I want to get that panel text on the second component. I do not know how to do this because I bind the panel in the map component [panel]="myPanel", according to the documentation above.
So is there a way I can transfer that bonded data in a different component ?
Here is the first component html:
<agm-map [zoom]="zoom" [latitude]="lat" [longitude]="lng">
...
<agm-direction
[origin]="origin"
[destination]="destination"
[travelMode]="transitOptions"
[visible]="show"
[renderOptions]="renderOptions"
[markerOptions]="markerOptions"
[panel]="myPanel" <!-- THIS IS IMPORTANT FOR INFO -->
>
</agm-direction>
...
</agm-map>
<div #myPanel></div>
The second component is just some generic html.
TLDR: How can I get <div #myPanel></div> to work in a different component when I bind [panel]="myPanel" in agm-direction.
Managed to get the value thanks to
#ViewChild("myPanel", {static: false}) myPanel;
Then, in a function that activates when the button is clicked:
let _this = this;
setTimeout(function() {
console.log(_this.myPanel);
if(_this.myPanel != undefined){
let ivan = _this.myPanel;
console.log("test myPath", ivan);
//console.log("Test", JSON.stringify(this.myPanel.nativeElement.innerHTML));
console.log(_this.myPanel.nativeElement.innerHTML); //THIS IS THE ANSWER
}
}, 500);

Vue.js: How to fix 'b-modal' not displaying within custom component

Tools:
Vue.js
Bootstrap-Vue (https://bootstrap-vue.js.org/docs/components/modal)
Problem:
I am trying to display a popup each time a row is clicked within a bootstrap grid. Then once the modal is hidden it disappears and the selected item goes away
I created a custom component for the Modal. And I am now trying to programmatically get rid of the selected certificate.
I have it working, but barely and it's very clunky. I want a more seamless approach on how someone would tackle this problem who has more Vue experience than I do
/// Modal component
<b-modal
size="lg"
id="certificate-details-modal"
hide-footer
header-bg-variant="dark"
header-text-variant="light"
#hidden="modalDismissed"
v-model="expiringCertificate"
>
<template slot="modal-title">
Certificate Details for <span class="certificateTitleHighlight">{{ expiringCertificate.commonName }}</span>
</template>
<b-container fluid>
<b-row>
<b-col>
<b-badge pill variant="dark">Identified</b-badge>
</b-col>
<b-col class="text-center">
<b-badge pill variant="info">Ready</b-badge>
</b-col>
<b-col class="text-right">
<b-badge pill variant="success">Resolved</b-badge>
</b-col>
</b-row>
...
/// Main Component
<ExpiringCertificateDetail
v-if="selectedCertificate"
v-on:modalDismissed="resetSelectedCertificate"
:expiringCertificate="selectedCertificate">
</ExpiringCertificateDetail>
...
data () {
...
selectedCertificate = undefined
},
methods: {
resetSelectedCertificate() {
this.selectedCertificate = undefined;
},
rowSelected(certificate) {
this.selectedCertificate = certificate[0];
this.$bvmodal.show('certificate-details-modal')
},
My goal would be to display a modal each time a row is clicked and have the selectedCertificate reset back to undefined once the modal is hidden (either closed or unfocused and closed (which should be the hidden event)
I have been thinking of two possible approaches. Each of them use a separate component for the modal.
1. Use v-model for the current selected item
Use the modal component as any other input: declare a v-model on the component. When the modal is hidden, reset the current item to null from inside the modal component. The v-model magic will do the rest.
Full example here:
https://codesandbox.io/s/bootstrap-vue-sandbox-w8j09
2. Reset the current selected item from outside the modal component
This is pretty much the approach you have shown in your code. The modal component is only responsible for displaying the details. When to show the modal, or when to set the current selected item is the parent's responsibility.
In this example, I used a similar implementation as yours. I just use v-model on the modal component to avoid the this.$bvmodal.show and make the code more 'declarative'.
https://codesandbox.io/s/bootstrap-vue-sandbox-rwll4
Both approaches make them possible to change the details component into something other than a modal.
Although the first approach is less verbose, I would go for the second approach because is leaves the responsibility of showing/hiding the details from outside.

Global media library returns array VueJS

I have a project which contains a global VueJS file with child vuejs file depending on what page you are on.
I am trying to work out how to create a media manager which I can call multiple times per page and have it open up with a modal where I can either select an image or upload an image. I already know how to upload an image. What i need is once an image is selected or uploaded and a "Select" button is pressed it will close the modal and return an array back to the original element that called it and assigns to that element's variable.
Example would be:
I have a vuejs element called article_image with a variable called "article_image_data" and contains a button. When the button is created it opens the modal manager which you will need to select the image you want and press "Select" this modal then closes and returns an array which will be assigned to "article_image_data" and on the same page there may be a "category" section as well.
To make it simple, in your modal manager, listen to the change event on your file input.
<!-- ModalManager.vue -->
<template>
<div class="modal">
<input type="file" #change="$emit('selectedFiles', $event.target.files)">
</div>
</template>
Then from your parent who implemented the modal manager, you can get the file list on the selectedFiles event.
<!-- ArticleImage.vue -->
<template>
<ModalManager
#selectedFiles="onSelectedFiles"
v-if="modalOpen">
</template>
Easy now to deal with a FileList
export default {
components: {
ModalManager
}
data () {
return {
modalOpen: false,
articleImageData: null,
}
},
methods: {
onSelectedFiles (fileList) {
console.log('Received a FileList from ModalManager', fileList)
}
}
}
If you need a deeper component management for your modals, you either propagate your events from bottom to top with something like this:
<element #childEvent="$emit('childEvent')">
But the idea stays the same

Categories