Vue Js Component issue- manipulating data - javascript

I've been devloping a project for a class and run into a problem where the components arent communicating well together as intended. The goal is that if there is one false in any component that it sends the message dont go to the location.
This is some of the code I currently have:
<template>
<div class="component">
<h3>Temperature Sensor</h3>
<input v-model = 'temperature' #keyup.enter = "checkTemp"> </input>
<p> Action to take : {{ action }} </p>
</div>
</template>
<script>
import { eventBus } from '../main.js';
export default {
props :{
action : String
},
computed () {
return {
//defines a movement value that we will use.
movement : true,
};
},
mounted(){
eventBus.$on('move',(movement) =>{
this.movement = movement;
if(movement == true){
this.action = "Go to Location";
}else{
this.action = "Dont go There";
}
})
},
methods : {
checkTemp(){
if(this.temperature >=50){
this.movement = false;
console.log(this.movement);
eventBus.$emit('move', this.movement);
}else {
this.movement = true;
console.log(this.movement);
eventBus.$emit('move', this.movement);
}
}
},
};
</script>
<style scoped>
div {
background-color: lightcoral;
}
</style>
And for the second component:
<template>
<div class="component">
<h3> Radiation Drone</h3>
<input v-model = 'radiation' #keyup.enter = "checkRadiation"></input>
<p > Action : {{ action }} </p>
</div>
</template>
<script>
import { eventBus } from '../main.js';
export default {
props : {
action : String
},
computed () {
return {
movement : true,
}
},
mounted(){
eventBus.$on('move',(movement) =>{
this.movement = movement;
if(movement == true){
this.action = "Go to Location";
}else{
this.action = "Dont go There";
}
})
},
methods : {
checkRadiation(){
if(this.radiation >=34){
this.movement = false;
console.log(this.movement);
eventBus.$emit('dontGo',this.movement);
}
else {
this.movement = true;
console.log(this.movement);
this.$root.$emit('goThere',this.movement);
}
}
},
};
</script>
<style scoped>
div {
background-color: lightgreen;
}
</style>
It is showing an error of not manipulating the prop directly I just dont know how to refactor my code.
Any suggestions?

You canĀ“t change the prop value directly from the child component, instead use emit to call a method created in the parent components

Related

emit value from mounted in child.vue to parent.vue

