NativeScript-Vue Update Data Object? - vue.js

Is it possible to update a data object with a new property?
The data is coming in from an external source, and justifiably the data does not contain a flag on whether or not to show the contents of the object.
<RadListView for="item in list" #itemTap="accordion">
<v-template>
<StackLayout>
<Label :text="item.title" />
</StackLayout>
<StackLayout v-bind:height="item.toggled? 'auto' : 0">
<Label :text="item.desc" />
</StackLayout>
</v-template>
</RadListView>
export default{
data(){
return{
list: [
{ "title" : "Sample Title", "desc" : "Lorem Ipsum" },
{ "title" : "New Title", "desc" : "Test 123 Test" }
]
}
},
methods: {
accordion: function(e){
e.item.toggled = !e.item.toggled;
},
loadData: function(){ ... },
generateData: function( data ){ ... }
}
}
So the 'list' objects do not contain a "toggled" property, but I am trying to expand/collapse the "desc" area when a user clicks on an item in the UI.
My code, as is, does return e.item.toggled in an expected fashion but the UI does not update
undefined -> true -> false -> true -> false -> ...
UPDATE:
Going off of #sundeqvist 's suggestion, I passed the item element through the method for accordion.
I also modified how the "toggled" property was amended to the data object, "list". By adding it directly, the console showed the value as a true boolean instead of a Vue [Getter/Setter] value.
<RadListView for="item in list">
<v-template>
<StackLayout #itemTap="accordion(item)">
<Label :text="item.title" />
</StackLayout>
<StackLayout v-bind:height="item.toggled? 'auto' : 0">
<Label :text="item.desc" />
</StackLayout>
</v-template>
</RadListView>
export default{
data(){
return{
list: [
{ "title" : "Sample Title", "desc" : "Lorem Ipsum" },
{ "title" : "New Title", "desc" : "Test 123 Test" }
]
}
},
methods: {
accordion: function(item){
item.toggled = !item.toggled;
},
loadData: function(){ ... },
generateData: function( data ){
let dataArr = [];
for( let d in data ){
d = { "toggled" : false, ...d };
dataArr.push( d );
}
this.list = dataArr;
}
}
}

Are you sure you're accessing item in your method?
By passing in item into your accordion method call:
<RadListView for="item in list" #itemTap="accordion(item)">
and then in your method toggle the toggle property on the item you pass in as a parameter:
accordion(item) {
item.toggled = !item.toggled;
}
you should be able to get it work.

Related

Passing selected option to the button to perform an action using vuejs

