I there any way to filter the data in table when clicked one part of section pie chart? laravel vue - laravel

i had implement the click event for the chart and it can filter the data in the table. But now i face the problem of the data does not return all the data in the table when click outside the chart are.How can make it return all the data when click outside the area chart? Thank you. Any help will be appreciated.
<script>
import VueApexCharts from "vue-apexcharts";
import faker from "faker";
export default {
components: {
VueApexCharts,
},
data() {
return {
Lists: [],
selectedData:{},
search1:'',
clicked:'',
clicked1:'',
isLoading1:true,
currentPage: 1,
series: [300, 200, 49, 100,290, 228, 119, 55],
chartOptions: {
colors: ['#7961F9', '#FF9F43', '#196EB0', '#2EAB56','#df87f2','#057FF2', '#14DA6E','#FF5500'],
legend: {
fontSize: "14px",
position: "top",
},
dataLabels: {
enabled: true,
minAngleToShowLabel: 0,
distributed: false,
style: {
colors: ['#111'],
},
background: {
enabled: true,
foreColor: '#fff',
borderWidth: 0,
}
},
chart: {
width: 500,
type: 'pie',
events: {
legendClick: (chartContext, seriesIndex,w, config) => {
this.clicked = w.config.labels[seriesIndex];
console.log(this.clicked);
console.log(seriesIndex);
},
dataPointSelection: (event,chartContext,config) => {
this.clicked1 = config.w.config.labels[config.dataPointIndex];
console.log(this.clicked1);
},
},
},
labels: ['Private', 'Local','Dental', 'Government','Cyber Security', 'Health', 'Foreign','Medical'],
responsive: [
{
breakpoint: 480,
options: {
legend: {
position: "bottom",
fontSize: "12px",
},
},
},
],
},
}
},
created() {
this.getData();
this.getData1();
},
computed:{
filterLists(){
let list = this.Lists;
if(this.search1 !=''){
list = list.filter((tr)=>{
return tr.agency.toUpperCase().includes(this.search1.toUpperCase())
});
}
if (this.clicked !=''&& this.clicked){
list = list.filter((tr)=>{
return tr.projectCategory.toUpperCase().includes(this.clicked.toUpperCase())
});
}
if (this.clicked1 !=''&& this.clicked1){
list = list.filter((tr)=>{
return tr.projectCategory.toUpperCase().includes(this.clicked1.toUpperCase())
});
}
return list;
},
},
methods: {
next(page) {},
getData1() {
this.isLoading1 = true;
for (let i = 0; i < this.randInt(8, 4); i++) {
let index = Math.floor(Math.random() * 2);
let projectCategory = this.rotate([
'Private', 'Local','Dental', 'Government','Cyber Security', 'Health', 'Foreign','Medical'
]);
this.Lists.push({
projectCategory:projectCategory,
project_name: faker.company.catchPhrase(),
agency: faker.company.companyName(),
logo: faker.image.abstract(),
});
}
this.maxPage = 2;
this.isLoading1 = false;
},
next1(page) {
if (page == -2) {
this.currentPage = 1;
} else if (page == -3) {
this.currentPage = this.maxPage;
} else {
if (
this.currentPage + page < 1 ||
this.currentPage + page > this.maxPage
) {
return;
}
this.currentPage += page;
}
this.showLoader("#card-list");
this.Lists = [];
this.isLoading1 = true;
setTimeout(() => {
this.closeLoader("#card-list");
this.getData1();
}, 1500);
},
},
};
</script>
<style>
#card > header{
padding: 1.5rem 2rem;
background-color: #2E3839;
}
#card{
--tw-bg-opacity: 1;
background-color: rgba(249, 250, 251, var(--tw-bg-opacity));
}
.con-img.vs-avatar--con-img img {
object-fit: cover !important;
}
.apexcharts-toolbar {
position:absolute;
margin-right:12px;
}
vs-button.btn:hover{
background-color: rgba(255,255,255,0);
cursor: pointer;
}
</style>
<template>
<div class="mb-base">
<div class="vx-row mb-base">
<div class="vx-col 2/3 w-full mb-base">
<vs-card
id="card"
class="vs-con-loading__container h-full"
>
<template slot="header">
<div class="flex">
<div>
<img
src=""
alt="Info"
class="h-12 inline-block mr-4 object-scale-down"
/>
</div>
<div class="flex flex-col justify-center w-full text-start">
<h3 class="text-white">Source of Fund</h3>
<span class="text-sm text-white">Based on Total Billed (Yearly)</span>
</div>
<div>
</div>
</div>
</template>
<div class="flex flex-wrap mt-2">
<div class="lg:w-1/3 w-full">
<vue-apex-charts
type="donut"
:options="chartOptions"
:series="series"
width="100%"
class="items-center justify-center flex mt-16 content-center"
/>
</div>
<div class="lg:w-2/3 w-full lg:pl-6 pl-0 mt-6">
<div class="flex justify-end items-end">
<vx-input-group class="mb-base lg:w-1/2 w-full">
<template slot="append">
<div class="append-text btn-addon">
<vs-button color="#A9A9A9"><i class="fas fa-search"></i></vs-button>
</div>
</template>
<vs-input
v-model="search1"
placeholder="Project Code or name"
/>
</vx-input-group>
</div>
<div id="card-list">
<vs-list v-if="!isLoading1">
<vs-list-item
v-for="(tr, index) in filtertLists"
:key="index"
class="hover:shadow cursor-pointer text-base mb-4"
>
<template slot="title">
<div
class="flex flex-col ml-2 cursor-pointer"
>
<div class="font-bold">{{ tr.project_name }}</div>
<div>{{ tr.agency }}</div>
</div>
</template>
<template slot="avatar">
<vs-avatar :src="tr.logo"></vs-avatar>
</template>
{{ tr.projectCategory }}
</vs-list-item>
<div v-if="!filterLists.length" class="flex">
<div class="items-center justify-center text-lg font-bold">No record...</div>
</div>
</vs-list>
<div v-else class="flex">
<div class="items-center justify-center">Fetching data...</div>
</div>
</div>
<div class="flex justify-end gap-4">
<div class="flex items-center justify-center text-sm">
Page {{ currentPage }} of
{{ maxPage }}
</div>
<div>
<vs-button
type="filled"
color=" rgba(243, 244, 246)"
class="w-10 mr-2 rounded-md bg-gray-400 text-black btn hover:text-black"
#click="next1(-1)"
>
<i class="fas fa-chevron-left"></i>
</vs-button>
<vs-button
type="filled"
color=" rgba(243, 244, 246)"
class="w-10 mr-2 rounded-md bg-gray-400 text-black btn"
#click="next1(1)"
>
<i class="fas fa-chevron-right"></i>
</vs-button>
</div>
</div>
</div>
</div>
</vs-card>
</div>
</div>
</div>
</template>
I am working with laravel vue pie chart. is there any way to filter the data in the table when click the element of pie chart. For example, when clicked the section pie chart, the table will be filter and display the the data in the table under that section..Any help will be appreciated