I'm working with BootstrapVue.
I need to emit a value to my parent.vue - but my code line this.$emit('info', this.hide); doesn't work out.
If I console.log(this.hide) i get my value correct in this case false, otherwise if my if-statement is correct I get it true.
What is the mistake in here?
script of my child.vue:
data(){
return {
hide: true,
}
}
mounted() {
if (statement) {
if(some statement) {
//do something
} else {
this.hide = false;
console.log(this.hide); //HERE I GET CORRECT VALUE
this.$emit('info', this.hide); //THIS DOESNT WORK
}
}
}
How it should work in my parent.vue:
<template>
<div #info="info">
<div> //THIS DIV SHOULD BE SHOWN IF this.hide = false
</div>
<div> //THIS DIV SHOULD BE SHOWN IF this.hide = true
</div>
</div>
</template>
Try something like following snippet :
Vue.component('Child', {
template: `
<div class="">
child
<button #click="changeHide">change hide</button>
</div>
`,
data(){
return {
hide: true,
}
},
methods: {
changeHide() {
this.hide = !this.hide
this.sendInfo()
},
sendInfo() {
this.$emit('info', this.hide);
}
},
mounted() {
//if (statement) {
//if(some statement) {
//do something
//} else {
this.hide = false;
this.sendInfo()
//}
}
})
new Vue({
el: '#demo',
data(){
return {
info: null,
}
},
methods: {
setInfo(val) {
this.info = val
}
}
})
Vue.config.productionTip = false
Vue.config.devtools = false
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<div v-if="!info"> //THIS DIV SHOULD BE SHOWN IF this.hide = false
</div>
<div v-if="info"> //THIS DIV SHOULD BE SHOWN IF this.hide = true
</div>
<p>from child info is: {{ info }}</p>
<Child #info="setInfo" />
</div>
Ideally in App.vue (Parent)
you should have something like
<login #info="someHandler"></login>
there is no need to go for a child component if you gonna jus use to manage some logic. It is appropriate only if have some contents in the template.
Also placing emit handler on some div. Thats not how emit works
Below is a simple working example
App.vue (parent)
<template>
<h1>{{ title }}</h1>
<Child #changeTitle="ChangeT($event)" />
</template>
<script>
import Child from "./components/Child"
export default{
name:'App',
components: {
Child,
},
data()
{
return{
title:'Rick Grimes'
}
},
methods:{
ChangeT(title)
{
this.title=title;
},
}
</script>
<style></style>
Child.vue
<template lang="html">
<button type="button" #click='passEvent'> Update me</button>
</template>
<script>
export default {
name:'Child',
methods:{
passEvent()
{
this.$emit('changeTitle','Awesome ')
}
}
}
</script>
<style lang="css" scoped>
</style>

Retrieve a props data in Vuejs and record to POST API with modal

I would like to retrieve the data "project.id" to record it when I click on the button "add study". When I click on this button, I send the name and the status but i would like to send the id of project too via my API.
Si this is the code :
<template>
<div>
<div v-for="(projet,index) in arrayProjects.projects" v-bind:key="index" class="box-project">
<h2>{{projet.title}} - {{projet.nameStructure}}</h2>
<ProjectTable v-bind:id-project="projet.id" />
<div>
<a-button type="secondary" #click="showModalStudy">
Add study {{projet.id}}
</a-button>
</div>
<a-modal
title="Add study :"
:visible="visible"
:confirm-loading="confirmLoading"
#cancel="cancelClick"
#ok="sendClick"
>
<div>
<a-form>
<a-form-item >
<a-input
v-model="newStudy.nameStudy"
/>
</a-form-item>
</a-form>
</div>
</a-modal>
</div>
</div>
</template>
And the javascript :
import ProjectTable from "#/components/ProjectTable";
import axios from "axios";
export default {
name: "ProjectCard",
components: {ProjectTable},
props: ["arrayProjects"],
data() {
return {
visible:false,
confirmLoading: false,
newStudy: {
nameStudy:"",
}
}
},
methods: {
showModalStudy() {
this.visible = true;
},
cancelClick() {
this.visible= false;
console.log("click cancel")
},
sendClick() {
console.log("send")
console.log(this.newStudy.nameStudy)
this.confirmLoading = true;
axios
.post('http://127.0.0.1:8000/api/studies', {
name : this.newStudy.nameStudy,
status: "On load",
})
setTimeout(() => {
this.visible = false;
this.confirmLoading = false;
}, 1000);
}
}
}
How can I add my id of my project in my axios lines to send it ?
Thanks for your help
You should determine the current project.id with a container in data scope for the Add study handler sendClick() with passing project.id to showModalStudy() to overwrite the defined data currentId for the next API call
<a-button type="secondary" #click="showModalStudy(project.id)">
Add study {{ projet.id }}
</a-button>
data() {
return {
currentId = null // will be a id value when user click the button
...
};
},
showModalStudy(projectId) {
this.visible = true;
this.currentId = projectId
},
sendClick() {
// do something with this.currentId
// ...
}

Toggling button issue in polymer

Im trying to toggle the displaying of message using a button.
Below is my code.
class DisplayMessage extends PolymerElement {
// DO YOUR CHANGES HERE
static get template() {
return html`
<style>
:host {
display: block;
}
</style>
<h2>Hello [[prop1]]!</h2>
<button on-click="toggle">Toggle Message</button> <br />
<template is="dom-if" if="{{user.authorise }}">
<br />
<span>I should now display message.</span>
</template>
`;
}
toggle() {
// DO YOUR CHANGES HERE
// hint: use set method to do the required changes
//console.log(this.user);
//this.user = !this.user
}
static get properties() {
return {
prop1: {
type: String,
value: 'user',
},
user: {
type: Object,
value: function () {
return { authorise: false}; // HINT: USE BOOLEAN VALUES TO HIDE THE MESSAGE BY DEFAULT
},
notify: true,
},
};
}
}
window.customElements.define('display-message', DisplayMessage);
I tried thinking for like hours, but couldn't solve. The requirement her is on clicking the button, the click handler toggle should change the value of authorize in user property to true. And on clicking again to false and so on. I need to use set method within toggle method. I'm not getting how to do this. Please help me on this.
Thanks in advance.
Why use a library/dependency for such a small component, that can be done with native code
<display-message id=Message name=Cr5>You are authorized</display-message>
<script>
customElements.define("display-message", class extends HTMLElement {
static get observedAttributes() {
return ["name", "authorized"]
}
connectedCallback() {
this.innerHTML = `<h2>Hello <span>${this.getAttribute("name")}</span></h2><button>Toggle message</button><br><div style=display:none>${this.innerHTML}</div>`;
this.querySelector('button').onclick = () => this._toggle();
}
_toggle(state) {
let style = this.querySelector('div').style;
style.display = state || style.display == "none" ? "inherit" : "none";
this.toggleAttribute("authorized", state);
console.log(Message.name, Message.authorized);
}
get name() { return this.getAttribute("name") }
set name(value) {
this.querySelector('span').innerHTML = value;
this.setAttribute("name", value);
}
get authorized() { return this.hasAttribute("authorized") }
set authorized(value) { this._toggle(value) }
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue) this[name] = newValue;
}
})
Message.name = "Cr5";
Message.authorized = true;
</script>
class DisplayMessage extends PolymerElement {
static get template() {
return html`
<style>
:host {
display: block;
}
</style>
<h2>Hello [[prop1]]!</h2>
<button on-click="toggle">Toggle Message</button> <br />
<template is="dom-if" if="{{user.authorise}}">
<br />
<span>I should now display message.</span>
</template>
`;
}
toggle() {
if(this.user.authorise==false)
{
this.set('user.authorise', true);
}
else
{
this.set('user.authorise', false);
}
}
static get properties() {
return {
prop1: {
type: String,
value: 'user',
},
user: {
type: Object,
value: function () {
return { authorise: false };
},
},
};
}
}
window.customElements.define('display-message', DisplayMessage);

