Extracting data from an API in typescript vue and to a prop - javascript

I need some help guys. Anytime I've done this I've used a v-for and extracted the data manually. The problem I am facing here is that the component I am passing the data to is a custom component already written to accept the data in an :options prop. But all I am getting is "null" for every piece of state.
Here is the template
<WDropDown label="Commissioner" :options="dropdownCommissionerValues" v-model="commissioner1"/>
And the script lang="ts"
// Data Section
apiInvoker= methods; //this is coming from a service file that logs to the API
public commissioner1 = "1";
dropdownCommissionerValues= [];
mounted () {
this.apiInvoker.viewRequestGet("resources/GetCommisionerList").then((response) => {
if(response.data) {
this.dropdownCommissionerValues = response.data.Table;
console.log("My Response is", this.dropdownCommissionerValues);
}
});
}
Inspecting the Vue Dev Tool, and the console, I am connecting to the API and I am getting data in the component. See image below
But this is what I am getting in the browser See image
NOTE, If I hardcode my data in dropdownCommissionerValues as shown below, it get it to work. But I need it to come from the API
this.dropdownCommissionerValues= [
{
value:"1",
text:"Something 1"
},
{
value:"2",
text:"Something 2"
}
]
I assume somehow I have to pass CommissionerId as value and CommissionerName as text because that's how the component reads it? I just can't figure out how
Thanks in advance!
Here is the custom component
<template>
<ValidationProvider :vid="id" :name="id" :rules="rules" v-slot="wDropDown">
<wFormGroup :class="classObject"
:id="groupId('lbl', id)"
:label="label"
:label-for="id"
:label-sr-only="labelSrOnly">
<template v-if="(pagePlaintext || plaintext || isPlaintext) && !dropDownBind">
<b-form-input plaintext
aria-readonly="true"
readonly="readonly"
:aria-label="label"
:data-vv-as="label"
:id="id"
:name="name"
:type="type"
:value="refDataValue" />
</template>
<template v-else-if="dropDownBind">
<div :class="divClass">
<b-form-select class="attachedSelect"
:id="id"
:aria-label="label"
:aria-required="required"
:data-vv-as="label"
:data-vv-name="label"
:data-vv-scope="scope"
:class="{ 'is-invalid': wDropDown.errors.length > 0 }"
:disabled="disabled || arcDisabledObj"
:label="label"
:multiple="multiple"
:name="name"
:options="options"
:select-size="selectSize"
:text-field="selectText"
v-model="computedVal"
:value-field="selectValue"
#input="onInput">
<template slot="first" v-if="!(multiple) && displayFirstSlot">
<option v-if="customFirstSlotValue !== null"
:value="customFirstSlotValue">
{{placeholderText}}
</option>
<option v-else-if="computedVal !==null && computedVal !== undefined && computedVal.length ===0"
computedVal>
{{placeholderText}}
</option>
<option v-else :value="null">{{placeholderText}}</option>
</template>
</b-form-select>
</div>
</template>
<template v-else>
<div :class="divClass">
<b-form-select class="form-control"
:id="id"
:aria-label="label"
:aria-required="required"
:data-vv-as="label"
:data-vv-name="label"
:data-vv-scope="scope"
:class="{ 'is-invalid': wDropDown.errors.length > 0 }"
:disabled="disabled || arcDisabledObj"
:label="label"
:multiple="multiple"
:name="name"
:options="options"
v-model="computedVal"
:select-size="selectSize"
:text-field="selectText"
:value-field="selectValue"
#input="onInput">
<template slot="first" v-if="!(multiple) && displayFirstSlot">
<option v-if="customFirstSlotValue !== null"
:value="customFirstSlotValue">
{{placeholderText}}
</option>
<option v-else-if="computedVal !==null && computedVal !== undefined && computedVal.length ===0"
computedVal>
{{placeholderText}}
</option>
<option v-else :value="null">{{placeholderText}}</option>
</template>
</b-form-select>
</div>
</template>
<b-form-text v-if="helpText" :id="groupId('hlp', id)">{{helpText}}</b-form-text>
<div class="invalid-feedback" v-if="wDropDown.errors.length > 0">{{ Message }}</div>
<!-- {{ isRequired && (!plaintext && !pagePlaintext) }} {{ isRequired }} {{ plaintext}} {{ overridePageMode }} -->
</wFormGroup>
</ValidationProvider>
</template>
<script>
import uuid from "uuid/v1";
import { groupId } from "#/helpers";
import { classObject } from "#/computed";
import { pagePlaintext } from "#/mixins";
/*
[DEPRECATED]: Left in to support Organization page. Do not use otherwise.
This should be removed in the future.
*/
import { arcInputStateMixin } from "#/components/mixins/UI/inputStateMixin";
export default {
data () {
return {
scope: uuid(),
Message: 'This field is required.',
};
},
methods: {
groupId,
onInput (value) {
this.$emit("input", value);
},
getSelectedDataValue (options, keyValue, keyField, valueField) {
let result = null;
let response = null;
console.log(options);
console.log(keyValue);
console.log(keyField);
console.log(valueField);
console.log(keyValue !== undefined && keyValue !== null);
if(keyValue !== undefined && keyValue !== null) {
response = options.filter(option => option[keyField].toString() === keyValue.toString());
}
console.log('wDropDown', this.id);
console.log(response);
if (this.isNotNullOrUndefined(response)) {
result = response[0][valueField] || null;
}
return result !== null ? result : this.readOnlyValue;
},
},
mixins: [arcInputStateMixin, pagePlaintext],
model: {
event: "input",
prop: "value",
},
name: "wDropDown",
props: {
isRequired: Boolean,
dropDownBind: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
},
displayonly: {
type: String,
},
helpText: {
type: String,
},
id: {
required: true,
type: String,
},
inputState: {
type: String,
},
label: {
required: true,
type: String,
},
labelFor: {
type: String,
},
labelSrOnly: {
type: Boolean,
},
maxlength: {
type: String,
},
multiple: {
type: [String, Boolean],
default: false,
},
name: {
type: String,
},
options: {
default () {
return [];
},
type: [Array, String],
},
placeholderText: {
type: String,
default: "- Select One -",
},
plaintext: {
type: Boolean,
},
required: {
type: Boolean,
},
selectSize: {
type: String,
},
selectText: {
type: String,
default: "text",
},
selectValue: {
type: String,
default: "value",
},
type: {
type: String,
},
value: {
type: [Number, String, Array],
default: null,
},
validation: {
type: String,
},
customFirstSlotValue: {
type: [String, Number],
default: null,
},
displayFirstSlot: {
type: [String, Boolean],
default: true,
},
lookUpTableName: {
type: String,
},
overridePageMode: {
type: Boolean,
default: false,
},
readOnlyValue: {
type: String,
},
},
computed: {
rules () {
return (this.isRequired && (!this.plaintext && !this.pagePlaintext) ) ? "required" : "";
},
computedVal: {
get () {
return this.value;
},
set (val) {
return val;
},
},
classObject,
refDataValue () {
return this.getSelectedDataValue(
this.options,
this.computedVal,
this.selectValue,
this.selectText,
);
},
divClass () {
return this.multiple
? "multiSelect"
: (this.disabled || this.arcDisabledObj) === true
? "disable"
: "autofill";
},
},
beforeDestroy () {
//this.$store.commit("Common/SET_ADDRESS_MIXIN_COUNT", 0);
},
};
</script>

