Vuetify v-select items-text property does not work in v3. (Fill value in v-select Vue3) - vuejs3

I use Vuetify v3 and try to fill a v-select by using items and items-text property but it doesn't work.
The v-select display 2 empty slot.
<v-select ref="structure" :items="structures" item-text="name" item-value="uuid" label="Structure" v-model="listStructure" return-object ></v-select>
The data is correctly filled in with an Get request in the create method of the page.
But the list seems two have two data but empty.
Below the example of one data
{
"name": "strcuture1",
"uuid": "1b0d3f5f-8806-47a5-be63-cef12865c07b"
}

In v.3 you should use item-title="name" instead of item-text="name".
They have changed from item-text in v2.6 to item-title in v3.
Check the 'v-select/v-combobox/v-autocomplete' section of the Vuetify's Upgrade Guide.
Here is the playground:
const { createApp, ref } = Vue;
const { createVuetify } = Vuetify;
const App = {
data() {
return {
listStructure: null,
structures: [
{
"name": "structure1",
"uuid": "1b0d3f5f-8806-47a5-be63-cef12865c07b"
},
{
"name": "structure2",
"uuid": "1b0d3f5f-8806-47a5-be63-cef12865c07b"
}
]
}
}
}
const vuetify = createVuetify();
const app = createApp(App);
app.use(vuetify);
app.mount('#app');
<link href="https://cdn.jsdelivr.net/npm/vuetify#3.1.2/dist/vuetify.min.css" rel="stylesheet"></link>
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#3.1.2/dist/vuetify.min.js"></script>
<div id="app">
<v-select ref="structure" :items="structures" item-title="name" item-value="uuid" label="Structure" v-model="listStructure" return-object ></v-select>
Selected:<br/>
<span v-if="listStructure != null">
Name: {{listStructure.name}}<br/>
uuid: {{listStructure.uuid}}
</span>
</div>

Related

How to dynamically load CSS with Angular

