how do i render searched beer/beers? - vue.js

hello I want to know how ik can render searched beers in vue
I have a component that renders a list of beers. I also have an component that searches beers. what i want to do is that when I search, my beer list component updates the list and shows me only the list of beers i searched.
this is my beer list component
<template>
<div class="container">
<div>
<Search v-bind:allBeers="allBeers" />
</div>
<div>
<b-card
v-bind:key="beer.id"
v-for="beer in allBeers "
:img-src="beer.image_url"
:alt="beer.name"
img-top
tag="article"
style="max-width: 22rem ;"
class="mb-2"
>
<b-card-text class="c-text">
<h4 class="title">{{ beer.name }}</h4>
<p>{{ beer.ingredients.malt[0].name }}</p>
<router-link
:to="{
name: 'BeerDetails',
params: { id: beer.id, beer:beer },
}"
>
<b-button class="link" size="sm" variant="outline-warning">View Beer details</b-button>
</router-link>
</b-card-text>
</b-card>
</div>
</div>
</template>
<script>
import Search from "./Search";
import { mapGetters, mapActions } from "vuex";
export default {
components: { Search },
name: "Beers",
// props: ["beers"],
methods: { ...mapActions(["fetchBeers"]) },
computed: mapGetters(["allBeers"]),
created() {
this.fetchBeers();
}
};
</script>
and this is my search component here I filter beers that match my input
<template>
<div>
<b-form #submit="onSubmit" #reset="onReset">
<b-form-group id="input-group-1">
<b-form-input id="input-1" v-model="form.search" type="text" required placeholder="search"></b-form-input>
</b-form-group>
</b-form>
</div>
</template>>
<script>
export default {
name: "Search",
props: ["allBeers"],
data() {
return {
form: {
search: ""
}
};
},
methods: {
onSubmit(evt) {
evt.preventDefault();
this.allBeers.filter(beer => {
console.log(beer.name.toLowerCase() == this.form.search);
});
//alert(JSON.stringify(this.form));
},
onReset(evt) {
evt.preventDefault();
this.form.search = "";
}
}
};
</script>
<style scoped>
</style>
How would I use the search component in my beer component to display only the searched beers?

In the search component set a property to each beer, something like this:
onSubmit(evt) {
evt.preventDefault();
this.allBeers.forEach(beer => {
beer.hidden = (beer.name.toLowerCase() == this.form.search);
});
},
And in the beers component, hide the irrelevant beers:
<template>
<div class="container">
<div>
<Search v-bind:allBeers="allBeers" />
</div>
<div>
<div v-for="beer in allBeers" v-bind:key="beer.id">
<b-card v-if="!beer.hidden"
...
</b-card>
</div>
</div>
</div>
</template>

Related

It's possible to pass an object which has some methods to the child component in the Vue 3 JS

Parent component has the method "startMethods" which just also has some other method "onDecrementStart ". OnDecrementStart method just only call Alert.
Under this line, added a code example but that code didn't work.
<template>
<div class="parent">
<div class="main">
<img alt="Vue logo" src="../assets/logo.png">
<SettingsBoard :max="maxValue" :start="startValue"
:startInc="startMethods"
/>
</div>
</div>
</template>
<script>
import SettingsBoard from "#/components/SettingsBoard";
export default {
name: "Main",
components: {
SettingsBoard
},
methods: {
startMethods: {
onDecrementStart () {
alert('Decrement')
},
onIncrementStart () {
alert('Incriment')
}
},
}
}
</script>
SettingsBoard component
<template>
<div class="container">
<label>
<button :#click="startInc.onDecrementStart()">-</button>
</label>
</div>
</template>
<script>
export default {
name: "SettingsBoard",
props: {
startInc: Object
},
}
</script>
I want to get like that if it's possible.
<template>
<div class="container">
<label>
<button :#click="startInc.onDecrementStart()">-</button>
<button :#click="startInc.onIncrementtStart()">+</button>
</label>
</div>
</template>
<script>
export default {
name: "SettingsBoard",
props: {
startInc: Object
},
}
</script>
To run a method in the parent component you should emit a custom event which has the parent method as handler :
<label>
<button #click="$emit('decrement')">-</button>
<button #click="$emit('increment')">+</button>
</label>
in parent component :
<div>
<SettingsBoard
#decrement="onDecrementStart"
#increment="onIncrementStart"
/>
...
methods: {
onDecrementStart() {
this.count--;
},
onIncrementStart() {
this.count++;
},
},
LIVE EXAMPLE

VueJs - pass slot from child component to parent