How to write a plugin that shows a modal popup using vue. Call should be made as a function()

I am trying to make a VueJS plugin that exports a global method, which when called, will popup a message with an input text field. Ideally, I want to be able to make the following call from any Vue component:
this.$disaplayMessageWithInput("Title","Body","Value");
And a popup should come on the screen.
I've tried building it but when the install() calls this.$ref., it isn't recognized:
DeleteConfirmation.vue
<template>
<b-modal size="lg" ref="deleteConfirmationModal" :title="this.title" header-bg-variant="danger" #ok="confirmDelete" #cancel="confirmCancel">
<p>
{{this.body}}
</p>
</b-modal>
</template>
<script>
export default {
data()
{
return {
title: null,
body: null,
valueCheck: null,
value: null
};
},
install(vue, options)
{
Vue.prototype.$deleteConfirmation = function(title, body, expectedValue)
{
this.title = title;
this.body = body;
this.valueCheck = expectedValue;
this.$refs.$deleteConfirmation.show()
}
},
}
</script>
app.js
import DeleteConfirmation from './components/global/DeleteConfirmation/DeleteConfirmation';
Vue.use(DeleteConfirmation);
The call I am trying to make is:
$vm0.$deleteConfirmation("title","body","val");
I get the below error at the run time:
app.js?id=c27b2799e01554aae7e1:33 Uncaught TypeError: Cannot read property 'show' of undefined
at Vue.$deleteConfirmation (app.js?id=c27b2799e01554aae7e1:33)
at <anonymous>:1:6
Vue.$deleteConfirmation # app.js?id=c27b2799e01554aae7e1:33
(anonymous) # VM1481:1
It looks like, this.$refs in DeleteConfirmation.vue is undefined.
Try to avoiding $ref with vue ( $ref is here for third party and some very special case )
$ref isn't reactive and is populate after the render ...
the best solution for me is using a event bus like this :
const EventBus = new Vue({
name: 'EventBus',
});
Vue.set(Vue.prototype, '$bus', EventBus);
And then use the event bus for calling function of your modal ...
(
this.$bus.on('event-name', callback) / this.$bus.off('event-name');
this.$bus.$emit('event-name', payload);
)
You can create a little wrapper around the bootstrap modal like mine
( exept a use the sweet-modal)
<template>
<div>
<sweet-modal
:ref="modalUid"
:title="title"
:width="width"
:class="klass"
class="modal-form"
#open="onModalOpen"
#close="onModalClose"
>
<slot />
</sweet-modal>
</div>
</template>
<script>
export default {
name: 'TModal',
props: {
eventId: {
type: String,
default: null,
},
title: {
type: String,
default: null,
},
width: {
type: String,
default: null,
},
klass: {
type: String,
default: '',
},
},
computed: {
modalUid() {
return `${this._uid}_modal`; // eslint-disable-line no-underscore-dangle
},
modalRef() {
return this.$refs[this.modalUid];
},
},
mounted() {
if (this.eventId !== null) {
this.$bus.$on([this.eventName('open'), this.eventName('close')], this.catchModalArguments);
this.$bus.$on(this.eventName('open'), this.modalRef ? this.modalRef.open : this._.noop);
this.$bus.$on(this.eventName('close'), this.modalRef ? this.modalRef.close : this._.noop);
}
},
beforeDestroy() {
if (this.eventId !== null) {
this.$off([this.eventName('open'), this.eventName('close')]);
}
},
methods: {
onModalOpen() {
this.$bus.$emit(this.eventName('opened'), ...this.modalRef.args);
},
onModalClose() {
if (this.modalRef.is_open) {
this.$bus.$emit(this.eventName('closed'), ...this.modalRef.args);
}
},
eventName(action) {
return `t-event.t-modal.${this.eventId}.${action}`;
},
catchModalArguments(...args) {
if (this.modalRef) {
this.modalRef.args = args || [];
}
},
},
};
</script>
<style lang="scss" scoped>
/deep/ .sweet-modal {
.sweet-title > h2 {
line-height: 64px !important;
margin: 0 !important;
}
}
</style>
AppModal.vue
<template>
<div class="modal-wrapper" v-if="visible">
<h2>{{ title }}</h2>
<p>{{ text }}</p>
<div class="modal-buttons">
<button class="modal-button" #click="hide">Close</button>
<button class="modal-button" #click="confirm">Confirm</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
visible: false,
title: '',
text: ''
}
},
methods: {
hide() {
this.visible = false;
},
}
}
</script>
Modal.js (plugin)
import AppModal from 'AppModal.vue'
const Modal = {
install(Vue, options) {
this.EventBus = new Vue()
Vue.component('app-modal', AppModal)
Vue.prototype.$modal = {
show(params) {
Modal.EventBus.$emit('show', params)
}
}
}
}
export default Modal
main.js
import Modal from 'plugin.js'
// ...
Vue.use(Modal)
App.vue
<template>
<div id="app">
// ...
<app-modal/>
</div>
</template>
This looks pretty complicated. Why don't you use a ready-to-use popup component like this one? https://www.npmjs.com/package/#soldeplata/popper-vue