For the last couple of days I've been trying several answers, suggestions and tutorials for the problem, but unfortunately non of them did the trick.
The closest one was this:
https://juristr.com/blog/2019/08/dynamically-load-css-angular-cli/
But it uses "extractCss" which has been deprecated since the article has been published.
According to the article:
"styles.js" file should disappear in the Inspector > Network > JS
Clicking the button should add its css file in Inspector > Network > CSS
But neither of these two is happening at the moment.
app.component.ts
const head = this.document.getElementsByTagName('head')[0];
console.log(head);
let themeLink = this.document.getElementById(
'client-theme'
) as HTMLLinkElement;
if (themeLink) {
themeLink.href = styleName;
} else {
const style = this.document.createElement('link');
style.id = 'client-theme';
style.href = `${styleName}`;
head.appendChild(style);
}
}
app.component.html
<head>
</head>
<body>
<button type="button" (click)="loadStyle('client-a-style.css')">STYLE 1</button>
<button type="button" (click)="loadStyle('client-b-style.css')">STYLE 2</button>
</body>
</html>
angular.json
"styles": [
"src/styles.css",
{
"input": "src/client-a-style.css",
"bundleName": "client-a",
"inject": false
},
{
"input": "src/client-b-style.css",
"bundleName": "client-b",
"inject": false
}
These are the main parts of my code.
Hopefully I've explained the problem sufficiently.
Thank you for helping!
You can put your additionals .css in the folder assets (and remove from angular.json)
Then the only change is add the "assets" folder to the href
loadStyle(styleName: string) {
const head = this.document.getElementsByTagName('head')[0];
let themeLink = this.document.getElementById(
'client-theme'
) as HTMLLinkElement;
if (themeLink) {
themeLink.href = `assets/${styleName}`; //<--add assets
} else {
const style = this.document.createElement('link');
style.id = 'client-theme';
style.rel = 'stylesheet';
style.type = 'text/css';
style.href = `assets/${styleName}`; //<--add assets
head.appendChild(style);
}
}
a stackblitz
I think you are missing a property in the link tag, add this to the place you create the link element and it should work.
style.rel = 'stylesheet';
One of possible solution to this task:
import { DOCUMENT } from '#angular/common';
import { Inject, OnDestroy, OnInit, Renderer2 } from '#angular/core';
export class MyComponent implements OnInit, OnDestroy {
private style?: HTMLLinkElement;
constructor(
#Inject(DOCUMENT) private document: Document,
private renderer2: Renderer2,
) {}
public ngOnInit(): void {
const cssPath = '/link/to/style.css';
// Create a link element via Angular's renderer to avoid SSR troubles
this.style = this.renderer2.createElement('link') as HTMLLinkElement;
// Add the style to the head section
this.renderer2.appendChild(this.document.head, this.style);
// Set type of the link item and path to the css file
this.renderer2.setProperty(this.style, 'rel', 'stylesheet');
this.renderer2.setProperty(this.style, 'href', cssPath);
}
public ngOnDestroy(): void {
// Don't forget to remove style after component destroying
this.renderer2.removeChild(this.document.head, this.style);
}
}
If and if your css-files are on the server, so you probably should update your proxy.conf.json file to have access this file from localhost while serve mode is on.

Vue.js change background-image on multiple conditions

is there a way to change a background-image on conditions?
Im trying to build a weatherapp and I will change the background of a div.
Like:
Weather api returns:
rainy -> change background image to rain.jpg
snow -> change background image to snow.jpg
sunny -> change background to sunny.jpg
etc.
I've tried multiple ways already but nothing worked.
<template>
<div :class="{background_image}"></div>
</template>
<script>
export default {
// ...
data() {
return {
backgroundImage: 'snow'
},
computed:{
background_image : function () {
switch(this.backgroundImage) {
case 'snow':
return 'bg-snow';
case 'rainy':
return 'bg-rainy';
case 'sunny':
return 'bg-sunny';
default:
return 'bg-default';
}
}
},
methods: {
apiCall() {
//api call update background image according to the response
this.backgroundImage = response ; // response contains sunny text
}
},
// ...
}
</script>
<style>
.bg-sunny{
background-image: url('sunny.jpg');
}
.bg-snow{
background-image: url('snow.jpg');
}
.bg-rainy{
background-image: url('rainy.jpg');
}
.bg-default{
background-image: url('default.jpg');
}
</style>
You can achieve this behavior by looking up the image in an object, where you have defined your key and the corresponding image value. In addition, you need to tell webpack to require that media file. That require tells webpack to treat this file as a request.
<template>
<div :style="{ backgroundImage: `url(${weatherTypes[getWeatherType()]})` }"></div>
</template>
<script>
export default {
// ...
data() {
return {
weatherTypes: {
snow: require('some/path/snow.png'),
sun: require('some/path/sun.png'),
// ...
}
}
},
methods: {
getWeatherType() { return 'snow'; },
},
// ...
}
</script>
Reproduction link

How can I implement vertical scroll snapping with `IntersectionObserver`?

I am inspired by Rolls Royce website and want to implement the same scroll snapping feature in mine as well, I did it with the HTML default scroll-snap-type which gives me expected behavior but creates two scrollbars, one for the container and another one for the body, which is not expected so I tried to go with the IntersectionObserver but it causes an issue, I can travel to only adjacent slide when directly jumping from 1st slide to 3rd slide.
Here is the code sandbox link: https://codesandbox.io/s/scrollsnap-forked-pre0c?file=/pages/index.vue
Here is the code that I am working
<template>
<main class="landing">
<nav class="scroller">
<ul class="scroller__list">
<li
class="scroller__item"
v-for="(slide, index) in slides"
:key="index"
#click.prevent="scroll(slide.id)"
>
<a
class="scroller__dot"
:href="'#' + slide.id"
#click.prevent="scroll(slide.id)"
></a>
</li>
</ul>
</nav>
<div class="slides-container">
<slide
class="slide"
v-for="(slide, index) in slides"
:key="index"
:img="slide.img"
:id="slide.id"
:format="slide.format"
:filter="slide.filter"
>{{ slide.content }}</slide
>
</div>
</main>
</template>
<script lang="ts">
import Vue from "vue";
export default Vue.extend({
data() {
return {
slides: [
{
img: "car-slide-1.png",
content: "hello world",
id: "car-slide-1",
filter: "color-burn",
},
{
img: "car-slide-2.png",
// promo-video.mp4
content: "Second Car",
id: "car-slide-2",
filter: "color-burn",
// format: "video",
},
{
img: "car-slide-3.png",
content: "Third slide",
id: "car-slide-3",
filter: "color-burn",
},
],
observer: null as any as IntersectionObserver,
options: {
threshold: [0.5],
root: process.browser
? document.getElementsByClassName("slides-container")[0]
: null,
} as IntersectionObserverInit,
};
},
methods: {
scroll(id: string, who: string | null = null) {
console.log("scrolling to ", id, who ? "by " + who : "");
document.getElementById(id)?.scrollIntoView({
behavior: "smooth",
block: "start",
});
},
},
mounted() {
let scrolling = false;
this.observer = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting && !scrolling) {
let top = entry.boundingClientRect.top;
scrolling = true;
window.scroll({ behavior: "smooth", top: window.pageYOffset + top });
}
scrolling = false;
});
}, this.options);
document
.querySelectorAll(".slide")
.forEach((slide) => this.observer.observe(slide));
},
});
</script>