I am having difficulty to make use of named slot from children in parent component.
So I am making tabs component with main Tabs and child Tab components.
My Tabs:
<template>
<div class="tabsMainContainer">
<div class="tabsContainer">
<ul class="tabsHeader">
<li
v-for="(tab, index) in tabs"
:key="tab.title"
:class="{tabActive: (index == selectedIndex)}"
#click="selectTab(index)"
>
<slot name="icon"/>
{{ tab.title }}
</li>
</ul>
</div>
<div class="tabsContent"><slot/></div>
</div>
</template>
I get tabs object with this.$children
and then my Tab component:
<template>
<div v-show="isActive" class="tab">
<slot name="icon"/>
<slot/>
</div>
</template>
And use of that:
<Tabs class="tabsComponent">
<Tab title="first">
<template v-slot:icon>Icon</template>
Content
</Tab>
<Tab title="second">Content second</Tab>
</Tabs >
while Content as such works fine, it appears in default slot, the icon slot also appears in default slot in main Tab component. How can I get icon slot from children, and display it in icon slot of main Tab component?
I also had a same kind of requirement. What I did was adding slot inside the main component's slot with the same name
In your Tab component
<template>
<div v-show="isActive" class="tab">
<template v-slot:icon>
<slot name="icon" />
</template>
</div>
</template>
First of all, I use google translate. Sorry, I may have spelled it wrong.
https://stackoverflow.com/a/64568036
I got an example from #ingrid-oberbüchler.
My solution
Layout.vue
<Tabs class="tabsComponent">
<Tab title="first">
<template v-slot:icon>Icon</template>
Content
</Tab>
<Tab title="second">Content second</Tab>
</Tabs>
Tabs.vue
<template>
<div class="title">
{{ tabsProvider.tabs[tabsProvider.selectedIndex].props.title }}
</div>
<div class="content">
<slot/>
</div>
<div class="icon">
<component :is="tabsProvider.tabs[tabsProvider.selectedIndex].children.icon"/>
</div>
</template>
<script>
import { defineComponent } from "vue";
export default defineComponent({
data() {
return {
tabsProvider: {
selectedIndex: 0,
tabs: [],
count: 0,
},
};
},
provide() {
return {
$tabsProvider: this.tabsProvider,
};
},
created() {
this.tabsProvider.tabs = this.$slots.default().filter((child) => child.type.name === "Tab");
},
}
</script>
Tab.vue
<template>
<div v-show="isActive">
<slot />
</div>
</template>
<script>
import { defineComponent } from "vue";
export default defineComponent({
name: "Tab",
props: {
title: {
type: String,
},
},
inject: ["$tabsProvider"],
data() {
return {
index: 0,
isActive: false,
};
},
beforeMount() {
this.index = this.$tabsProvider.count;
this.$tabsProvider.count++;
this.isActive = this.index === this.$tabsProvider.selectedIndex;
},
watch: {
"$tabsProvider.selectedIndex": {
handler(val, oldVal) {
this.isActive = this.index === this.$tabsProvider.selectedIndex;
},
deep: true,
},
},
});
</script>
Simplest example
let's admit x component is parent
<div class="x">x component</div>
let's admit y component is child
<div class="y">y component</div>
How to be process?
<Ycomponent>
<slot name="example1"/>
<slot name="example2"/>
<slot name="example3"/>
</Ycomponent>
<Xcomponent>
<Ycomponent>
<button slot="example1">button</button>
<span slot="example2">Span</span>
<img slot="example3" src="/"/>
</Ycomponent>
</Xcomponent>
I hope I could help.

How do you pass data in the router-view

Like for example
<router-link :to="{ name : 'criminalView', params : { criminalId : this.criminal.id , criminal : this.criminal } }" tag="a" >
<img class="h-18 w-18 rounded-full mr-4 mt-2" src="{{ asset('assets/images/'.$criminal->photo) }}" id="criminalsPhoto" alt="Criminals View" >
</router-link>
how can i accept those params in my CriminalView.vue which handles the router-view component
This is my routes.js
import VueRouter from 'vue-router';
import CriminalView from './components/CriminalView.vue';
import GroupView from './components/GroupView.vue';
let routes = [
{
path : '/criminal/:criminalId',
name : 'criminalView',
component : CriminalView,
props : { criminals }
},
{
path : '/group/:groupId',
name : 'groupView',
component : GroupView,
},
]
export default new VueRouter({
routes,
linkActiveClass: 'is-active'
});
how am I gonna display this in my template such as like this
<section class="w-2/5 ml-2 font-basic" id="criminalProfile">
<p class="font-basic tracking-normal text-2xl mb-4 mt-4 font-normal text-black mr-2">Criminal Profile of {{ this.criminal.full_name }}</p>
<div class="bg-white px-8 py-8 pt-4">
<div class="text-center">
<div id="avatar" class="inline-block mb-6" >
<img :src="avatarPath" class="h-50 w-50 rounded-full border-orange border-2">
<p class="font-bold mt-2 text-blue">$15,000</p>
<p class="mt-2 text-lg font-bold" >Notable Crimes:
<p class="mt-2 text-lg font-normal" >
<em class="font-bold roman">Offenses</em>Descr..
</p>
</p>
<div class="w-full flex justify-between">
<button class="w-full bg-green-theme p-3 text-white mt-4 ml-2 hover:bg-green-second" href="/criminal/" >View Full Profile</button> </div>
</div>
</div>
</div>
</section>
This is in my script tag..
export default {
props : ['criminals'],
name: 'CriminalProfile',
data(){
return {
criminal : this.criminals,
url : window.App.apiDomain
}
},
}
How can i display the props in my router-view which is there in my CriminalView.vue
I'm not 100% sure about camelCasing for groupId so I'm using groupid. Try it out, and let us know about camelCase in the comments.
props: {
//you could set the watch to this if you need.
//groupid: {default: 1}
},
watch: {
$route(to, from) {
var groupid = (typeof to.params.groupid != 'undefined') ? to.params.groupid : 1;
//... do some stuff
}
},
In order to display data from the route params you should be doing something like this
// start route.js
export default new Router({
mode: 'hash', // https://router.vuejs.org/api/#mode
routes: [
{
path: 'my-route/:criminal',
name: 'MyRoute',
component: MyComponent
}
]
})
// End route.js
<template>
<section>
<div>
{{criminals}}
</div>
</section>
</template>
<script>
export default {
name: 'CriminalProfile',
data(){
return {
criminals: ''
}
},
created(){
this.criminals = this.$route.query.myparam
}
}
</script>
Here it's the router link
<router-link :to="{ name: 'CriminalView', params: { criminalId: 123 }}">Criminal</router-link>
In my case this work perfectly,
Try this:
Using this.$route.params.paramName you can access params in router-view component.
<script>
export default{
data() {
criminal_id:'',
criminal_data: ''
},
created() {
this.criminal_id = this.$route.params.criminalId;
this.criminal_data = this.$route.params.criminal;
}
}
</script>

Conditional rendering template on Vue?

I have a simple situation that I want to render cart-badge based on window width value but it always show the last one even though windowWidth does change the value. Please advise me what to do !
<template v-if="windowWidth>=1024">
<div class="nav-user-account"> {{windowWidth}}
<div class="nav-cart nav-cart-box">
<span class="text hidden-sm">Cart</span>
<span class="cart-number" id="nav-cart-num">{{cartItemCount}}</span>
</div>
</div>
</template>
<template v-if="windowWidth<1024">
<a href="#" style="color:#ff6a00" class="icon-cart">
{{windowWidth}}
<i class="fa fa-shopping-cart fa-2x" aria-hidden="true"></i>
<span class="cart-num">2</span>
</a>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
data() {
return {
windowWidth: window.innerWidth,
}
},
computed: {
...mapGetters('cart', {
cartItemCount: 'getShoppingCartItemsCount'
})
},
mounted() {
this.$nextTick(() => {
window.addEventListener('resize', () => {
this.windowWidth= window.innerWidth
});
})
},
}
</script>
UPDATED: as Tony19 pointed out, i need a master template outside of these 2 template.
<template>
<div>
<template v-if="windowWidth>=1024">...</template>
<template v-else></template>
</div>
</template>

