How to use same popover for more than one target? - javascript

I am trying to use vue bootstrap popover for more than one target in vue js 2.x. i am able to change target property dynamically but it's still targeting the last target always. Is that possible with popover control or not? sample code as follows:
JSFiddle Sample is here
My example code is as follows:
new Vue({
el: '#app',
data: {
name: '',
email: '',
popover: {
target: '',
show: false,
message: ''
}
},
methods: {
callPopover() {
setTimeout(() => {
this.setPopover()
}, 3000)
},
setPopover() {
this.popover.target = 'txtName'
this.popover.show = true
if (!this.name) {
this.popover.message = 'please enter your name to register!!'
} else if (this.name === 'John') {
this.popover.message = 'Hey John!!'
} else if (!this.email) {
this.popover.target = 'txtEmail'
this.popover.message = 'email example is xx#yy.com'
} else {
this.popover.target = 'txtEmail'
this.popover.message = 'thanks for entering details!!'
}
},
onClosePopover() {
this.popover.show = false
}
},
mounted() {
this.callPopover()
}
})
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.css" />
<script src="//unpkg.com/babel-polyfill#latest/dist/polyfill.min.js"></script>
<script src="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.js"></script>
<div id="app">
<b-row class="mb-2 mt-4 ml-4">
<b-col cols="6">
<b-form-input id="txtName" v-model="name" type="text" placeholder="Enter your name"></b-form-input>
</b-col>
<b-col cols="6">
</b-col>
</b-row>
<b-row class="mb-2 ml-4">
<b-col cols="6">
<b-form-input id="txtEmail" v-model="email" type="email" placeholder="Enter your email"></b-form-input>
</b-col>
<b-col cols="6">
<b-btn #click="setPopover"> Show popover</b-btn>
</b-col>
</b-row>
<b-popover :target="popover.target" :show.sync="popover.show" triggers="" placement="topright">
<div class="popover-body">
<p>{{popover.message}}</p>
<div class="d-flex justify-content-center">
<b-btn #click="onClosePopover" size="sm"> gotcha!!</b-btn>
</div>
</div>
</b-popover>
</div>

Related

How can I make row number in vue component?

