i am trying to build an app where user inputs some data and then press a button to a next step. The problem is when i am on the next page i have another button which takes you back to the previous step, the page renders all and well but the inputs are empty, i want to display previously entered data. My first page looks like this
<template>
<div class="py-4">
<h1 class="text-center text-4xl py-5">Welcome!</h1>
<div class="max-w-4xl flex m-auto">
<div class="shadow-lg bg-white rounded-lg py-10 px-20 flex flex-col justify-between leading-normal">
<div class="mb-3 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative alert" role="alert" v-if="errors.length">
<strong class="font-bold">Please correct the following error(s):</strong>
<p>
<ul>
<li v-for="error in errors">{{ error }}</li>
</ul>
</p>
<span class="absolute top-0 bottom-0 right-0 px-4 py-3">
<svg class="fill-current h-6 w-6 text-red-500" role="button" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" #click="dismiss">
<title>Close</title>
<path
d="M14.348 14.849a1.2 1.2 0 0 1-1.697 0L10 11.819l-2.651 3.029a1.2 1.2 0 1 1-1.697-1.697l2.758-3.15-2.759-3.152a1.2 1.2 0 1 1 1.697-1.697L10 8.183l2.651-3.031a1.2 1.2 0 1 1 1.697 1.697l-2.758 3.152 2.758 3.15a1.2 1.2 0 0 1 0 1.698z" />
</svg>
</span>
</div>
<div class="mb-8">
<div class="text-gray-900 font-bold text-xl mb-2">Terms of Use</div>
<ul class="list-disc ml-10">
<li>Glider requires some personally-identifiable information (name, email, etc.) in order to complete this
assessment.</li>
<li>To understand more about our privacy policy, data capture, data processing, etc., you can read our
privacy policy and terms and service.</li>
</ul>
</div>
<div class="flex items-center">
<div class="mb-4 w-2/4">
<label class="text-gray-700 leading-none" for="location">
Current location<span class="text-red-600">*</span>
</label>
<select class="block appearance-none w-100 bg-white border border-gray-400 hover:border-gray-500 py-2 rounded shadow leading-tight mt-2" v-model="country" :value="country" name="location">
<option v-for="country in countries" v-bind:key="country.code" v-bind:value="country.name">
{{country.name}}</option>
</select>
</div>
<div class="mb-4 w-2/4">
<label class="block text-gray-700 text-sm font-bold mb-2" for="username">
City, State
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight" id="username" type="text" placeholder="City, State" v-model="state">
</div>
</div>
<div class="bg-blue-100 border border-blue-200 rounded text-blue-700 px-4 py-3" role="alert">
<input type="checkbox" name="terms" v-model="terms" />
<label for="terms" class="text-sm align-middle">I agree to the above terms..</label>
</div>
<a class="mt-4 m-auto duration-75 text-center w-1/4 bg-green-500 cursor-pointer hover:bg-green-700 text-white font-bold py-2 px-4 rounded-full" #click="submit">
Next Step
</a>
</div>
</div>
</div>
</template>
<script>
export default {
components: {},
data() {
return {
errors: [],
state: this.state,
country: this.country,
terms: this.terms,
countries: [{
name: 'Afghanistan',
code: 'AF'
},
{
name: 'Åland Islands',
code: 'AX'
},
...
]
}
},
methods: {
submit: function(e) {
this.errors = [];
if (!this.state) {
this.errors.push('State is required!');
return;
}
if (!this.country) {
this.errors.push('Country is required!');
}
if (!this.terms) {
this.errors.push('You need to accept the terms!');
}
if (this.errors.length == 0) {
this.$router.push("details")
}
},
dismiss: function(e) {
this.errors = [];
}
}
}
</script>
<style scoped>
ul>li {
margin-top: 10px;
}
</style>
How can i display the data when user returns back to / route?
Achieved the results by using localStorage.
<script>
export default {
components: {},
data() {
return {
errors: [],
state: localStorage.getItem('state'),
country: localStorage.getItem('country'),
terms: localStorage.getItem('terms'),
countries: [{
name: 'Afghanistan',
code: 'AF'
},
{
name: 'Åland Islands',
code: 'AX'
},
...
]
}
},
methods: {
submit: function(e) {
this.errors = [];
if (!this.state) {
this.errors.push('State is required!');
}
if (!this.country) {
this.errors.push('Country is required!');
}
if (!this.terms) {
this.errors.push('You need to accept the terms!');
}
if (this.errors.length == 0) {
console.log(this.country,this.state,this.terms);
let terms_of_use = {'country': this.country, 'state': this.state, 'terms': this.terms};
localStorage.setItem('country', this.country);
localStorage.setItem('state', this.state);
localStorage.setItem('terms', this.terms);
this.$router.push("details")
}
},
dismiss: function(e) {
this.errors = [];
}
}
}
</script>
Related
I am trying to build a website and have this carousel, that was made with Alpine JS and Tailwind CSS. I took the Alpine Javascript from a template and it works according to specifications. But I would want to make it mouse draggable as well.
Here is the carousel slider
<div class="mt-24">
<script>
window.carousel = function () {
return {
container: null,
prev: null,
next: null,
init() {
this.container = this.$refs.container
this.update();
this.container.addEventListener('scroll', this.update.bind(this), {passive: true});
},
update() {
const rect = this.container.getBoundingClientRect();
const visibleElements = Array.from(this.container.children).filter((child) => {
const childRect = child.getBoundingClientRect()
return childRect.left >= rect.left && childRect.right <= rect.right;
});
if (visibleElements.length > 0) {
this.prev = this.getPrevElement(visibleElements);
this.next = this.getNextElement(visibleElements);
}
},
getPrevElement(list) {
const sibling = list[0].previousElementSibling;
if (sibling instanceof HTMLElement) {
return sibling;
}
return null;
},
getNextElement(list) {
const sibling = list[list.length - 1].nextElementSibling;
if (sibling instanceof HTMLElement) {
return sibling;
}
return null;
},
scrollTo(element) {
const current = this.container;
if (!current || !element) return;
const nextScrollPosition =
element.offsetLeft +
element.getBoundingClientRect().width / 2 -
current.getBoundingClientRect().width / 2;
current.scroll({
left: nextScrollPosition,
behavior: 'smooth',
});
}
};
}
</script>
<style>
.scroll-snap-x {
scroll-snap-type: x mandatory;
}
.snap-center {
scroll-snap-align: center;
}
.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
</style>
<div class="flex mx-auto items-center">
<div x-data="carousel()" x-init="init()" class="relative overflow-hidden group">
<div x-ref="container"
class="ml-4 flex overflow-x-scroll scroll-snap-x space-x-4 no-scrollbar touch-pan-x cursor-pointer">
{% for origin in origins %}
<div class="group/item relative ml-4 flex-auto flex-grow-0 flex-shrink-0 w-4/5 sm:w-4/5 xl:w-2/5 rounded-lg bg-gray-100 items-center justify-center snap-center overflow-hidden shadow-md"><!-- items container -->
<div class="min-w-full h-full rounded-lg bg-gray-100 overflow-hidden shadow-md">
<div><img src="{{ origin.imgurl }}" alt="{{ origin.alt }}" class="object-cover h-96" /></div>
<div class="absolute bg-gray-800 bg-opacity-0 group-hover/item:bg-opacity-50 top-2/3 inset-x-0 text-center px-2 py-3">
<div class="text-2xl text-transparent group-hover/item:text-emerald-500 font-extrabold uppercase">{{ origin.name }}</div>
<div class="text-xl text-transparent group-hover/item:text-white font-bold">{{ origin.country }}</div>
</div>
</div>
</div>
{% endfor %}
</div>
<div #click="scrollTo(prev)" x-show="prev !== null"
class="block absolute top-1/2 left-0 bg-white text-3xl p-2 rounded-full transition-transform ease-in-out transform -translate-x-full -translate-y-1/2 group-hover:translate-x-0 cursor-pointer">
<div><ion-icon name="chevron-back-outline"></ion-icon></div>
</div>
<div #click="scrollTo(next)" x-show="next !== null"
class="block absolute top-1/2 right-0 bg-white p-2 text-3xl rounded-full transition-transform ease-in-out transform translate-x-full -translate-y-1/2 group-hover:translate-x-0 cursor-pointer">
<div><ion-icon name="chevron-forward-outline"></ion-icon></div>
</div>
</div>
</div>
</div>
</div>
I tried adding Alpine script from a different carousel that has this function, but it does not do anything. My understanding of Javascript is rudimentary.
i'm making a questions list and i'm trying to change the color of a question in the list with v-model and v-for for options but when change the color of one question it changes all question at once
<template>
<div class="container" id="app">
<h1>questions list</h1>
<div class="pol">
<input type="text" v-model="que" :class="inputCls" autofocus>
<span class="addBtn">
<button #click="inputCls='inputbox extend'"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full"
v-if="!showIcon">Add questions</button>
<button #click="addqueS"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full"
v-else>Add</button>
</span>
</div>
<div class="section">
<ul class="queS" v-if="queS.length > 0">
<transition-group name="list">
<li :class="bgColour" v-for="(item) in queS" :key="item">
<span>{{ item.task }}</span>
<span>
<button #click="deleteque()"
class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded-full">Delete</button>
<select class="bg-gray-500" v-model="bgColour">
<option v-for="myClass in classes" :value="myClass">{{ myClass }}
</option>
</select>
</span>
</li>
</transition-group>
</ul>
<h3 v-else>No question to display. Add one.</h3>
</div>
</div>
</template>
the delete methode is working perfectly i tried to implemnt the same methode but it didint work out im still new to vuejs
export default {
name: "App",
data() {
return {
que: '',
queS: [],
inputCls: 'inputbox',
bgColour: 'white',
classes: [ 'Blue', 'Red', 'Green'
],
};
},
watch: {
que(value) {
if(value.length > 2) {
this.showIcon = true;
}
else {
this.showIcon = false;
}
}
},
methods: {
addqueS() {
this.inputCls = 'inputbox';
this.queS.unshift(
{
task: this.que,
completed: false
}
);
this.que = '';
setTimeout(() => {
this.showIcon = false;
}, 1000);
},
deleteque(index) {
this.queS.splice(index, 1);
},
},
};
CSS
<script>
.Blue {
background: blue;
}
.Red {
background: red;
}
.Green {
background: green;
}
</script>
You can add bgColor to queS array item, and connect v-model for select to that:
new Vue({
el: "#demo",
data() {
return {
que: '',
queS: [],
showIcon: true,
inputCls: 'inputbox',
bgColour: 'white',
classes: [ 'Blue', 'Red', 'Green'],
};
},
watch: {
que(value) {
if (value.length > 2) {
this.showIcon = true;
}
else {
this.showIcon = false;
}
}
},
methods: {
addqueS() {
this.inputCls = 'inputbox';
this.queS.unshift({ task: this.que, completed: false });
this.que = '';
setTimeout(() => {
this.showIcon = false;
}, 1000);
},
deleteque(index) {
this.queS.splice(index, 1);
},
},
})
.Blue {
background: blue;
}
.Red {
background: red;
}
.Green {
background: green;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" integrity="sha512-wnea99uKIC3TJF7v4eKk4Y+lMz2Mklv18+r4na2Gn1abDRPPOeef95xTzdwGD9e6zXJBteMIhZ1+68QC5byJZw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div class="container" id="demo">
<h1>questions list</h1>
<div class="pol">
<input type="text" v-model="que" :class="inputCls" autofocus>
<span class="addBtn">
<button #click="inputCls='inputbox extend'"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full"
v-if="!showIcon">Add questions</button>
<button #click="addqueS"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full"
v-else>Add</button>
</span>
</div>
<div class="section">
<ul class="queS" v-if="queS.length > 0">
<transition-group name="list">
<!-- 👇 here connect to -->
<li :class="item.bgColour" v-for="(item, idx) in queS" :key="idx">
<span>{{ item.task }}</span>
<span>
<button #click="deleteque()"
class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded-full">Delete</button>
<select class="bg-gray-500" v-model="item.bgColour"> <!-- 👈 here change v-model -->
<option v-for="myClass in classes" :value="myClass">{{ myClass }}</option>
</select>
</span>
</li>
</transition-group>
</ul>
<h3 v-else>No question to display. Add one.</h3>
</div>
</div>
I have a product that I want to programatically set a Checkout Session URL and then redirect a user when they click on an tag.
const PlanComponent = () => {
const [tiers, setTiers] = useState([]);
async function CheckoutSessionUrl(tierId) {
var result = null
await CreateCheckoutSession(
"PRODUCT_ID",
tierId,
"UID"
).then(function(response) {
result = response
})
return result;
};
async function LoadProducts() {
var result = []
await GetProducts("PRODUCT_ID").then(function(response) {
for (var i = 0; i < response.length; i++) {
var tier_id = response[i]["tier_id"]
CheckoutSessionUrl(tier_id).then(function(tier_response) {
response[i]["url"] = tier_response;
})
}
console.log("printing tiers");
console.log(response);
return response;
}).then(function(response){
result = response;
setTiers(result)
return result;
});
return result;
}
useEffect(() => {
// Some initialization logic here
LoadProducts().then(function(response) {
console.log("setting tiers")
console.log(response)
//setTiers(response)
})
}, []);
return (
<div className="bg-white">
<div className="max-w-7xl mx-auto py-24 px-4 sm:px-6 lg:px-8">
<div className="sm:flex sm:flex-col sm:align-center">
<h1 className="text-5xl font-extrabold text-gray-900 sm:text-center">Pricing Plans</h1>
<p className="mt-5 text-xl text-gray-500 sm:text-center">
Start building for free, then add a site plan to go live. Account plans unlock additional features.
</p>
</div>
<div className="mt-12 space-y-4 sm:mt-16 sm:space-y-0 sm:grid sm:grid-cols-2 sm:gap-6 lg:max-w-4xl lg:mx-auto xl:max-w-none xl:mx-0 xl:grid-cols-4">
{tiers.map((tier) => (
<div key={tier.name} className="border border-gray-200 rounded-lg shadow-sm divide-y divide-gray-200">
<div className="p-6">
<h2 className="text-lg leading-6 font-medium text-gray-900">{tier.name}</h2>
<p className="mt-4 text-sm text-gray-500">{tier.description}</p>
<p className="mt-8">
<span className="text-4xl font-extrabold text-gray-900">${tier.price}</span>{' '}
<span className="text-base font-medium text-gray-500">/mo</span>
</p>
<a
href={tier.url}
className="mt-8 block w-full bg-gray-800 border border-gray-800 rounded-md py-2 text-sm font-semibold text-white text-center hover:bg-gray-900"
>
Buy {tier.name}
</a>
</div>
<div className="pt-6 pb-8 px-6">
<h3 className="text-xs font-medium text-gray-900 tracking-wide uppercase">What's included</h3>
<ul role="list" className="mt-6 space-y-4">
{tier.users.map((feature) => (
<li key={feature} className="flex space-x-3">
{/*<CheckIcon className="flex-shrink-0 h-5 w-5 text-green-500" aria-hidden="true" />*/}
<span className="text-sm text-gray-500">{feature}</span>
</li>
))}
</ul>
</div>
</div>
))}
</div>
</div>
</div>
)
}
so if you look at the href={tier.url} it's not being set correctly. I feel like I'm not doing this correctly. Would love some feedback on how to actually get this working properly. IT looks like the tier.url new field isn't being set correctly (doesn't exist in the initial request but all the other attributes work).
The other option I wanted to do was when a user clicked a Button, it would generate a URL and redirect a user to that new external url but the navigation kept breaking.
Oh I found out what was happening. I needed to replace
await GetProducts("PRODUCT_ID").then(function(response) {
with
await GetProducts("PRODUCT_ID").then(async function(response) {
What I want to accomplish is for the form to change as the user changes the form type from the radio.
Standard basically has 2 selects (one classic and a fancier one, made with ng-select) and custom has a simple classic text input.
I am trying to change the form's functionality dynamically as the form type changes using the radio.
Besides trying to use formBuilder.group, I also tried using .setValidators on the individual inputs, but the result is the same: when I change the radio and the custom_channel_name input is shown i get this console error "Error: Cannot find control with name: 'custom_channel_name'"
What am I doing wrong and how do I properly handle reactive forms in this fashion?
What I have so far looks like this: https://i.imgur.com/n24mKs7.png , https://i.imgur.com/FfCgXFX.png
[ component.html ]
<div>
<form [formGroup]="organizationChannelForm" (ngSubmit)="submitOrganizationChannelsForm()">
<div *ngIf="isChannelTypeStandard" class="grid gap-4 grid-cols-2">
<!-- <form-picker label="Countries" [values]="selectCountriesSources" labelField="name" valueField="warehouse_id"
formControlName="warehouse_id"></form-picker> -->
<div>
<label for="channel_id" class="text-gray-700 dark:text-gray-400 block w-full pb-1 text-sm">Channel</label>
<select [(ngModel)]="selectedChannel" id="channel_id" formControlName="channel_id"
class="block w-full dark:bg-gray-700 dark:text-gray-300 form-input">
<option *ngFor="let channel of selectChannelsSources" [value]="channel">{{ channel }}</option>
</select>
</div>
<div>
<label for="countries_ids" class="text-gray-700 dark:text-gray-400 block w-full pb-1 text-sm">Countries</label>
<ng-select [items]="selectCountriesSources" [(ngModel)]="selectedCountries"
[ngModelOptions]="{ standalone: true }" id="countries_ids" [multiple]="true" bindLabel="name"
name="countries_ids"></ng-select>
</div>
</div>
<div *ngIf="!isChannelTypeStandard">
<label for="custom_channel_name" class="text-gray-700 dark:text-gray-400 block w-full pb-1 text-sm">Custom Channel</label>
<input class="form-input block w-full dark:bg-gray-700 dark:text-gray-300"
type="text" id="custom_channel_name" formControlName="custom_channel_name">
</div>
<div class="flex mt-5">
<div class="flex ml-auto items-center">
<span class="dark:text-gray-400">Channel Type</span>
<div class="ml-6 flex">
<label class="flex items-center cursor-pointer">
<input [(ngModel)]="isChannelTypeStandard" [ngModelOptions]="{ standalone: true }" (change)="updateOrganizationChannelForm($event)"
type="radio" class="form-radio text-purple-600 h-4 w-4" name="channelType" [value]="true">
<span class="dark:text-gray-300 font-medium ml-2">Standard</span>
</label>
<label class="flex items-center cursor-pointer ml-4">
<input [(ngModel)]="isChannelTypeStandard" [ngModelOptions]="{ standalone: true }" (change)="updateOrganizationChannelForm($event)"
type="radio" class="form-radio text-purple-600 h-4 w-4" name="channelType" [value]="false">
<span class="dark:text-gray-300 font-medium ml-2">Custom</span>
</label>
</div>
</div>
<div class="ml-8 min-w-0 text-white flex flex-col items-end rounded-lg shadow-xs">
<button type="submit" aria-label="add" [disabled]="organizationChannelForm.errors || organizationChannelForm.pristine"
class="flex items-end justify-between px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-purple-600 border border-transparent rounded-lg active:bg-purple-600 hover:bg-purple-700 focus:outline-none focus:shadow-outline-purple disabled:bg-grey-600">
Assign Channel
<fa-icon class="ml-2" [icon]="icons.plus"></fa-icon>
</button>
</div>
</div>
</form>
</div>
[ component.ts ]
export class OrganizationChannelsComponent implements OnInit {
selectChannelsSources: Array<string> = ["eMag Marketplace", "Vtex", "Shopify", "Magento1", "Magento2", "WooCommerce", "Prestashop", "TeamShare", "Gomag", "Opencart", "MerchantPro", "Cscart", "Allegro", "Idosell", "ChannelAdvisor", "Shoprenter", "Transfer", "Defecte/Defects", "Manual Order"];
selectCountriesSources: Array<Country> = [];
icons = {
close: faTimes,
plus: faPlus
}
organizationChannelForm!: FormGroup;
selectedCountries: Array<Country> = [];
selectedChannel: Channel | undefined;
isChannelTypeStandard: boolean = true;
#Input() organizationId!: ID;
organizationChannels$: Observable<OrganizationChannel[]> = new BehaviorSubject<OrganizationChannel[]>([]);
channels$: Observable<Channel[]> = new BehaviorSubject<Channel[]>([]);
constructor(
private organizationChannelsService: OrganizationsChannelsService,
private organizationChannelsQuery: OrganizationChannelsQuery,
private countriesService: CountriesService,
private toasterService: ToasterService,
private formBuilder: FormBuilder,
) { }
ngOnInit(): void {
this.organizationChannelForm = this.formBuilder.group({
channel_id: ['', Validators.required],
});
this.organizationChannelsService.getOrganizationChannels(this.organizationId).subscribe();
this.organizationChannels$ = this.organizationChannelsQuery.selectOrganizationChannels(this.organizationId as number);
this.countriesService.get().subscribe(countries => this.selectCountriesSources = countries);
}
updateOrganizationChannelForm() {
if (this.isChannelTypeStandard) {
this.organizationChannelForm = this.formBuilder.group({
channel_id: ['', Validators.required],
});
}
else {
this.organizationChannelForm = this.formBuilder.group({
custom_channel_name: [Validators.required, Validators.minLength(8)]
});
}
}
}
Documentation to the rescue! here is the official link to creating dynamic forms:
https://angular.io/guide/reactive-forms#creating-dynamic-forms
basically you need formArray instead of formGroup for all the controls that are going to be conditionally visible on UI, read the docs and if it becomes difficult to understand then let me know I'll create a demo.
I have loaded a data from API and displayed here with VueJS. I have users information inside users[] array. I also have users with two types of plan: basic_plan and standard_plan. Currently it shows all users.
Now I want to apply filters equally to this example: https://codepen.io/marn/pen/jeyXKL?editors=0010
I also got an error filter not defined
Filters:
<input type="radio" v-model="selectedItems" value="All" /> All
<input type="radio" v-model="selectedItems" value="basic_plan" /> Basic
<ul
v-for="(user, index) in selectedUser.data"
:key="index"
class="watchers divide-y divide-gray-200"
>
<li class="py-4">
<div class="mx-4 flex space-x-3">
<span
class="inline-flex items-center justify-center h-8 w-8 rounded-full bg-gray-500"
>
</span>
<div class="flex-1 space-y-1">
<h3 class="text-sm font-medium">
{{ user.name }}
</h3>
<div class="flex items-center justify-between">
<p class="text-sm font-medium text-indigo-500">
{{ user.plan }}
</p>
</div>
</div>
</div>
</li>
</ul>
</div>
</template>
<script
export default {
data() {
return {
users: [],
selectedItems:"All"
};
},
created() {
this. users();
},
methods: {
users {
axios
.get('api/users')
.then(response => {
this.users = response.data;
}
},
computed: {
selectedUser: function() {
if(this.selectedItems ==="All"){
return this.users
}else{
return this.users.data.filter(function(item) {
console.log(item)
return item.plan === this.selectedItems;
});
}
}
}
};
</script>
when All is selected vue dev tool shows this
selectedUser:Object //OBJECT SHOWING
data:Array[10]
links:Object
meta:Object
but when basic radio is selected vue shows this
selectedUser:Array[1] //ARRAY SHOWING
0:Object
price:"10"
plan:"basic_planl"
If you want to filter out specific users you must apply the "filter" function to the users variable like this:
this.users.filter(...)
With this function you then can filter the users based on their plan like this:
this.users.filter((user) =>
user.plan === this.selectedItems;
});
For a modern approach I used an arrow function. And without using curly brackets the statement inside the function is returned by default, so that's why there is no "return" statement.
Try this way instead, as you are already using v-for in your HTML, you can conveniently filter out users without any more loops, if you are getting value as "basic_plan" in the "user.plan" key.
Also, I think that you should move your v-for to <li> tag instead of <ul> along with the validation on <ul> if there are no users in the array.
<template>
<div>
<input type="radio" v-model="selectedItems" value="All" /> All
<input type="radio" v-model="selectedItems" value="basic_plan" /> Basic
<ul v-if="selectedUser.data.length" class="watchers divide-y divide-gray-200">
<li v-for="(user, index) in selectedUser.data" :key="index" class="py-4">
<div v-if="filterUser(user)" class="mx-4 flex space-x-3">
<span class="inline-flex items-center justify-center h-8 w-8 rounded-full bg-gray-500"></span>
<div class="flex-1 space-y-1">
<h3 class="text-sm font-medium">
{{ user.name }}
</h3>
<div class="flex items-center justify-between">
<p class="text-sm font-medium text-indigo-500">
{{ user.plan }}
</p>
</div>
</div>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
users: [],
selectedItems:"All"
};
},
methods: {
filterUser(user){
if(this.selectedItems === 'All'){
return true;
}
if(this.selectedItems === 'basic_plan'){
return this.selectedItems === user.plan;
}
}
},
}
</script>