(VueJS) Update parent data from child component

I have a parent who's passing props to a child and the child emits events to the parent. However this is not fully working and I am not sure why. Any suggestions?
Parent:
<template>
<div class="natural-language">
<div class="natural-language-content">
<p class="natural-language-results">
<msm-select :options="payments" :model="isShowingUpdate" />
</p>
</div>
</div>
</template>
<script>
import msmSelect from '../form/dropdown.vue'
export default {
components: {
'msm-select': msmSelect
},
data() {
return {
isShowingUpdate: true,
payments: [
{'value': 'paying anuualy', 'text': 'paying anuualy'},
{'value': 'paying monthly', 'text': 'paying monthly'}
],
}
}
}
</script>
Child:
<template>
<div class="form-pseudo-select">
<select :model="flagValue" #change="onChange($event.target.value)">
<option disabled value='Please select'>Please select</option>
<option v-for="(option, index) in options" :value="option.value">{{ option.text }}</option>
</select>
</div>
</template>
<script>
export default {
props: {
options: {
elType: Array
},
isShowingUpdate: {
type: Boolean
}
},
data() {
return {
selected: '',
flagValue: false
}
},
methods: {
onChange(value) {
if (this.selected !== value) {
this.flagValue = true;
} else {
this.flagValue = false;
}
}
},
watch: {
'flagValue': function() {
console.log('it changed');
this.$emit('select', this.flagValue);
}
},
created() {
console.log(this.flagValue);
this.flagValue = this.isShowingUpdate;
}
}
</script>
Basically, when the option in the select box changes, the boolean flag should be updated. However, in my child I am getting undefined for isShowingUpdate. What am I missing?
There isn't the relation that you said between the two components.
The component that you called parent is in reality the child... and the child is parent.
The parent component is always the one that calls the other, in your case:
//Parent component
<template>
...
<msm-select :options="policies" :model="isShowingUpdate" /> << the child
...
</template>
You should change the props/events between the two components.
Edit:
You can edit the:
onChange(value) {
if (this.selected !== value) {
this.flagValue = true;
} else {
this.flagValue = false;
}
}
To a new one like the following:
On the children:
onChange(value) {
if (this.selected !== value) {
this.flagValue = true;
} else {
this.flagValue = false;
}
this.$emit('flagChanged', this.flagValue)
}
On the parent use the emit event to capture and call some other method:
//HTML part:
<msm-select :options="payments" :model="isShowingUpdate" v-on:flagChanged="actionFlagChanged" />
//JS part:
methods: {
actionFlagChanged () {
//what you want
}
}
Can I give you some tips?
It's not a (very) good name of a function the one called: onChange
inside a onChange event... try something like: updateFlag (more
semantic).
I think that you can delete the watch part and do it in the onChange
event
Try to find a good documentation/tutorial (i.e the official documentation) to learn more about parent/child communication.
Remember to add the event-bus import:
import { EventBus } from './event-bus'
Hope it helps!

Categories

Resources