How to fix the warning "The data property is already declared as a prop. Use prop default value instead" in Vue JS - vue.js

Company Profile
<dashboard-card
title="Company Profile"
titleColor="primary"
#click.native="componentClicked"
id="company-profile" class="vs-con-loading__container"
:isData= "isData"
>
<div class="w-full text-xl md:text-2xl font-bold">
{{ profile.companyName }}
</div>
<div class="md:text-md">
<div class="py-2">
{{ profile.description }}
</div>
<table class="table-auto border-collapse">
<tr>
<td class="py-1 pr-2 md:pr-4 font-bold">CEO:</td>
<td>{{ profile.ceo }}</td>
</tr>
<tr>
<td class="py-1 pr-2 md:pr-4 font-bold">Exchange:</td>
<td>{{ profile.exchange }}</td>
</tr>
<tr>
<td class="py-1 pr-2 md:pr-4 font-bold">Ticker:</td>
<td class="uppercase text-primary font-semibold">
{{ profile.symbol }}
</td>
</tr>
<tr>
<td class="py-1 pr-2 md:pr-4 font-bold">Industry:</td>
<td>{{ profile.industry }}</td>
</tr>
<tr>
<td class="py-1 pr-2 font-bold">Sector:</td>
<td>{{ profile.sector }}</td>
</tr>
</table>
<div class="pt-2">
<vs-icon
:icon="'icon-globe'"
icon-pack="feather"
class="text-primary pr-2"
></vs-icon>
<a class="text-primary" :href="profile.website" target="blank">
{{ profile.website }}
</a>
</div>
</div>
</dashboard-card>
</template>
<script>
import DashboardCard from "../dashboard-card";
export default {
name: "CompanyProfile",
extends: DashboardCard,
components: { DashboardCard },
props: {
ticker: {
type: String,
default: "",
},
},
data() {
return {
profile: {},
isData: "",
};
},
watch: {
ticker(value) {
this.isData = "";
this.getProfile(value);
},
},
mounted() {
this.isData = "";
this.getProfile(this.ticker);
},
methods: {
getProfile(ticker) {
this.$vs.loading({
container: "#company-profile",
type: "point",
scale: 0.8,
});
this.$api.ticker.profile(ticker).then((response) => {
if (Object.keys(response).length == 0) {
console.log("response is empty");
this.isData = "404";
this.$vs.loading.close("#company-profile > .con-vs-loading");
return;
}
this.profile = response;
this.$vs.loading.close("#company-profile > .con-vs-loading");
}).catch(error => {
console.log("error is in company Profile ", error);
if(error.response) {
this.isData = error.response.status.toString();
} else {
this.isData = "Network Error";
}
this.$vs.loading.close("#company-profile > .con-vs-loading");
});
},
},
};
</script>
Dashboard Card (where I'm applying the Blur effect)
<template>
<BaseTHCard
:title="title"
:titleColor="titleColor"
:actionable="actionable"
:fixed-height="fixedHeight"
#click.native="componentClicked"
>
<blur :isData="isData">
<!-- DEFAULT TRADEHAT CARD HEADER SLOT -->
<div slot="header">
<slot name="header"> </slot>
</div>
<!-- DEFAULT TRADEHAT CARD MEDIA SLOT -->
<div slot="media">
<slot name="media"> </slot>
</div>
<!-- DEFAULT TRADEHAT CARD BODY SLOT -->
<slot></slot>
<!-- DEFAULT TRADEHAT CARD EXTRA CONTENT SLOT -->
<div slot="extra-content">
<slot name="extra-content"> </slot>
</div>
<!-- DEFAULT TRADEHAT CARD FOOTER SLOT -->
<div slot="footer">
<slot name="footer"> </slot>
</div>
</blur>
</BaseTHCard>
</template>
<script>
import BaseTHCard from "#/components/common/base-th-card";
import blur from "../../ticker-dashboard/shared/Blur";
export default {
name: "DashboardCard",
extends: BaseTHCard,
components: {
BaseTHCard,
blur
},
props: {
isData: {
type: String,
},
title: {
type: String,
default: null,
},
titleColor: {
type: String,
default: "white",
},
fixedHeight: {
type: Boolean,
default: false,
},
actionable: {
type: Boolean,
default: false,
},
},
};
</script>
I'm getting two following warnings from vue:-
I am setting the isData data option in the CompanyProfile component and passing it as a prop to DashboardCard component and sending that isData prop value to the blurr component. The functionality works fine but I'm getting the above mentioned warnings from vue. What should be the approach to fix them.

Related

vue slot does not render when there is a data passed

I am testing my vue component that uses vuejs-smart-table
by rendering the headers and rows from head and body slot that is in my vue component.
this is my vue component (it is just a wrapper that wraps vuejs-smart-table and a pagination component):
<template>
<div class="custom-table-wrapper">
<div class="table-container" :style="tableContainerStyle">
<v-table
:id="id"
:class="tableClass"
:data="data"
:hideSortIcons="true"
:pageSize="pageSize"
:filters="filters"
:currentPage="currentPage"
:selectionMode="selectionMode"
:selectedClass="selectedClass"
#update:currentPage="currentPageChanged"
#totalPagesChanged="totalPagesChanged"
#selectionChanged="selectionChanged">
<template slot="head">
<slot name="head">
</slot>
</template>
<template slot="body" slot-scope="{displayData}">
<slot name="body" :displayData="displayData">
</slot>
</template>
</v-table>
</div>
<div v-show="showTableFooter" class="paging" id="pagination">
<smart-pagination
v-show="totalPages>1"
:currentPage="currentPage"
:totalPages="totalPages"
:maxPageLinks="5"
#update:currentPage="currentPageChanged"/>
<div class="footer-action-btns-container">
<slot name="footer-action-btns">
</slot>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
id: {
type: String,
required: true,
},
data: {
type: Array | null,
default() {
return [];
},
},
filters: {
type: Object,
default() {
return {};
},
},
tableClass: {
type: [String, Object],
default: 'table table-bordered',
},
hideSortIcons: {
type: Boolean,
default: false,
},
pageSize: {
type: Number,
default: 20,
},
selectionMode: {
type: String,
default: 'single',
},
selectedClass: {
type: String,
default: 'vt-selected',
},
tableContainerStyle: {
type: [String, Object],
value: undefined,
},
},
data() {
return {
totalPages: 0,
currentPage: 1,
};
},
methods: {
totalPagesChanged(page) {
this.totalPages = page;
},
currentPageChanged(page) {
this.currentPage = page;
},
selectionChanged($event) {
this.$emit('selectionChanged', $event);
},
},
computed: {
showTableFooter() {
return this.data.length > this.pageSize || this.$slots['footer-action-btns']?.[0];
},
},
};
</script>
this is my spec file for testing my vue component:
import { createLocalVue, mount, $createElement } from '#vue/test-utils';
import SmartTable from 'vuejs-smart-table';
import CustomTable from '#/components/custom-table.vue';
const localVue = createLocalVue();
localVue.use(SmartTable);
describe('CustomTable.vue Test', () => {
let wrapper;
let data = [
{
id: '1',
first_name: 'user1',
last_name: 'user1',
},
{
id: '2',
first_name: 'user2',
last_name: 'user2',
},
{
id: '3',
first_name: 'user3',
last_name: 'user3',
},
];
beforeEach(() => {
wrapper = mount(CustomTable, {localVue,
propsData: {
data: data,
id: 'custom-table',
},
slots: {
head: `
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
</tr>
</thead>
`,
body: `
<tbody #body="{displayData}">
<tr v-for="(row, index) in displayData" :key="index">
<td>{{row.first_name}}</td>
<td>{{row.last_name}}</td>
</tr>
</tbody>
`
},
});
});
it('renders items from data as rows', () => {
expect(wrapper).toMatchSnapshot();
});
});
this is the output of the snapshot:
exports[`CustomTable.vue Test renders items from data as rows 1`] = `
<div class="custom-table-wrapper">
<div class="table-container">
<table id="custom-table" class="table table-bordered">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<div id="pagination" class="paging" style="display: none;">
<nav class="smart-pagination" style="display: none;">
<ul class="pagination">
<!---->
<li class="page-item disabled"><a href="javascript:void(0)" aria-label="Previous" class="page-link"><svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512">
<path fill="currentColor" d="M34.52 239.03L228.87 44.69c9.37-9.37 24.57-9.37 33.94 0l22.67 22.67c9.36 9.36 9.37 24.52.04 33.9L131.49 256l154.02 154.75c9.34 9.38 9.32 24.54-.04 33.9l-22.67 22.67c-9.37 9.37-24.57 9.37-33.94 0L34.52 272.97c-9.37-9.37-9.37-24.57 0-33.94z"></path>
</svg></a></li>
<li class="page-item active">1</li>
<li class="page-item disabled"><a href="javascript:void(0)" aria-label="Next" class="page-link"><svg width="16" height="16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512">
<path fill="currentColor" d="M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z"></path>
</svg></a></li>
<!---->
</ul>
</nav>
<div class="footer-action-btns-container"></div>
</div>
</div>
`;
as you can see the tbody doesnt render any rows here.
i have tried using scopedSlots but it just results to more errors.