The custom component has selectValue and selectText props, which allows setting the b-form-select's value-field and text-field, respectively. You could set those to the desired values without having to remap your data:
<WDropDown :options="dropdownCommissionerValues"
selectText="CommissionerName"
selectValue="CommissionerId"
/>
demo

While not familiar with boostrapVue you could add keys (text and value) either after api call or from the component and using a computed.
Something like:
this.dropdownCommissionerValues = response.data.Table.map(option=>{
option.text=option.commissionerName
option.value=option.commissionerValue
delete option.commissionerName // optional .... clean up the object
delete option.commissionerValue // optional .... clean up the object
return option
})

Related

Vuetify/VueJS - Autocomplete results not showing until input is cleared

I'm at a loss here as to why this is happening.
When I type into the autocomplete input, my data gets fetched via axios fine, it populates "searchResults", but the results do not display until the input is cleared.
Autocomplete:
<v-slide-x-transition>
<v-autocomplete
:items="searchResults"
:loading="isSearching"
:search-input.sync="search"
color="primary"
hide-no-data
hide-selected
item-text="Description"
item-value="API"
label="Search"
placeholder="Start typing to Search"
prepend-icon="mdi-database-search"
return-object
class="ml-2 mt-2"
v-if="searchBox"
>
<template v-slot:item="data">
<v-list-item-avatar>
<v-icon>
mdi-account
</v-icon>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title v-html="data.item.first_name"></v-list-item-title>
<v-list-item-subtitle v-html="data.item.last_name"></v-list-item-subtitle>
</v-list-item-content>
</template>
</v-autocomplete>
</v-slide-x-transition>
Code:
export default {
name: 'navBar',
data: () => ({
searchBox: true,
search: null,
select: null,
searchResults: [],
isSearching: false,
}),
watch: {
search(val) {
this.querySelections(val);
},
},
methods: {
showSearch() {
this.searchBox = true;
},
hideSearch() {
this.searchBox = false;
this.search = null;
this.searchResults = [];
},
async querySelections() {
this.isSearching = true;
const res = await this.callApi('get', '/app/search?query=' + this.search);
if (!res.data) {
this.searchResults = [];
} else {
this.searchResults = res.data;
}
console.log(this.searchResults);
this.isSearching = false;
}
},
mounted() {
}
}
Json:
[
{
"id":1,
"first_name":"Nigel",
"last_name":"Brian",
"email":"n.br#thes.co.uk",
"email_verified_at":"2021-12-02T00:00:00.000000Z",
"super_admin":1,
"profile_photo":"1642002293.jpg",
"created_at":null,
"updated_at":"2022-01-12T15:53:41.000000Z",
"deleted_at":null,
"permissions_for_vue":"{\"users_add\":true,\"users_view\":true,\"users_edit\":true,\"users_delete\":true,\"users_permissions\":true}",
"permissions_alt":{
"users_add":true,
"users_view":true,
"users_edit":true,
"users_delete":true,
"users_permissions":true
}
},
{
"id":2,
"first_name":"Jane",
"last_name":"Parker",
"email":"j.parks#test.co.uk",
"email_verified_at":"2021-12-02T00:00:00.000000Z",
"super_admin":0,
"profile_photo":"1641313913.jpg",
"created_at":null,
"updated_at":"2022-01-12T14:18:29.000000Z",
"deleted_at":null,
"permissions_for_vue":"{\"users_permissions\":true,\"users_add\":true,\"users_view\":true,\"users_edit\":true,\"users_delete\":true}",
"permissions_alt":{
"users_permissions":true,
"users_add":true,
"users_view":true,
"users_edit":true,
"users_delete":true
}
},
{
"id":5,
"first_name":"Nick",
"last_name":"Walters",
"email":"n.w#email.co.uk",
"email_verified_at":"2021-12-02T00:00:00.000000Z",
"super_admin":0,
"profile_photo":null,
"created_at":null,
"updated_at":"2022-01-12T15:27:22.000000Z",
"deleted_at":null,
"permissions_for_vue":"{\"users_permissions\":true,\"users_add\":true,\"users_view\":true,\"users_delete\":true,\"users_edit\":true}",
"permissions_alt":{
"users_permissions":true,
"users_add":true,
"users_view":true,
"users_delete":true,
"users_edit":true
}
},
{
"id":16,
"first_name":"Billy",
"last_name":"Brag",
"email":"234edgh#Erg.com",
"email_verified_at":null,
"super_admin":1,
"profile_photo":null,
"created_at":"2022-01-12T15:34:02.000000Z",
"updated_at":"2022-01-12T15:43:17.000000Z",
"deleted_at":null,
"permissions_for_vue":"[]",
"permissions_alt":[
]
}
]
Is there something obvious I'm missing here? Thanks for reading!
As I pointed out earlier in the comment, the problem is in item-value and item-text properties.
There are no Description and API fields in your JSON data, but these fields are specified in the template.
According to docs, these fields can contain string, array or function.
So you may change it to string value with name of field that should exist in your JSON:
...
item-text="first_name"
item-value="id"
...
or create a method with function that will return some combined value:
...
:item-text="getFullName"
item-value="id"
...
methods: {
...
getFullName(item) {
return [item.first_name, item.last_name].join(' ')
}
}
You may check this solution with static data at CodePen.

