Vue.js send data to a child component and display them - javascript

Hi I'm beginning with vue.js and I don't understand why my infos are not sent to the child component. I tried several differents things and it's not working. I think I'm not that far but I can't figure it out :
App.vue
<template>
<div class="app">
<Header/>
<div class="row">
<div class="col-3">
<Services v-bind:services="services"></Services>
</div>
</div>
</div>
</template>
<script>
import Header from "./components/Header.vue";
import Services from "#/components/Service";
export default {
nam: 'App',
components: {
Services,
Header,
},
data: function () {
return {
services: [{
title: "Logo flatdesign",
description: "Je fais de super flatdesign",
image: "https://fiverr-res.cloudinary.com/t_gig_cards_web_x2,q_auto,f_auto/gigs/22527722/original/3b5876ffb817872561d9eba6788dced76cb78224.jpg",
price: 6.7,
rate: 4,
id:1
},{
title: "Logo rapide",
description: "Je fais vite un logo",
image: "https://fiverr-res.cloudinary.com/t_gig_cards_web_x2,q_auto,f_auto/gigs/142024147/original/2abc0f9433df4f98790707a591772e56bf8777a1.jpg",
price: 5.5,
rate: 3,
id:2
},{
title: "Logo flatdesign",
description: "Je fais de super flatdesign",
image: "https://fiverr-res.cloudinary.com/t_gig_cards_web_x2,q_auto,f_auto/gigs/22527722/original/3b5876ffb817872561d9eba6788dced76cb78224.jpg",
price: 6.7,
rate: 4,
id:3
}]
}
}
}
</script>
<style>
</style>
Service.vue
<template>
<div class="home">
<div class="row">
<div class="col-3" v-for="service in services" v-bind:key="service.id">
<div class="jumbotron">
<img :src="service.image" height="100%" width="100%">
<h4>
{{service.title}}
</h4>
<div class="row">
<div class="col rate">
{{service.rate}} ★
</div>
<div class="col price">
{{service.price}} €
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
</script>
<style scoped>
.rate{
color: yellow;
}
.price{
font-weight: bold;
}
</style>
Thank you so much for your help :) !

Add this to your child component's script to define the passed prop before using it:
export default {
name: "Service",
props: ['services']
};

Related

How to toggle between components in vue.js?

