I made a fiddle where I tried to change one value of an array via code while it's used as v-model of a v-text-field from Vuetify.
The problem is, when I write something in the text field it does change the value correctly immediately, so I made a button that sets the value of the array in that specific index, it doesn't seem to be working, so I made another button to print it and it does work.
The first button changes the value to 0 as I'd like but in the v-text-field it still has the last typed value.
How to change the value of the v-text-field when the "reset" button is pressed?
new Vue({
el: '#app',
data() {
return {
model: [1, 2]
}
},
methods: {
restart() {
this.model[0] = 0
},
showInConsole() {
console.log("Current value:", this.model[0])
}
}
})
<!DOCTYPE html>
<html>
<head>
<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet">
<link href="https://unpkg.com/vuetify/dist/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
</head>
<body>
<div id="app">
<v-layout row wrap>
<v-flex xs4>
<div class="headline">Change value of model[0]</div>
<div class="headline text-xs-center">{{model[0]}}</div>
</v-flex>
<v-flex xs8>
<v-text-field solo v-model="model[0]"></v-text-field>
</v-flex>
<v-flex xs12>
<v-btn #click="restart ()">Reset to 0</v-btn>
<v-btn #click="showInConsole ()">Show in console current value</v-btn>
</v-flex>
</v-layout>
</div>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify/dist/vuetify.js"></script>
</body>
</html>
Link to my fiddle
It's Vue reactivity problem.
Basically, Vue cannot detect the changes to an array when you directly set an item with the index
You should use Vue.$set
restart () {
this.$set(this.model, 0, 0)
}
Related
I would like to ask if someone see the problem in my code.
Everything render ok, no problem with the render, the issue is that every time I click in one panel content all the panel-content of every group with the same index expand or collapse and not only the one that I clicked.
Any idea?? Thanks a lot.
<div v-for="(group, i) in groups" :key="i" class="pb-4">
<p class="text-h5 primary text-center white--text">{{ group.title }}</p>
<div v-if="group.links">
<a v-for="(link, u) in group.links" :key="u" :href="link.src" class="black--text"> {{ link.title }}</a>
</div>
<v-expansion-panels v-model="activesPanels" multiple accordion dense flat>
<v-expansion-panel v-for="(item, v) in group.fact" v-show="shouldRender(v)" :key="v">
<v-expansion-panel-header class="px-0">{{ item.title }}</v-expansion-panel-header>
<v-expansion-panel-content>
<type :fact="item" :models="models" />
</v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panels>
</div>
The reason for this issue is that you are using the same v-model variable activesPanels for every group. The activesPanels should belong to each group to maintain their opening and closing state individually.
Also, use the unique properties for template keys to avoid errors i.e, "Duplicate keys detected".
Here is the working demo-
<!DOCTYPE html>
<html>
<head>
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#6.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
</head>
<body>
<div id="app">
<v-app id="inspire">
<div v-for="(group, i) in groups" :key="i">
<p class="primary text-center white--text">{{ group.title }}</p>
<div v-if="group.links">
<a v-for="link in group.links" :key="link.src" :href="link.src" class="black--text"> {{ link.title }}</a>
</div>
<v-expansion-panels v-model="group.activesPanels" multiple accordion dense flat>
<v-expansion-panel v-for="item in group.fact" :key="item.title">
<v-expansion-panel-header class="pa-0">{{ item.title }}</v-expansion-panel-header>
<v-expansion-panel-content class="pa-0">
Hello
</v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panels>
</div>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<script>
new Vue({
el: '#app',
data () {
return {
groups: [
{
title: "group1",
activesPanels: [],
fact: [
{
title: 'fact1'
}
],
links:[
{
src: "https://google.com",
title: "Hello"
},
{
src: "https://twitter.com",
title: "Hello"
}
]
},
{
title: "group2",
activesPanels: [],
fact: [
{
title: 'fact2'
}
],
links:[
{
src: "https://www.shorturl.at/",
title: "Hello"
},
{
src: "https://www.npmjs.com/package/vuex-map-fields",
title: "Hello"
}
]
}
]
}
},
vuetify: new Vuetify(),
})
</script>
</body>
</html>
You should avoid using indexes as keys in your v-for loop. The keys need to be unique for every element. By using index you have probably 2 elements with index 0 as a key. Vue cannot keep track of changes in the DOM that way. It is getting even worse if you edit the array and items in the array get another index.
So, you can use a unique value for the key, maybe an id or link.url and item.title in your code.
See docs:
https://vuejs.org/guide/essentials/list.html#maintaining-state-with-key
I've got task with I'm struggling a day, to do but I'm trying to figure out why vuetify components aren't visible. How did they write in task I need to resend this file so I'm thinking all code should be put in ui.html
My task looks like that:
ui.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="robots" content="noindex">
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.24/browser.min.js"></script>
</head>
<body>
<h1>Task</h1>
<p>Please download this file and prepare nice UI using provided data.<br>
For task you can use Vuetify with minimal (if any) additional style.</p>
<p>In response please send this file with your UI proposal.</p>
<div id="app"></div>
<script>
new Vue({
el: "#app",
vuetify: new Vuetify(),
data: {
workerList: [
{
"gender":"male",
"name":{
"first":"Brian",
"last":"Adams"
},
"location":{
"street":{
"number":734,
"name":"Park Road"
},
"city":"Stoke-on-Trent",
"state":"County Fermanagh",
"country":"United Kingdom",
"postcode":"XR3 9EY",
"coordinates":{
"latitude":"18.0015",
"longitude":"-86.0374"
}
},
"email":"brian.adams#example.com",
"registered":"2008-11-07T11:53:14.120Z",
"phone":"015394 84142",
"cell":"0737-492-043",
"isActive": true,
"picture":{
"medium":"https://randomuser.me/api/portraits/med/men/42.jpg",
"thumbnail":"https://randomuser.me/api/portraits/thumb/men/42.jpg"
},
"nationality":"GB"
}
]
},
computed: {
perState() {
return this.workerList.reduce((acc, i) => {
acc[i.location.state] = (acc[i.location.state] || 0) + 1;
return acc;
}, {});
}
}
});
</script>
</body>
</html>
And when I put something from vuetify between <div id="app"> </div> something like
<v-alert
border="top"
color="red lighten-2"
dark
>
I'm an alert with a top border and red color
</v-alert>
then I'm only seeing plain html without style from vue like in the image
You forgot to link css:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="robots" content="noindex">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#6.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.24/browser.min.js"></script>
</head>
<body>
<h1>Task</h1>
<p>Please download this file and prepare nice UI using provided data.<br>
For task you can use Vuetify with minimal (if any) additional style.</p>
<p>In response please send this file with your UI proposal.</p>
<div id="app">
<v-app>
<v-main>
<v-container>
<v-alert
border="top"
color="red lighten-2"
dark
>
I'm an alert with a top border and red color
</v-alert>
</v-container>
</v-main>
</v-app>
</div>
<script>
new Vue({
el: "#app",
vuetify: new Vuetify(),
data(){
return {
workerList: [{"gender":"male", "name":{"first":"Brian", "last":"Adams"}, "location":{"street":{ "number":734, "name":"Park Road"}, "city":"Stoke-on-Trent", "state":"County Fermanagh", "country":"United Kingdom", "postcode":"XR3 9EY", "coordinates":{"latitude":"18.0015", "longitude":"-86.0374"}}, "email":"brian.adams#example.com", "registered":"2008-11-07T11:53:14.120Z", "phone":"015394 84142", "cell":"0737-492-043", "isActive": true, "picture":{ "medium":"https://randomuser.me/api/portraits/med/men/42.jpg", "thumbnail":"https://randomuser.me/api/portraits/thumb/men/42.jpg"}, "nationality":"GB"}]
}
},
computed: {
perState() {
return this.workerList.reduce((acc, i) => {
acc[i.location.state] = (acc[i.location.state] || 0) + 1;
return acc;
}, {});
}
}
});
</script>
</body>
</html>
Because I'm using v-select in a loop and I need to mark each of the loop iteration. I want to be able to add the selected value to an array, to check if the value exists and then add it or change it, something like this:
<v-card-text>
<ul>
<li v-for="ap in ['1','2','3']" :key="app">
<v-select
:items="findelem(appi)"
item-text="number"
item-value="number"
#input="check_update_array(_mark_ , _VALUE_)"
></v-select>
</li>
</ul>
</v-card-text>
</v-card>
and have check_update_array(mark , VALUE) as a method that checks and updates the array
is this possible ?
Edit:
I'll try to be more explicit, I want to do a v-for and to iterate over an array
for each selection I want to send to a function the position (value in iteration) and the selected value:
<ul>
<li v-for="ap in ARRAY">
<v-select
...
#input="check_update_array(ap, SELECTED_VALUE)"
></v-select>
</li>
</ul>
if I do #input="check_update_array" the SELECTED_VALUE is passed as param to the function and if I do #input="check_update_array(ap)" the position in iteration is passed.
Please have a look at the following code.
I took your code as a basis.
<!DOCTYPE html>
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
</head>
<body>
<div id="app">
<v-app>
<v-card>
<v-card-text>
<ul>
<li v-for="(ap, index) in ['1','2','3']" :key="index">
<v-select
:items="findelem()"
item-text="number"
item-value="number"
label="My Select"
#input="check_update_array"
></v-select>
</li>
</ul>
<p>
{{ elements }}
</p>
</v-card-text>
</v-card>
</v-app>v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<script>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: function() {
return {
elements: []
}
},
methods: {
check_update_array(value) {
this.elements.push(value);
},
findelem(appi) {
return [{'number':'some element'},{'number':'another element'},{'number':'third element'}]
}
}
})
</script>
</body>
https://jsfiddle.net/ewatch/gn3eyxp2/
However some context was missing for me from your code regarding the "appi" parameter for the findelem function and the parameters "mark" and "VALUE" for the check_update_array function.
The function that is bind to the #input event receives the selected value as parameter already (see documentation here: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event).
I hope it helps.
Please let me know if you have any questions.
Best regards,
ewatch
Assuming you have a data named 'selected_array'
data() {
return {
selected_array:[]
}
}
Now in your component, it should be look like this.
<v-card>
<v-card-text>
<ul>
<li v-for="ap in ['1','2','3']" :key="app">
<v-select
:items="findelem(appi)"
item-text="number"
item-value="number"
#chanage="check_update_array"
return-object
></v-select>
</li>
</ul>
</v-card-text>
</v-card>
Then in your check_update_array function, you code should look like this.
check_update_array(data){
let item_exist = false
this.selected_array.forEach(item => { // check if value exists.
if (item.value == data.value) {
item.mark = data.mark
item_exist = true
return
}
})
if (!item_exist) { // if not existing then append
this.selected_array.push(
{mark:value, value:value}
)
}
item_exist = false // reset value
}
Just make sure that your findelem(appi) is an array of objects containing mark and value elements
I want to add an eventhandler on my component. When I click the button I want the component to show. I tried all kinds of solutions but I think my knowledge is lacking. Here is my code, I would be thankful for help. Feel free to explain what the best methods are for solving this problem and how I should think in scenarios like these. Appricitate your time.
HTML
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
<link rel="stylesheet" href="main.css">
<title>SneakPeak</title>
</head>
<body>
<div id="body-div">
<div id="app">
<modal-button></modal-button>
<modal-1 v-if="modalStatus == 2"></modal-1>
</div>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
<!-- development version, includes helpful console warnings -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="app.js"></script>
</div>
</body>
</html>
JAVASCRIPT
// Define a new component called button-counter
Vue.component('modal-button', {
data: function () {
return {
count: 0,
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
// Defining a component called modal-1
Vue.component('modal-1', {
props: ["sModal"],
template: `
<transition name="modal">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header"
default header
</slot>
</div>
<div class="modal-body">
<slot name="body">
default body
</slot>
</div>
<div class="modal-footer">
<slot name="footer">
default footer
<button class="modal-default-button" #click="$emit('close')">
OK
</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
`
})
var shaba = new Vue({
el: '#app',
props: ["sModal"],
data: {
modalStatus: 0
},
})
// // register modal component
// Vue.component('modal', {
// template: modalTemplate
// })
// // start app
// new Vue({
// el: '#body-div',
// data: {
// showModal: false
// }
// })
Old Answer:
if you want to show/hide your component on click then use #click="val = !val" on the button component and use v-if or v-show="val" on the modal componant to enable/ disable Display.
set val: false in your data.
New Answer:
Looks Like you button is already capturing click so please follow this pen https://codepen.io/ashwinbande/pen/WPozgR
what we are doing is on button click executing a method which increments count as well as emits a custom event. In button component on that event, we change the value of val. then the val is used to show hide the modal component`!
I have set up a template that pulls the headers from some data from a JSON file then creates a set of paper-radio-buttons from those headers. When I call this template (in a paper-shadow) I am having trouble capturing the value of the selected paper-radio-button when I click submit (in the paper-shadow).
Here's what is generated in the paper-shadow:
<polymer-element name="add-graphItem">
<template>
<div id="div" layout horizontal>
<paper-button raised class="colored" self-center>Upload File</paper-button>
<paper-input-decorator label="Enter URL for Dataset" floatingLabel error="A URL is required!" flex self-center>
<input is="core-input" id="graphSource" required>
</paper-input-decorator>
</div>
<div layout horizontal center-justified id="upload-options">
<paper-shadow class="card upload-options-box" z="1">
<core-header-panel flex id="graphHeaderList">
<core-toolbar class="upload-option-header">
<span flex>Variable</span>
</core-toolbar>
<graph-optionsLoadTest><graph-optionsLoadTest> <!-- this is where I call the paper-radio-button -->
</core-header-panel>
</paper-shadow>
<paper-shadow class="card upload-options-box" z="1">
<core-header-panel flex>
<core-toolbar class="upload-option-header top">
<span flex>Transform</span>
</core-toolbar>
<div class="upload-options">
<graph-functionsLoad><graph-functionsLoad> <!-- I also do another set here but I'm just working on the one for now since they do the same thing -->
</div>
</core-header-panel>
</paper-shadow>
</div>
<paper-button dismissive hover on-tap="{{cancelGraph}}">Cancel</paper-button>
<paper-button affirmative hover on-tap="{{addNewGraph}}">Submit</paper-button>
</template>
<script>
Polymer("add-graphItem",{
addNewGraph: function () {
console.log(this.$.headerValue.selectedItem);
var hValue = this.$.headerValue.selectedItem;
console.log(hValue);
},
cancelGraph: function () {
this.$.addGraph.toggle();
},
})
</script>
And here is the paper-radio-button template:
<link rel="import" href="/static/bower_components/core-ajax/core-ajax.html">
<polymer-element name="graph-optionsLoadTest">
<template>
<core-ajax auto url="/getDataHeaders"
handleAs="json" response="{{headerList}}"></core-ajax>
<paper-radio-group id="headerValue">
<template repeat="{{h in headerList.headers}}">
<paper-radio-button name='{{h}}' label='{{h}}'></paper-radio-button>
</template>
</paper-radio-group>
</template>
<script>
Polymer( "graph-optionsLoadTest", {
headerListChanged: function(oldValue) {
console.log(this.headerList);
// this.headers;
}
});
</script>
</polymer-element>
I've tried numerous ways to get the value of the selected paper-radio-group with no success, any help would be appreciated
Mike, I see a few issues that might help you.
First, you can only use the $ hash to access nodes defined with an id attribute in your template. To access other nodes, such as those defined dynamically or without an id attribute, you can use the querySelector method. This method also lets you search by selector (somewhat like JQuery). In addition, to access nodes within the shadow DOM of a Polymer component that do not have id attributes, you may use the shadowRoot member. These ideas are discussed in the Polymer documentation for Automatic Node Finding and, in more detail, in the section on Shadow DOM. (In addition, the closing tag for the <graph-optionsLoadTest> element is missing a /.)
The upshot is that in order to access a node within a nested component's shadow DOM, you need to use something either like
this.$.idOfgraphOptionsLoadTest.$.idOfRadioGroup, when you have defined id attributes for your elements;
this.shadowRoot.querySelector('graphOptionsLoadTest').shadowRoot.querySelector('paper-radio-group'), whether or not id attributes are defined; or
some combination of the two, when one component has ids and the other does not.
I would suggest giving the <graph-optionsLoadTest> element an id and using the first option above.
Example Code
Unfortunately, your code is rather difficult to use without the pieces that I surmise are hosting it. Therefore, I created the following to illustrate the principle:
<!doctype html>
<html>
<head>
<title>Home</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<script src="bower_components/webcomponentsjs/webcomponents.js"></script>
<link rel="import" href="bower_components/paper-radio-button/paper-radio-button.html">
<link rel="import" href="bower_components/paper-radio-group/paper-radio-group.html">
</head>
<body unresolved fullbleed>
<polymer-element name="options-load" attributes="selected">
<template>
<paper-radio-group id="headerValue" selected="{{selected}}">
<template repeat="{{h in headerList.headers}}">
<paper-radio-button name='{{h}}' label='{{h}}' style="height: 100px"></paper-radio-button>
</template>
</paper-radio-group>
</template>
<script>
Polymer({
created: function() {
this.selected = 0;
this.headerList = {
headers: ['Option 1', 'Option 2', 'Option C']
};
}
});
</script>
</polymer-element>
<polymer-element name="add-graph-item">
<template>
<div layout horizontal around-justified>
<div>Options:</div>
<options-load id="optionsLoad"></options-load>
<button on-tap="{{doIt}}">Go</button>
<div>Results: </div>
<div id="results"></div>
</template>
<script>
Polymer({
doIt: function(event, detail, sender) {
var radioVal = this.$.optionsLoad.$.headerValue.selectedItem;
var msgTxt = 'Go clicked; ' + radioVal.label + ' selected'
console.log(msgTxt);
this.$.results.innerText = msgTxt;
}
});
</script>
</polymer-element>
<add-graph-item></add-graph-item>
</body>
</html>
Good luck!