It is not possible to point directly to a solution as you have given so little detail. I will still try to explain the logic. For this process, you will get an input from the screen working with Vue.js and you will manipulate a data displayed on Vue.js.
So first; you need to know which part of your pie chart clicked on. I assume the pie chart you are using on your project have some events which triggered when you interact with charts. You will listen that event and catch the value of clicked item.
Now you have the value of clicked item and you need to filter your results by that.
To accomplish that you can use Vue.js Computed Properties and Watchers :https://v2.vuejs.org/v2/guide/computed.html
Lets say you have your data on your Vue.js application:
data () {
return {
clickedItem: null,
itemsOnTable: [ ... ]
}
}
You have all your table content in itemsOnTable and selected item's data in clickedItem
You can use computed to filter your data:
data () {
return {
clickedItem: null,
itemsOnTable: [ ... ]
}
},
computed: {
// filter itemsOnTable if clickedItem have any value
filteredItems: function () {
if(this.clickedItem==null) return this.itemsOnTable;
return this.itemsOnTable.filter(item => item.column = this.clickedItem);
}
}
Now in your Vue.js component you can directly use filteredItems for your table elements v-for
<table>
<tr v-for="items in filteredItems">
<td>{{ item.column }}</td>
<!-- other columns -->
</tr>
</table>
This examples explains basics of interactions and computed properties and aims to help you to understand basics.

Related

show editable field when empty

I have this problem: I have a component with which I create editable fields.
If the field is already populated, everything works fine. I see the database value, I can click on it and change it.
If, on the other hand, in the database that value is empty, it is "hidden" in the view. In this way it is not possible to click on it to insert a value in the editable field.
I tried to get around the obstacle by inserting a: placeholder = "placeholder" but I don't even see that.
How can I do?
This is my visualization file:
<div class="py-0 sm:grid sm:grid-cols-10 sm:gap-4 my-2">
<dt class="text-md leading-6 font-medium text-gray-900 sm:col-span-2 self-center">
{{ $trans('labels.description') }}
</dt>
</div>
<div class="py-1 sm:grid sm:grid-cols-10 sm:gap-4">
<dd class="text-sm leading-5 text-gray-600 sm:mt-0 sm:col-span-10 self-center">
<v-input-text-editable
v-model="task.description"
#input="updateTask()"
:placeholder = "placeholder"
/>
</dd>
</div>
props: {
users: {
type: Array,
default: []
},
description: {
type: String,
default: null
}
},
data() {
return {
task: new Form({
description: this.description
})
}
},
this is my component:
<template>
<div class="block w-full">
<div v-if="!editable" class="cursor-pointer" #click="enableEditMode()">
{{ valueAfterEdit }}
</div>
<div v-else class="mt-1 flex rounded-md shadow-sm" v-click-outside="handleClickOutside">
<div class="relative flex-grow focus-within:z-10">
<input #keyup.enter="commitChanges()" class="form-input block w-full rounded-none rounded-l-md transition ease-in-out duration-150 sm:text-sm sm:leading-5" v-model="valueAfterEdit" ref="input"/>
</div>
<span class="btn-group">
<button #click="discardChanges()" type="button" class="btn btn-white rounded-l-none border-l-0">
<svg class="h-4 w-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path d="M10 8.586L2.929 1.515 1.515 2.929 8.586 10l-7.071 7.071 1.414 1.414L10 11.414l7.071 7.071 1.414-1.414L11.414 10l7.071-7.071-1.414-1.414L10 8.586z"/>
</svg>
</button>
<button #click="commitChanges()" type="button" class="btn btn-white">
<svg class="h-4 w-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path d="M0 11l2-2 5 5L18 3l2 2L7 18z"/>
</svg>
</button>
</span>
</div>
</div>
</template>
<script>
export default {
props: {
value: {
type: String,
default: null
},
allowEmpty: {
type: Boolean,
default: false
}
},
data() {
return {
editable: false,
valueBeforeEdit: this.value,
valueAfterEdit: this.value
}
},
watch: {
value(val) {
this.valueBeforeEdit = val;
}
},
methods: {
handleClickOutside() {
this.disableEditMode();
this.commitChanges();
},
enableEditMode() {
this.editable = true;
this.$emit('edit-enabled');
this.$nextTick(() => {
this.$refs.input.focus();
});
},
disableEditMode() {
this.editable = false;
this.$emit('edit-disabled');
},
commitChanges() {
if (!this.allowEmpty && this.valueAfterEdit !== '.') {
this.$emit('input', this.valueAfterEdit);
this.disableEditMode();
}
},
discardChanges() {
this.valueAfterEdit = this.valueBeforeEdit;
this.disableEditMode();
}
}
}
</script>