I developed one page called Dashboard.vue and this page contains three child components(Display,sortBooksLowtoHigh,sortBooksHightoLow). Dashboard component contains one select options also inside that it have two options "Price:High to Low and Price:Low to High ",
if i click on price:LowToHigh option then it hides the Display component and displays the sortBooksLowtoHigh component utpo this it's working fine,
Now i import one more component called sortBooksHightoLow when i click on "price:High to Low" option it should hides the DisplayComponent and displays the sortBooksHightoLow component.How to acheive this thing please help me
Dashboard.vue
<template>
<div class="main">
<div class="navbar navbar-default navbar-fixed-top">
<div class="navbar-header">
<img src="../assets/education.png" alt="notFound" class="education-image" />
</div>
<ul class="nav navbar-nav">
<li>
<p class="brand">Bookstore</p>
</li>
</ul>
<div class="input-group">
<i #click="handlesubmit();" class="fas fa-search"></i>
<div class="form-outline">
<input type="search" v-model="name" class="form-control" placeholder='search...' />
</div>
</div>
<ul class="nav navbar-nav navbar-right" id="right-bar">
<li><a> <i class="far fa-user"></i></a></li>
<p class="profile-content">profile</p>
<li><a><i class="fas fa-cart-plus"></i></a></li>
<p class="cart-content">cart</p>
</ul>
</div>
<div class="mid-body">
<h6>Books<span class="items">(128items)</span></h6>
<select class="options" #change="applyOption">
<option disabled value="">Sort by relevance</option>
<option value="HighToLow">price:High to Low</option>
<option value="LowToHigh">price:Low to High</option>
</select>
</div>
<DisplayBooks v-show="flag==='noOrder'" />
<sortBooksLowtoHigh v-show="flag==='lowToHigh'" />
<sortBooksHightoLow v-show="flag==='highToLow'" />
</div>
</template>
<script>
import service from '../service/User'
import sortBooksLowtoHigh from './sortBooksLowtoHigh.vue'
import sortBooksHightoLow from './sortBooksHightoLow.vue'
import DisplayBooks from './DisplayBooks.vue'
export default {
components: {
DisplayBooks,
sortBooksLowtoHigh,
sortBooksHightoLow
},
data() {
return {
flag: 'noOrder',
brand: 'Bookstore',
name: '',
visible:true,
books: [{
}]
}
},
methods: {
flip() {
this.flag = !this.flag;
},
applyOption(evt) {
if (evt.target.value === "HighToLow") this.flag = 'highToLow';
else this.flag = 'lowToHigh';
},
}
}
</script>
<style lang="scss" scoped>
#import "#/styles/Dashboard.scss";
</style>
sortBooksHightoLow.vue
<template>
<div class="carddisplay-section">
<div v-for="book in books" :key="book.id" class="card book">
<div class="image-section">
<div class="image-container">
<img v-bind:src="book.file" />
</div>
</div>
<div class="title-section">
{{book.name}}
</div>
<div class="author-section">
by {{book.author}}
</div>
<div class="price-section">
Rs. {{book.price}}<label class="default">(2000)</label>
<button v-if="flag" class="btn-grp" type="submit" #click="handlesubmit();Togglebtn();">close</button>
</div>
<div class="buttons">
<div class="button-groups">
<button type="button" #click="toggle(book.id);flip(book.id);" v-if="state==true" class="AddBag">Add to Bag</button>
<button v-if="state==true" class="wishlist">wishlist</button>
</div>
<div v-if="state==false" class="AddedBag">
<button class="big-btn">Added to Bag</button>
</div>
</div>
</div>
</div>
</template>
<script>
import service from '../service/User'
export default {
data() {
return {
result: 0,
authorPrefix: 'by',
pricePrefix: 'Rs.',
defaultStrikePrice: '(2000)',
buttonValue: 'close',
flag: true,
state: true,
clickedCard: '',
books: [{
id: 0,
file: 'https://images-na.ssl-images-amazon.com/images/I/41MdP5Tn0wL._SX258_BO1,204,203,200_.jpg',
name: 'High to Low',
author: 'Saioii',
price: '1500'
}, ]
}
},
methods: {
toggle(id) {
this.clickedCard = id;
// this.card.content = this.notes.filter((note) => note.id === id);
console.log(this.clickedCard);
},
flip() {
this.state = !this.state;
},
Togglebtn() {
this.flag = !this.flag;
},
handlesubmit() {
service.userDisplayBooksHightoLow().then(response => {
this.books.push(...response.data);
})
},
}
}
</script>
<style lang="scss" scoped>
#import "#/styles/DisplayBooks.scss";
</style>
sortBooksLowtoHigh.vue
<template>
<div class="carddisplay-section">
<div v-for="book in books" :key="book.id" class="card book">
<div class="image-section">
<div class="image-container">
<img v-bind:src="book.file" />
</div>
</div>
<div class="title-section">
{{book.name}}
</div>
<div class="author-section">
by {{book.author}}
</div>
<div class="price-section">
Rs. {{book.price}}<label class="default">(2000)</label>
<button v-if="flag" class="btn-grp" type="submit" #click="handlesubmit();Togglebtn();">close</button>
</div>
<div class="buttons">
<div class="button-groups">
<button type="button" #click="toggle(book.id);flip(book.id);" v-if="state==true" class="AddBag">Add to Bag</button>
<button v-if="state==true" class="wishlist">wishlist</button>
</div>
<div v-if="state==false" class="AddedBag">
<button class="big-btn">Added to Bag</button>
</div>
</div>
</div>
</div>
</template>
<script>
import service from '../service/User'
export default {
data() {
return {
result: 0,
authorPrefix: 'by',
pricePrefix: 'Rs.',
defaultStrikePrice: '(2000)',
buttonValue: 'close',
flag: true,
state: true,
clickedCard: '',
books: [{
id: 0,
file: 'https://images-na.ssl-images-amazon.com/images/I/41MdP5Tn0wL._SX258_BO1,204,203,200_.jpg',
name: 'Default Card',
author: 'Sai',
price: '..'
}, ]
}
},
methods: {
toggle(id) {
this.clickedCard = id;
// this.card.content = this.notes.filter((note) => note.id === id);
console.log(this.clickedCard);
},
flip() {
this.state = !this.state;
},
Togglebtn() {
this.flag = !this.flag;
},
handlesubmit() {
service.userDisplayBooksLowtoHigh().then(response => {
this.books.push(...response.data);
console.log(this.response);
})
},
}
}
</script>
<style lang="scss" scoped>
#import "#/styles/DisplayBooks.scss";
</style>
emmmm...
HightoLow => HighToLow.
There can be several methods, in my opinion, to achieve conditional rendering of components which I think your question asks for. Two of them which are highly useful are:
Using v-if and v-else where you must define a flag that handles the logic for component rendering. Also, wrapping them in a transition tag would a good idea to make the switch with a transition.
<transition>
<component1 v-if="flag" />
<component2 v-else />
</transition>
Dynamic Components, we use the component tag and is attribute. The component can then be switched using the name of the component.
<component is="nameofComponent" />
You can read more about dynamic components in vuejs docs.
While the dynamic component looks neat, a switch with transition can be a nice addition.