Simple validation in vue

I am beginner web developer.
I make my first application in vue project.
I have this code:
<template>
<CRow>
<CCol col="12" lg="6">
<CCard no-header>
<CCardBody>
<h3>
Create Note
</h3>
<CAlert
:show.sync="dismissCountDown"
color="primary"
fade
>
({{ dismissCountDown }}) {{ message }}
</CAlert>
<CInput label="Title" type="text" id="title" placeholder="Title" v-model="note.title"></CInput>
<CInput textarea="true" label="Content" id="content" :rows="9" placeholder="Content.."
v-model="note.content"></CInput>
<CInput label="Applies to date" type="date" id="applies_to_date" v-model="note.applies_to_date"></CInput>
<CSelect id="status_id"
label="Status"
:value.sync="note.status_id"
:plain="true"
:options="statuses"
>
</CSelect>
<CInput label="Note type" type="text" id="note_type" v-model="note.note_type"></CInput>
<template>
<div id="app">
<ckeditor v-model="editorData" :config="editorConfig"></ckeditor>
</div>
</template>
<CButton color="primary" #click="store()">Create</CButton>
<CButton color="primary" #click="goBack">Back</CButton>
</CCardBody>
</CCard>
</CCol>
</CRow>
</template>
import axios from 'axios';
import Vue from 'vue';
import CKEditor from 'ckeditor4-vue';
Vue.use(CKEditor);
export default {
name: 'EditUser',
props: {
caption: {
type: String,
default: 'User id'
},
},
data: () => {
return {
note: {
title: '',
content: '',
applies_to_date: '',
status_id: null,
note_type: '',
},
statuses: [],
message: '',
dismissSecs: 7,
dismissCountDown: 0,
showDismissibleAlert: false,
editorData: '',
editorConfig: {
// The configuration of the editor.
filebrowserImageBrowseUrl: Vue.prototype.$apiAdress+'/api/laravel-filemanager?token=' + localStorage.getItem("api_token"),
filebrowserImageUploadUrl: Vue.prototype.$apiAdress+'/api/laravel-filemanager/upload?type=Images&_token=&token=' + localStorage.getItem("api_token"),
filebrowserBrowseUrl: Vue.prototype.$apiAdress+'/api/laravel-filemanager?type=Files&token=' + localStorage.getItem("api_token"),
filebrowserUploadUrl: Vue.prototype.$apiAdress+'/api/laravel-filemanager/upload?type=Files&_token=&token=' + localStorage.getItem("api_token"),
height: 500,
language: 'pl',
//extraPlugins: 'facebookvideo, youtube, html5video',
editorUrl: "facebookvideo.js",
extraPlugins: 'a11yhelp,about,basicstyles,bidi,blockquote,clipboard,colorbutton,colordialog,contextmenu,copyformatting,dialogadvtab,div,editorplaceholder,elementspath,enterkey,entities,exportpdf,filebrowser,find,flash,floatingspace,font,format,forms,horizontalrule,htmlwriter,iframe,image,indentblock,indentlist,justify,language,link,list,liststyle,magicline,maximize,newpage,pagebreak,pastefromgdocs,pastefromlibreoffice,pastefromword,pastetext,preview,print,removeformat,resize,save,scayt,selectall,showblocks,showborders,smiley,sourcearea,specialchar,stylescombo,tab,table,tableselection,tabletools,templates,toolbar,undo,uploadimage, wysiwygarea,autoembed,image2,embedsemantic',
image2_alignClasses: ['image-align-left', 'image-align-center', 'image-align-right'],
image2_disableResizer: true,
}
}
},
methods: {
goBack() {
this.$router.go(-1)
// this.$router.replace({path: '/users'})
},
store() {
let self = this;
axios.post(this.$apiAdress + '/api/notes?token=' + localStorage.getItem("api_token"),
self.note
)
.then(function (response) {
self.note = {
title: '',
content: '',
applies_to_date: '',
status_id: null,
note_type: '',
};
self.message = 'Successfully created note.';
self.showAlert();
}).catch(function (error) {
if (error.response.data.message == 'The given data was invalid.') {
self.message = '';
for (let key in error.response.data.errors) {
if (error.response.data.errors.hasOwnProperty(key)) {
self.message += error.response.data.errors[key][0] + ' ';
}
}
self.showAlert();
} else {
console.log(error);
self.$router.push({path: 'login'});
}
});
},
countDownChanged(dismissCountDown) {
this.dismissCountDown = dismissCountDown
},
showAlert() {
this.dismissCountDown = this.dismissSecs
},
},
mounted: function () {
let self = this;
axios.get(this.$apiAdress + '/api/notes/create?token=' + localStorage.getItem("api_token"))
.then(function (response) {
self.statuses = response.data;
}).catch(function (error) {
console.log(error);
self.$router.push({path: 'login'});
});
}
}
I need add to my project validation with alert box when input is empty.
I need required for title and content. When user click button "Create" I need check this inputs. If it's empty - then I need show alert().
How can I make it?
Please help me :)
You can make method:
checkInputs() {
if(!this.note.title) {
this.message = 'pls enter title'
this.showAlert()
return true
}
if(!this.note.content) {
this.message = 'pls enter content'
this.showAlert()
return true
}
return false
}
and in store method:
store() {
if(this.checkInputs()) return
...

How to emit and event within a promise in Vuejs [duplicate]

I'm learning on how to render HTML contents in Vuejs I'm trying to build a small input component which gets generated from render function. It looks something like this:
export default {
name: "nits-input",
methods: {
},
props: {
label: String,
hint: String,
error: String,
placeholder: String
},
render (createElement) {
//Help action text
let helpText = this.hint ? createElement('span', { class: 'm-form__help' }, this.hint) : ''
//Error Text
let errorText = this.error ? createElement('span', { class: 'm--font-danger' }, this.error) : ''
return createElement('div', { class: ''}, [
createElement('label', this.label),
createElement('input', {
class: 'form-control m-input',
attrs: { type: this.type, placeholder: this.placeholder },
domProps: { value: self.value},
on: {
input: function (event) {
this.$emit('input', event.target.value)
}
}
}),
helpText, errorText
])
}
}
While calling this component I'm doing below:
<div class="form-group m-form__group">
<nits-input
label="Email Address"
type="email"
hint="We'll never share your email with anyone else."
placeholder="Enter email"
v-model="email"
>
</nits-input>
</div>
<div class="form-group m-form__group">
<nits-input
label="Password"
type="password"
placeholder="Enter password"
v-model="password"
>
</nits-input>
</div>
I want the value to be stored into v-model, to check the values are being set properly I'm using a watch function
watch: {
email () {
console.log('Email v-model defined as '+this.email)
},
password() {
console.log('Password v-model defined as '+this.password)
}
}
But this always gives me error:
Uncaught TypeError: Cannot read property '$emit' of null
I've taken the references from This VueJS Documentation Link. Help me out in this. Thanks.
you should use arrow function since you're loosing the scope inside that callback :
on: {
input:(event)=> {
this.$emit('input', event.target.value)
}
}

How to display response on the option select ? (vue.js 2)

My component vue.js is like this :
<script>
export default{
name: 'CategoryBsSelect',
template: '\
<select class="form-control" v-model="selected" required>\
<option v-for="option in options" v-bind:value="option.id" v-bind:disabled="option.disabled">{{ option.name }}</option>\
</select>',
//props: {list: {type: String, default: ''}},
mounted() {
this.fetchList();
},
data() {
return {
selected: '',
options: [{id: '', name: 'Pilih Kategori'}]
};
},
methods: {
fetchList: function() {
this.$http.post(window.BaseUrl+'/member/category/list').then(function (response) {
//this.$set('list', response.data);
console.log(JSON.stringify(response.body))
Object.keys(response.body).forEach(function (key) {
console.log(key)
console.log(response.body[key])
this.$set(this.options, key, response.body[key]);
}, this);
});
},
}
};
</script>
The result of console.log(JSON.stringify(response.body)) :
{"20":"Category 1","21":"Category 2","22":"Category 3"}
I want display the response on the value of select. But when executed, on the console exist error like this :
TypeError: Cannot read property 'id' of undefined
Is there anyone who can help me?
The issue you are having is the final this.options variable is an hash nat an array, which should be an input to select element, you can modify you code inside fetchList method like following:
Object.keys(resp).forEach((key) => {
console.log(key)
console.log(resp[key])
this.options.push({
id: key,
name: resp[key]
})
});
Have a look at working fiddle here.

method on the server side is invoked two times, why?

I have problem with method on the server side. It is invoked two times, but it should only one. Error occur when I change password. To change password I use Accounts.setpassword(). Below code of that method:
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { TAPi18n } from 'meteor/tap:i18n';
import { Accounts } from 'meteor/accounts-base';
import _ from 'lodash';
Meteor.methods({
'users/change-user-settings'(formData) {
if (!this.userId) {
throw new Meteor.Error(401, TAPi18n.__('errors.you_are_not_logged'));
}
check(formData, App.Schemas.userSettingsSchema);
//change password
if (formData.password) {
Accounts.setPassword(this.userId, formData.password, {logout: false});
}
//change email
if (formData.email) {
Meteor.users.update({_id: this.userId}, {
$set: {
'emails.0.address': formData.email
}
});
}
//change phone number
if (formData.phoneNumber) {
Meteor.users.update({_id: this.userId}, {
$set: {
'phoneNumber': formData.phoneNumber
}
});
}
return true;
}
});
My autoform form and schema to it:
<template name="userSettingsTemplate">
<div class="user-settings-form-wrapper">
<h4>Change settings</h4>
{{ #autoForm id="userSettingsForm" type="method" meteormethod="users/change-user-settings" schema=getUserSettingsSchema class="form-horizontal"}}
{{ > afQuickField name="password" label="Change password: " template="bootstrap3-horizontal" label-class="col-xs-6" input-col-class="col-xs-6"}}
{{ > afQuickField name="passwordConfirmation" label="Confirm password: " template="bootstrap3-horizontal" label-class="col-xs-6" input-col-class="col-xs-6"}}
{{ > afQuickField name="email" label="E-mail: " template="bootstrap3-horizontal" label-class="col-xs-6" input-col-class="col-xs-6"}}
{{ > afQuickField name="phoneNumber" label="Phone number: " template="bootstrap3-horizontal" label-class="col-xs-6" input-col-class="col-xs-6"}}
<div class="form-group">
<button type="submit" class="btn btn-primary full-width">Send</button>
</div>
{{ / autoForm }}
</div>
</template>
Schema:
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
import { TAPi18n } from 'meteor/tap:i18n';
import { Meteor } from 'meteor/meteor';
Meteor.startup(() => {
// for running tests this is temporary workaround
try {
SimpleSchema.messages({
"passwordMismatch": "passwords are not the same"
});
}
catch (e) {
console.log(e.message, e.name, e.stack);
}
});
App.Schemas.userSettingsSchema = new SimpleSchema({
password: {
type: String,
optional: true,
min: 6,
autoform: {
type: "password"
}
},
passwordConfirmation: {
type: String,
min: 6,
optional: true,
autoform: {
type: "password"
},
custom: function() {
if (this.value !== this.field('password').value) {
return "passwordMismatch";
}
}
},
email: {
type: String,
optional: true,
regEx: SimpleSchema.RegEx.Email
},
phoneNumber: {
type: String,
optional: true
}
});
Just a side note. Unless I'm missing something here, you should not be using Accounts.setPassword() like that. It's a server side method, so the new password gets sent as plain text over the wire. Instead, take a look at Accounts.changePassword(), which runs on client and is meant to do what you want to do.

Categories

Resources