VueJS - Add lazy loading to component v-for

I want to increase the loading times on my website. I currently have a list of all my projects and all the projects load on the first pageload. I want to implement lazy loading to those components but I can't seem to find a way to do it.
I have a component called project-card:
<project-card v-bind:project="project" v-bind:key="project.id" v-if="projects" v-for="project in filteredProjects"></project-card>
The template file:
<template>
<div class="project-card rounded dark:bg-gray-800 bg-white overflow-hidden shadow-lg rounded-lg flex flex-col relative">
<img class="w-full h-64 object-cover" :src="'/uploads/' + project.image" alt="">
<div class="px-6 py-4 h-full flex flex-col">
<div class="project-info block min-h-8">
<div class="w-full">
<p class="tracking-widest text-xs title-font font-medium dark:text-white text-gray-500 mb-1">{{ project.language }}</p>
</div>
</div>
<div class="project-language flex-1">
<div class="w-5/6 float-left">
<p class="font-bold dark:text-white gilroy text-xl">{{ project.name }}</p>
</div>
</div>
<div class="project-description flex-2 space-y-4 py-3 h-full">
<p class="dark:text-white ">{{ project.description | str_limit(128) }}</p>
</div>
<div class="read-more mt-auto">
<div class="flex items-center flex-wrap ">
<button type="button" #click="openModal" class="font-bold text-sm text-indigo-600 hover:text-indigo-500 transition duration-150 ease-in-out hover:text-indigo-900 read-more-button flex items-center focus:outline-none">
Lees meer <span class="read-more-arrow ml-2">→</span>
</button>
<span class="text-gray-600 mr-3 inline-flex items-center lg:ml-auto md:ml-0 ml-auto leading-none text-sm pr-3 py-1 border-r-2 border-gray-300">
</span>
<span class="text-gray-600 inline-flex items-center leading-none text-sm">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4 mr-1">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
</svg>
{{ project.stargazers_count }}
</span>
</div>
<project-card-modal :project="project" ref="projectModal"></project-card-modal>
</div>
</div>
</div>
</template>
<script>
import projectCardModalComponent from "./projectCardModalComponent";
export default {
components: {
projectCardModalComponent
},
methods: {
openModal() {
this.$refs.projectModal.show = true;
}
},
props: ['project']
}
</script>
Currently It is looking something like this, so it gives an idea of how it is supposed to turn out. But I want to implement lazy loading to this component. How can I achieve that?
APP.JS
import Vue from "vue";
require('./bootstrap');
window.Vue = require('vue');
Vue.component('project-card', require('./components/projectCardComponent.vue').default);
Vue.component('project-card-modal', require('./components/projectCardModalComponent.vue').default);
Vue.component('contact-form', require('./components/contactFormComponent.vue').default);
Vue.component('contact-form-flash', require('./components/contactFormFlashComponent.vue').default);
Vue.component('project-language-filter-button', require('./components/projectLanguageButtonFilterComponent.vue').default);
Vue.component('project-language-filter-dropdown', require('./components/projectLanguageDropdownFilterComponent.vue').default);
Vue.component('education', require('./components/educationComponent.vue').default);
Vue.component('work-experience', require('./components/workExperienceComponent.vue').default);
Vue.component('sidebar', require('./components/dashboard/dashboardSidebarComponent.vue').default);
Vue.component('projects', require('./components/dashboard/dashboardProjectsComponent.vue').default);
import MugenScroll from 'vue-mugen-scroll'
const app = new Vue({
el: '#app',
data: {
showModal: false,
projects: [],
filteredProjects: [],
languages: [],
languageFilterKey: 'all',
workExperiences: [],
educations: [],
search: '',
pageSizeBegin: 0,
pageSizeEnd: null,
projectsLoading: false,
},
mounted: function() {
this.getProjects();
this.getWorkExperiences();
this.getAllEducations();
this.getProjectLanguages();
},
created() {
this.getFilteredProjects();
},
methods: {
getProjects: function() {
axios.get('/api/get/projects')
.then(response => {
this.filteredProjects = this.projects = response.data
}).catch(err => {
console.log(err)
});
},
getFilteredProjects: function() {
for(let i = 0; i < 6; i++) {
let count = this.filteredProjects.length + i
}
},
getProjectLanguages: function() {
axios.get('/api/get/project/languages')
.then(response => {
this.languages = response.data
}).catch(err => {
console.log(err)
});
},
selectedLanguage: function() {
if(this.languageFilterKey !== null) {
this.languages.forEach((item) => {
item.active = item.language === this.languageFilterKey;
});
} else {
this.languageFilterKey = null
}
},
filterProjectsByLanguage () {
if(this.languageFilterKey === 'all') {
this.filteredProjects = this.projects;
} else {
this.filteredProjects = this.projects.filter((project) => {
return this.languageFilterKey === project.language
});
}
},
getWorkExperiences: function() {
axios.get('/api/get/workexperiences')
.then(response => {
this.workExperiences = response.data
}).catch(err => {
console.log(err)
});
},
getAllEducations: function() {
axios.get('/api/get/educations')
.then(response => {
this.educations = response.data
}).catch(err => {
console.log(err)
});
},
amountOnChange(event) {
if(!event.target.value) {
this.pageSizeEnd = null;
} else {
this.pageSizeEnd = event.target.value;
}
}
},
computed: {
filteredList() {
if(!this.pageSizeEnd) {
return this.projects.filter((project) => {
return project.name.toLowerCase().includes(this.search.toLowerCase()) || project.language.toLowerCase().includes(this.search.toLowerCase())
})
}
return this.projects.filter((project) => {
return project.name.toLowerCase().includes(this.search.toLowerCase()) || project.language.toLowerCase().includes(this.search.toLowerCase())
}).slice(this.pageSizeBegin, this.pageSizeEnd)
},
}
})
Vue.filter('str_limit', function (value, size) {
if (!value) return '';
value = value.toString();
if (value.length <= size) {
return value;
}
return value.substr(0, size) + '...';
});
I think what you want is actually infinite scrolling.
A lot of libs are doing that, my favorite being vue-mugen-scroll.
Take a look at their demo, I think it's close to your use case.
var vm = new Vue({
el: '#vue-instance',
data: {
posts: [],
loading: false
},
created() {
this.getPosts()
},
methods: {
getPosts() {
for (var i = 0; i < 16; i++) {
var count = this.posts.length + i
this.posts.push({
title: 'title ' + count
})
}
}
}
});
<script src="https://unpkg.com/vue-mugen-scroll#0.2.5/dist/vue-mugen-scroll.min.js"></script>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="vue-instance" class="container">
<div class="row">
<div class="col-sm-6" v-for="(post, index) in posts">
<div class="card m-4" style="width: 18rem;">
<img class="card-img-top" src="https://via.placeholder.com/350x150">
<div class="card-body">
<h5 class="card-title"><strong>{{ post.title }}</strong></h5>
</div>
</div>
</div>
</div>
<mugen-scroll :handler="getPosts" :should-handle="!loading">
loading...
</mugen-scroll>
</div>

