Background
I am using the Material Vue AutoComplete component to provide TypeAhead functionality to my users in a vue application.
When the Chrome browser is minimized in width to check for responsiveness I noticed the suggestion container gets smaller in width but, the text inside of the suggestion container does not break in the box. Instead, the sentence that is being displayed runs off the box to the right of the screen.
Problem
I can not figure out how to add styles to correct the before mentioned issue.
Example
<div class="md-layout md-gutter">
<div class="md-layout-item md-small-size-100">
<md-autocomplete
v-model="currentCustomer"
:md-options="customers"
#md-changed="getCustomers"
#md-opened="getCustomers"
#md-selected="getSelected"
:md-fuzzy-search="false"
>
<label>Search Customers...</label>
<template slot="md-autocomplete-item" slot-scope="{ item, term }">
<md-highlight-text :md-term="term">{{ item.email }}</md-highlight-text>
</template>
<template slot="md-autocomplete-empty" slot-scope="{ term }">
No customers matching "{{ term }}" were found. <a #click="addSearchedCustomer(term)">Create </a>this customer.
</template>
</md-autocomplete>
</div>
Specifically this line runs off the screen when there are no search results,
<template slot="md-autocomplete-empty" slot-scope="{ term }"> No customers matching "{{ term }}" were found. <a #click="addSearchedCustomer(term)">Create </a>this customer.</template>
Image Example
Link AutoComplete
UPDATE
What I have tried
When I inspect the AutoComplete with Chrome Dev Tools, I expand the div and this is what it looks like,
Suggestion Container Div -
Question
Looking at the documentation I can not seem to find a way to handle this. How can I apply styles to this suggestion box so it will break the text as the screen gets smaller?
The templated slot does not appear to respond to word-wrap styling (but other styles like color do work).
One way, a bit hacky, is to use a <label style="white-space: pre-wrap;"> to get a muli-line label, and use a directive to set the height.
template
<md-autocomplete v-model="value" :md-options="colors">
<label>Color</label>
<template slot="md-autocomplete-item" slot-scope="{ item, term }">
<span class="color" :style="`background-color: ${item.color}`"></span>
<label v-wrapit
style="white-space: pre-wrap;"
>{{item.name}}</label>
</template>
<template slot="md-autocomplete-empty" slot-scope="{ term }">
<label v-wrapit
style="white-space: pre-wrap;"
>No colors matching "{{ term }}" were found</label>
</template>
directive
<script>
export default {
name: 'AutocompleteTemplate',
directives: {
wrapit: {
inserted: function (el, binding, vnode) {
el.style.height = `${el.scrollHeight}px`
el.style.color = 'red'
console.log('el', el.scrollHeight, el.offsetHeight, el)
}
}
},
data: () => ({
value: null,
colors: [
{ name: 'Aqua blue blue blue blue blue', color: '#00ffff' },
{ name: 'Aquamarine blue', color: '#7fffd4' },
]
}),
style
This style sets overall list width. It is non-scoped because the menu appears outside <div id="app">
<style>
.md-menu-content {
width: 200px !important;
}
</style>
Here is a CodeSandbox to play with.
Related
How i can change style (border color) of v-autocomplete if at least one item is selected?
Now style is changed (blue) if focus is on the field, but become default (grey) if item is selected and focus is not on the field.
I need to stay blue border after removing focus
I try to change css but without success
.v-label .v-label--active .theme--light {
color: green !important;
border: 1px solid red !important;
}
<v-autocomplete
dense
v-model="filtered"
:items="filters"
:menu-props="{ maxHeight: '200' }"
label="Filter"
multiple
outlined
class="mr-md-1"
#change="fetchFilters"
>
<template v-slot:selection="{ item, index }">
<v-chip text-color="grey darken-4" class="indigo lighten-5">
<span>{{ item }}</span>
</v-chip>
</template>
</v-autocomplete>
No additional CSS is required. You could add classes as a Vue class binding:
:class="{'v-input--is-focused primary--text' : filtered.length}"
Using v-input--is-focused primary--text classes will be sufficient,
so your autocomplete will look like:
<v-autocomplete
dense
v-model="filtered"
:items="filters"
:menu-props="{ maxHeight: '200' }"
:class="{'v-input--is-focused primary--text' : filtered.length}"
label="Filter"
multiple
outlined
class="mr-md-1"
#change="fetchFilters"
>
This code is just checking if any items are present in the filtered array.
It has class "v-input--is-dirty". Try to use it in css, like
.theme--light.v-text-field.v-input--is-dirty>.v-input__control>.v-input__slot:before {
border-color: red;
}
On your custom selection template you can access the selected parameter and then just add a custom class if it's true or not
<template v-slot:selection="{ item, index, selected }">
<v-chip
text-color="grey darken-4"
class="indigo lighten-5"
:class=" selected ? 'customActiveClass' : '' "
>
<span>{{ item }}</span>
</v-chip>
</template>
Here is the link of vuetify doc for the v-autocomplete https://vuetifyjs.com/en/api/v-autocomplete/#api-slots
I'm attempting to use Vue Material on a Vue Router Dashboard page, but I'm trying to store the panel in a separate file. I'm absolutely clueless as to why this is not working, I've spent the last 2 hours googling this issue and I don't have anything. Even using the Vue chrome extension doesn't show it, which rules out styling. Putting a red background color on the component does work, yet it still does not work. And also, please forgive my bad code-- I'm about 3 days into Vue.
<template>
<div class="page-container md-layout-row">
<md-app>
<md-app-toolbar class="md-primary">
<span class="md-title">{{ usernameTitleCase }}</span>
</md-app-toolbar>
<PagePanel></PagePanel>
<md-app-content>
<div class="user">
<h1>{{ user.username }}</h1>
<h2>{{ user.customThing }}</h2>
<h3>{{ user.id }}</h3>
</div>
</md-app-content>
</md-app>
</div>
</template>
<script>
import PagePanel from '#/components/panel.vue';
export default {
name: 'Dashboard',
components: {
PagePanel
},
data() {
return {}
},
computed: {
usernameTitleCase() {
const letters = this.user.username.split('');
letters[0] = letters[0].toUpperCase();
return letters.join('')
}
},
created() {
this.user = JSON.parse(localStorage.getItem('user'));
}
}
</script>
<style>
.md-app {
min-height: 350px;
}
.md-drawer {
width: 230px;
max-width: calc(100vw - 125px);
}
</style>
Component File Here:
<template>
<md-app-drawer md-permanent="full">
<md-toolbar class="md-transparent" md-elevation="0">
Navigation
</md-toolbar>
<md-list>
<md-list-item>
<md-icon>move_to_inbox</md-icon>
<span class="md-list-item-text">Inbox</span>
</md-list-item>
<md-list-item>
<md-icon>send</md-icon>
<span class="md-list-item-text">Sent Mail</span>
</md-list-item>
<md-list-item>
<md-icon>delete</md-icon>
<span class="md-list-item-text">Trash</span>
</md-list-item>
<md-list-item>
<md-icon>error</md-icon>
<span class="md-list-item-text">Spam</span>
</md-list-item>
</md-list>
</md-app-drawer>
</template>
<script>
export default {
name: 'PagePanel'
}
</script>
I'm also NOT in production mode and am not getting any errors in console.
It's not easy to spot, but towards the end of this page of the VueMaterial docs, it says:
In these examples we have 3 distinct areas: Toolbar, Drawer and Content. You should create them using the following tags:
md-app-toolbar: ...
md-app-drawer: ...
md-app-content: ...
Any other tag passed as a direct child of the md-app tag will be ignored. The component will only look for these three tags and choose the right placement for them.
Fortunately, they added the ability to use slots (but didn't document them, you have to look at merge requests to see it). You can use them like so:
<template>
<div class="page-container md-layout-row">
<md-app>
<md-app-toolbar> ... </md-app-toolbar>
<page-panel slot="md-app-drawer"></page-panel>
<md-app-content> ... </md-app-content>
</md-app>
</div>
</template>
However, note that the slot value can only be one of the 3 values defined above.
I have the following code, which renders the expected table:
<v-data-table
dense
:headers="table_headers"
:items="table_data"
:items-per-page="table_rows"
:page="table_page"
:hide-default-footer=true
:loading="table_loading"
class="elevation-2"
>
</v-data-table>
I was hoping that appending the display-2 class will do the trick for me, but it seems it doesn't have any effect:
<v-data-table
dense
:headers="table_headers"
:items="table_data"
:items-per-page="table_rows"
:page="table_page"
:hide-default-footer=true
:loading="table_loading"
class="elevation-2 display-2"
>
</v-data-table>
I also tried the following, but it simply renders a bunch of empty row elements. This is not shocking, as I suppose vuetify now expects that I will provide a complete template for each of the rows?
<v-data-table
dense
:headers="table_headers"
:items="table_data"
:items-per-page="table_rows"
:page="table_page"
:hide-default-footer=true
:loading="table_loading"
class="elevation-2"
>
<template v-slot:item="props">
<tr class="display-2"></tr>
</template>
</v-data-table>
In my last attempt my code rendered more td elements per row than I expected, also all the columns were empty:
<v-data-table
dense
:headers="table_headers"
:items="table_data"
:items-per-page="table_rows"
:page="table_page"
:hide-default-footer=true
:loading="table_loading"
class="elevation-2"
>
<template v-slot:body="{ items }">
<tbody>
<tr v-for="row in items">
<td v-for="col in row"
class="display-2">
{{ col }}
</td>
</tr>
</tbody>
</template>
</v-data-table>
What am I doing wrong, is there a simpler way?
BTW: I'm not a JavaScript dev.
If you don't have too many columns, you can use a slot for each and wrap them in a div with a custom class like below:
<template v-slot:item.name="{ item }">
<div class="customStyle">{{ item.name }}</div>
</template>
edit:
In your case when you get columns dynamically, you can use body slot as you already tried but then you will have a custom styling for the whole table body. Give it a try as below, it is untested.
<template v-slot:body="{ items }">
<tr v-for="item in items" :key="item.id" class="customClass">
<td v-for="col in cols">
{{ item.col }}
</td>
</tr>
</template>
'cols' should be an array of column names as string that you are fetching dynamically.
It's important you don't use the style 'scoped'.
<style>
.my_class td{
font-size: small!important;
height: 0!important;
padding: 1px!important;
}
</style>
The v-data-table must be the specified class 'my-class'
<v-data-table class="my-class" :items="my_output" >
</v-data-table>
It works for me don't use the style scoped.
.v-data-table > .v-data-table__wrapper > table > thead > tr > th{
font-size: 16px !important;
}
You dont need to apply any style in particular through "style".
When you are working with tables, you should declare your headers in data() like this:
data() { headers: [{ text: "Title of your row", sortable: true, class: "*"}]
Here in the * you can replace it with any font size Vuetify offers you. You can see all of these in here:
https://v15.vuetifyjs.com/en/framework/typography/
So if you want the font to be good for an h3, you should replace the * above with:
class: "display-2"
Hope this helps. Also, if you want to have a specific color, you can use the class "primary--text" and it will grab the "primary" value from your actual vuetify theme. See more of this in here:
https://vuetifyjs.com/en/features/theme/
I'm trying to learn vue.js. I'm adding list of elements, I want to add class="active" only to the first element in the for loop. following is my code:
<div class="carousel-inner text-center " role="listbox">
<div class="item" v-for="sliderContent in sliderContents">
<h1>{{ sliderContent.title }}</h1>
<p v-html="sliderContent.paragraph"></p>
</div>
</div>
So the first element should look like something this:
<div class="item active">
<h1>WELCOME TO CANVAS</h1>
<p>Create just what you need for your Perfect Website. Choose from a wide<br>range of Elements & simple put them on your own Canvas</p>
</div>
I'm able to get the data so everything is working perfectly fine.
And following is my script code:
<script>
export default{
data() {
return {
sliderContents: [
{
title: "WELCOME TO CANVAS",
paragraph: "Create just what you need for your Perfect Website. Choose from a wide<br>range of Elements & simple put them on your own Canvas"
},
{
title: "WELCOME TO CANVAS",
paragraph: "Create just what you need for your Perfect Website. Choose from a wide<br>range of Elements & simple put them on your own Canvas"
},
{
title: "WELCOME TO CANVAS",
paragraph: "Create just what you need for your Perfect Website. Choose from a wide<br>range of Elements & simple put them on your own Canvas"
},
{
title: "WELCOME TO CANVAS",
paragraph: "Create just what you need for your Perfect Website. Choose from a wide<br>range of Elements & simple put them on your own Canvas"
}
]
}
}
}
Help me out.
const TextComponent = {
template: `
<p>{{ text }}</p>
`,
props: ['text'],
};
new Vue({
components: {
TextComponent,
},
template: `
<div>
<text-component
v-for="(item, index) in items"
:class="{ 'active': index === 0 }"
:text="item.text">
</text-component>
</div>
`,
data: {
items: [
{ text: 'Foo' },
{ text: 'Bar' },
{ text: 'Baz' },
],
},
}).$mount('#app');
.active {
background-color: red;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title></title>
</head>
<body>
<div id="app"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.js"></script>
</body>
</html>
Above is a snippet demonstrating a solution to your problem. Here's an outline of it:
Inside v-for blocks we have full access to parent scope properties. v-for also supports an optional second argument for the index of the current item.
– https://v2.vuejs.org/v2/guide/list.html#Basic-Usage
The v-for directive has a second argument giving you the index of the item.
v-for="(item, index) in items"
Since you want the active class on the first item you can use an expression testing if the index is 0 and bind it to the class attribute.
:class="{ 'active': index === 0 }"
The easiest solution is to check every element if index is equal to 0 and then setting the active class (or the class you needed). Here is the code of class definition:
:class="{ 'active': index === 0 }"
Here is working example of making Bootstrap Tabs:
<ul class="nav nav-tabs tab-nav-right" role="tablist">
<li role="presentation" :class="{ 'active': index === 0 }" v-for="(value, index) in some_array" v-bind:key="index">
{{some_array[index]}}
</li>
</ul>
Also, if you have multiple classes you can mix them like this:
:class="['tab-pane fade', (index === 0 ? 'active in' : 'something_else')]"
To put active class to first element in loop, basically, you are trying to programattically control the class via VuejS.
VueJS allows you to bind the class of the anchor tag (say, but it could even be an li tag) directly to the index of the li element so that when the vuejs variable bound to the index changes, the class also changes. Check these two links for more details
This is the crux of the solution
:class="{current:i == current}
available on the fiddle below and another post that explains in blog format how class of an li element can be dynamically controlled in vuejs
https://jsfiddle.net/Herteby/kpkcfcdw/
https://stackoverblow.wordpress.com/2021/04/03/how-modern-javascript-makes-click-simulation/
I am using Polymer.js, and I am creating an input element with a dropdown suggestions list (like google's search bar).
Basically, I have a core-input, and a core-dropdown, containing a core-menu. I have added two core-a11y-keys to observe arrow keys pressed on the core-input, in order to move the selected item in the core-menu.
I am trying to bind the core-a11y-keys' "on-keys-pressed" event directly to core-menu's selectNext(), and selectPrevious(), but I can't get it to work.
<template>
<link rel="stylesheet" href="suggest-input.css">
<div class="suggest-input-wrapper">
<paper-shadow layout start-justified horizontal center id="feedSearchInput" class="input-shadow">
<input id="searchInput" is="core-input" on-input="{{searchChanged}}" flex placeholder="{{placeholder}}">
<paper-icon-button icon="search" class="feed-search-icon"></paper-icon-button>
<core-dropdown autoFocusDisabled id="suggestionsDropDown" class="suggestions-dropdown">
<core-menu class="suggestions-menu" id="suggestionsMenu">
<template repeat="{{suggestion in suggestionList}}">
<paper-item class="suggestion-item">
<template if="{{suggestion.icon}}">
<core-icon src="{{suggestion.icon}}"></core-icon>
</template>
<template if="{{!suggestion.icon}}">
<core-icon icon="search"></core-icon>
</template>
<h5>{{suggestion.caption}}</h5>
</paper-item>
</template>
</core-menu>
</core-dropdown>
</paper-shadow>
</div>
// First Trial
<core-a11y-keys target="{{searchInput}}" keys="down" on-keys-pressed="{{suggestionsMenu.selectNext}}"></core-a11y-keys>
<core-a11y-keys target="{{searchInput}}" keys="up" on-keys-pressed="{{suggestionsMenu.selectPrevious}}"></core-a11y-keys>
// Second Trial
<core-a11y-keys target="{{searchInput}}" keys="down" on-keys-pressed="{{$.suggestionsMenu.selectNext}}"></core-a11y-keys>
<core-a11y-keys target="{{searchInput}}" keys="up" on-keys-pressed="{{$.suggestionsMenu.selectPrevious}}"></core-a11y-keys>
// Third Trial
<core-a11y-keys target="{{searchInput}}" keys="down" on-keys-pressed="{{this.$.suggestionsMenu.selectNext}}"></core-a11y-keys>
<core-a11y-keys target="{{searchInput}}" keys="up" on-keys-pressed="{{this.$.suggestionsMenu.selectPrevious}}"></core-a11y-keys>
I know I can use custom event handlers, and than call selectNext() and selectedPrevious(), but knowing if it can be done, and how, could help in the future.
Try
target="{{$.searchInput}}"