I use an array as parameter and I receive an observer - javascript

I'm need this array as a parameter but when I try to use it, I only receive a observer. How to use the information of the array instead of the observer?
When I console.log in the function that retrieves the information from the DB it shows the data as it should.
I don't know what else to do, I've already tried other ways to try to solve this but I wasn't sucessfull.
<script>
import { ClientTable } from 'vue-tables-2';
import Vue from 'vue';
import Multiselect from 'vue-multiselect';
import options from './../../../commons/helpers/grid.config';
import edit from './edit';
Vue.use(ClientTable, options, false, 'bootstrap4', 'default');
Vue.component('multiselect', Multiselect);
export default {
name: 'Reporting',
removable: false,
components: {
edit,
},
showLoading: true,
data() {
return {
selected: null,
columns: ['period', 'costCenter', 'hours', 'actions'],
reportingsList: [],
periods: [],
totalHours: 0,
options: {
sortable: [],
columnsClasses: {
actions: 'action-column text-center',
period: 'period-column',
costCenter: 'costCenter-Column',
hours: 'hours-column',
},
},
};
},
mounted() {
this.getAll();
},
methods: {
getTotalHours(reportings) {
let result = 0;
for (let i = 0, length = reportings.length; i < length; i += 1) {
result += reportings[i].hours;
}
console.log(result); //eslint-disable-line
console.log(this.reportingsList); //eslint-disable-line
this.totalHours = result;
},
getAll() {
const url = 'reportings/getAll';
this.$http().get(url).then((response) => {
this.reportingsList = response.data;
console.log(this.reportingsList.length);//eslint-disable-line
});
this.getTotalHours(this.reportingsList);
}
</script>

It's asynchronous code, this.getTotalHours will be execute before this.$http.get finish.
You need to chain .then
this.$http().get(url).then((response) => {
this.reportingsList = response.data;
console.log(this.reportingsList.length);//eslint-disable-line
return response.data
}).then(() => {
this.getTotalHours(this.reportingsList);
});

Related

How can I use data defined in data() in other methods in Vue js?

First I defined Types, Severities, and Statuses as [] and returned them in data().
Then I filled them with data in the methods getTypes(), getSeverities(), and getStatuses().
I want to use Types, Severities, and Statuses in the method getName()(just has console.log() as an example for now).
I noticed when debugging getNames(), type in the second for loop is undefined. Is it because the method is using Type before it was assigned values in getTypes()? How can I make it work?
Note: Types, Severities, and Statuses do get assigned values in the methods getTypes(), getSeverities(), and getStatuses(), the issues is how to use the data in other methods.
<script>
import IssuesTable from '../MyIssuesPage/IssuesTable.vue'
import AddIssue from '../MyIssuesPage/AddIssue.vue'
import axios from 'axios'
export default {
props: ['id', 'project', 'issuesList', 'index'],
components: { IssuesTable, AddIssue },
data() {
return {
Issues: this.issuesList[this.index],
tab: null,
items: [{ tab: 'Issues' }, { tab: 'Calender' }, { tab: 'About' }],
Types: [],
Severities: [],
Statuses: [],
}
},
setup() {
return {
headers: [
{ text: 'Title', value: 'title' },
{ text: 'Description', value: 'description' },
{ text: 'Estimate', value: 'time_estimate' },
{ text: 'Assignees', value: 'userid' },
{ text: 'Type', value: 'issueTypeId' },
{ text: 'Status', value: 'issueStatusId' },
{ text: 'Severity', value: 'issueSeverityId' },
],
}
},
mounted() {
this.getTypes(), this.getSeverities(), this.getStatuses(), this.getNames()
},
methods: {
getTypes() {
axios
.get('http://fadiserver.herokuapp.com/api/v1/my-types')
.then(response => {
this.Types = response.data
})
.catch(error => {
console.log(error)
})
},
getSeverities() {
axios
.get('http://fadiserver.herokuapp.com/api/v1/my-severities')
.then(response => {
this.Severities = response.data
})
.catch(error => {
console.log(error)
})
},
getStatuses() {
axios
.get('http://fadiserver.herokuapp.com/api/v1/my-status')
.then(response => {
this.Statuses = response.data
})
.catch(error => {
console.log(error)
})
},
getNames() {
for (var issue of this.Issues) {
for (var type of this.Types) {
if (issue.issueTypeId == type.id) console.log('test')
}
}
},
},
}
</script>
First of all, use created() instead of mounted() for calling methods that fetch data.
Next, you need to call getNames() only after all fetch methods complete.
created() {
this.getTypes()
.then(this.getSeverities())
.then(this.getStatuses())
.then(this.getNames());
}
In order to chain methods like this you need to put return statement before each axios like this
getTypes() {
return axios
.get("https://fadiserver.herokuapp.com/api/v1/my-types")
.then((response) => {
this.Types = response.data;
})
.catch((error) => {
console.log(error);
});
}
In this component, I see you are receiving issuesList and index props from the outside. I cannot know those values but you can console.log both of them inside created() and see what is happening because issuesList[index] is undefined.
That probably means issuesList is an array and that index does not exist in that array.

Quasar QSelect is not opening when performing AJAX call

I have been trying to create a simple auto complete using Quasar's select but I'm not sure if this is a bug or if I'm doing something wrong.
Problem
Whenever I click the QSelect component, it doesn't show the dropdown where I can pick the options from.
video of the problem
As soon as I click on the QSelect component, I make a request to fetch a list of 50 tags, then I populate the tags to my QSelect but the dropdown doesn't show.
Code
import type { PropType } from "vue";
import { defineComponent, h, ref } from "vue";
import type { TagCodec } from "#/services/api/resources/tags/codec";
import { list } from "#/services/api/resources/tags/actions";
import { QSelect } from "quasar";
export const TagAutoComplete = defineComponent({
name: "TagAutoComplete",
props: {
modelValue: { type: Array as PropType<TagCodec[]> },
},
emits: ["update:modelValue"],
setup(props, context) {
const loading = ref(false);
const tags = ref<TagCodec[]>([]);
// eslint-disable-next-line #typescript-eslint/ban-types
const onFilterTest = (val: string, doneFn: (update: Function) => void) => {
const parameters = val === "" ? {} : { title: val };
doneFn(async () => {
loading.value = true;
const response = await list(parameters);
if (val) {
const needle = val.toLowerCase();
tags.value = response.data.data.filter(
(tag) => tag.title.toLowerCase().indexOf(needle) > -1
);
} else {
tags.value = response.data.data;
}
loading.value = false;
});
};
const onInput = (values: TagCodec[]) => {
context.emit("update:modelValue", values);
};
return function render() {
return h(QSelect, {
modelValue: props.modelValue,
multiple: true,
options: tags.value,
dense: true,
optionLabel: "title",
optionValue: "id",
outlined: true,
useInput: true,
useChips: true,
placeholder: "Start typing to search",
onFilter: onFilterTest,
"onUpdate:modelValue": onInput,
loading: loading.value,
});
};
},
});
What I have tried
I have tried to use the several props that is available for the component but nothing seemed to work.
My understanding is that whenever we want to create an AJAX request using QSelect we should use the onFilter event emitted by QSelect and handle the case from there.
Questions
Is this the way to create a Quasar AJAX Autocomplete? (I have tried to search online but all the answers are in Quasar's forums that are currently returning BAD GATEWAY)
What am I doing wrong that it is not displaying the dropdown as soon as I click on the QSelect?
It seems updateFn may not allow being async. Shift the async action a level up to solve the issue.
const onFilterTest = async (val, update /* abort */) => {
const parameters = val === '' ? {} : { title: val };
loading.value = true;
const response = await list(parameters);
let list = response.data.data;
if (val) {
const needle = val.toLowerCase();
list = response.data.data.filter((x) => x.title.toLowerCase()
.includes(needle));
}
update(() => {
tags.value = list;
loading.value = false;
});
};
I tested it by the following code and mocked values.
// import type { PropType } from 'vue';
import { defineComponent, h, ref } from 'vue';
// import type { TagCodec } from "#/services/api/resources/tags/codec";
// import { list } from "#/services/api/resources/tags/actions";
import { QSelect } from 'quasar';
export const TagAutoComplete = defineComponent({
name: 'TagAutoComplete',
props: {
modelValue: { type: [] },
},
emits: ['update:modelValue'],
setup(props, context) {
const loading = ref(false);
const tags = ref([]);
const onFilterTest = async (val, update /* abort */) => {
// const parameters = val === '' ? {} : { title: val };
loading.value = true;
const response = await new Promise((resolve) => {
setTimeout(() => {
resolve({
data: {
data: [
{
id: 1,
title: 'Vue',
},
{
id: 2,
title: 'Vuex',
},
{
id: 3,
title: 'Nuxt',
},
{
id: 4,
title: 'SSR',
},
],
},
});
}, 3000);
});
let list = response.data.data;
if (val) {
const needle = val.toLowerCase();
list = response.data.data.filter((x) => x.title.toLowerCase()
.includes(needle));
}
update(() => {
tags.value = list;
loading.value = false;
});
};
const onInput = (values) => {
context.emit('update:modelValue', values);
};
return function render() {
return h(QSelect, {
modelValue: props.modelValue,
multiple: true,
options: tags.value,
dense: true,
optionLabel: 'title',
optionValue: 'id',
outlined: true,
useInput: true,
useChips: true,
placeholder: 'Start typing to search',
onFilter: onFilterTest,
'onUpdate:modelValue': onInput,
loading: loading.value,
});
};
},
});

How to implement object from other one

I'm learning Javascript by myself. I work with Vuejs & express on a CRUD App.
On a component, I request my back-end trough my api : localhost:3000/api/transport
Response give me all objects from the db.
With this response I want to implement an object to make a calendar using vue-val component.
This is my component script :
<script>
import VueCal from 'vue-cal'
import 'vue-cal/dist/vuecal.css'
import axios from 'axios'
export default {
components: { VueCal },
data() {
return {
transport: [],
events: [
/*{
start: '2021-01-21 10:30',
end: '2021-01-21 11:30',
title: 'Rdv dentiste',
content: '<i class="v-icon material-icons">local_hospital</i>',
class: 'health'
},*/
],
};
},
mounted() {
this.getTransport();
},
methods: {
getTransport: function() {
this.loading = true;
axios.get('http://127.0.0.1:3000/api/transport/')
.then((response) => {
this.transport = response.data;
this.implementEvents(this.transport);
this.loading = false;
})
.catch((err) => {
this.loading = false;
console.log(err);
})
},
implementEvents: function(transports) {
for(var transport in transports) {
console.log(transport.id)
this.events.push({
start: transport.startDate,
end: transport.endDate,
title: transporte.designation
})
}
}
}
};
</script>
implementEvents function take my response on parameter but I don't know why don't implement events array.
If I replace with code below, it's work.
implementEvents: function(transports) {
for(var transport in transports) {
console.log(transport.id)
this.events.push({
start: '2021-01-21 10:30',
end: '2021-01-21 10:30',
title: 'test'
Anyone have an idea ?
Try using forEach instead of for-in
implementEvents: function(transports) {
trnsports.forEach(transport => {
console.log(transport.id)
this.events.push({
start: transport.startDate,
end: transport.endDate,
title: transporte.designation
})
})
}

How to avoid [Vue warn] for custom directive?

I created a custom directive and it's working good, but when I run the mocha test for the component where I use this custom directive I receive this warning message [Vue warn]: Failed to resolve directive: scroll-text, tell me please how to fix that
test file:
import { shallowMount } from "#vue/test-utils"
import { scrollText } from "z-common/services"
import ZSourcesList from "./ZSourcesList"
Vue.use(scrollText)
const stubs = [
"z-text-field",
"v-progress-circular",
"v-icon",
"z-btn"
]
describe("ZSourcesList.vue", () => {
const sources = []
for (let i = 0; i < 20; i++) {
sources.push({
field: "source",
// format numbers to get 2 diggit number with leading zero 1 -> 01
value: `cluster-${i.toLocaleString('en-US', { minimumIntegerDigits: 2, useGrouping: false })}`,
__typename: "SuggestV2Result"
})
}
it("displays 'No matching sources found' if there are no sources", () => {
const wrapper = shallowMount(ZSourcesList, {
mocks: {
$apollo: {
queries: {
suggestions: {
loading: false,
},
},
},
},
stubs,
sync: false,
data() {
return {
suggestions: [],
}
},
})
expect(wrapper.find(".notification .z-note")).to.exist
})
})
Try registering your custom directive on a local vue instance and then mounting to that local vue instance.
import { shallowMount, createLocalVue } from "#vue/test-utils"
import { scrollText } from "z-common/services"
import ZSourcesList from "./ZSourcesList"
const localVue = createLocalVue()
localVue.use(scrollText) // Register the plugin to local vue
const stubs = [
"z-text-field",
"v-progress-circular",
"v-icon",
"z-btn"
]
describe("ZSourcesList.vue", () => {
const sources = []
for (let i = 0; i < 20; i++) {
sources.push({
field: "source",
// format numbers to get 2 diggit number with leading zero 1 -> 01
value: `cluster-${i.toLocaleString('en-US', { minimumIntegerDigits: 2, useGrouping: false })}`,
__typename: "SuggestV2Result"
})
}
it("displays 'No matching sources found' if there are no sources", () => {
const wrapper = shallowMount(ZSourcesList, {
mocks: {
$apollo: {
queries: {
suggestions: {
loading: false,
},
},
},
},
localVue, // Mount this component to localVue
stubs,
sync: false,
data() {
return {
suggestions: [],
}
},
})
expect(wrapper.find(".notification .z-note")).to.exist
})
})
Using a local vue instance instead of the global in test cases will also prevent polluting the global vue instance and help to prevent side effects in other test cases.

How can I return each value from my for-each loop in javascript

I'm using Bootstrap vue table with contentful's API and could use some help with my code. I'm attempting to use a for loop to iterate over an array and get the property values. The console.info(episodes); call prints out each iteration for the var episodes, but now how do I bind this to my variable episodes. Using return only returns one result even outside of the for each loop. Any help or suggestions on another implementation is greatly appreciated. Full Template below.
<template>
<div>
<h1>Bootstrap Table</h1>
<b-table striped responsive hover :items="episodes" :fields="fields"></b-table>
</div>
</template>
<style>
</style>
<script>
import axios from "axios";
// Config
import config from "config";
// Vuex
import store from "store";
import { mapGetters, mapActions } from "vuex";
// Services
import { formatEntry } from "services/contentful";
// Models
import { entryTypes } from "models/contentful";
// UI
import UiEntry from "ui/Entry";
import UiLatestEntries from "ui/LatestEntries";
const contentful = require("contentful");
const client = contentful.createClient({
space: "xxxx",
environment: "staging", // defaults to 'master' if not set
accessToken: "xxxx"
});
export default {
name: "contentful-table",
data() {
return {
fields: [
{
key: "category",
sortable: true
},
{
key: "episode_name",
sortable: true
},
{
key: "episode_acronym",
sortable: true
},
{
key: "version",
sortable: true
}
],
episodes: []
};
},
mounted() {
return Promise.all([
// fetch the owner of the blog
client.getEntries({
content_type: "entryWebinar",
select: "fields.title,fields.description,fields.body,fields.splash"
})
])
.then(response => {
// console.info(response[0].items);
return response[0].items;
})
.then(response => {
this.episodes = function() {
var arrayLength = response.length;
var episodes = [];
for (let i = 0; i < arrayLength; i++) {
// console.info(response[i].fields.title + response[i].fields.splash + response[i].fields.description + response[i].fields.body );
var episodes = [
{
category: response[i].fields.title,
episode_name: response[i].fields.splash,
episode_acronym: response[i].fields.description,
version: response[i].fields.body
}
];
// episodes.forEach(category => episodes.push(category));
console.info(episodes);
}
return episodes;
};
})
.catch(console.error);
}
};
</script>
You can use the map method on the response array to return all the elements.
In your current example you keep re-setting the episodes variable, instead of the push() you actually want to do. The map method is still a more elegant way to solve your problem.
this.episodes = response.map((item) => {
return {
category: item.fields.title,
episode_name: items.fields.splash,
episode_acronym: item.fields.description,
version: item.fields.body
}
})
You can update the last then to match the last then below
]).then(response => {
return response[0].items;
})
.then((response) => {
this.episodes = response.map((item) => {
return {
category: item.fields.title,
episode_name: items.fields.splash,
episode_acronym: item.fields.description,
version: item.fields.body
};
});
})
.catch(console.error)
You do have an unnecessary second then, but I left it there so that you could see what I am replacing.

Categories