How to pass on the array from parent component to chid component Vue 3.0?

I have a sample Vue 3.0 project where, I have few child components with app.vue as parent component. I want to pass an array from app.vue component to list-product component which accepts an array but unable to do so. Code is as follows.
Error: On mount of list-product component, I am logging the input provided array from app.vue which turned out be a string and type check failed .
Stackbliz Link
app.vue
<template>
<Navbar title="FirstCry" />
<div class="row mt-3">
<div class="col-md-4 p-3 border bordered shadow-sm">
<Addproduct />
</div>
<div class="col-md-8">
<ListProduct products={{products}} />
</div>
</div>
<ViewProduct />
</template>
<script>
import Navbar from "./components/Navbar.vue";
import Addproduct from "./components/Addproduct.vue";
import ListProduct from "./components/list-product.vue";
import ViewProduct from "./components/View-Product.vue";
export default {
name: "App",
components: {
Navbar,
Addproduct,
ListProduct,
ViewProduct,
},
data() {
return {
products: [
{
name: "Iphone",
price: "22000",
image: "https://i.imgur.com/J9yBaqj.jpg",
}
]
}
}
};
</script>
<style>
</style>
list-products.vue
<template>
<div class="row">
<div class="col-md-4 product" >
<div class="card">
<div style="overflow: hidden;" class="card-header p-0">
<img class="card-img-top img-fluid" src='https://i.imgur.com/J9yBaqj.jpg' alt="Card image cap" style="height: 300;">
</div>
<div class="card-body">
<center><h5 class="card-title">Iphone</h5></center>
<p class="card-text text-secondary">
<!-- {{products[0].name}} -->
</p>
<center><h4>
<small>
<s class="text-secondary">
22
</s>
</small>
<span class="price">22</span>
</h4>
<a class="btn btn-warning ng-star-inserted" style="margin: 20px;">Add to Cart</a>
<a class="btn btn-primary">Buy Now</a></center>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "ListProduct",
props: {
title: String,
products: Array
},
mounted() {
console.log(this.products);
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.product {
margin-top: 10px;
}
.img-fluid:hover {
transform: scale(1.2);
overflow: hidden;
}
small {
font-size: 80%;
font-weight: 400;
}
.text-secondary {
color: #6c757d !important;
}
s {
text-decoration: line-through;
}
</style>
You should bind it using v-bind: or the shorthand syntax : :
<ListProduct :products="products" />

Data does not show in view using AngularJS directives

I trying to use directives to show controller data in the my view but nothing displays and there are no errors in the console. I am able to log the data but the data will not show in view. Am I using directives the correct way? How do I show the data in the view? Thank you.
index.html
<body ng-app="GameboardApp">
<div class="header">
<h1 class="logo">GameBoard</h1>
</div>
<div class="main" ng-controller="ScoreController">
<div class="container">
<div class="row" >
<game ng-repeat="score in scores" info="score"></game>
</div>
</div>
</div>
<!-- Modules -->
<script src="js/app.js"></script>
<!-- Controllers -->
<script src="js/controllers/ScoreController.js"></script>
<!-- Directives -->
<script src="js/directives/game.js"></script>
App.js
var app = angular.module('GameboardApp', []);
Controllers
ScoreController:
app.controller('ScoreController', ['$scope', function($scope) {
$scope.scores = [
{
datetime: 1420848000000,
visitor_team: {
city: "Charlotte",
name: "Hornets"
},
home_team: {
city: "New York",
name: "Knicks"
},
period: "Final",
visitor_score: 110,
home_score: 82
},,
...
},
{
datetime: 1420848000000,
visitor_team: {
city: "Orlando",
name: "Magic"
},
home_team: {
city: "Portland",
name: "Trail Blazers"
},
period: "First Quarter",
visitor_score: 13,
home_score: 26
}
];
console.log($scope.scores);
}]);
directives
game.js
app.directive('game', function() {
return {
restrict: 'E',
score: {
info: '='
},
templateUrl: 'js/directives/game.html'
}
})
game.html
<div class="col-sm-4">
<div class="row scorecard">
<p class="period">{{info.period}}</p>
<div class="visitor col-xs-4">
<h2 class="visitor-score">{{info.visitor_score}}</h2>
<h3>
<span class="visitor-city">{{info.visitor_team.city}}</span><br/>
<span class="visitor-name">{{info.visitor_team.name}}</span>
</h3>
</div>
<div class="dash col-xs-3">
<h2>-</h2>
</div>
<div class="home col-xs-4">
<h2 class="home-score">{{info.home_score}}</h2>
<h3>
<span class="home-city">{{info.home_team.city}}</span><br/>
<span class="home-name">{{info.home_team.name}}</span>
</h3>
</div>
</div>
In game.js file, where you have created the directive, you have misspelled the field name. It should be scope not score.

