Can't Access Props in NativeScript Vue Modal - vue.js

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.

Related

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);
},

How to handle navigation on a Nativescript Vue app?

I'm having issue understanding how navigation works using NativeScript Vue. I started yesterday and can't seem to make it work. It says my AppDrawer component is recursive.
The main frame contain a page, which contain the menu and the action bar. If I click on something, it loads the new page in the main frame and reload the menu and the action bar. Or does people just use a frame in the middle to load the pages without having to redraw the menu/drawer? Doing so, how would I change the data in the actionbar or the drawer from inside the frame? I'm guessing using Vuex, but I'm not quite sure what is the optimal approach when designing an android app.
Sorry if I get some terminology wrong.
nav/AppDrawer.vue:
<template>
<StackLayout ~drawerContent backgroundColor="#444">
<Label class="drawer-header" text="Drawer" />
<Label class="drawer-item" text="Accueil" #tap="btnHome()" />
<Label class="drawer-item" text="Item 2" #tap="btnAdd2()" />
<Label class="drawer-item" text="Item 3" #tap="btnHistory()" />
</StackLayout>
</template>
<script>
// Load pages
import App from "../App";
import Add2 from "../Add2";
import History from "../History";
export default {
components: {},
methods: {
btnHome() {
this.$navigateTo(App);
},
btnAdd2() {
this.$navigateTo(Add2);
},
btnHistory() {
this.$navigateTo(History);
},
},
};
</script>
App.vue:
<template>
<Page>
<ActionBar>
<GridLayout width="100%" columns="auto, *">
<Label
text="MENU"
#tap="$refs.drawer.nativeView.showDrawer()"
col="0"
/>
<Label class="title" text="Title" col="1" />
</GridLayout>
</ActionBar>
<RadSideDrawer ref="drawer">
<AppDrawer />
<GridLayout ~mainContent columns="*" rows="*">
<TextView editable="false">
<FormattedString>
<Span :text="msg" />
</FormattedString>
</TextView>
</GridLayout>
</RadSideDrawer>
</Page>
</template>
<script>
import AppDrawer from "./nav/AppDrawer.vue";
import { device } from "tns-core-modules/platform";
export default {
components: {
AppDrawer,
},
data() {
return {
msg:
"Vous n'êtes pas connecté. Les téléversements seront assigné avec l'ID: " +
device.uuid,
};
},
methods: {},
};
</script>
History.vue:
<template>
<Frame>
<Page>
<ActionBar>
<GridLayout width="100%" columns="auto, *">
<Label
text="MENU"
#tap="$refs.drawer.nativeView.showDrawer()"
col="0"
/>
<Label class="title" text="History" col="1" />
</GridLayout>
</ActionBar>
<RadSideDrawer ref="drawer">
<AppDrawer />
<GridLayout ~mainContent columns="*" rows="*">
<TextView editable="false">
<FormattedString>
<Span text="testing 1, 2" />
</FormattedString>
</TextView>
</GridLayout>
</RadSideDrawer>
</Page>
</Frame>
</template>
<script>
import AppDrawer from "./nav/AppDrawer.vue";
export default {
components: {
AppDrawer,
},
data() {
return {};
},
methods: {},
};
</script>
EDIT:
Ok, I understand the problem is that I import AppDrawer in App, so the App page have a menu. And I then import App in AppDrawer, because I want a "Home" button in the menu, so the user can return the the main page/screen.
This create a loop. I still don't understand how people import menus on each page and still have each page imported in the menu so user can be redirected on that page.
Based on the answer below, I tried:
<script>
export default {
components: {
App: () => import("../App.vue"),
AddProduct: () => import("../Add2.vue"),
History: () => import("../History.vue")
},
methods: {
btnHome() {
this.$navigateTo(App);
},
btnAdd2() {
this.$navigateTo(Add2);
},
btnHistory() {
this.$navigateTo(History);
}
}
};
</script>
But I get JS: [Vue warn]: Error in v-on handler: "ReferenceError: Add2 is not defined".
You are importing AppDrawer in App, and importing App in AppDrawer.vue, this import loop never end, you have to use lazyloading if you need do this (Although you imported and didnt use App componenet):
// in AppDrawer.vue
components: {
'App': () => import('../App.vue')
},

Nativescript vue tooltip use

I'm using nativescript vue
I want to run this tooltip.
code like in tutorial is not working:
<Button text="Get Json Data" id="tool" ref="tooltip" #tap="getData" class="btn btn-primary m-t-20"></Button>
I made button like this and in created trying to make it working but
created() {
let tipref = this.$refs.tooltip;
let tippref = this.$refs.page.tooltip;
//new ToolTip(this.nativeView.topmost().tip,{text:"Text"});
//const tip = new ToolTip(this.nativeView.tipref,{text:"Some Text"});
new ToolTip(tipref,{text:"Some Text"});
},
still not working: TypeError: Cannot read property 'tooltip' of undefined
TypeError: Cannot read property 'nativeView' of undefined
Can't figure out how to do it.
The code from the answer How to create a floating help layout? is not working too.
In order to access the NativeScript View via ref, you should do this.$refs.tooltip.nativeView. Also wait for the loaded event of the element to make sure it's ready to use.
<template>
<Page class="page">
<ActionBar title="Home" class="action-bar"/>
<ScrollView>
<StackLayout class="home-panel">
<Button
text="Get Json Data"
ref="tooltip"
class="btn btn-primary m-t-20"
#loaded="onLoaded"
></Button>
</StackLayout>
</ScrollView>
</Page>
</template>
<script>
export default {
data() {
return {};
},
methods: {
onLoaded: function(args) {
const ToolTip = require("nativescript-tooltip").ToolTip;
const tip = new ToolTip(args.object, {
text: "Some Text",
backgroundColor: "pink",
textColor: "black"
});
tip.show();
}
}
};
</script>

NativeScript-Vue Update Data Object?

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.

How to bubble up a click event from a custom component in vue.js?

I have a custom component <item> which looks like this:
item.vue
<script>
export default {
render: function (c) {
var self = this;
const contentEl = c('div', {staticClass:'item-content', domProps: {innerHTML: self.content}});
return c('div', {
staticClass: 'item',
class: {
'item-left': self.side === 'left',
'item-right': self.side === 'right'
}
}, [contentEl])
},
props: {
content: String,
}
}
</script>
It can be used like this:
<item :content="Hello world"></item>
This will print "Hello world" and works fine but now I want the item to be clickable like this:
<item v-on:click="myClickEvent" :content="Hello world"></item>
Question:
How can I make the <item> component to fire a click event when its inner <div> was clicked?
In the child component:
<button #click="$emit('click')"></button>
And then in the parent component:
<MyButton #click="someFunction"/>
<template>
<div id="action-button">
<input type="button" id="in" #click="clicked" :value="value" />
</div>
</template>
<script>
export default {
name: 'action-button',
props: {
'value': String
},
methods: {
clicked () {
this.$emit('action-button-clicked')
}
}
}
</script>
And then instead of v-on:click you should use v-on:action-button-clicked="handleClick".
<action-button v-on:action-button-clicked="handleClick"></action-button>
So the general idea is to handle clicks internally and then use emit inside your component.
In the child component you can simply do it as #Nathan said.
<button #click="$emit('click', $event)">My button component</button>
Hope it helps