Access infromation of grandparent component from grandchild component in vue.js

I have a parent component in Vue called RecipeView, and it is an inline-component. inside it, i have these components:
comments. and inside comments, i have comment and NewCommentForm, has it shows in the picture below.
I am passing in the RecipeView component the id as a prop, and would like to access it in the NewCommentForm component in order to set an endpoint that i will post to and save the comment.
This is the RecipeView component:
<recipe-view :id="{{$recipe->id}}">
<comments :data="{{$recipe->comment}}"#added="commentsCount++"></comments>
</recipe-view>
and the script for it is this:
<script>
import Comments from '../components/Comments.vue';
export default {
props: ['initialCommentsCount','id'],
components: {Comments},
data(){
return {
commentsCount: this.initialCommentsCount,
recipe_id:this.id
};
}
}
</script>
The comments component looks like this:
<template>
<div>
<div v-for="comment in items">
<comment :data="comment"></comment>
</div>
<new-comment-form :endpoint="'/comments/**Here should go the id from parent RecipeView component**'" #created="add"></new-comment-form>
</div>
</template>
<script>
import Comment from './Comment.vue';
import NewCommentForm from './NewCommentForm.vue';
export default {
props: ['data'],
components: {Comment, NewCommentForm},
data() {
return {
items: this.data,
endpoint: ''
}
},
methods: {
add(comment) {
this.items.push(comment);
this.$emit('added');
}
}
}
</script>
and this is the NewCommentForm component:
<template>
<div>
<div class="field">
<p class="control">
<input class="input"
type = "text"
name="name"
placeholder="What is your name?"
required
v-model="name">
</p>
</div>
<div class="field">
<p class="control">
<textarea class="textarea"
name="body"
placeholder="Have your say here..."
required
v-model="body">
</textarea>
</p>
</div>
<button type="submit"
#click="addComment"
class="button is-medium is-success">send</button>
</div>
</template>
<script>
export default {
props:['endpoint'],
data(){
return {
body:'',
name:'',
}
},
methods:{
addComment(){
axios.post(this.endpoint, {
body:this.body,
name: this.name
}).then(({data}) => {
this.body = '';
this.name = '';
this.$emit('created', data);
});
}
}
}
</script>
Thanks for the help.