VUE3 - Select element on mounted() or created() in a custom component without using "event"

i have a custom input component and i want to select it without clicking on it or something so i cant use "event", i want to select it on created() or mounted()
how can i do that?
<template>
<div class="form-control">
<label :for="id">
{{ label }}
</label>
<input
:id="id"
:type="mode"
:value="currentValue"
#input="$emit('update:modelValue', $event.target.value)"
#focus="pullLabel"
#blur="pushLabel"
/>
</div>
</template>
<script>
export default {
created() {
this.test();
},
methods: {
test() {
console.log(this); // -> How could this be like '<input id="title" type="text">'
},
},
};
</script>
I figured it out myself after 6~ hours of work.
The answer is $refs
I sent a "ref" from parent component like this:
<form ref="settingsForm" #submit.prevent="updateConfig">
Then in methods, i selected all the inputs in form like this: (except submit input)
methods: {
...
focusInput() {
const inputs = this.$refs.settingsForm.querySelectorAll(
'input:not([type="submit"])'
); // $refs.settingsForm -> comes from the parent element, you can name "settingsForm" whatever you want
Array.from(inputs).map((e) => {
if (e.value.length > 0) {
e.previousElementSibling.classList.add("clicked-input");
}
});
},
...
}
By the way, the most important thing is, you have to call the function on mounted() method, not on created()

(Meteor + React) Cannot access props from within subcomponent

I am passing data from one component to another (MyApplicants) via my router (FlowRouter):
FlowRouter.route('/applicants', {
name: 'Applicants',
action: function () {
var currentUser = Meteor.user();
ReactLayout.render(App, {
content: <MyApplicants institutionID={Meteor.user().profile.institutionID} />,
nav: <Nav />,
header: <Header />
});
}
});
As you can see I'm passing institutionID to the new component via a prop in the router. I know that the institutionID is being passed because I can see it in the render of the MyApplicants component.
Here is the MyApplicants component:
MyApplicants = React.createClass({
mixins: [ReactMeteorData],
pagination: new Meteor.Pagination(Applicants, {
perPage: 25,
filters: {institution_id: this.props.institutionID },
sort: {institution_id: 1, "last_name":1, "first_name":1}
}),
getMeteorData() {
return {
currentUser: Meteor.user(),
applicants: this.pagination.getPage(),
ready: this.pagination.ready()
}
},
RenderApplicantRow(applicant, key) {
return (
<div key={key}>
<p>[{applicant.institution_id}] {applicant.last_name}, {applicant.first_name}</p>
</div>
)
},
render : function () {
return (
<div>
<section className="content">
{this.data.applicants.map(this.RenderApplicantRow)}
{console.log(this.data.applicants)}
<DefaultBootstrapPaginator
pagination={this.pagination}
limit={6}
containerClass="text-center"/>
</section>
</div>
)
}
});
(FYI, I'm using krounin:pagination.) The problem is that I cannot access this.props.institutionID inside of the pagination component. I know the value is getting passed (I can see it if I'm just testing output in the render) but can't figure out why it doesn't pass into the pagination call. And I know the pagination works because I do not get an error if I hard code in a value.
Thanks for the help.
This is a simple scope problem I think, you need to bind it to the right context
Try something like this:
pagination: function() {
var self= this;
new Meteor.Pagination(Applicants, {
perPage: 25,
filters: {institution_id: self.props.institutionID },
sort: {institution_id: 1, "last_name":1, "first_name":1}
})
}
,

Resources