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.
Related
I have form data of:
const form = {
title: null,
content: null,
language: {
en: { title: null, content: null },
es: { title: null, content: null }
}
}
My Form inputs
<v-form ref="formEl" #submit.prevent="validate">
<div v-if="Object.keys(form.language).length > 0">
<div v-for="(language, langCode) in form.language" :key="langCode">
<v-text-field
v-model="form.language[langCode].title"
:label="$t('form.title')"
/>
<v-textarea
v-model="form.language[langCode].content"
:label="$t('form.content')"
/>
</div>
</div>
</form>
Now when I change the input of 1 form input, it updates both to be the same.
I tried placed a KEY on each input field, with the same result. Does anyone have any thoughts or direction on this? Much appreciated.
const form = ref(lodash.cloneDeep(state.pages.one))
// GET CONTENT
useFetch(async () => {
loading.value = true
try {
await dispatch('pages/getOne', route.value.params.id).then((res) => {
if (res !== false) {
form.value = lodash.cloneDeep(res)
}
})
} catch(e) {
$system.log({
comp: 'AdminPagesEdit', msg: 'useFetch', val: e
})
} finally {
loading.value = false
}
})
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
...
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
})
I am using Bootstrap Vue and would like to add a column dynamically or only show the column if some condition is met in the data. I'm trying to understand scoped slots and whether this is the direction I should take with a formatter callback function. Ideally I would like to add a column "Version 2" only if it is available in the data. I'm not sure where to start.
Code
<b-table striped responsive hover :items="episodes" :fields="fields" :filter="filter">
<template v-slot:cell(version_1)="data">
<a :href="`${data.value.replace(/[^a-z]-/i,'-').toLowerCase()}`">Definition</a>
</template>
</b-table>
</template>
<script>
data() {
return {
fields: [{
key: 'category',
sortable: true
},
{
key: 'episode_name',
sortable: true
},
{
key: 'episode_acronym',
sortable: true
},
{
key: 'version_1',
sortable: true
}
],
episodes: [],
versions: [],
}
},
mounted() {
return Promise.all([
// fetch the owner of the blog
client.getEntries({
content_type: 'entryEpisodeDefinitions',
select: 'fields.title,fields.category,fields.slug,fields.episodeAcronym,fields.version,sys.id'
})
])
.then(response => {
// console.info(response[0].items);
return response[0].items
})
.then((response) => {
this.episodes = response.reduce(function( result, item){
if ( item.fields.version === 1 ) {
return result.concat({
category: item.fields.category,
episode_name: item.fields.title,
episode_acronym: item.fields.episodeAcronym,
version_1: 'episodes/' + item.fields.slug + '/' + item.sys.id,
})
}
return result
}, [])
this.versions = response.reduce(function( result, item){
if ( item.fields.version === 2 ) {
return result.concat({
version_2: 'episodes/' + item.fields.slug + '/' + item.sys.id,
})
}
return result
}, [])
console.info(response);
})
.catch(console.error)
},
</script>
I think this is something, you are looking for:
<b-table :items="items" :fields="fields">
<template v-for="field in dynamicFields" v-slot:[`cell(${field.key})`]="{ item }">
</b-table>
in scripts:
this.dynamicFields.push({key: 'someTestKey', label: 'Dynamic Label'})
I'm building a key-command resource and giving VueJS a whirl while doing so. I'm a newbie but am gaining the grasp of things (slowly...).
I want to be able to search in a global search form for key commands I'm defining as actions within sections of commands (see data example below). I would like to search through all the actions to show only those that match the search criteria.
My HTML is below:
<div id="commands">
<input v-model="searchQuery" />
<div class="commands-section" v-for="item in sectionsSearched"
:key="item.id">
<h3>{{ item.section }}</h3>
<div class="commands-row" v-for="command in item.command" :key="command.action">
{{ command.action }}
</div>
</div>
</div>
My main Vue instance looks like this:
import Vue from 'vue/dist/vue.esm'
import { commands } from './data.js'
document.addEventListener('DOMContentLoaded', () => {
const element = document.getElementById("commands")
if (element != null) {
const app = new Vue({
el: element,
data: {
searchQuery: '',
commands: commands
},
computed: {
sectionsSearched() {
var self = this;
return this.commands.filter((c) => {
return c.command.filter((item) => {
console.log(item.action)
return item.action.indexOf(self.searchQuery) > -1;
});
});
},
}
});
}
});
And finally the data structure in data.js
const commands = [
{
section: "first section",
command: [
{ action: '1' },
{ action: '2' },
{ action: '3' },
],
},
{
section: "second section",
command: [
{ action: 'A' },
{ action: 'B' },
{ action: 'C' },
]
},
]
export { commands };
I'm able to output the commands using the console.log(item.action) snippet you see in the computed method called sectionsSearched.
I see no errors in the browser and the data renders correctly.
I cannot however filter by searching in real-time. I'm nearly positive it's a combination of my data structure + the computed method. Can anyone shed some insight as to what I'm doing wrong here?
I'd ideally like to keep the data as is because it's important to be sectioned off.
I'm a Rails guy who is new to this stuff so any and all feedback is welcome.
Thanks!
EDIT
I've tried the proposed solutions below but keep getting undefined in any query I pass. The functionality seems to work in most cases for something like this:
sectionsSearched() {
return this.commands.filter((c) => {
return c.command.filter((item) => {
return item.action.indexOf(this.searchQuery) > -1;
}).length > 0;
});
},
But alas nothing actually comes back. I'm scratching my head hard.
There is a issue in your sectionsSearched as it is returning the array of just commands.
See this one
sectionsSearched() {
return this.commands.reduce((r, e) => {
const command = e.command.filter(item => item.action.indexOf(this.searchQuery) > -1);
const section = e.section;
r.push({
section,
command
});
}, []);
}
const commands = [
{
section: "first section",
command: [
{ action: '1' },
{ action: '2' },
{ action: '3' },
],
},
{
section: "second section",
command: [
{ action: 'A' },
{ action: 'B' },
{ action: 'C' },
]
},
]
const element = document.getElementById("commands")
if (element != null) {
const app = new Vue({
el: element,
data: {
searchQuery: '',
commands: commands
},
computed: {
sectionsSearched() {
var self = this;
return this.commands.filter((c) => {
// the code below return an array, not a boolean
// make this.commands.filter() not work
// return c.command.filter((item) => {
// return item.action.indexOf(self.searchQuery) > -1;
// });
// to find whether there has command action equal to searchQuery
return c.command.find(item => item.action === self.searchQuery);
});
},
}
});
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="commands">
<input v-model="searchQuery" />
<div class="commands-section" v-for="item in sectionsSearched"
:key="item.id">
<h3>{{ item.section }}</h3>
<div class="commands-row" v-for="command in item.command" :key="command.action">
{{ command.action }}
</div>
</div>
</div>
Is that work as you wish ?
sectionsSearched() {
return this.commands.filter((c) => {
return c.command.filter((item) => {
return item.action.indexOf(this.searchQuery) > -1;
}).length > 0;
});
},
}
since filter will always return an array(empty or not) which value always is true.