how to display 2 lists filtered by another field from one model

Model: Article.
id.
name.
type: ['code', 'design']
API gets all articles
How can I display two lists:
all articles with type ='Code',
all articles with type = 'Design'
In other words, is it possible to filter the API query
Or is it better to do it on the API side?
Extra: same as above but in a nested environment (ie Articles belong to Category. How to do it on the category detail page.
You can use computed properties. I built a sample component:
EDIT: Took some time to DRY it up.
Parent.vue
<template>
<div class="parent">
<div class="row">
<div class="col-md-6">
<article-list title="Code Articles" :articles="codeArticles" />
</div>
<div class="col-md-6">
<article-list title="Design Articles" :articles="designArticles" />
</div>
</div>
</div>
</template>
<script>
import ArticleList from './ArticleList.vue'
export default {
components: {
ArticleList
},
data() {
return {
articles: [
{
id: 1,
name: 'Article1',
type: 'Code'
},
{
id: 2,
name: 'Article2',
type: 'Design'
},
{
id: 3,
name: 'Article3',
type: 'Code'
},
{
id: 4,
name: 'Article4',
type: 'Design'
},
]
}
},
computed: {
codeArticles() {
return this.articles.filter(article => article.type === 'Code');
},
designArticles() {
return this.articles.filter(article => article.type === 'Design');
}
}
}
</script>
ArticleList.vue
<template>
<div class="two-filtered-lists">
<h5>{{ title }}</h5>
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>NAME</th>
<th>TYPE</th>
</tr>
</thead>
<tbody>
<tr v-for="article in articles" :key="article.id">
<td>{{ article.id }}</td>
<td>{{ article.name }}</td>
<td>{{ article.type }}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
props: {
title: {
type: String,
required: true
},
articles: {
type: Array,
required: true
}
}
}
</script>