- Cannot use v-for on stateful component root element because it renders multiple elements

Getting the error the error in the title, I am new to vue.js and I cannot figure it out
Cannot debug this for the life of me, can somebody help?
const app = new Vue({
el: '#location-box',
data: {
locations: [
{
name: "Europe",
desc: "Loren ipsum"
},
{
name: "America",
desc: "Loren ipsum"
}
],
},
})
My HTML:
<div id="location-section">
<div class="main-container">
<div class="location-grid-container">
<div id="info-box" class="outline">
</div>
<div>
<div id="location-box" v-for="location in locations" class="outline">
<h1>{{location.name}}</h1>
</div>
</div>
</div>
</div>
The root element that you pass to the el option of your new Vue app must be unique, and serve only as a placeholder.
Then you can use Vue directives on its children.
So in your case you could do:
<div id="location-section">
<div class="main-container">
<div class="location-grid-container">
<div id="info-box" class="outline">
</div>
<div id="location-box"><!-- Root element for your Vue app -->
<div v-for="location in locations" class="outline">
<h1>{{location.name}}</h1>
</div>
</div>
</div>
</div>
new Vue({
el: '#location-box',
// etc.
});

AngularJS: ng-repeat across multilple divs

I would like to have part of my website be a collage of images and information as shown below:
I can do this manually as a series of divs:
<div class="options">
<div class="option">
<img class="opt_img" src="images/hotels/BBH.jpg">
<h3 class="centered" id="hotel_info">Bailbrook House Hotel<br>BA1 7JD<br>Approx Price: £xx</h3>
</div>
<div class="option">
<img class="opt_img" src="images/hotels/bath_central_travelodge.jpg">
<h3 class="centered" id="hotel_info">Bath Central Travelodge<br>BA1 2EB<br>Approx Price: £xx </h3>
</div>
<div class="option">
<img class="opt_img" src="images/hotels/bath_waterside_travelodge.jpg">
<h3 class="centered" id="hotel_info">Bath Waterside Travelodge<br>BA2 4JP<br>Approx Price: £xx</h3>
</div>
</div>
<div class="options">
<div class="option">
<img class="opt_img" src="images/hotels/Bath_premier_inn.jpg">
<h3 class="centered" id="hotel_info">Bath City Centre Premier Inn<br>BA1 2BX<br>Approx Price: £xx</h3>
</div>
<div class="option">
<img class="opt_img" src="images/hotels/YHA.jpg">
<h3 class="centered" id="hotel_info">YHA Bath<br>BA2 6LA <br>Approx Price: £xx </h3>
</div>
<div class="option">
<img class="opt_img" src="images/hotels/bailbrook_lodge.jpg">
<h3 class="centered" id="hotel_info">Bailbrook Lodge<br>BA1 7HZ<br>Approx Price: £xx</h3>
</div>
and the following CSS:
*{
font-family: "Palatino Linotype", "Book Antiqua", Palatino, serif
}
#hotel_info{
background-color: rgba(130, 130, 130,0.7);
width: 80%;
}
.centered {
position: absolute;
width: 100%;
top: 40%;
left: 50%;
transform: translate(-50%, -50%);
}
.options{
width: 100%;
overflow: hidden;
}
.option {
position: relative;
text-align: center;
width: 33%;
float:left;
}
.opt_img{
width: 100%;
opacity: 0.7;
}
I have now started playing with AngularJS as a way to more easily display this information.
I have MainController.js
app.controller('MainController', function($scope){
$scope.title = 'Hotels';
$scope.hotels = [
{
name: 'Bailbrook House Hotel',
postcode: "BA1 7JD",
price: 0.00,
website: "https://www.handpickedhotels.co.uk/bailbrookhouse",
img: "images/hotels/BBH.jpg"
},
{
name: "Bath Central Travelodge",
postcode: "BA1 2EB",
price: 0.00,
website: "https://www.travelodge.co.uk/hotels/75/Bath-Central-hotel",
img: "images/hotels/bath_central_travelodge.jpg"
},
{
name: "Bath Waterside Travelodge",
postcode: "BA2 4JP",
price: 0.00,
website: "https://www.travelodge.co.uk/hotels/361/Bath-Waterside-hotel",
img: "images/hotels/bath_waterside_travelodge.jpg"
},
//... [and so on]
]
});
I have then used
<div ng-controller="MainController">
<div class = "options" ng-repeat="hotel in hotels">
<div class = "option">
<a ng-href={{hotel.website}}><img class="opt_img" ng-src={{hotel.img}}></a>
<h3 class="centered" id="hotel_info">{{hotel.name}}<br>{{hotel.postcode | uppercase}}<br>Approx Price: {{hotel.price | currency:'£'}}</h3>
</div>
</div>
</div>
This puts all the options one underneath each other. I can see why this is, as it doesn't create the options div around each set of 3 as I had done when making it manually. Is it possible to achieve this with the ng-repeat or do I need to rethink how I am doing it?
I am very new to web development so please make your answers beginner friendly, and bear with me if I have to ask for clarification. I am also open to people suggesting a better or simpler way of doing things, I merely used Codecademy's AngularJS course as a starting point.
This can easily be achieved by using css flex property
<div ng-controller="MainController" class="hotel-container">
<div class = "options" ng-repeat="hotel in hotels">
<div class = "option">
<a ng-href={{hotel.website}}><img class="opt_img" ng-src={{hotel.img}}></a>
<h3 class="centered" id="hotel_info">{{hotel.name}}<br>{{hotel.postcode | uppercase}}<br>Approx Price: {{hotel.price | currency:'£'}}</h3>
</div>
</div>
</div>
.hotel-container {
display: flex;
}
.options {
width : 33.33%;
}
Read more flex property here : https://css-tricks.com/almanac/properties/f/flex/
JS solution
A really simple solution is to group your list into sublists of 3 elements:
$scope.hotels = [
[{
name: 'Bailbrook House Hotel',
postcode: "BA1 7JD",
price: 0.00,
website: "https://www.handpickedhotels.co.uk/bailbrookhouse",
img: "images/hotels/BBH.jpg"
}, {
name: "Bath Central Travelodge",
postcode: "BA1 2EB",
price: 0.00,
website: "https://www.travelodge.co.uk/hotels/75/Bath-Central-hotel",
img: "images/hotels/bath_central_travelodge.jpg"
}, {
name: "Bath Waterside Travelodge",
postcode: "BA2 4JP",
price: 0.00,
website: "https://www.travelodge.co.uk/hotels/361/Bath-Waterside-hotel",
img: "images/hotels/bath_waterside_travelodge.jpg"
}],
//...
]
and then just add one more ng-repeat directive
<div ng-controller="MainController">
<div ng-repeat="group in hotels">
<div class = "options" ng-repeat="hotel in group">
<div class = "option">
<a ng-href={{hotel.website}}><img class="opt_img" ng-src={{hotel.img}}></a>
<h3 class="centered" id="hotel_info">{{hotel.name}}<br>{{hotel.postcode | uppercase}}<br>Approx Price: {{hotel.price | currency:'£'}}</h3>
</div>
</div>
</div>
</div>
CSS solution
Another simple solution with CSS is to use flex-direction: row and then set the flex property accordingly on the elements. Here's a code pen using the bootstrap library.

Categories