Why the images of the publications are altered at the moment that I make a new publication with images?

when I reload browser I can see the post's pics
I have a component referring to the posts, but when you make a post that contains images, they are not updated and the post remains with the images from the previous post, the only way to prevent this from happening is by updating the browser and it should not be like that
Could someone help me to know what is happening, if I should update the component when loading the images or what should I do?
Thanks!
fomPost.vue =>
<template>
<div class="card bg-dark border-left-primary border-right-primary shd mb-4">
<div class="card-body">
<div v-if="status_msg" :class="{ 'alert-green': status, 'alert-danger': !status }" class="alert"
role="alert">{{ status_msg }}
</div>
<div class="form-group">
<textarea id="content-body" class="form-control" v-model="body"></textarea>
<a href="javascript:void(0)" class="text-lg float-right" data-toggle="tooltip"
data-title="Agregar imágenes" data-placement="bottom" #click="toggle()">
<i class="fas fa-cloud-upload-alt"></i>
</a>
</div>
<div v-show="isOpen" :style="'margin-top: 2rem'">
<div class="uploader"
#dragenter="OnDragEnter"
#dragleave="OnDragLeave"
#dragover.prevent
#drop="onDrop"
:class="{ dragging: isDragging }">
<div class="upload-control" v-show="images.length">
<label for="file">Agregar más imágenes</label>
</div>
<div v-show="!images.length">
<p>Arrastra las imágenes ó</p>
<div class="file-input">
<label for="file">Selecciónalas</label>
<input type="file" id="file" #change="onInputChange"
accept="image/x-png,image/gif,image/jpeg" multiple>
</div>
</div>
</div>
<div class="images-preview" v-show="images.length">
<div class="img-wrapper" v-for="(image, index) in images" :key="index">
<div class="thumbnail" :style="`background-image: url(${image.replace(/(\r\n|\n|\r)/gm)})`">
<div class="options">
<div>
<a href="javascript:void(0)" class="text-light" uk-tooltip="title: Eliminar"
#click="removeimage(index)">
<i class="fas fa-trash-alt"></i>
</a>
</div>
<div>
<a href="javascript:void(0)" class="text-light" uk-tooltip="title: Previsualizar"
uk-toggle="target: #modal-media-image" #click="showImage(image)">
<i class="fas fa-search-plus"></i>
</a>
</div>
</div>
</div>
<div class="details">
<span class="size" v-text="getFileSize(files[index].size)"></span>
</div>
<div id="modal-media-image" class="uk-flex-top" uk-modal>
<div class="uk-modal-dialog uk-width-auto uk-margin-auto-vertical">
<button class="uk-modal-close-outside" type="button" uk-close></button>
<img width="1024px" :src="dialogImageUrl">
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card-footer bg-opacity-7-dark">
<button type="button" #click="createPost" class="btn btn-primary float-right">
<template v-if="isCreatingPost" class="align-items-center">
<div class="d-flex align-items-center">
<span class="mr-1">Publicando</span>
<div class="loadingio-spinner-dual-ring-botj7pu8xqc">
<div class="ldio-ifliw7yncz">
<div></div>
<div>
<div></div>
</div>
</div>
</div>
</div>
</template>
<template v-else>
Publicar
</template>
</button>
</div>
</div>
</template>
<script>
import {mapActions} from 'vuex'
export default {
name: "FormPostText",
props: ['profile'],
data() {
return {
dialogImageUrl: "",
dialogVisible: false,
isDragging: false,
dragCount: 0,
files: [],
images: [],
status_msg: "",
status: "",
isCreatingPost: false,
title: "",
body: "",
isOpen: false
}
},
mounted() {
$("#content-body").emojioneArea({
placeholder: "¿Qué estás pensando?",
searchPlaceholder: "Buscar",
buttonTitle: "Usa la tecla [TAB] para insertarlos más rápido",
pickerPosition: 'bottom',
filtersPosition: "bottom",
searchPosition: "top"
});
},
methods: {
...mapActions({
create: "post/makePost"
}),
showImage(file) {
this.dialogImageUrl = file;
this.dialogVisible = true;
},
OnDragEnter(e) {
e.preventDefault();
this.dragCount++;
this.isDragging = true;
return false;
},
OnDragLeave(e) {
e.preventDefault();
this.dragCount--;
if (this.dragCount <= 0)
this.isDragging = false;
},
onInputChange(e) {
const files = e.target.files;
Array.from(files).forEach(file => this.addImage(file));
},
onDrop(e) {
e.preventDefault();
e.stopPropagation();
this.isDragging = false;
const files = e.dataTransfer.files;
Array.from(files).forEach(file => this.addImage(file));
},
addImage(file) {
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
this.showNotification("La imágen no puede ser mayor a 2MB.");
return;
}
if (!file.type.match('image.*')) {
this.showNotification(`${file.name} no es una imágen`);
return;
}
this.files.push(file);
const img = new Image(), reader = new FileReader();
reader.onload = (e) => this.images.push(e.target.result);
reader.readAsDataURL(file);
return isLt2M;
},
removeimage: function (index) {
this.images.splice(index, 1);
},
getFileSize(size) {
const fSExt = ['Bytes', 'KB', 'MB', 'GB'];
let i = 0;
while (size > 900) {
size /= 1024;
i++;
}
return `${(Math.round(size * 100) / 100)} ${fSExt[i]}`;
},
toggle() {
this.isOpen = !this.isOpen;
},
createPost() {
var body = $("#content-body").val();
var text = emojione.toImage(body);
if (!this.validateForm()) {
return false;
}
this.isCreatingPost = true;
const formData = new FormData();
formData.append("user", this.profile.uid);
formData.append("body", text);
this.files.forEach(file => {
formData.append('images[]', file, file.name);
});
this.create(formData).then((res) => {
if (res.data.status === 0) {
this.status = code;
this.showNotification(res.data.msg);
}
document.querySelector(".emojionearea-editor").innerHTML = '';
this.isCreatingPost = false;
this.images = [];
this.files = [];
this.isOpen = false;
let post = res.data;
this.$emit("new", post);
}).catch(error => {
console.log(error)
this.isCreatingPost = false;
});
},
validateForm() {
if (!$("#content-body").val()) {
this.status = false;
this.showNotification("Debes escribir algo para publicar...");
return false;
}
return true;
},
showNotification(message) {
this.$swal.fire({
icon: 'error',
html: message
});
}
}
}
</script>
Post.vue =>
<template>
<div id="timeline">
<div v-if="authenticated.username === username || isFriend">
<FormPost :profile="profile" #new="addPostText"></FormPost>
</div>
<!--<pre>{{postsArr}}</pre>-->
<div v-if="postsCount > 0">
<div v-for="(post, index) in postsArr" :key="index">
<div class="cardbox shadow-lg bg-opacity-5-dark shd">
<div class="cardbox-heading">
<div class="dropdown float-right">
<button class="btn btn-sm btn-dark btn-circle" data-toggle="dropdown"
data-boundary="window">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu dropdown-scale dropdown-menu-right" role="dropdown">
<template v-if="post.user.id === user.uid || post.friend.id === user.uid">
<b-dropdown-item href="javascript:void(0)" #click="deletePost(post.post.id, index)">
<span class="uk-margin-small-right" uk-icon="icon: trash"></span> Eliminar
</b-dropdown-item>
<b-dropdown-divider></b-dropdown-divider>
</template>
<b-dropdown-item href="javascript:void(0)">
<span class="uk-margin-small-right text-danger" uk-icon="icon: warning"></span>
Reportar
</b-dropdown-item>
</div>
</div>
<div class="media m-0">
<div class="d-flex mr-3">
<a v-bind:href="post.user.username">
<img class="img-fluid rounded-circle" v-bind:src="post.friend.photo" alt="User">
</a>
</div>
<div class="media-body">
<p class="m-0"><a v-bind:href="post.friend.username">{{ '#' + post.friend.username }}</a></p>
<small><span><i
class="far fa-clock"></i> {{ since(post.post.created_at) }}</span></small>
</div>
</div>
</div>
<div class="cardbox-item">
<p class="mx-4">{{ post.post.body | setEmoji }}</p>
<div v-if="post.images.length > 0">
<!--<photo-grid
:box-height="'600px'"
:box-width="'100%'"
:boxBorder="0"
:excess-text="'+ {{count}} imágenes'"
uk-lightbox="animation: slide"
>
<img v-for="(imahe, index) in post.images" v-bind:src="`http://127.0.0.1:8000/storage/images/${post.friend.id}/${imahe.url}`" :key="index"/>
</photo-grid>-->
<ImagesGrid :images="post.images" :idFriend="post.friend.id"
uk-lightbox="animation: slide"></ImagesGrid>
</div>
</div>
<div class="cardbox-base">
<ul class="float-right">
<li><a><i class="fa fa-comments"></i></a></li>
<li><a><em class="mr-5">{{ post.comments_count || comments_count }}</em></a></li>
<li><a><i class="fa fa-share-alt"></i></a></li>
<li><a><em class="mr-3">03</em></a></li>
</ul>
<ul>
<li>
<Likes :postid="post.post.id" :user="user"></Likes>
</li>
<li><a href="#"><img
src="http://www.themashabrand.com/templates/bootsnipp/post/assets/img/users/3.jpeg"
class="img-fluid rounded-circle" alt="User"></a></li>
<li><a href="#"><img
src="http://www.themashabrand.com/templates/bootsnipp/post/assets/img/users/1.jpg"
class="img-fluid rounded-circle" alt="User"></a></li>
<li><a href="#"><img
src="http://www.themashabrand.com/templates/bootsnipp/post/assets/img/users/5.jpg"
class="img-fluid rounded-circle" alt="User"></a></li>
<li><a href="#"><img
src="http://www.themashabrand.com/templates/bootsnipp/post/assets/img/users/2.jpg"
class="img-fluid rounded-circle" alt="User"></a></li>
<li><a><span>242 Likes</span></a></li>
</ul>
</div>
<CommentsPost
#new="commentsCount"
:postid="post.post.id"
:postuserid="user.uid"
:user="user"
></CommentsPost>
</div>
</div>
<nav class="pagination-outer">
<ul class="pagination">
<li class="page-item" :class="[pagination.current_page > 1 ? '' : 'disabled']">
<a href="#" class="page-link" aria-label="Previous" #click.prevent="changePage(pagination.current_page - 1)">
<span aria-hidden="true">«</span>
</a>
</li>
<li class="page-item" v-for="page in pagesNumber" :class="[page == isActived ? 'active' : '']">
<a class="page-link" href="#" v-bind:data-hover="page" #click.prevent="changePage(page)">{{page}}</a>
</li>
<li class="page-item" :class="[pagination.current_page < pagination.last_page ? '' : 'disabled']">
<a href="#" class="page-link" aria-label="Next" #click.prevent="changePage(pagination.current_page + 1)">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</div>
<div v-else class="card bg-opacity-5-dark mb-4">
<div class="card-body">
<span class="text-light">No tiene posts aún.</span>
</div>
</div>
</div>
</template>
<script>
import Vue from 'vue'
var moment = require("moment");
moment.locale("es");
import {mapActions, mapGetters} from 'vuex'
import FormPost from "../Posts/FormPost";
import Likes from "../Posts/Likes";
import CommentsPost from "../Posts/CommentsPost";
import ImagesGrid from "../Posts/ImagesGrid";
export default {
name: "Posts",
props: {
username: {
type: String,
required: true
},
profile: {
type: Object,
required: true
},
user: {
type: Object,
required: true
},
isFriend: {
type: Boolean,
required: true
}
},
data() {
return {
page: 0,
offset: 4,
comments_count: ''
}
},
mounted() {
this.getPosts();
},
beforeRouteEnter(to, from, next) {
this.getPosts()
next()
},
beforeRouteUpdate(to, from, next) {
this.getPosts()
next()
},
computed: {
...mapGetters({
authenticated: "auth/authenticated",
postsArr: "post/postsLists",
pagination: "post/postsPagination",
postsCount: "post/postsCount"
}),
isActived: function() {
return this.pagination.current_page;
},
pagesNumber: function () {
if(!this.pagination.to) {
return [];
}
var from = this.pagination.current_page - this.offset;
if(from < 1) {
from = 1;
}
var to = from + (this.offset * 2);
if(to >= this.pagination.last_page) {
to = this.pagination.last_page;
}
var pagesArray = [];
while(from <= to) {
pagesArray.push(from);
from++;
}
return pagesArray;
}
},
methods: {
...mapActions({
getPostsByUser: "post/fetchPosts",
removePost: "post/deletePost",
}),
async getPosts(page) {
await this.getPostsByUser(page);
await this.$forceUpdate();
},
async addPostText(post) {
await this.postsArr.unshift(post);
await this.$forceUpdate();
await this.changePage(1);
},
since(d) {
return moment(d).fromNow();
},
commentsCount(count) {
this.comments_count = count;
},
deletePost(post, index) {
this.postsArr.splice(index, 1)
this.removePost(post)
},
changePage(page) {
this.pagination.current_page = page;
this.getPosts(page);
}
},
filters: {
setEmoji: (value) => {
var rx = /<img\s+(?=(?:[^>]*?\s)?class="[^">]*emojione)(?:[^>]*?\s)?alt="([^"]*)"[^>]*>(?:[^<]*<\/img>)?/gi;
return value.replace(rx, "$1")
}
},
components: {
FormPost,
Likes,
CommentsPost,
ImagesGrid
},
watch: {
'$route': 'getPosts'
}
ImagesGrid.vue =>
<template>
<photo-grid
:box-height="'600px'"
:box-width="'100%'"
:boxBorder="0"
:excess-text="'+ {{count}} imágenes'"
uk-lightbox="animation: slide"
>
<a :href="`http://127.0.0.1:8000/storage/images/${idFriend}/${imahe.url}`"
v-for="(imahe, index) in images"
v-bind:data-image="`http://127.0.0.1:8000/storage/images/${idFriend}/${imahe.url}`"
:key="index">
</a>
</photo-grid>
</template>
<script>
export default {
props: ['idFriend', 'images'],
watch: {
$route(to, from, next) {
this.$forceUpdate();
next()
}
}
};
</script>
Post.js =>
import axios from 'axios'
import store from './index'
export default {
namespaced: true,
state: {
posts: [],
posts_count : 0,
pagination: {
'total': 0,
'current_page': 0,
'per_page': 0,
'last_page': 0,
'from': 0,
'to': 0,
}
},
getters: {
postsLists(state) {
return state.posts;
},
postsCount(state) {
return state.posts_count
},
postsPagination(state) {
return state.pagination
}
},
mutations: {
SET_POST_COLLECTION (state, data) {
state.posts = data.posts.data ;
state.pagination = data.paginate;
state.posts_count = data.paginate.total;
}
},
actions: {
makePost({commit,dispatch}, data) {
return new Promise((resolve, reject) => {
axios.post('user/post/create', data)
.then((response) => {
dispatch("fetchPosts", 1)
resolve(response);
})
.catch((error) => {
reject(error);
});
})
},
fetchPosts({commit}, page) {
return new Promise((resolve, reject) => {
axios.get(`user/${store.state.user.profile.username}/posts?page=${page}`)
.then((response) => {
//console.log(response.data.posts.data);
commit('SET_POST_COLLECTION', response.data);
resolve(response);
})
.catch((error) => {
reject(error);
});
})
},
deletePost({commit}, id) {
return new Promise((resolve, reject) => {
let params = {'id': id};
axios.post('user/post/delete', params)
.then((response) => {
console.log(response.data)
resolve(response);
})
.catch((error) => {
reject(error);
});
})
},
createComment({commit}, comment) {
return new Promise((resolve, reject) => {
axios.post('user/post/comment/create', comment)
.then((response) => {
console.log(response.data)
resolve(response);
})
.catch((error) => {
reject(error);
});
})
}
}
}