dynamically call an object property in a v-for loop

so i ran into a problem again,
i want to make a table component where you can send a array to the component, and it will render a table for you
we set it up like this
<template>
<section class="container">
<Apptable :search="true" :loader="true" title="User data" :data="users"/>
</section>
</template>
<script>
import Apptable from "~/components/table.vue";
export default {
components: {
Apptable
},
data() {
return {
users: [
{
id: 1,
name: "Lars",
Adres: "hondenstraat 21",
phone: "06555965"
},
{
id: 1,
name: "John",
Adres: "verwelstraat 35",
phone: "06555965"
}
]
};
}
};
</script>
i send data to the component and loop it from there like this
<template>
<section class="container">
<h2 v-if="title">{{title}}</h2>
<input v-if="search" class="search" placeholder="Search">
<button v-if="loader" class="update" #click="dialog = true">Update</button>
<table class="table">
<thead>
<tr class="tableheader">
<th v-for="(item, index) in Object.keys(data[0])" :key="index">{{item}}</th>
</tr>
</thead>
<tbody>
<tr class="userdata" v-for="(item, index) in data" :key="index">
<td v-for="(name, index) in Object.keys(data[index])" :key="index">{{//TODO: I WANT TO SELECT THE ITEM.DYNAMIC PROPERTY}}</td>
</tr>
</tbody>
</table>
<loader v-if="loader" :trigger="dialog"/>
</section>
</template>
<script>
import loader from "~/components/loader.vue";
export default {
components: {
loader
},
data() {
return {
dialog: false
};
},
watch: {
dialog(val) {
if (!val) return;
setTimeout(() => (this.dialog = false), 1500);
}
},
props: {
data: {
type: Array,
required: true
},
title: {
type: String,
required: false,
default: false
},
loader: {
type: Boolean,
required: false,
default: false
},
search: {
required: false,
type: Boolean,
default: true
}
}
};
</script>
so if you look at the table. were i left the todo, if i put in the {{item}} in the todo place. i will get this in my column
enter image description here
but i want to select the key of the object dynamically. but if i put {{item.name}} in the todo place it will not select the key dynamically.
so the point is that i want to dynamically call a property from the object in the v-for so the columns will get the data in the cells.
You should use item[name] instead of item.name
<tbody>
<tr class="userdata" v-for="(item, index) in data" :key="index">
<td v-for="(name, nIndex) in Object.keys(data[index])" :key="nIndex">
{{ item[name] }}
</td>
</tr>
</tbody>

Why child component not mounted?

Why child component not mounted?
Base template:
<TableComponent :data="data">
<template slot="thead">
<tr>
<TableHeader></TableHeader>
</tr>
</template>
</TableComponent>
TableComponent:
<template>
<div>
<table>
<thead>
{{ renderThead() }}
</thead>
<tbody>
</tbody>
<tfoot>
</tfoot>
</table>
</div>
</template>
<script>
export default {
name: 'TableComponent',
props: {
data: {
required: true,
type: [Array, Function]
},
itemKey: {
default: 'id',
},
},
methods: {
renderThead: function () {
let method = this.$scopedSlots.thead
? this.$scopedSlots.thead
: () => this.$slots.thead
console.info(method())
// return method();
},
},
}
</script>
It is TableHeader component :
<template>
<th>
{{ name }}
</th>
</template>
<script>
export default {
name: 'TableHeader',
props: {
name: {
type: String
}
},
mounted() {
console.info("MOUNTED")
}
}
</script>
Console info on TableHeader not working.
Do you have any ideas?

Vue - axios - handling same requests

In my project I've got such structure:
Client page which has sidebar, general client's info and router-view for children views.
routes:
{ path: '/clients/:id', component: Client,
children: [
{
path: '/',
component: ClientReview,
name: 'client-review'
},
{
path: 'balances',
component: ClientBalances,
name: 'client-balances'
},
{
path: 'report',
component: MainReport,
name: 'client-report'
},
Client's component (Client.vue):
<template>
<el-row>
<client-menu></client-menu>
<el-col class="client-view" :md="{ span: 22, offset: 2}" :sm="{ span: 20, offset: 4}" :xs="{ span: 18, offset: 6}">
<client-bar></client-bar>
<transition name="el-zoom-in-center">
<router-view></router-view>
</transition>
</el-col>
</el-row>
</template>
<script>
import ClientMenu from './ClientMenu.vue'
import ClientBar from './ClientBar.vue'
export default {
data () {
return {
loading: false,
};
},
components: {
'client-menu': ClientMenu,
'client-bar': ClientBar,
}
}
</script>
ClientBar component (ClientBar.vue):
<template>
<div class="client-bar">
<el-col :span="18">
<h3>{{ client.Name }}</h3>
<h4>{{ client.Address }}</h4>
</el-col>
<el-col :span="6" style="text-align: right;">
<el-button-group>
<el-button icon="edit" size="small"></el-button>
<el-button icon="share" size="small"></el-button>
</el-button-group>
</el-col>
<div class="clrfx"></div>
</div>
</template>
<script>
export default {
data () {
return {
client: {}
}
},
mounted () {
this.loadClient()
},
methods: {
loadClient: function() {
self = this;
this.axios.get('http://127.0.0.1:8020/clients/'+self.$route.params.id)
.then(function(response) {
self.client = response.data;
self.loading = false;
})
.catch(function(error) {
console.log(error);
});
}
}
}
</script>
And I've got ClientReview component, which is root component for clients/:id route and use the same api to load clients information as ClientBar:
<template>
<div>
<el-row v-loading.body="loading">
<el-col :span="12">
<table class="el-table striped">
<tr>
<td class="cell">Полное наименование</td>
<td class="cell">{{ clientInfo.FullName }}</td>
</tr>
<tr>
<td class="cell">УНП</td>
<td class="cell">{{ clientInfo.UNP }}</td>
</tr>
<tr>
<td class="cell">ОКЭД</td>
<td class="cell">{{ clientInfo.Branch.code }}<br>{{ clientInfo.Branch.name }}</td>
</tr>
<tr>
<td class="cell">Адрес</td>
<td class="cell">{{ clientInfo.Address }}</td>
</tr>
<tr>
<td class="cell">Аналитик</td>
<td class="cell">{{ clientInfo.Analytic.first_name }} {{ clientInfo.Analytic.last_name }}</td>
</tr>
<tr>
<td class="cell">Менеджер</td>
<td class="cell">{{ clientInfo.Manager.first_name }} {{ clientInfo.Manager.last_name }}</td>
</tr>
</table>
</el-col>
<el-col :span="12">
<classification-report></classification-report>
</el-col>
</el-row>
</div>
</template>
<script>
import ClassificationReport from '../reports/ClassificationReport.vue'
export default {
data () {
return {
loading: false,
clientInfo: {}
}
},
created () {
this.Client();
},
methods: {
Client: function() {
self = this;
self.loading = true;
self.axios.get('http://127.0.0.1:8020/clients/'+self.$route.params.id)
.then(function(response) {
self.clientInfo = response.data;
self.loading = false;
})
.catch(function(error) {
console.log(error);
});
}
},
components: {
'classification-report': ClassificationReport
}
}
</script>
The problem is when I load page client/:id first time or refresh the page client's data in ClientReview doesn't load.
The component is rendered (as I see it in Vue Devtools), and both requests to server are sent, but clientInfo object in ClientReview still empty.
Than if I go to balances or report page and after that go to client-review page everything is loaded.
Hope someone could help me.
It's because self happens to be another reference to the window object, and is available in all modules.
Let's walk thru the steps and see why this bug is happening.
You load up client:/id.
The created method in ClientReview does ajax request, and assigns self to this.
The mounted method in ClientBar does ajax request, and reassigns self to this. Note that this also changed the self variable reference in ClientReview.
The ClientReview ajax finishes and assigns self.clientInfo = response.data;. Since self is a reference to ClientBar, and ClientBar does not declare clientInfo as a root data property, this assignment does nothing.
ClientBar ajax finishes and assigns self.client = response.data;, which works.
You route away from ClientReview.
You route back to ClientReview. Repeat step 2.
ClientReview successfully renders because ClientBar has already rendered, and does not reassign self.
The answer is to do let self = this instead of self = this.
The lesson is ALWAYS DECLARE YOUR VARIABLES with var, let or const.