Vue bind value to object by key when button click - javascript

I am new to Vue, i want to create some buttons from my data and display their information when clicking the button. The object 'pets' has two keys: id and info. (My data is larger and my code more complicated, i was trying to simplify it here.)
data() {
return {
selectedpet: undefined,
message: undefined,
pets : [
{
id: 1,
info: "yellow"
},
{
id: 2,
info: "brown"
},
{
id: 3,
info:"huge"
}
]
}
}
And I created some buttons by the data with the 'id' displayed on the buttons, and the variable 'selectedpet' will record the clicked button:
<div v-for="pet in pets :key="pet.id">
<button #click="selectedpet = pet">
<i>{{pet.id}}</i>
</button>
What i need to do is to create a div, which text will display the 'info' of the clicked button. How can i set the message to the 'info' of the click button?
<div id="printselectedpet">Selected pet: {{ message }}</div>

No need for message data
<div id="printselectedpet">Selected pet: {{ selectedpet.info }}</div>

I think you just need to set pet.info to message.
See below code:
var vm = new Vue({
el : "#vueRoot",
data: {
selectedpet: undefined,
message: undefined,
pets : [
{
id: 1,
info: "yellow"
},
{
id: 2,
info: "brown"
},
{
id: 3,
info:"huge"
}
]
}
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.js"></script>
<div id="vueRoot">
<div v-for="pet in pets" :key="pet.id">
<button #click="message = pet.info">
<i>{{pet.id}}</i>
</button>
</div>
<div id="printselectedpet" v-if="message">Selected pet: {{ message }}</div>
</div>

Below is my solution
new Vue({
el:"#app",
data:{
message:"I am initial message",
buttonz:[{id:1,info:"this is first button"},{id:2,info:"this is secoind button"},{id:3,info:"this is my third button"}]
},
methods:{
getMyKey(myKey){
if(myKey != undefined){
this.message = this.buttonz[myKey].info;
}else{
this.message = "Cannot Get the Key";
}
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.js"></script>
<div id="app">
<div>
{{message}}
</div>
<button v-for="(but,index) in buttonz" :key="but.id" #click="getMyKey(index)">
My ID: {{but.id}}
</button>
</div>

Related

I need to save single objects in LocalStorage, but i have the whole array saved

I don't get how i am supposed to save only a single object a not the whole array. I am trying to create a movie watchlist. If I click "add to watchlist", the single object should be saved in LocalStorage. If I hit remove from watchlist, the object should get removed. I tried to write down methods to regulate all of that, but i guess somethings wrong. The data comes from an API request. Here's the code:
<template>
<div>
<div class="card" v-for="movie in movies"
:key="movie.id">
{{movie.title}}
{{movie.release_date}}
<button type="submit" #click="storeMovie" >
Aggiungi
</button>
<button type="submit" #click="removeMovie">
Rimuovi
</button>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
//Cambiare il nome con quello del componente creato
name: 'HomeComp',
data () {
return {
movies: [],
movie: "",
}
},
mounted () {
axios
.get('https://api.themoviedb.org/3/movie/popular?api_key=###&language=it-IT&page=1&include_adult=false&region=IT')
.then(response => {
this.movies = response.data.results
// console.log(response.data.results)
})
.catch(error => {
console.log(error)
this.errored = true
})
.finally(() => this.loading = false)
if (localStorage.movies) {
this.movies = JSON.parse(localStorage.movies);
}
},
watch: {
movies: {
handler(newMovies) {
localStorage.movies = JSON.stringify(newMovies);
},
deep:true
}
},
methods: {
getMovie() {
this.movies = JSON.parse(localStorage.getItem("movie"));
},
storeMovie() {
if (this.movie.length) {
// push the new movie to list
this.movies.push(this.movie);
// store the data in localStorage
localStorage.setItem("movies", JSON.stringify(this.movies));
// clear the input
this.movie = "";
}
},
removeMovie() {
localStorage.removeItem('movie');
}
},
}
</script>
<style scoped lang="scss">
/*Inserire style componente*/
</style>
tried to parse ad stringify, but i think i'm doing it wrong in some way. Also written some methods, not working
Few observations as per the code you posted :
As you want to store the new movie through input, Aggiungi button should come outside of v-for loop.
For removeStore event, You need to pass the store id from a template so that we can filter out the movies array.
Live Demo :
new Vue({
el: '#app',
data: {
movies: [],
movie: ''
},
mounted() {
// This data will come from API, Just for a demo purpose I am using mock data.
this.movies = [{
id: 1,
title: 'Movie A',
release_date: '06/12/2022'
}, {
id: 2,
title: 'Movie B',
release_date: '07/12/2022'
}, {
id: 3,
title: 'Movie C',
release_date: '08/12/2022'
}, {
id: 4,
title: 'Movie D',
release_date: '09/12/2022'
}, {
id: 5,
title: 'Movie E',
release_date: '10/12/2022'
}]
},
methods: {
storeMovie() {
const newMovieID = this.movies.at(-1).id + 1;
this.movies.push({
id: newMovieID,
title: this.movie,
release_date: '06/12/2022'
})
},
removeMovie(movieID) {
this.movies = this.movies.filter(({ id }) => id !== movieID)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div>
Add new movie : <input type="text" v-model="movie"/>
<button type="submit" #click="storeMovie()">
Aggiungi
</button>
</div><br>
<div class="card" v-for="movie in movies"
:key="movie.id">
{{movie.title}}
{{movie.release_date}}
<button type="submit" #click="removeMovie(movie.id)">
Rimuovi
</button>
</div>
</div>

How to Store Data Property Value from a Specific Item in Rendered List in Vue

I'm trying create a follow button on list items in Vue. My strategy is to grab the value of a particular list item property and store it in the data object. Then use this value in a method to add it to an array in my database.
<div v-for="result in results" :key="result.symbol">
{{ result.name }}
<button #click="followStock">+follow</button>
</div>
I'm not sure how to get the value of result.symbol "into" the button element to set the value symbol in the data object below.
<script>
export default {
data() {
return {
results: [ // this is populated by an api call
{
currency: "USD"
exchangeShortName: "NYSE"
name: "International Game Technology PLC"
stockExchange: "NYSE"
symbol: "IGT"
},
{...},
...
],
symbol: "",
};
},
followStock() {
// add this.symbol to database array
},
},
};
</script>
I'm guessing there might be an easier strategy I'm overlooking as I'm still new to Vue, so any other solution that essentially allows me to fire off the value of result.symbol from any rendered result to my database would be awesome.
You can just pass the result as a parameter to your method.
<div v-for="result in results" :key="result.symbol">
{{ result.name }}
<button #click="followStock(result)">+follow</button>
</div>
And in your method:
methods: {
followStock(result) {
// do something with result
console.log({result});
let symbol = result.symbol;
},
}
P.S I didn't see you put your followStock() inside a methods object, but I did so in the example. https://v2.vuejs.org/v2/api/#methods
Write directly as a function call.
The vue compiler will turn followStock(result.symbol) into function(event) {followStock(result.symbol)}.
new Vue({
el: '#app',
data() {
return {
results: [
{
name: "International Game Technology PLC",
symbol: "IGT"
},
{
name: "A name",
symbol: "A symbol"
}
]
};
},
methods: {
followStock(symbol) {
console.log(symbol)
},
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="result in results" :key="result.symbol">
{{ result.name }}
<button #click="followStock(result.symbol)">+follow</button>
</div>
</div>
As Nazaire mentioned you can access the results anywhere inside the child elements when using v-for.
(it works like a normal for-loop)
It's not only limited to the corresponding element (the element in which you do v-for)
<div v-for="result in results" :key="result.symbol">
{{ result.name }}
<button #click="followStock(result.symbol)">+follow</button>
</div>
followStock(symbol){
// you can now add symbol to db
}

How can I update the button clicked in bootstrap-vue

I am using Bootstrap-Vue to display a table, and I've added in an extra column with an update button using vue-slot. I have this displaying fine, and I have a method being called when you click the button. Within that method I can access all the information on the item however I can't seem to find a way to access the button. I want to disable it and change the contents of it. How can I access the button element? I have created a codepen example here that shows what I have set up and need to do.
HTML
<div id='app'>
<div>{{ this.output }}</div>
<b-table hover head-variant="dark"
id="pages-table"
:items="items"
:fields="fields">
<template slot="actions" slot-scope="data">
<button class="btn btn-dark" #click="update(data)">Update</button>
</template>
</b-table>
</div>
JavaScript
new Vue({
el: "#app",
data: {
output: null,
items: [
{
id: 1,
name: "Tony"
},
{
id: 2,
name: "John"
},
{
id: 3,
name: "Paul"
}
],
fields: [
{
key: "id",
label: "ID",
sortable: true
},
{ key: "name" },
{ key: "actions" }
]
},
methods: {
update(data) {
// I need to disable the button here
this.output = data;
data.item.name = "Dave";
}
}
});
You could add a dynamic ref to the button
<button class="btn btn-dark" #click="update(data)" :ref="'btn' + data.index">Update</button>
And then just access the button by that ref
this.$refs["btn" + data.index].disabled = true
This is a codepen with the example
https://codepen.io/vlaem/pen/gNjGQE
Instead of the index you could also use the id property of your data to create the ref (data.item.id)
Though personally this doens't feel right, I think it would be better if we could track the status of all the buttons on the same or a different array, maybe like in the following example
https://codepen.io/vlaem/pen/GbBMLe

How to output html from filter inside mustache

I have a input (top right) where users can search things, when it's directive length get 3 characters it will display a list of products and highlight the matches...
Look at my code:
html
<div id="app">
<div id="header">
<div class="right"><input type="text" v-model="message" v-on:keyup="searchStart()" v-on:blur="searchLeave()"/>
<ul v-if="this.searchInput" class="product-list">
<li v-for="product in products">
{{ product.id }} - {{ product.name | highlight }} - {{ product.qtd }}</li></ul>
</div>
</div>
<div id="main">
<div id="menu">fdfds</div>
<div id="container">{{ message }}</div>
</div>
</div>
js
var search = new Vue({
el: "#app",
data: {
message: "",
searchInput: false,
products: [
{
id: 1,
name: "produto 01",
qtd: 20
},
{
id: 2,
name: "produto 02",
qtd: 40
},
{
id: 3,
name: "produto 03",
qtd: 30
},
]
},
methods: {
searchStart: function(){
if(this.message.length >= 3)
this.searchInput = true;
console.log(this.searchInput);
},
searchLeave: function(){
this.searchInput = false;
this.message = "";
console.log(this.searchInput);
}
},
filters: {
highlight: function(value){
return value.replace(search.message, '<span class=\'highlight\'>' + search.message + '</span>');
}
}
});
Here you can see a live pen: http://codepen.io/caiokawasaki/pen/dXaPyj
try to type prod inside the pen...
Is my filter correct? The way I created the filter is correct?
The main question is: How to output the HTML from my filter?
Edit/Solution
The problem in the case was codepen, there is some kind of conflict with vue, so I was not able to escape the html using {{{}}}, put the code in another editor (jsfidle) and it worked.
I'm accepting the answer given to the reward because it's right.
You'll need 3 steps here for achieve what you want:
Use triple braces {{{ }}} to display unescaped html
Filter your users by your v-model variable, in order to just show the matches
Replace the substring matching by the <span> tag
Check out the computed property filteredUsers and the filter in this working jsfiddle

Angular - can I use a single function on multiple objects within a nested ng-repeat that triggers the ng-show of just that object?

SUMMARYI have a list of brands and a list of products. I am using an ng-repeat to show the list of brands, and an ng-repeat with a filter to show the list of products within their respective brands. I want each brand and each product to have a button that shows more about that brand/product. All of these buttons should use the same function on the controller.
PROBLEMThe button that shows more about the brand also shows more about each of that brand's products, UNLESS (this is the weird part to me) I click the button of a product within that brand first, in which case it will work correctly.
CODEPlease see the Plunker here, and note that when you click on 'show type' on a brand, it also shows all the types of the products within that brand: http://plnkr.co/edit/gFnq3O3f0YYmBAB6dcwe?p=preview
HTML
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
</head>
<body>
<div ng-app="myApp">
<div ng-controller="MyController as vm">
<div ng-repeat="brand in brands">
<h1>
{{brand.name}}
</h1>
<button ng-click="showType(brand)">
Show Brand Type
</button>
<div ng-show="show">
{{brand.type}}
</div>
<div ng-repeat="product in products
| filter:filterProducts(brand.name)">
<h2>
{{product.name}}
</h2>
<button ng-click="showType(product)">
Show Product Type
</button>
<div ng-show="show">
{{product.type}}
</div>
</div>
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
<script src="script.js"></script>
</body>
</html>
JAVASCRIPT
var app = angular.module('myApp', []);
app.controller('MyController', function($scope) {
$scope.brands = [{
name: 'Kewl',
type: 'Cereal'
}, {
name: 'Joku',
type: 'Toy'
}, {
name: 'Loko',
type: 'Couch'
}]
$scope.products = [{
name: 'Kewlio',
type: 'Sugar Cereal',
brand: 'Kewl'
}, {
name: 'Kewliano',
type: 'Healthy Cereal',
brand: 'Kewl'
}, {
name: 'Jokurino',
type: 'Rattle',
brand: 'Joku'
}, {
name: 'Lokonoko',
type: 'Recliner',
brand: 'Loko'
}, {
name: 'Lokoboko',
type: 'Love Seat',
brand: 'Loko'
}]
$scope.showType = function(item) {
this.show = !this.show;
}
$scope.filterProducts = function(brand) {
return function(value) {
if(brand) {
return value.brand === brand;
} else {
return true;
}
}
}
});
IMPORTANT NOTE: I realize I could add an attribute to the object (brand.show) and pass the object into the function, then change that attribute to true/false, but I don't want to do this because in my actual application, the button will show a form that edits the brand/product and submits the info to Firebase, and I don't want the object to have a 'show' attribute on it. I would rather not have to delete the 'show' attribute every time I want to edit the info in Firebase.
ng-repeat directive create own scope, when you do
this.show = !this.show
you create/change show property in current scope, if click brand button - for brand scope, that global for product, and when click in product button - for scope concrete product.
To avoid this, you should create this property before clicking button, for example with ng-init, like
ng-init="show=false;"
on element with `ng-repeat" directive
Sample
var app = angular.module('myApp', []);
app.controller('MyController', function($scope) {
$scope.brands = [{
name: 'Kewl',
type: 'Cereal'
}, {
name: 'Joku',
type: 'Toy'
}, {
name: 'Loko',
type: 'Couch'
}]
$scope.products = [{
name: 'Kewlio',
type: 'Sugar Cereal',
brand: 'Kewl'
}, {
name: 'Kewliano',
type: 'Healthy Cereal',
brand: 'Kewl'
}, {
name: 'Jokurino',
type: 'Rattle',
brand: 'Joku'
}, {
name: 'Lokonoko',
type: 'Recliner',
brand: 'Loko'
}, {
name: 'Lokoboko',
type: 'Love Seat',
brand: 'Loko'
}]
$scope.showType = function(item) {
this.show = !this.show;
}
$scope.filterProducts = function(brand) {
return function(value) {
if (brand) {
return value.brand === brand;
} else {
return true;
}
}
}
});
/* Styles go here */
h1 {
font-family: impact;
}
h2 {
font-family: arial;
color: blue;
margin-bottom: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.js"></script>
<div ng-app="myApp">
<div ng-controller="MyController as vm">
<div ng-repeat="brand in brands" ng-init="show=false">
<h1>
{{brand.name}}
</h1>
<button ng-click="showType(brand)">
Show Brand Type
</button>
<div ng-show="show">
{{brand.type}}
</div>
<div ng-repeat="product in products
| filter:filterProducts(brand.name)" ng-init="show=false">
<h2>
{{product.name}}
</h2>
<button ng-click="showType(product)">
Show Product Type
</button>
<div ng-show="show">
{{product.type}}
</div>
</div>
</div>
</div>
</div>
The easiest fix for this, if you don't mind putting temporary properties in your data is the following changes:
<div ng-show="product.show">
{{product.type}}
</div>
and
<div ng-show="brand.show">
{{brand.type}}
</div>
and then in your controller
$scope.showType = function(item) {
item.show = !item.show;
}
Alternatively, if you don't want to touch the object properties, you can create an $scope.shownTypes array and have your click either push the object into or remove the object from the shown array. THen you can check for the object's existence in the array and show or not show the type appropriately. Let me know if you need a sample of that.
Your show boolean attribute same for whole tree (is in same scope). Using angular directive with child scope scope:true in ng-repeat helps to isolate each show property. I have forked your plunker code:
http://plnkr.co/edit/cMSvyfeCQOnTKG8F4l55?p=preview

Categories

Resources