I have two component vue
My first component like this :
<template>
<div>
<b-row>
<div class="pl-2 d-flex">
<div class="card-body">
<p class="mb-0 w-5 w-sm-100">Number</p>
<div class="w-30 w-sm-100">Description</div>
<div class="w-20 w-sm-100">Date</div>
<div class="w-10 w-sm-100">Modified By</div>
</div>
</div>
</b-row>
<b-row key="list">
<b-colxx xxs="12" class="mb-3" v-for="(item,index) in items" :key="index" :id="item.id">
<list-item
:key="item.id"
:data="item"
:index="index"
/>
</b-colxx>
</b-row>
...
<b-pagination-nav
...
>
</b-pagination-nav>
...
</div>
</template>
<script>
import ListItem from "./ListItem";
export default {
components: {
"list-item": ListItem
},
};
</script>
My second component / child component like this :
<template>
<b-card no-body>
<div class="pl-2 d-flex">
<div class="card-body">
<p class="mb-0 text-muted w-5">{{index+1}}</p>
<p class="mb-0 text-muted w-30">{{data.description}}</p>
<p class="mb-0 text-muted w-20">{{data.date}}</p>
<p class="mb-0 text-muted w-10">{{data.created_by}}</p>
</div>
</div>
</b-card>
</template>
<script>
export default {
props: ['data', 'index'],
}
</script>
I use index to give row number. But the problem is when I move the page to another page, the line number will return to number 1
How can I solve this problem?
Please help. Thanks
You can create computed property, add line numbers to your items and loop over it:
new Vue({
el: '#demo',
data() {
return {
items: [],
fields: [{key: "lineNumber", label: "Number",}, {key: "postId", label: "Post ID",}, {key: "id", label: "ID",}, {key: "name", label: "Name",}, {key: "email", label: "Email",}, {key: "body", label: "Body",},],
currentPage: 0,
perPage: 10,
totalItems: 0,
}
},
computed: {
itemsWithLineNumber() {
return this.items.map((item, idx) => {
return {
...item,
lineNumber: (this.currentPage - 1) * this.perPage + idx + 1,
};
});
},
},
methods: {
async fetchData() {
await axios
.get(
`https://jsonplaceholder.typicode.com/comments?_page=${this.currentPage}&_limit=${this.perPage}`
)
.then((res) => {
this.totalItems = 500;
this.items = res.data;
});
},
changePage(nr) {
this.pageNr = nr
}
},
watch: {
currentPage: {
handler: function (value) {
this.fetchData().catch((error) => {
console.error(error);
});
},
},
},
async mounted() {
await this.fetchData()
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.css" />
<script src="https://polyfill.io/v3/polyfill.min.js?features=es2015%2CIntersectionObserver" crossorigin="anonymous"></script>
<script src="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue-icons.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.27.2/axios.min.js" integrity="sha512-odNmoc1XJy5x1TMVMdC7EMs3IVdItLPlCeL5vSUPN2llYKMJ2eByTTAIiiuqLg+GdNr9hF6z81p27DArRFKT7A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="demo">
<div>
<b-table
show-empty
:items="itemsWithLineNumber"
:fields="fields"
:current-page="currentPage"
:per-page="0"
></b-table>
<b-pagination
size="md"
:total-rows="totalItems"
v-model="currentPage"
:per-page="perPage"
></b-pagination>
</div>
</div>

Delete button spawning multiple confirm modals in vue-bootstrap>

Looking to solve this problem with having a unique ref to the modal in this loop, tried giving the ref a grade.id at the end but wouldn't work with String interpolation with the b-modal ref, also tried :ref.
This is the code in the vue file, and below that a screenshot of what it looks like, when I press to delete a grade then it shows all of the modals as the modal is in a loop.
<template>
<div>
<b-form #submit="onSubmit" #reset="onReset">
<b-row>
<b-col class="underline">Add Grades</b-col>
</b-row>
<br />
<b-row>
<b-col>
<b-form-group
inline
label-cols-sm="4"
label-cols-lg="3"
id="input-group-1"
label="Select Farm:"
>
<b-form-select v-model="form.farm" :options="farms" required />
</b-form-group>
</b-col>
</b-row>
<b-row class="my-1">
<b-col>
<b-form-group label-cols-sm="4" label-cols-lg="3" label="name:">
<b-form-input
v-model="form.gradeName"
size="sm"
placeholder="name"
></b-form-input>
</b-form-group>
</b-col>
</b-row>
<b-row class="my-1">
<b-col>
<b-form-group
label-cols-sm="4"
label-cols-lg="3"
label="min weight(gms):"
>
<b-form-input
v-model="form.gradeMinWeight"
size="sm"
placeholder="minimum"
></b-form-input>
</b-form-group>
</b-col>
</b-row>
<b-row class="my-1">
<b-col>
<b-form-group
label-cols-sm="4"
label-cols-lg="3"
label="max weight(gms):"
>
<b-form-input
v-model="form.gradeMaxWeight"
size="sm"
placeholder="maximum"
></b-form-input>
</b-form-group>
</b-col>
</b-row>
<b-row class="my-1">
<b-col>
<b-form-group label-cols-sm="4" label-cols-lg="3" label="shape:">
<b-form-select v-model="form.gradeShape">
<option value="rounded">rounded</option>
<option value="flat">flat</option>
<option value="curve">curve</option>
</b-form-select>
</b-form-group>
</b-col>
<b-col>
<b-form-group label-cols-sm="4" label-cols-lg="3" label="dimension:">
<b-form-select v-model="form.gradeDimension">
<option value="rounded">seeds</option>
<option value="rounded">small</option>
<option value="flat">medium</option>
<option value="curve">large</option>
<option value="curve">extra large</option>
<option value="curve">jumbo</option>
</b-form-select>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col>
<b-button type="submit" variant="primary" block>Add</b-button>
</b-col>
<b-col>
<b-button type="reset" variant="secondary" block>Cancel</b-button>
</b-col>
</b-row>
</b-form>
<br />
<b-row>
<b-col class="underline">My Grades</b-col>
</b-row>
<br />
<div v-if="grades.length > 0">
<b-card-group class="mb-4" v-for="grade in grades" :key="grade.id">
<b-card :title="getTitle(grade)">
<b-card-text class="list-card">
{{ getProperties(grade) }}
<span style="float: right">
<b-button
class="mr-1"
type="reset"
variant="danger"
#click="showModal"
size="sm"
>
<!-- Fix css to icon middle -->
<b-icon icon="trash-fill" size="is-small"></b-icon>
</b-button>
<b-modal
scrollable
id="modal-delete-grade"
ref=' `modal-delete-grade'
centered
#ok="actionDelete(grade.id)"
class="d-block text-center"
title="Confirm Delete Grade"
>
<p>
Confirm that you are about to delete grade
{{ getTitle(grade) }}
</p>
</b-modal>
</span>
</b-card-text>
</b-card>
</b-card-group>
</div>
</div>
</template>
<script>
import makeToast from "../../helper/toast";
import farmService from "../../service/FarmService";
import { getUser } from "#/helper/storage";
import userService from "#/service/UserService";
import DashEdit from "#/views/DashEdit";
import { contentMixin } from "#/mixins/contentMixin";
import { setupMixin } from "#/mixins/setupMixin";
export default {
name: "AddSetupGrade",
mixins: [contentMixin, setupMixin],
data() {
return {
farms: [],
grades: [],
form: {
farm: null,
gradeName: null,
gradeMinWeight: null,
gradeMaxWeight: null,
gradeShape: null,
gradeDimension: null,
},
};
},
mounted() {
this.getUserFarm();
this.getGrades();
},
created() {
this.getGrades();
},
methods: {
async onSubmit(evt) {
try {
evt.preventDefault();
let response = await farmService.addGrades({
name: this.form.gradeName,
farmId: this.form.farm.id,
properties: {
minWeight: this.form.gradeMinWeight,
maxWeight: this.form.gradeMaxWeight,
units: "gms",
shape: this.form.gradeShape,
dimension: this.form.gradeDimension,
},
});
if (response.status === 200) {
makeToast.call(
this,
"Success",
"Successfully created grade",
"success"
);
this.onReset();
this.getGrades();
}
} catch (e) {
console.log(`Error in creating grade. ${e}`);
makeToast.call(this, "Error", "Error adding grade", "danger");
}
},
onReset() {
this.form.gradeName = null;
this.form.gradeMinWeight = null;
this.form.gradeMaxWeight = null;
this.form.gradeShape = null;
this.form.gradeDimension = null;
},
async getUserFarm() {
try {
let { id: userId } = getUser();
let response = await userService.getFarmsOfUser(userId);
let farmList = response.data.data;
this.farms = farmList.map((each) => {
return {
value: { id: each.id, name: each.name },
text: each.name,
};
});
this.farms.push({ value: null, text: "Select Farm" });
} catch (e) {
console.log(`Error: ${e}`);
}
},
getTitle(grade) {
if (grade) {
return grade.name + " - " + grade.alias;
}
},
showModal(gradeId) {
this.$bvModal.show("modal-delete-grade");
},
keyValString(obj) {
return Object.entries(obj).reduce((str, [key, value], index) => {
if (value) {
if (str) str += ", ";
str += `${key}: ${value}`;
}
return str;
}, "");
},
getProperties(grade) {
return (
(grade && grade.properties && this.keyValString(grade.properties)) ||
"No properties on grade."
);
},
async getGrades() {
try {
let response = await farmService.getGrades();
this.grades = response.data.data;
} catch (e) {
console.log(e);
}
},
async actionDelete(id) {
try {
let response = await farmService.deleteGrade(id);
this.$bvModal.hide("modal-delete-grade");
this.getGrades();
} catch (e) {
console.log(e.response.data);
}
},
},
};
</script>
<style scoped>
.underline {
border-bottom: 2px solid currentColor;
border-bottom-color: #d3d3d3;
font-weight: bold;
font-size: 16px;
}
.list-card {
font-family: Open Sans;
font-size: 14px;
line-height: (48 / 24);
letter-spacing: -0.046em;
color: black;
}
.card-title {
font-size: 1em;
font-family: Open Sans;
}
</style>
You could move the modal outside of the loop. To pass the data of the clicked grade, simply save it to your data for example as "activeGrade". In your modal you display the data of "activeGrade". Now you can open the (same) modal with the clicked grade specific data.
I simplified your code a bit to show only the asked issue:
<template>
<div class="hello">
<b-card-group class="mb-4" v-for="grade in grades" :key="grade.id">
<b-card :title="grade.title">
<b-card-text class="list-card">
Some Text (grade properties)
<span style="float: right">
<b-button
class="mr-1"
type="reset"
variant="danger"
#click="showModal(grade)"
size="sm">
<!-- Fix css to icon middle -->
<b-icon icon="trash-fill" size="is-small"></b-icon>
</b-button>
</span>
</b-card-text>
</b-card>
</b-card-group>
<b-modal v-if="activeGrade"
scrollable
id="modal-delete-grade"
centered
#ok="actionDelete(activeGrade.id)"
class="d-block text-center"
title="Confirm Delete Grade">
<p>
Confirm that you are about to delete grade
{{ activeGrade.title }}
</p>
</b-modal>
</div>
</template>
<script>
export default {
data() {
return {
grades: [
{ id: 1, title: 'Grate 1' },
{ id: 2, title: 'Grate 2' },
{ id: 3, title: 'Grate 3' },
],
activeGrade: null
}
},
methods: {
actionDelete(gradeId) {
console.log('delete ' + gradeId)
},
showModal(grade) {
this.activeGrade = grade
this.$nextTick(() => {
this.$bvModal.show("modal-delete-grade");
})
}
}
}
</script>

BootstrapVue form tags input v-model

I have a b-form-tag like this:
<b-form-group label="Search" label-for="search">
<b-form-tags
id="search"
v-model="search"
separator=","
remove-on-delete
tag-variant="primary"
tag-pills
placeholder="Search here..."
></b-form-tags>
</b-form-group>
And in the data section:
data() {
return {
search: []
}
}
In the search variable only tags will be stored, buy I also need to access to the current typing text of the input and bind it to one variable in data. I know it must be done using inputAttrs or inputHandlers but I don't know how?
You can use custom inputs. This will force you to recreate some functionality for clearing the input and adding tags. Here is a demo where I've simplified the docs' advanced example. It initializes the tag value, reimplements adding tags with Enter, and shows setting v-model programatically:
new Vue({
el: "#app",
data() {
return {
newTag: 'starting text',
value: []
}
},
methods: {
resetInputValue() {
this.newTag = ''
},
setTag(text) {
this.newTag = text;
}
}
});
<div id="app">
<b-form-tags
v-model="value"
#input="resetInputValue()"
tag-variant="success"
class="mb-2 mt-2"
placeholder="Enter a new tag value and click Add"
>
<template v-slot="{tags, inputId, placeholder, addTag, removeTag }">
<b-input-group>
<!-- Always bind the id to the input so that it can be focused when needed -->
<b-form-input
v-model="newTag"
:id="inputId"
:placeholder="placeholder"
#keydown.enter="addTag(newTag)"
></b-form-input>
<b-input-group-append>
<b-button #click="addTag(newTag)" variant="primary">Add</b-button>
</b-input-group-append>
</b-input-group>
<div v-if="tags.length" class="d-inline-block" style="font-size: 1.5rem;">
<b-form-tag
v-for="tag in tags"
#remove="removeTag(tag)"
:key="tag"
:title="tag"
class="mr-1"
>{{ tag }}</b-form-tag>
</div>
<b-form-text v-else>
There are no tags specified. Add a new tag above.
</b-form-text>
</template>
</b-form-tags>
<div>Text from `v-model`: {{ newTag }}</div>
<div><button #click="setTag('programatically')">Set v-model programatically</button></div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.3/js/bootstrap.min.js"></script>
<script src="https://unpkg.com/vue#2.6.12/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-vue/2.19.0/bootstrap-vue.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.3/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-vue/2.19.0/bootstrap-vue.min.css" rel="stylesheet" />

data to a v model ? do i have to initialize the data if its coming from another component?

I am trying to put the value of content data from another component in this.newTutorial.content push function.
so far I get the data now I need to put it inside my v-model, like putting data from a v-model to another v-model? I tried this this.newTutorial.content = this.content; on the created function but no luck yet says type check failed for prop "rules". Expected Array, got Boolean with value true.
here is my code:
<style scoped>
img.preview {
width:200px;
}
.v-btn {
height: 50px !important;
min-width: 50px !important;
}
</style>
<template>
<div id="app">
<v-dialog v-model="dialog" width="500">
<template v-slot:activator="{ on, attrs }">
<v-btn style="z-index:9;" color="blue lighten-1" dark rounded v-bind="attrs" v-on="on" fixed left>
<v-tooltip right >
<template v-slot:activator="{ on, attrs }">
<v-icon fab dark v-bind="attrs" v-on="on">
mdi-plus
</v-icon>
</template>
<img class="monk-ico" src="https://celfonica.s3-us-west-1.amazonaws.com/logos/monk-circle+50px.png">
<span style="display:inline;">
Add Tutorial
</span>
</v-tooltip>
</v-btn>
</template>
<div class="left">
<v-btn color="primary" #click="dialog = false" width="10px">
<v-icon>
mdi-close
</v-icon>
</v-btn>
</div>
<div class="panel-heading">
</div>
<div>
<h1>Tutorial form</h1>
<h3> create one</h3>
<form id="form" class="form-inline" v-on:submit.prevent="addTutorial">
<v-divider class="m-tb-20"></v-divider>
<h4>Author details</h4>
<div class="form-group">
<v-text-field :rules="nameRules" required label="First Name" type="text" id="tutorialFirst" class="form-control" v-model="newTutorial.first">
</v-text-field>
</div>
<div class="form-group">
<v-text-field :rules="nameRules" required label="Last Name" type="text" id="tutorialLast" class="form-control" v-model="newTutorial.last">
</v-text-field>
</div>
<div class="form-group">
<v-text-field :rules="emailRules" required label="Email" type="text" id="tutorialEmail" class="form-control" v-model="newTutorial.email">
</v-text-field>
</div>
<div class="form-goup">
<!-- Img upload input field-->
<div>
<h4 class="m-tb-20">Upload tutorial picture:</h4>
<input class="form-control" type="file" #change="previewImage" accept="image/+">
<br><v-btn class="m-tb-20" #click=" onUpload();"><v-icon>mdi-upload</v-icon></v-btn>
</div>
<div>
<p> Progress: {{uploadValue.toFixed()+"%"}}
<progress :value="uploadValue" max="100"></progress>
</p>
</div>
</div>
<v-divider class="m-tb-20"></v-divider>
<h4>Tutorial content</h4>
<div class="form-group">
<v-select required label="Language"
id="tutorialLanguage" v-model="newTutorial.language"
multiple type="text" autocomplete tags :items="languages" class="form-control">
<template slot="selection" slot-scope="data">
<v-btn>
{{ data.item }}
</v-btn>
</template>
</v-select>
</div>
<div class="form-group">
<v-text-field :rules="titleRules" required label="Tutorial Title" type="text" id="tutorialTitle" class="form-control" v-model="newTutorial.title">
</v-text-field>
</div>
<!--tiptap--> <v-card >
<div >
<editor-menu-bar v-on:submit.prevent="addTutorial" :editor="editor" v-slot="{ commands, isActive }">
<div>
<v-btn :class="{ 'is-active': isActive.bold() }" #click="commands.bold">
<v-icon class="mdi mdi-format-bold mdi-24px"> </v-icon>
</v-btn>
<v-btn :class="{ 'is-active': isActive.italic() }" #click="commands.italic">
<v-icon class="mdi mdi-format-italic mdi-24px "> </v-icon>
</v-btn>
<v-btn :class="{ 'is-active': isActive.underline() }" #click="commands.underline">
<v-icon class="mdi mdi-format-underline mdi-24px "> </v-icon>
</v-btn>
<v-btn :class="{ 'is-active': isActive.code() }" #click="commands.code">
<v-icon class="mdi mdi-code-tags mdi-24px "> </v-icon>
</v-btn>
<v-btn :class="{ 'is-active': isActive.link() }" #click="commands.link">
<v-icon class="mdi mdi-link mdi-24px"> </v-icon>
</v-btn>
<v-divider></v-divider>
</div>
</editor-menu-bar>
<editor-content label="Tutorial content" :editor="editor" v-model="content" />
</div>
</v-card>
<div class="form-group">
<v-text-field required label="Date" class="form-control" type='date' v-model='newTutorial.date'>
</v-text-field>
</div>
<div class="form-group">
<v-text-field required label="Tutorial Sample Code Link" type="url" id="tutorialCode" class="form-control" v-model="newTutorial.code">
</v-text-field>
</div>
<div>
<br>
</div>
<v-divider class="m-tb-20"></v-divider>
<h4> Preview </h4>
<v-card class="m-tb-20" v-model="newTutorial">
<img class="preview " :src="picture"><br>
<v-card-title class="center">{{ newTutorial.title }} </v-card-title>
<v-card-subtitle> {{ newTutorial.first }} {{ newTutorial.last }} </v-card-subtitle>
<v-divider class="m-tb-20"></v-divider>
<v-card-text>{{ newTutorial.content }}</v-card-text>
{{ content }}
<v-card-text>
<h5>{{ newTutorial.language }}</h5>
<h5>{{ newTutorial.email }}</h5>
<h5>{{ newTutorial.date }}</h5>
</v-card-text>
</v-card>
<!-- Form push btn -->
<v-btn class="m-tb-20" #click="markcompleted();" type="submit" small color="primary" dark>
{{ displayText }}
</v-btn>
</form>
</div>
</v-dialog>
</div>
</template>
<script>
import firebase from '../plugins/firebase'
import EditorContent from "../components/EditorContent";
import toastr from 'toastr';
// to debug multiple Fire apps
//if (!firebase.apps.length) {
// firebase.initializeApp(config);
// this.newTutorial.userID= uid;
//}
import { Editor, EditorMenuBar } from 'tiptap'
import {
Blockquote,
CodeBlock,
HardBreak,
Heading,
OrderedList,
BulletList,
ListItem,
TodoItem,
TodoList,
Bold,
Code,
Italic,
Link,
Strike,
Underline,
History,
} from 'tiptap-extensions'
let db = firebase.database();
let messagesRef = db.ref('tutorials');
export default {
name: 'tutform',
firebase: {
tutorials: messagesRef
},
components: {
EditorMenuBar,
EditorContent,
},
data() {
return {
content: null,
editor: new Editor({
extensions: [
new Blockquote(),
new CodeBlock(),
new HardBreak(),
new Heading({ levels: [1, 2, 3] }),
new BulletList(),
new OrderedList(),
new ListItem(),
new TodoItem(),
new TodoList(),
new Bold(),
new Code(),
new Italic(),
new Link(),
new Strike(),
new Underline(),
new History(),
],
content: '',
}),
imageData:null,
picture:null,
uploadValue: 0,
dialog: false,
displayText: 'Push me!',
newTutorial: {
first: '',
email: '',
last: '',
language: [],
title: '',
content: '',
date: '',
picture:'',
code: '',
},
languages: [
'Html', 'CSS', 'VUE', 'React', 'Ruby', 'JS', 'SASS', 'Python','PHP','C#','JAVA','Other',
],
nameRules: [
v => !!v || 'you must type something',
v => v.length <= 10 || 'hum.. this monk smelling somthing strange... must be less than 10 characters',
],
emailRules: [
v => !!v || 'E-mail is required',
v => /.+#.+/.test(v) || 'Please enter a valid email containing # ',
],
contentRules: [
v => !!v || 'Content is required amigo!'
],
titleRules: [
v => !!v || 'Tittle is required buddy!',
v => v.length <= 100 || 'Woots!, Lets try making this one shorter'
]
}
},
methods: {
previewImage(event){
this.uploadValue=0;
this.picture=null;
this.imageData=event.target.files[0];
},
onUpload() {
this.picture=null;
const storageRef=firebase.storage().ref(`tutorials/images/${this.imageData.name}`).put(this.imageData);
storageRef.on(`state_changed`, snapshot=>{
this.uploadValue=(snapshot.bytesTransferred/snapshot.totalBytes)*100;
}, error=>{console.log(error.message)},
()=>{this.uploadValue=100;
storageRef.snapshot.ref.getDownloadURL().then((url)=>{
this.picture=url;
this.newTutorial.picture = url;
console.log(this.picture);
toastr.success('Image Uploaded successfully');
})}
)
},
addTutorial: function() {
messagesRef.child(this.newTutorial.userID).push(this.newTutorial);
this.newTutorial.first = '';
this.newTutorial.last = '';
this.newTutorial.content = '';
this.newTutorial.email = '';
this.newTutorial.language = '';
this.newTutorial.title = '';
this.newTutorial.date = '',
this.newTutorial.picture= '',
this.newTutorial.code= '',
toastr.success('Horray! message sent successfully');
this.displayText = 'Nice job!';
this.nameRules = true;
this.emailRules = true;
this.contentRules = true;
this.titleRules = true;
},
markcompleted: function() {
this.displayText = 'hum.. somthing still missing';
}
},
// this functions trow in uid from user in data valu to {uid}
created: function(){
var user = firebase.auth().currentUser;
var uid;
if (user != null) {
uid = user.uid; // The user's ID, unique to the Firebase project. Do NOT use
// this value to authenticate with your backend server, if
// you have one. Use User.getToken() instead.
}
this.newTutorial.userID = uid;
this.newTutorial.content = this.content;
},
beforeDestroy() {
this.editor.destroy()
}
}
</script>
Then my component code for importing the content data :
<script>
export default {
name: 'EditorContent',
props: {
editor: {
default: null,
type: Object
},
value: {
default: "",
type: String
}
},
watch: {
editor: {
immediate: true,
handler(editor) {
if (!editor || !editor.element) return;
this.editor.setContent(this.value);
this.editor.on("update", ({ getHTML }) => {
this.$emit("input", getHTML());
});
this.$nextTick(() => {
this.$el.appendChild(editor.element.firstChild);
editor.setParentComponent(this);
});
}
},
value: {
handler(value) {
this.editor.setContent(value);
}
}
},
render(createElement) {
return createElement("div");
},
beforeDestroy() {
this.editor.element = this.$el;
}
};
</script>
vue data:
console error:
I see you are trying to implement two-way binding with v-model on a custom element.
You will need to create a prop called content, then emit input events to the parent component. Check this sample code out:
Child component:
<template>
<div class="date-picker">
Month: <input type="number" ref="monthPicker" :value="value.month" #input="updateDate()"/>
Year: <input type="number" ref="yearPicker" :value="value.year" #input="updateDate()"/>
</div>
</template>
<script>
export default {
props: ['value'],
methods: {
updateDate() {
this.$emit('input', {
month: +this.$refs.monthPicker.value,
year: +this.$refs.yearPicker.value
})
}
}
};
</script>
Parent component:
<template>
<div class="wrapper">
<date-picker v-model="date"></date-picker>
<p>
Month: {{date.month}}
Year: {{date.year}}
</p>
</div>
</template>
<script>
import DatePicker from './DatePicker.vue';
export default {
components: {
DatePicker
},
data() {
return {
date: {
month: 1,
year: 2017
}
}
}
})
</script>
For more information, check out this short article
The solution was to remove my content v-model inside the editor content tag and add the v-model I had made for sending the tutorials. here is the working code.
code:
<template>
<div>
<editor-content label="Tutorial content" :editor="editor" v-model="newTutorial.content" />
</div>
</template>
inside the script in my data :
newTutorial: {
first: '',
email: '',
last: '',
language: [],
title: '',
content: '',
date: '',
picture:'',
code: '',
},
inside the script in my send funciton :
addTutorial: function() {
messagesRef.child(this.newTutorial.userID).push(this.newTutorial);
this.newTutorial.first = '';
this.newTutorial.last = '';
this.newTutorial.content = '';
this.newTutorial.email = '';
this.newTutorial.language = '';
this.newTutorial.title = '';
this.newTutorial.date = '',
this.newTutorial.picture= '',
this.newTutorial.code= '',
toastr.success('Horray! message sent successfully');
this.displayText = 'Nice job!';
this.nameRules = true;
this.emailRules = true;
this.contentRules = true;
this.titleRules = true;
},
data in db :
form component code:
<style scoped>
img.preview {
width:200px;
}
.v-btn {
height: 50px !important;
min-width: 50px !important;
}
</style>
<template>
<div id="app">
<v-dialog v-model="dialog" width="500">
<template v-slot:activator="{ on, attrs }">
<v-btn style="z-index:9;" color="blue lighten-1" dark rounded v-bind="attrs" v-on="on" fixed left>
<v-tooltip right >
<template v-slot:activator="{ on, attrs }">
<v-icon fab dark v-bind="attrs" v-on="on">
mdi-plus
</v-icon>
</template>
<img class="monk-ico" src="https://celfonica.s3-us-west-1.amazonaws.com/logos/monk-circle+50px.png">
<span style="display:inline;">
Add Tutorial
</span>
</v-tooltip>
</v-btn>
</template>
<div class="left">
<v-btn color="primary" #click="dialog = false" width="10px">
<v-icon>
mdi-close
</v-icon>
</v-btn>
</div>
<div class="panel-heading">
</div>
<div>
<h1>Tutorial form</h1>
<h3> create one</h3>
<form id="form" class="form-inline" v-on:submit.prevent="addTutorial">
<v-divider class="m-tb-20"></v-divider>
<h4>Author details</h4>
<div class="form-group">
<v-text-field :rules="nameRules" required label="First Name" type="text" id="tutorialFirst" class="form-control" v-model="newTutorial.first">
</v-text-field>
</div>
<div class="form-group">
<v-text-field :rules="nameRules" required label="Last Name" type="text" id="tutorialLast" class="form-control" v-model="newTutorial.last">
</v-text-field>
</div>
<div class="form-group">
<v-text-field :rules="emailRules" required label="Email" type="text" id="tutorialEmail" class="form-control" v-model="newTutorial.email">
</v-text-field>
</div>
<div class="form-goup">
<!-- Img upload input field-->
<div>
<h4 class="m-tb-20">Upload tutorial picture:</h4>
<input class="form-control" type="file" #change="previewImage" accept="image/+">
<br><v-btn class="m-tb-20" #click=" onUpload();"><v-icon>mdi-upload</v-icon></v-btn>
</div>
<div>
<p> Progress: {{uploadValue.toFixed()+"%"}}
<progress :value="uploadValue" max="100"></progress>
</p>
</div>
</div>
<v-divider class="m-tb-20"></v-divider>
<h4>Tutorial content</h4>
<div class="form-group">
<v-select required label="Language"
id="tutorialLanguage" v-model="newTutorial.language"
multiple type="text" autocomplete tags :items="languages" class="form-control">
<template slot="selection" slot-scope="data">
<v-btn>
{{ data.item }}
</v-btn>
</template>
</v-select>
</div>
<div class="form-group">
<v-text-field :rules="titleRules" required label="Tutorial Title" type="text" id="tutorialTitle" class="form-control" v-model="newTutorial.title">
</v-text-field>
</div>
<!--tiptap--> <v-card >
<div >
<editor-menu-bar v-on:submit.prevent="addTutorial" :editor="editor" v-slot="{ commands, isActive }">
<div>
<v-btn :class="{ 'is-active': isActive.bold() }" #click="commands.bold">
<v-icon class="mdi mdi-format-bold mdi-24px"> </v-icon>
</v-btn>
<v-btn :class="{ 'is-active': isActive.italic() }" #click="commands.italic">
<v-icon class="mdi mdi-format-italic mdi-24px "> </v-icon>
</v-btn>
<v-btn :class="{ 'is-active': isActive.underline() }" #click="commands.underline">
<v-icon class="mdi mdi-format-underline mdi-24px "> </v-icon>
</v-btn>
<v-btn :class="{ 'is-active': isActive.code() }" #click="commands.code">
<v-icon class="mdi mdi-code-tags mdi-24px "> </v-icon>
</v-btn>
<v-btn :class="{ 'is-active': isActive.link() }" #click="commands.link">
<v-icon class="mdi mdi-link mdi-24px"> </v-icon>
</v-btn>
<v-divider></v-divider>
</div>
</editor-menu-bar>
<editor-content label="Tutorial content" :editor="editor" v-model="newTutorial.content" />
</div>
</v-card>
<div class="form-group">
<v-text-field required label="Date" class="form-control" type='date' v-model='newTutorial.date'>
</v-text-field>
</div>
<div class="form-group">
<v-text-field required label="Tutorial Sample Code Link" type="url" id="tutorialCode" class="form-control" v-model="newTutorial.code">
</v-text-field>
</div>
<div>
<br>
</div>
<v-divider class="m-tb-20"></v-divider>
<h4> Preview </h4>
<v-card class="m-tb-20" v-model="newTutorial">
<img class="preview " :src="picture"><br>
<v-card-title class="center">{{ newTutorial.title }} </v-card-title>
<v-card-subtitle> {{ newTutorial.first }} {{ newTutorial.last }} </v-card-subtitle>
<v-divider class="m-tb-20"></v-divider>
<v-card-text>{{ newTutorial.content }}</v-card-text>
<v-card-text>
<h5>{{ newTutorial.language }}</h5>
<h5>{{ newTutorial.email }}</h5>
<h5>{{ newTutorial.date }}</h5>
</v-card-text>
</v-card>
<!-- Form push btn -->
<v-btn class="m-tb-20" #click="markcompleted();" type="submit" small color="primary" dark>
{{ displayText }}
</v-btn>
</form>
</div>
</v-dialog>
</div>
</template>
<script>
import firebase from '../plugins/firebase'
import EditorContent from "../components/EditorContent";
import toastr from 'toastr';
// to debug multiple Fire apps
//if (!firebase.apps.length) {
// firebase.initializeApp(config);
// this.newTutorial.userID= uid;
//}
import { Editor, EditorMenuBar } from 'tiptap'
import {
Blockquote,
CodeBlock,
HardBreak,
Heading,
OrderedList,
BulletList,
ListItem,
TodoItem,
TodoList,
Bold,
Code,
Italic,
Link,
Strike,
Underline,
History,
} from 'tiptap-extensions'
let db = firebase.database();
let messagesRef = db.ref('tutorials');
export default {
name: 'tutform',
firebase: {
tutorials: messagesRef
},
components: {
EditorMenuBar,
EditorContent,
},
data() {
return {
editor: new Editor({
extensions: [
new Blockquote(),
new CodeBlock(),
new HardBreak(),
new Heading({ levels: [1, 2, 3] }),
new BulletList(),
new OrderedList(),
new ListItem(),
new TodoItem(),
new TodoList(),
new Bold(),
new Code(),
new Italic(),
new Link(),
new Strike(),
new Underline(),
new History(),
],
content: '',
}),
imageData:null,
picture:null,
uploadValue: 0,
dialog: false,
displayText: 'Push me!',
newTutorial: {
first: '',
email: '',
last: '',
language: [],
title: '',
content: '',
date: '',
picture:'',
code: '',
},
languages: [
'Html', 'CSS', 'VUE', 'React', 'Ruby', 'JS', 'SASS', 'Python','PHP','C#','JAVA','Other',
],
nameRules: [
v => !!v || 'you must type something',
v => v.length <= 10 || 'hum.. this monk smelling somthing strange... must be less than 10 characters',
],
emailRules: [
v => !!v || 'E-mail is required',
v => /.+#.+/.test(v) || 'Please enter a valid email containing # ',
],
contentRules: [
v => !!v || 'Content is required amigo!'
],
titleRules: [
v => !!v || 'Tittle is required buddy!',
v => v.length <= 100 || 'Woots!, Lets try making this one shorter'
]
}
},
methods: {
previewImage(event){
this.uploadValue=0;
this.picture=null;
this.imageData=event.target.files[0];
},
onUpload() {
this.picture=null;
const storageRef=firebase.storage().ref(`tutorials/images/${this.imageData.name}`).put(this.imageData);
storageRef.on(`state_changed`, snapshot=>{
this.uploadValue=(snapshot.bytesTransferred/snapshot.totalBytes)*100;
}, error=>{console.log(error.message)},
()=>{this.uploadValue=100;
storageRef.snapshot.ref.getDownloadURL().then((url)=>{
this.picture=url;
this.newTutorial.picture = url;
console.log(this.picture);
toastr.success('Image Uploaded successfully');
})}
)
},
addTutorial: function() {
messagesRef.child(this.newTutorial.userID).push(this.newTutorial);
this.newTutorial.first = '';
this.newTutorial.last = '';
this.newTutorial.content = '';
this.newTutorial.email = '';
this.newTutorial.language = '';
this.newTutorial.title = '';
this.newTutorial.date = '',
this.newTutorial.picture= '',
this.newTutorial.code= '',
toastr.success('Horray! message sent successfully');
this.displayText = 'Nice job!';
this.nameRules = true;
this.emailRules = true;
this.contentRules = true;
this.titleRules = true;
},
markcompleted: function() {
this.displayText = 'hum.. somthing still missing';
}
},
// this functions trow in uid from user in data valu to {uid}
created: function(){
var user = firebase.auth().currentUser;
var uid;
if (user != null) {
uid = user.uid; // The user's ID, unique to the Firebase project. Do NOT use
// this value to authenticate with your backend server, if
// you have one. Use User.getToken() instead.
}
this.newTutorial.userID = uid;
},
beforeDestroy() {
this.editor.destroy()
}
}
</script>
child component:
<script>
export default {
name: 'EditorContent',
props: {
editor: {
default: null,
type: Object
},
value: {
default: "",
type: String
}
},
watch: {
editor: {
immediate: true,
handler(editor) {
if (!editor || !editor.element) return;
this.editor.setContent(this.value);
this.editor.on("update", ({ getHTML }) => {
this.$emit("input", getHTML());
});
this.$nextTick(() => {
this.$el.appendChild(editor.element.firstChild);
editor.setParentComponent(this);
});
}
},
value: {
handler(value) {
this.editor.setContent(value);
}
}
},
render(createElement) {
return createElement("div");
},
beforeDestroy() {
this.editor.element = this.$el;
}
};
</script>
Hope it helps someone, I did this to add tip tap editor.

Printing received JSON into a table

I have a VueJS template which includes a table and a function that sends out a request to the backend API with the specified key and then receives back a JSON response.
What I am having trouble is with writing the function so it takes the output it receives and then prints into the table.
JSON Sample:
{"driver_id":1,"driver_name":"{driver_first_name}, {driver_last_name}","driver_truck":"13","driver_trailer":"83","driver_status":"driving","has_violations":false},
{"driver_id":2,"driver_name":"{driver_first_name}, {driver_last_name}","driver_truck":"58","driver_trailer":"37","driver_status":"sleeping","has_violations":true},
{"driver_id":3,"driver_name":"{driver_first_name}, {driver_last_name}","driver_truck":"80","driver_trailer":"27","driver_status":"driving","has_violations":true},
Here is my code:
<template>
<b-container fluid>
<!--Search Controls-->
<b-row>
<b-col md="6" class="my-1">
<b-form-group horizontal label="Filter" class="mb-0">
<b-input-group>
<b-form-input v-model="filter" placeholder="Type to Search" />
<b-input-group-append>
<b-btn :disabled="!filter" #click="filter = ''">Clear</b-btn>
</b-input-group-append>
</b-input-group>
</b-form-group>
</b-col>
<b-col md="6" class="my-1">
<b-form-group horizontal label="Sort" class="mb-0">
<b-input-group>
<b-form-select v-model="sortBy" :options="sortOptions">
<option slot="first" :value="null">-- none --</option>
</b-form-select>
<b-form-select :disabled="!sortBy" v-model="sortDesc" slot="append">
<option :value="false">Asc</option>
<option :value="true">Desc</option>
</b-form-select>
</b-input-group>
</b-form-group>
</b-col>
<b-col md="6" class="my-1">
<b-form-group horizontal label="Sort direction" class="mb-0">
<b-input-group>
<b-form-select v-model="sortDirection" slot="append">
<option value="asc">Asc</option>
<option value="desc">Desc</option>
<option value="last">Last</option>
</b-form-select>
</b-input-group>
</b-form-group>
</b-col>
<b-col md="6" class="my-1">
<b-form-group horizontal label="Per page" class="mb-0">
<b-form-select :options="pageOptions" v-model="perPage" />
</b-form-group>
</b-col>
</b-row>
<!--Search Controls-->
<!-- Main table element -->
<b-table show-empty
stacked="md"
:items="items"
:fields="fields"
:current-page="currentPage"
:per-page="perPage"
:filter="filter"
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
:sort-direction="sortDirection"
#filtered="onFiltered"
>
<template slot="truck_number" slot-scope="row">{{row.value.driver_truck}}</template>
<template slot="trailer_number" slot-scope="row">{{row.value.driver_trailer}}</template>
<template slot="violation_date" slot-scope="row">{{row.value.violation_date}}</template>
<!-- View Specific Violation -->
<template slot="actions" slot-scope="row">
<b-button #click="getSpecificViolation(logbook_id)" id="view_specific_violation">View Violation</b-button>
</template>
</b-table>
<b-row>
<b-col md="6" class="my-1">
<b-pagination :total-rows="totalRows" :per-page="perPage" v-model="currentPage" class="my-0" />
</b-col>
</b-row>
</b-container>
</template>
<script>
export default {
name: 'List of Violations',
data () {
return {
items: items,
fields: [
{ key: 'truck_number', label: 'Truck Number', sortable: true, 'class': 'text-center' },
{ key: 'trailer_number', label: 'Trailer Number', sortable: true, 'class': 'text-center' },
{ key: 'violation_date', label: 'Date of Violation' },
{ key: 'actions', label: 'Actions' }
],
currentPage: 1,
perPage: 5,
totalRows: items.length,
pageOptions: [ 5, 10, 15 ],
sortBy: null,
sortDesc: false,
sortDirection: 'asc',
filter: null,
}
},
},
computed: {
sortOptions () {
return this.fields
.filter(f => f.sortable)
.map(f => { return { text: f.label, value: f.key } })
}
},
created() {
this.id = this.$route.params.id;
},
methods: {
onFiltered (filteredItems) {
// Trigger pagination to update the number of buttons/pages due to filtering
this.totalRows = filteredItems.length
this.currentPage = 1
}
navigate() {
router.go(-1);
}
//Send Request all Violations for Driver_ID
axios.post('http://localhost:3030/api/allViolations/driverID',
this.driver_id, // the data to post
{ headers: {
'Content-type': 'application/x-www-form-urlencoded',
}
}).then(response => ....???); //print api json response
}
}
</script>
First of all, your code structure is disrupted. You shouldn't define computed,created and methods functions outside the export default module.
I created a sandbox for you just to rearrange the code and assign the value of response to items variable.
.then(response => (this.items = response.data));
If i assume correctly you are using Bootstrap-Vue plugin. After obtaining the items object you can follow the documentation from here
First of all, your initial value for items in data should be [] (an empty array).
After that, pretty much all you should need to do is set this.items = response.data when you get the JSON back. Although the code you pasted seems to be bad (the axios.post call is directly under methods).

Categories