How can I put an Image for a specific user in vuejs template?

I'm actually working on profile picture for users.
I now want to put this profil picture next to their name in my chat which is working with pusher and vuejs.
My img is like this :
<img class="profil_pic" src="{{url('uploads/logo_'.Auth::user()->firstName."_".Auth::user()->lastName.".png")}}" onerror="this.src='images/user.jpg'" alt="">
I can not just past it in the file it's not working and it would be for every user, but I want it to be for the auth user. I'm kinda new to vue.js any help would be thankful.
This is my chatMessage.vue :
<template>
<ul class="chat messages" >
<li class="clearfix list-group-item" v-for="message in messages" v-bind:class="{
classForUser: (message.user.id === currentuserid),
classForNotUser: (message.user.id !== currentuserid)}" >
<div class="chat-body clearfix" >
<div class="header">
<strong class="primary-font"
v-bind:class="{
classForAuthorSameAsUser: (message.user.id === currentuserid),
classForAuthorDiffThanUser: (message.user.id !== currentuserid)
}">
{{ message.user.firstName }}
{{ message.user.lastName}}
:
</strong>
{{ message.human_readable_time}}
</div>
<p>{{ message.message }}</p>
</div>
</li>
</ul>
</template>
<script>
export default {
props: ['messages','currentuserid']
};
</script>
<style scoped>
.classForAuthorSameAsUser {
color: lightseagreen ;
}
.classForAuthorDiffThanUser {
color: black;
}
.classForUser{
width: 70%;
left: 30%;
}
.classForNotUser{
width: 70%;
}
</style>
And this is the html of my chat :
<div class="row">
<div class="col-md-12 col-md-offset-2">
<div class="col-md-12 col-md-offset-2">
<div class="panel-body panel-content" id="mess_cont" :userid="{{Auth::user()->id}}">
<chat-messages id="mess" :messages="messages" :currentuserid="{{Auth::user()->id}}"></chat-messages>
</div>
<div class="panel-footer">A
<chat-form
v-on:messagesent="addMessage"
:user="{{ Auth::user() }}">
</chat-form>
</div>
</div>
</div>
</div>
I think I have to use again the thing that help me to get the user id, but I don't really know how it works with vue.js.
Thanks for your time
And now I have this :
<template>
<ul class="chat messages" >
<li class="clearfix list-group-item" v-for="message in messages" v-bind:class="{
classForUser: (message.user.id === currentuserid),
classForNotUser: (message.user.id !== currentuserid)}" >
<div class="chat-body clearfix" >
<div class="header">
<strong class="primary-font"
v-bind:class="{
classForAuthorSameAsUser: (message.user.id === currentuserid),
classForAuthorDiffThanUser: (message.user.id !== currentuserid)
}">
<img class="profil_pic" :src="`path/to/uploads/logo_${user.firstName}_${user.lastName}.png`" alt="Auth image"/>
{{ message.user.firstName }}
{{ message.user.lastName}}
:
</strong>
{{ message.human_readable_time}}
</div>
<p>
{{ message.message }}
</p>
</div>
</li>
</ul>
</template>
<script>
export default {
props: ['messages','currentuserid'],
data() {
return {
user: null,
}
},
methods: {
this:$http.get('/api/get-auth')
.then((response) => {
this.user = response.data.auth
})
.catch((error) => {
// handle error
}),
}
};
</script>
<style scoped>
.classForAuthorSameAsUser {
color: lightseagreen ;
}
.classForAuthorDiffThanUser {
color: black;
}
.classForUser{
width: 70%;
left: 30%;
}
.classForNotUser{
width: 70%;
}
</style>
And it's not working
As I see, what you want to achieve is to take this img tag (with blade's logic):
<img class="profil_pic" src="{{url('uploads/logo_'.Auth::user()->firstName."_".Auth::user()->lastName.".png")}}" onerror="this.src='images/user.jpg'" alt="">
And put it inside your chatMessage.vue component. If that's it, you can do so like this:
1. Create a function inside a controller in order to return the authenticated user information:
public function getAuth()
{
return response()->json([
'auth' => auth()->user(),
], 200);
}
2. Create a new route in order to get that information inside your api.php file:
Route::get('/get-auth', 'YourController#getAuth');
3. Create a new param inside the data() returned object in the chatMessage.vue component:
data() {
return {
user: null,
}
},
4. Create a method inside your chatMessage.vue component with the necessary code to fetch that data:
fetchAuth() {
this.$http.get('/api/get-auth')
.then((response) => {
this.user = response.data.auth
})
.catch((error) => {
// handle error
});
}
5. Call that method inside your created() life cycle method:
created() {
this.fetchAuth();
}
6. Create the img tag like this:
<img class="profil_pic" :src="`path/to/uploads/logo_${user.firstName}_${user.lastName}.png`" alt="Auth image"/>

RTL picker with dependent values

The reequirement is to make an RTL picker with multiple levels.
I copied the example from the documentation into a page equiped with the official frameworks' CSS for RTL users.
I have modified "textAlign" properties of each column as well.
For some reason the picker isn't acting as expected. open the snippet in full page mode.
var app = new Framework7({
root: '#app',
rtl: true,
theme: 'md'
});
app.views.create('#mainView', {
});
var carVendors = {
Japanese: ['Honda', 'Lexus', 'Mazda', 'Nissan', 'Toyota'],
German: ['Audi', 'BMW', 'Mercedes', 'Volkswagen', 'Volvo'],
American: ['Cadillac', 'Chrysler', 'Dodge', 'Ford']
};
var pickerDependent = app.picker.create({
inputEl: '#demo-picker-dependent',
rotateEffect: true,
formatValue: function(values) {
return values[1];
},
cols: [{
textAlign: 'right',
values: ['Japanese', 'German', 'American'],
onChange: function(picker, country) {
if (picker.cols[1].replaceValues) {
picker.cols[1].replaceValues(carVendors[country]);
}
}
},
{
textAlign: 'right',
values: carVendors.Japanese,
width: 160,
},
],
routableModals:false
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/framework7/2.3.0/css/framework7.rtl.md.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/framework7/2.3.0/js/framework7.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="block-title">
Dependent values</div>
<div id="app">
<div id="mainView">
<div class="list no-hairlines-md">
<ul>
<li>
<div class="item-content item-input">
<div class="item-inner">
<div class="item-input-wrap">
<input type="text" placeholder="Your car" readonly="readonly" id="demo-picker-dependent" />
</div>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
That seems like a bug in Framework7.
CSS 'right' and 'left' properties for first and last '.picker-column' don't fit RTL layout (flipped). The attached repair solved it.
var app = new Framework7({
root: '#app',
rtl: true,
theme: 'md'
});
app.views.create('#mainView', {
});
var carVendors = {
Japanese: ['Honda', 'Lexus', 'Mazda', 'Nissan', 'Toyota'],
German: ['Audi', 'BMW', 'Mercedes', 'Volkswagen', 'Volvo'],
American: ['Cadillac', 'Chrysler', 'Dodge', 'Ford']
};
var pickerDependent = app.picker.create({
inputEl: '#demo-picker-dependent',
rotateEffect: true,
formatValue: function(values) {
return values[1];
},
cols: [{
textAlign: 'right',
values: ['Japanese', 'German', 'American'],
onChange: function(picker, country) {
if (picker.cols[1].replaceValues) {
picker.cols[1].replaceValues(carVendors[country]);
}
}
},
{
textAlign: 'right',
values: carVendors.Japanese,
width: 160,
},
],
routableModals:false
});
.picker-column.picker-column-first:after{
left:100% !important;
right:0 !important;
}
.picker-column.picker-column-last:after{
left:0 !important;
right:100% !important;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/framework7/2.3.0/css/framework7.rtl.md.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/framework7/2.3.0/js/framework7.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="block-title">
Dependent values</div>
<div id="app">
<div id="mainView">
<div class="list no-hairlines-md">
<ul>
<li>
<div class="item-content item-input">
<div class="item-inner">
<div class="item-input-wrap">
<input type="text" placeholder="Your car" readonly="readonly" id="demo-picker-dependent" />
</div>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
EDIT:
github issue

Resources