<template>
<topView viewInfo="cardInfo"></topView>
<bottomView viewInfo="cardInfo"></bottomView>
<template>
<script>
module.exports = {
data: {
viewInfo:{
attr0:value0,
attr1:value1
}
},
mounted:function(){
getDataFromServer();
},
methods:{
getDataFromServer:function(){
var me = this;
var httpRequest = Service.getViewInfo();
httpRequest.on("success",function(data){
me.viewInfo.attr2 = data.info.attr2;
me.viewInfo.attr3 = data.info.attr3;
me.viewInfo.attr4 = data.info.attr4;
});
httpRequest.send();
}
}
}
</script>
topView.vue
<template>
<div>
<text>{viewInfo.attr0}</text>
<div v-for="(item, index) in getItems">
<text>{item.text}</text>
<text>{item.info}</text>
</div>
<text>{viewInfo.attr1}</text>
</div>
<template>
<script>
module.exports = {
data: {
itemInfo:[
{text:'text 0',info:"****"},
{text:'text 1',info:"****"},
{text:'text 2',info:"****"}
]
},
props:{
viewInfo:{}
},
computed:{
getItems:function(){
this.itemInfo[0].info = this.viewInfo.attr2 +" point";
this.itemInfo[1].info = this.viewInfo.attr3 +" point";
this.itemInfo[2].info = this.viewInfo.attr4 +" point";
return itemInfo;
}
},
methods:{
}
}
</script>
when i get data from server,and add some attr value to viewInfo. the child comphonent can update direct value.the compute values can't update relation to props data from parent component.
need some help.how can i update compute items value when i update parent component "viewInfo" values.
Vue cannot detect the changes when you directly set an item with the index, i.e. this.itemInfo[0].info = this.viewInfo.attr2 +" point" Is not reactive.
Use Vue.set instead, for the above:
// create a new item
var newItem = Object.assign({}, this.itemInfo[0], { info: this.viewInfo.attr2 +" point" })
// set the new item to the specific index
this.$set(this.itemInfo, 0, newItem)
You can read more about List Rendering Caveats here:
Related
Can i get model name if i now id input?
For examle
<input v-model="data.name" id="name_db">
I have in db value for data.name
Before vue i did this:
valuesFromDb.forEach(data=>{
if(data.fromdb==name_db)
$("#name_db").val(data.fromdb)
}
...
But it can't work with vueJS
I know i can do this:
data.name = data.fromdb
But i have many data in db and before vue i put data with help forloop.
Model and id have different names and it will take a long time to manually iterate through all the data
Now i want get model name and put value to it
Somethinks like this:
var modelName = $("#name_db").getModelNameVue();
modelName=data.fromdb
If i do this, in input value change but in data dont
data(){
return{
mainPdf:{
left: 5,
bottom:5,
top:5,
right:5
}
}
}
<input v-model="mainPdf.left" id="left_margin">
<input v-model="mainPdf.bottom" id="bot_margin">
<input v-model="mainPdf.isMargin" id="right_margin">
<input v-model="mainPdf.isMargin" id="up_margin">
getFromdb(){
api.getFromdb(e=>{ // string=e
var string = "left_margin=0&bot_margin=1&right_margin=2&up_margin=3"
var rPlus = /\+/g;
$.each( string.split( "&" ), function( index, field ) {
$.each( string.split( "&" ), function( index, field ) {
var current = field.split( "=" );
if( current[ 1 ] && current[ 0 ]) {
var name = decodeURIComponent(current[0].replace(rPlus, "%20"));
var value = decodeURIComponent(current[1].replace(rPlus, "%20"));
$("#"+ name).val(value);
}
});
})
})
I can't dynamic-binding because i can't change name of properties in mainPdf, because i have entity with same fields(left,bottom,top,right) in my backend
==========i found solution
i used dispatchEvent
$("#" + NAME).prop("checked", true);
$("#"+ NAME")[0].dispatchEvent(new Event('change')); //or input
Using Vue.js and dynamic programming techniques, it's a piece of cake.
Vue.component('dynamic-binding', {
template: `
<div style="display: flex;">
<input
v-for="field in Object.keys(mainPdf)" :key="field"
v-model="mainPdf[field]"
>
</div>
`,
data() {
return {
mainPdf: {},
};
},
methods: {
fromDb(val = 'left_margin=0&bot_margin=1&right_margin=2&up_margin=3') {
const db = new URLSearchParams(val);
this.mainPdf = Object.fromEntries(db);
},
},
mounted() {
this.fromDb();
},
});
new Vue({ el: '#app' });
<div id="app">
<dynamic-binding></dynamic-binding>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
I'm trying to create a dynamic table in Vue in two levels: child component contain the cells with 'idx' as :key, and the parent has the child components in table with 'line' as :key.
My question is: when I capture the customized event #update-cellValue, I have the idx, oldValue and newValue brought to scope, but I don't have access to the details of the parent component where the event was emitted (which line from the v-for). I mean, my intention is to have both (line, idx) and work like a matrix (2D array).
I have a workaround doing it all in one level, just using v-for directly in "child's component" , but I would like to have it separately in two different components
Child (Nested) component -> Table line containing cells
<template>
<div >
<tr class='center'>
<!-- add title to the tr as prop -->
<td
id='cell'
#click="selectCellEv"
v-for="(cell, idx) in cells" :key="idx">
<input type=number #input="updateCellValueEv"
v-model.lazy.trim="cells[idx]"/>
</td>
</tr>
</div>
</template>
<script>
// var editable = document.getElementById('cell');
// editable.addEventListener('input', function() {
// console.log('Hey, somebody changed something in my text!');
// });
export default {
name: 'CaRow',
props: {
cellValArr: {
type: Array,
required: false,
default: () => [],
},
title: {
type: String,
default: `noNameRow`
}
},
data(){
return {
cells: []
}
},
methods: {
updateCellValueEv(event){
const idx = event.target.parentElement.cellIndex;
let val = event.target.value;
if (val === '') val = 0;
const oldNumber = parseFloat(this.cells[idx]);
const newNumber = parseFloat(val);
if(!isNaN(newNumber) && !isNaN(oldNumber)){
// console.log(`new ${newNumber}, old ${oldNumber}`)
this.$emit('update-cellValue', idx, newNumber, oldNumber);
this.cells[idx] = newNumber;
}
},
},
created() {
this.cellValArr.map((val,idx) => {
this.cells[idx] = val;
});
},
}
</script>
Parent component (will be used directly in the app)-> Table containing the child components as lines
<table class="center">
<CaRow
v-for="(line, idx) in table"
:key="idx"
:cellValArr="table[idx]"
#update-cellValue="updateCellVal"
>{{line}} </CaRow>
</table>
</template>
<script>
import CaRow from './components/CaRow.vue'
export default {
name: 'App',
components: {
CaRow
},
data(){
return{
table: [[1,2,3],
[4,5,6],
[7,8,9]],
selectedCell: null,
}
},
methods: {
updateCellVal(idx, newValue, oldValue) {
console.log(`cell[${idx}: New Value= ${newValue}, Old Value= ${oldValue}]`)
// this.table[line][idx] = newValue;
//HERE IS THE PROBLEM!!!
//LINE IS NOT IN THIS SCOPE
}
},
}
</script>
In CaRow.vue, wrap the event data into a single object:
//this.$emit('update-cellValue', idx, newNumber, oldNumber);
this.$emit('update-cellValue', { idx, newValue: newNumber, oldValue: oldNumber });
Then in the parent template, update the event handler binding to pass the line index (which is idx in this context) and $event (a special variable that stores the emitted event data):
<CaRow #update-cellValue="updateCellVal(idx, $event)">
And update the handler to receive the line index and $event:
export default {
methods: {
updateCellVal(line, { idx, newValue, oldValue }) {
console.log({ line, idx, newValue, oldValue });
}
}
}
Vue 2 cannot detect the change when you directly set an item with the index, so you have to use this.$set():
//this.table[line][idx] = newValue;
this.$set(this.table[line], idx, newValue);
demo
I am using VUE JS and I want to have group of checkboxes as below. When someone clicked on main checkbox all the checkboxes under that check box should be selected. Please find the attached image for your reference
To accomplish this scenario I am using 2 main components. When someone clicked on a component I am adding that component to selectedStreams array. Selected stream array structure is similar to below structure
checked: {
g1: ['val1'],
g2: []
}
When I click on heading checkbox I am triggering function
clickAll and try to change the selectedStreams[keyv].
But this action doesn't trigger the child component and automatically checked the checkbox.
Can I know the reason why when I changed the parent v-model value it is not visible in child UI.
Parent Component
<template>
<div>
<b-form-group>
<StreamCheckBox
v-for="(opts, keyv) in loggedInUsernamesf"
:key="keyv"
:name="opts"
:main="keyv"
v-model="selectedStreams[keyv]"
#clickAll="clickAll($event, keyv)"
></StreamCheckBox>
</b-form-group>
</div>
</template>
<script>import StreamCheckBox from "./StreamCheckBox";
export default {
name: "GroupCheckBox",
components: {StreamCheckBox},
data(){
return {
selectedStreams:{}
}
},
computed: {
loggedInUsernamesf() {
var username_arr = JSON.parse(this.$sessionStorage.access_info_arr);
var usernames = {};
if (!username_arr) return;
for (let i = 0; i < username_arr.length; i++) {
usernames[username_arr[i].key] = [];
var payload = {};
payload["main"] = username_arr[i].val.name;
payload["type"] = username_arr[i].val.type;
if (username_arr[i].val.permissions) {
for (let j = 0; j < username_arr[i].val.permissions.length; j++) {
payload["value"] = username_arr[i].key + username_arr[i].val.permissions[j].streamId;
payload["text"] = username_arr[i].val.permissions[j].streamId;
}
}
usernames[username_arr[i].key].push(payload);
}
return usernames;
},
},
methods: {
clickAll(e, keyv) {
if (e && e.includes(keyv)) {
this.selectedStreams[keyv] = this.loggedInUsernamesf[keyv].map(
opt => {
return opt.value
}
);
}
console.log(this.selectedStreams[keyv]);
}
}
}
</script>
<style scoped>
</style>
Child Component
<template>
<div style="text-align: left;">
<b-form-checkbox-group style="padding-left: 0;"
id="flavors"
class="ml-4"
stacked
v-model="role"
:main="main"
>
<b-form-checkbox
class="font-weight-bold main"
:main="main"
:value="main"
#input="checkAll(main)"
>{{ name[0].main }}</b-form-checkbox>
<b-form-checkbox
v-for="opt in displayStreams"
:key="opt.value"
:value="opt.value"
>{{ opt.text }}</b-form-checkbox>
</b-form-checkbox-group>
</div>
</template>
<script>
export default {
name:"StreamCheckBox",
props: {
value: {
type: Array,
},
name: {
type: Array,
},
main:{
type:String
}
},
computed:{
role: {
get: function(){
return this.value;
},
set: function(val) {
this.$emit('input', val);
}
},
displayStreams: function () {
return this.name.filter(i => i.value)
},
},
methods:{
checkAll(val)
{
this.$emit('clickAll', val);
}
}
}
</script>
First of all, I am not sure what is the purpose of having props in your parent component. I think you could just remove props from your parent and leave in the child component only.
Second of all, the reason for not triggering the changes could be that you are dealing with arrays, for that you could set a deep watcher in your child component:
export default {
props: {
value: {
type: Array,
required: true,
},
},
watch: {
value: {
deep: true,
//handle the change
handler() {
console.log('array updated');
}
}
}
}
You can find more info here and here.
I am diving into Vue for the first time and trying to make a simple filter component that takes a data object from an API and filters it.
The code below works but i cant find a way to "reset" the filter without doing another API call, making me think im approaching this wrong.
Is a Show/hide in the DOM better than altering the data object?
HTML
<button v-on:click="filterCats('Print')">Print</button>
<div class="list-item" v-for="asset in filteredData">
<a>{{ asset.title.rendered }}</a>
</div>
Javascript
export default {
data() {
return {
assets: {}
}
},
methods: {
filterCats: function (cat) {
var items = this.assets
var result = {}
Object.keys(items).forEach(key => {
const item = items[key]
if (item.cat_names.some(cat_names => cat_names === cat)) {
result[key] = item
}
})
this.assets = result
}
},
computed: {
filteredData: function () {
return this.assets
}
},
}
Is a Show/hide in the DOM better than altering the data object?
Not at all. Altering the data is the "Vue way".
You don't need to modify assets to filter it.
The recommended way of doing that is using a computed property: you would create a filteredData computed property that depends on the cat data property. Whenever you change the value of cat, the filteredData will be recalculated automatically (filtering this.assets using the current content of cat).
Something like below:
new Vue({
el: '#app',
data() {
return {
cat: null,
assets: {
one: {cat_names: ['Print'], title: {rendered: 'one'}},
two: {cat_names: ['Two'], title: {rendered: 'two'}},
three: {cat_names: ['Three'], title: {rendered: 'three'}}
}
}
},
computed: {
filteredData: function () {
if (this.cat == null) { return this.assets; } // no filtering
var items = this.assets;
var result = {}
Object.keys(items).forEach(key => {
const item = items[key]
if (item.cat_names.some(cat_names => cat_names === this.cat)) {
result[key] = item
}
})
return result;
}
},
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<button v-on:click="cat = 'Print'">Print</button>
<div class="list-item" v-for="asset in filteredData">
<a>{{ asset.title.rendered }}</a>
</div>
</div>
I have a <select> component. Once I select a category I need to get the ID and also a boolean which checks if the category has a subcategory, if it does, I make an API call to fetch the subcategories.
Parent template:
<material-selectcat v-model="catId" name="category" id="selcat">
<option
v-for="cat in cats"
:value="cat.cat_id"
:subcat="cat.has_subCat"
v-text="cat.category_name"
></option>
</material-selectcat>
Child component:
<template>
<select><slot></slot></select>
</template>
<script>
export default {
props: ['value', 'subcat'],
watch: {
watcher: function() {}
},
computed: {
watcher: function() {
this.relaod(this.value);
this.fetchSubCategories(this.value, this.subcat);
}
},
methods: {
relaod: function(value) {
var select = $(this.$el);
select.val(value || this.value);
select.material_select('destroy');
select.material_select();
},
fetchSubCategories: function(val, sub) {
var mdl = this;
var catID = val || this.value;
console.log("sub: " + sub);
mdl.$emit("reset-subcats");
if (catID) {
if (sub == 1) {
if ($('.subdropdown').is(":visible") == true) {
$('.subdropdown').fadeOut();
}
} else {
axios.get(URL.API + '/subcategories/' + catID)
.then(function(response) {
response = response.data.subcatData;
response.unshift({
subcat_id: '0',
subcategory_name: 'All Subcategories'
});
mdl.$emit("update-subcats", response);
$('.subdropdown').fadeIn();
})
.catch(function(error) {
if (error.response.data) {
swal({
title: "Something went wrong",
text: "Please try again",
type: "error",
html: false
});
}
});
}
} else {
if ($('.subdropdown').is(":visible") == true) {
$('.subdropdown').fadeOut();
}
}
}
},
mounted: function() {
var vm = this;
var select = $(this.$el);
select
.val(this.value)
.on('change', function() {
vm.$emit('input', this.value);
});
select.material_select();
},
updated: function() {
this.relaod();
},
destroyed: function() {
$(this.$el).material_select('destroy');
}
}
</script>
But inside the fetchSubCategories() function this line always returns undefined:
console.log("sub: " + sub);
If I check the Vue Devtools tab in my Chrome inspector, I can see that all of the data exists:
cat_id:0
category_name:"All Subcategories"
has_subCat:0
But why doesnt has_subCat get passed as a prop?
The subcat prop is undefined because you are not passing it to the component. But, a subcat prop doesn't make much sense anyway, since you want to check the value for each option in the select which could all be different.
If you compose the options inside of the component definition:
// child component script
props: ['value', 'options'],
// child component template
<template>
<select>
<slot>
<option
v-for="option in options"
:key="option.id"
:value="option.id"
:subcat="option.subcat"
v-text="option.text"
></option>
</slot>
</select>
</template>
And then pass a correctly formatted options prop to the component:
// parent component script
computed: {
options() {
return this.cats.map((cat) => {
return {
id: cat.cat_id,
subcat: cat.has_subcat,
text: cat.category_name
}
});
}
}
// parent component template
<material-selectcat v-model="catId" :options="options" name="category" id="selcat">
</material-selectcat>
Then, you could get the correct subcat value for any option's corresponding value by referencing the options prop in the fetchSubCategories method:
fetchSubCategories: function(val) {
var mdl = this;
var catID = val || this.value;
var sub = (this.options.find(o => o.id === val) || {}).subcat;
...
The reason your value prop is defined in your code is that you are using the v-model directive on the component tag. The v-model directive is just syntactic sugar for :value="something" #input="something = $event.target.value". Since you've specified catID as the v-model, that will be passed to your component as the value prop.