I have a select element that contains some options elements built from an array.
Right now, when I select an option it immediately calls the function and produces the output but what I want is that once I select any option, and if that option's value is "students" then only it should run the function instantly.
When I click on the add button it does not work at all.
<form name="Form1" >
<b>Field Name</b> : <p>Student Council</p>
<b>Field Type:</b> <p>List of Links</p>
<b>Range:</b><br>
<select name="selectOption" id="listSelect" v-model="selected">
<option hidden value="">Select a list</option>
<option v-for="List in fieldlist" v-if="List.ListName != 'student_council'"> {{ List.ListName}}</option>
</select>
<button #click="add"> Save </button>
<br><br>
<b> Student_Council list:</b>
{{fieldlist[5].student_council}}</v-list-item>
</form>
<script>
var vm = new Vue({
el : '#new_list' , // id of the elemnt we ware working on to call it
data: {
selected : null,
fieldlist: [
{
ListName: 'students',
Students: [
{
StudentName: 'Yousef',
StudentAge: '24',
isStudent: true,
},
{
StudentName: 'Baqir',
StudentAge: '23',
isStudent: true,
},
]
},
{
ListName: 'departments',
departments: [],
},
{
ListName: 'department_with_most_students',
Department_With_Most_Students: [],
},
{
ListName: 'active_students',
active_students: [],
},
{
ListName: 'dean',
dean: [],
},
{
ListName: 'student_council',
student_council: [],
},
],
},
methods: { // functions
add: function(option){
if(this.selectOption.option.target.value=== "students"){
if(!this.fieldlist[5].student_council.includes(this.fieldlist[0].Students)){
this.fieldlist[5].student_council.push(this.fieldlist[0].Students);
}else{
this.fieldlist[5].student_council.length = 0;
return false;
}
}
},
}
I tried to call the select element to check its option, but this is also not working so far.
I'm expecting if I select the option "students" and click on the add button, it should pass the data inside the students array to the student_council array
I have read your question I think you have to deal with
this.selected
instead of
this.selectOption.option.target.value
in vuejs we don't need to deal with name of tag element we deal with v-model
please update me if i miss something

How to combine two values from child components with an event?

I can't assign data from child component.
I am simply trying to implement emitting a specific value with an event
Child component is:
<template>
<StackLayout orientation="vertical" width="210" height="210"
backgroundColor="blue" color="white">
<Label text="Father" row="0" col="1"
class="h2 text-center p-t-5 text-primary" />
<ListPicker :items="maleName" v-model="maleIndex" row="1" col="1"
#selectedIndexChange="selectedIndexofFatherChanged" />
</StackLayout>
</template>
<script>
export default {
data() {
return {
maleName: [
"Rey",
"Antonia",
"Victor",
"Lincoln",
],
maleIndex: 0
};
},
methods: {
selectedIndexofFatherChanged() {
this.$emit("fatherChanged", this.fatherName);
// console.log("changed to: " + this.fatherName);
}
},
computed: {
fatherName() {
return this.maleName[this.maleIndex];
}
}
};
</script>
and Parent Component is:
<template>
<Page>
<ActionBar title="Choose Parent" />
<GridLayout columns="*, *" rows="*,90">
<SelectMother col="0" #motherChanged="onMotherChanged">
</SelectMother>
<SelectFather col="1" #fatherChanged="onFatherChanged">
</SelectFather>
<Button text="Show Result" #tap="onButtonTap" row="2"
colSpan="2" />
</GridLayout>
</Page>
</template>
<script>
import SelectMother from "./SelectMother";
import SelectFather from "./SelectFather";
export default {
components: {
SelectMother,
SelectFather
},
data() {
return {
motherName: null,
fatherName: null
};
},
methods: {
onButtonTap() {
alert("father: " + this.fatherName + " mother: " + this
.motherName);
},
},
watch: {
onFatherChanged(nameFromComponent) {
nameFromComponent = this.fatherName;
},
onMotherChanged(nameFromComponent) {
nameFromComponent
= this.motherName;
}
},
};
</script>
When I console log payload in onFatherChanged method it turns null
How to combine father and mother and show results.
Select Father from child component
Select Mother from second child component
Assign incoming payload and Show Results
Please check {N} Playground
https://play.nativescript.org/?template=play-vue&id=Dez2fV&v=26
Instead of
onFatherChanged(payload) {
payload = this.fatherName;
console.log(payload);
},
It should be;
onFatherChanged(payload) {
this.fatherName = payload;
console.log(payload);
},

Bootstrap-vue Checkbox, Check additional options when selected

What i want to do is also check the default option when a user checks one of the checkbox item.
i have created a snippet of the error i am encountering,
usually i thought its because of my nested components.
but i encountered the error
You may have an infinite update loop in watcher with expression "localChecked"
even on this simple code snippet.
vue js script
new Vue({
el: "#app",
data: {
application: [
{
app_name : 'Netflix',
app_default : 'videoshare_default',
options : [
{ text : 'Video Stream', value : 'video_streaming'},
{ text : 'Download Video' , value : 'video_download'},
{ text : 'Share Video' , value : 'videoshare_default'}
]
},
{
app_name : 'Messenger',
app_default : 'message',
options : [
{ text : 'Messaging', value : 'message'},
{ text : 'Voice Calls' , value : 'voice_calls'},
{ text : 'Video Calls' , value : 'video_calls'},
{ text : 'Media Sharing' , value : 'file_transfer'}
]
}
],
selected : []
},
methods: {
selectDefault: function(data,$event){
this.selected[data.app_name].push(data.videoshare_default)
}
}
})
HTML
<div id="app">
<b-col v-for="(data , index) in application" v-bind:key="index" class="p-2" cols="5">
<b-form-group :label="data.app_name" label-class="font-weight-bold">
<b-form-checkbox-group
#input="selectDefault(data,$event)"
v-model="selected[data.app_name]"
:options="data.options"
name="application[]"
stacked
></b-form-checkbox-group>
</b-form-group>
</b-col>
</div>
a FIDDLE:
https://jsfiddle.net/tomexsans/194m0jdq/1/
or is there any other way to do this than what i am doing.
Your selected property is an array, but you want to use key value pairs, which is why you need to make it an object instead, which will store an array of each application type.
To make sure that Vue stays reactive, you need to use the Vue.set or this.$set method to add a property to an object, if that property DOESN'T already exist in that object.
The $event on b-form-checkbox-group returns the entire array of selected values, which we don't want. That's why i use the .native modifier on the event, so i can access the clicked checkbox and it's value.
new Vue({
el: "#app",
data: {
application: [{
app_name: 'Netflix',
app_default: 'videoshare_default',
options: [{
text: 'Video Stream',
value: 'video_streaming'
},
{
text: 'Download Video',
value: 'video_download'
},
{
text: 'Share Video',
value: 'videoshare_default'
}
]
},
{
app_name: 'Messenger',
app_default: 'message',
options: [{
text: 'Messaging',
value: 'message'
},
{
text: 'Voice Calls',
value: 'voice_calls'
},
{
text: 'Video Calls',
value: 'video_calls'
},
{
text: 'Media Sharing',
value: 'file_transfer'
}
]
}
],
selected: {}
},
methods: {
selectDefault(data, event) {
/* Return if the checkbox was unchecked */
if (!event.target.checked) return;
/* Return if the selected value was the default */
if (data.app_default === event.target.value) return;
/* Init the array if it doesn't exist yet.*/
if (!this.selected[data.app_name]) {
this.$set(this.selected, data.app_name, []);
}
const nestedSelected = this.selected[data.app_name];
/* Push in the default value if it doesn't exist alreayd */
if (!nestedSelected.find(value => value === data.app_default)) {
this.selected[data.app_name].push(data.app_default)
}
}
}
})
<link href="https://unpkg.com/bootstrap#4.4.1/dist/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://unpkg.com/bootstrap-vue#2.3.0/dist/bootstrap-vue.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.3.0/dist/bootstrap-vue.js"></script>
<div id="app">
<b-col v-for="(data , index) in application" v-bind:key="index" class="p-2" cols="5">
<b-form-group :label="data.app_name" label-class="font-weight-bold">
<b-form-checkbox-group
v-model="selected[data.app_name]"
#input.native="selectDefault(data, $event)"
:options="data.options"
name="application[]"
stacked
></b-form-checkbox-group>
</b-form-group>
</b-col>
{{ selected }}
</div>

Best way to do calculation on a property of a Prop(coming from the parent) object and put the result in the template

I have an object that is being passed to the child component and I want to display those values in the child component template. However, the object contains properties that are arrays and objects so I need to do some parsing/manipulation on them before I set the value.
object:
{
"id": "111",
"ip": "10.192.1.112",
"profiles": [
"Japan",
"Japan222"
],
"network": [
{
"plan_id": "PLAN-UUID",
"plan_name": "1BCT"
},
{
"plan_id": "PLAN-UUID",
"plan_name": "1BCT2"
}
],
"status": "LOCKED",
"last_downloaded": "1547672769000"
}
template code in child:
<label label="id" :value=this.objectFromParent.id />
<label label="ip" :value=this.objectFromParent.ip />
<label label="ip" :value= calculateProfiles />
where calculateProfiles is the method to calculate it and return the string value? I'm not sure what's a good way to handle this.
Try this
<template>
<div>
<label label="id" :value="objectFromParent.id" />
<label label="ip" :value="objectFromParent.ip" />
<label label="ip" :value="calculateProfiles" />
</div>
</template>
<script>
export default {
props: {
objectFromParent: {
type: Object,
default() {
return {
id: '111',
ip: '10.192.1.112',
profiles: ['Japan', 'Japan222'],
network: [
{
plan_id: 'PLAN-UUID',
plan_name: '1BCT'
},
{
plan_id: 'PLAN-UUID',
plan_name: '1BCT2'
}
],
status: 'LOCKED',
last_downloaded: '1547672769000'
}
}
}
},
computed: {
calculateProfiles() {
return this.profiles.join(',')
}
}
}
</script>
To help him on the comment:
<label label="ip">
<md-icon v-if="objectFromParent.status==='LOCKED'">lock</md-icon>
{{calculateProfiles}}
</label>

Can't Access Props in NativeScript Vue Modal

I'm using a modal to show a listView and I want to pass the list into the modal as an array. I call the modal, like so:
this.$showModal(Picker, { props: { list: [
{ name: "Item 1" },
{ name: "Item 2" },
{ name: "Item 3" }
]}});
The modal loads fine, and I can see the props in the modal when I console.log
created: function(){
console.log(this.list);
},
However, I can't access the props in the template or loop over them.
<ListView for="item in listOfItems">
<v-template>
<Label :text="item.name" class="listItem" />
</v-template>
</ListView>
I have also tried:
<ListView :for="item in $props.list">
My full code for the modal component is below:
<template>
<Page>
<ListView for="item in listOfItems">
<v-template>
<Label :text="item.name" class="listItem" />
</v-template>
</ListView>
</Page>
</template>
<script>
export default {
props: ["list"],
created: function(){
console.log(this.list);
},
data(){
return {
listOfItems: this.list
}
}
}
</script>
What am I doing wrong?
Change <ListView for="item in listOfItems"> to <ListView for="item in list">
Don’t know why, but when I changed the
data() return {
so that the variable names are the same,
{animals: animals,
vegetables: vegetables }
it worked.
Didn’t have to do this for strings or integers, just objects.
<ListView for="item in $props.list">
Remove colon?
I'm taking care of removing unnecessary : of head of for and refering to $props property when coding with NativeScript-Vue.