anyone can help?
i have this one page app to show the problem:
if you modify a field and then press 'save' at the top the changed field is not shown on the console...
<template>
<Page>
<ActionBar>
<Label text="SAVE" #tap="saveScreen()" />
</ActionBar>
<StackLayout>
<RadDataForm :source="person"/>
</StackLayout>
</Page>
</template>
<script>
export default {
data () {
return {
person: {
name: 'John',
age: 23,
email: 'john#company.com',
city: 'New York',
street: '5th Avenue',
streetNumber: 11,
},
};
},
methods: {
saveScreen() {
console.log('=======personName: ' + JSON.stringify(this.person))
}
}
}
</script>
<style>
</style>
i realize this is pretty much a basic question, i searched the internet for an answer however could not find it...
thanks in advance for your help.
Regards,
Hans
RadDataForm is a little bit tricky to work with since it doesn't bind data automatically so you'll have to add some events to get changed data.
Initial data is used to create form and on change data is saved to another object so you can listen to propertyCommitted event and get editedObject.
<RadDataForm :source="person" #propertyCommitted="onPropertyCommitted" />
data() {
return {
person: {
name: "John",
age: 23,
email: "john#company.com",
city: "New York",
street: "5th Avenue",
streetNumber: 11
},
committedPerson: {}
};
},
methods: {
onPropertyCommitted (data) {
this.committedPerson = data.object.editedObject
},
saveScreen () {
console.log(this.committedPerson);
}
}
Don't know if this is the best way to do it in Vue but I see that there are open issues on github regarding this and some workarounds are posted but none for Vue.
It should be explained better in official docs.
Here is working example https://play.nativescript.org/?template=play-vue&id=98Xyjv&v=5
And here you can find some examples on validation, grouping etc.https://github.com/telerik/nativescript-ui-samples-vue/tree/master/dataform/app/examples
Here is my answer for this. Look at the save function.
<StackLayout>
<StackLayout orientation="vertical" backgroundColor="lightgray">
<RadDataForm
id="myDataForm"
:source="record"
v-on:propertyCommitted="save"/>
</StackLayout>
<script>
import { mapState } from 'vuex'
import { getViewById } from "tns-core-modules/ui/core/view";
export default {
data() {}
},
methods:{
save(){
console.log('save')
let data = args.object
var dataform = getViewById(data, "myDataForm");
console.log(dataform.editedObject)//<--updated Data Here
},
}
computed: mapState({
record(state){
return record = this.$store.getters.record;
}
};
Related
I have a v-navigation-drawer which can be opened by clicking a button in a sub component.
So I changed v-model="drawer" to simply value="drawer" otherwise I get a warning about mutating a prop which makes sense (feels like doing some dirty angular double-way data binding ^^).
Here's the code:
layouts/default.vue:
<template>
<Header :toggleLeftMenu="toggleLeftMenu" />
<LeftMenu :show="showLeftMenu" :toggleLeftMenu="toggleLeftMenu" />
</template>
<script>
export default {
data() {
return {
showLeftMenu: true,
}
},
methods: {
toggleLeftMenu() {
this.showLeftMenu = !this.showLeftMenu;
},
}
}
</script>
components/layout/LeftMenu.vue:
<v-navigation-drawer
:value="show"
width="300"
clipped
fixed
app
>
This issue is that the drawer can be closed by clicking on the backdrop (on small devices). I need to plug the backdrop click to toggleLeftMenu prop, but according to the doc, this doesn't seem to be possible.
How can I achieve full control on the component? Is this #backdropClick event missing or something?
I tried to use #input but it creates an infinite loop which also makes sense.
Thanks
Using vuetify 2.6.1.
I changed v-model="drawer" to simply value="drawer" otherwise I get a warning about mutating a prop
This is not quite the right decision. Of course you should not use drawer as model, but you can create an internalDrawer prop in LeftMenu component, and leave the v-model where it is.
One of the possible ways to resolve your issue is to emit events from both sub-components into its parent.
So let's rewrite your LeftMenu component this way:
<template>
<v-navigation-drawer v-model="internalShow" width="200" clipped fixed app>
some drawer data
</v-navigation-drawer>
</template>
<script>
export default {
props: {
show: Boolean,
},
data() {
return {
internalShow: this.show,
};
},
watch: {
show (val) {
this.internalShow = val;
},
internalShow (val) {
if (val !== this.show) {
this.$emit("change-drawer-state");
}
},
},
};
</script>
In this case, every time when the internalShow state changes, an change-drawer-state event will be emitted.
Your Header component can be rewrited the same way:
<template>
<v-btn #click="$emit('change-drawer-state')">Drawer button</v-btn>
</template>
And this is the code of your parent component:
<template>
<div>
<Header #change-drawer-state="toggleLeftMenu" />
<LeftMenu :show="showLeftMenu" #change-drawer-state="toggleLeftMenu" />
</div>
</template>
<script>
import LeftMenu from "./LeftMenu";
import Header from "./Header";
export default {
components: {
LeftMenu,
Header,
},
data() {
return {
showLeftMenu: false,
};
},
methods: {
toggleLeftMenu() {
this.showLeftMenu = !this.showLeftMenu;
},
},
};
</script>
Both change-drawer-state event handlers are calling the same method - toggleLeftMenu and then the method changes show prop of navigation-drawer.
You can test this solution in a CodeSandbox playground.
I'm currently trying to do a very simple sort on an array of items within a Nativescript Vue app, however it is pulling back some very weird results. I have a playground link that recreates the issue.
Basically, when my page loads, the items are ordered based on a data property for sort direction. This is ordered correctly. However, if I then try to change this direction (ascending or descending) then it just orders really strange.
On load, my items are ordered ascending like this:
When I then try to sort them descending, the order changes to this:
When I then try to sort ascending again, the order changes to this:
Here is basically the code from my playground entry - I know some of it is over-engineered but this is just a result of me testing numerous things:
<template>
<Page>
<ActionBar title="Sort Test" icon="">
</ActionBar>
<StackLayout>
<StackLayout>
<Label :text="item.title" textWrap="true"
v-for="item in orderedItems" :key="item.id" />
</StackLayout>
<Label :text="`Sort Ascending: ${sortAscending}`"
textWrap="true" />
<Label :text="`Sort Descending: ${sortDescending}`"
textWrap="true" />
<Button text="Sort Ascending" #tap="sortAsc" />
<Button text="Sort Descending" #tap="sortDesc" />
</StackLayout>
</Page>
</template>
<script>
export default {
methods: {
sortAsc() {
this.sortAscending = true;
this.sortDescending = false;
},
sortDesc() {
this.sortAscending = false;
this.sortDescending = true;
}
},
computed: {
orderedItems() {
//return (b.date > a.date) ? 1 : (b.date < a.date) ? -1 : 0;
return this.items.sort((a, b) => {
if (this.sortAscending) {
// return (a.id > b.id) ? 1 : (a.id < b.id) ? -1 : 0;
return a.id - b.id;
} else {
// return (b.id > a.id) ? 1 : (b.id < a.id) ? -1 : 0;
return b.id - a.id;
}
});
}
},
data() {
return {
sortAscending: true,
sortDescending: false,
items: [{
id: 1,
title: "Label 1"
},
{
id: 2,
title: "Label 2"
},
{
id: 4,
title: "Label 4"
},
{
id: 5,
title: "Label 5"
},
{
id: 3,
title: "Label 3"
}
]
};
}
};
</script>
<style lang="postcss" scoped></style>
Now, I've found a similar issue that seems to match my exact use case, however changing the behaviour of my orderedItems property doesn't seem to make a difference.
Is there something I'm doing wrong? Is my v-for in the wrong place, or is it a side-effect of using a computed property to display it - should I be using a watch on the sort directions instead to re-order the items data property?
Any help would be appreciated - I haven't tried this on iOS yet but this behaviour is happening in Android.
OK, for anyone interested, the above sort solution works fine - I don't know enough to be sure but the problem seems to be when Nativescript is rendering out the items.
The solution I have in place is to add a loading state in between the sorts. So, when someone clicks the sort ascending / descending button, a loading message appears, the sort happens, then the loading state disappears. Below is a pretty rudimentary version:
<template>
<Page>
<Label v-if="loadingItems">Loading Items</Label>
<StackLayout v-else>
<Label v-for="item in items" :key="item.id" :text="item.title" />
<Button #tap="sort('asc')" text="Sort Ascending" />
<Button #tap="sort('desc')" text="Sort Descending" />
</StackLayout>
</template>
<script>
export default {
data() {
return {
loadingItems: false,
items: [
{
id: 1,
title: "Item 1"
},
{
id: 3,
title: "Item 3"
},
{
id: 3,
title: "Item 3"
},
{
id: 4,
title: "Item 4"
},
{
id: 5,
title: "Item 5"
}
]
},
methods: {
sort(dir) {
this.items = this.items.sort((a,b) => {
return dir == 'asc' ? a.id - b.id : b.id - a.id
});
}
}
}
</script>
Hope this helps anyone with the same issue
I am trying to load topics (just string values) from a backend and display them in the ListPicker. However the ListPicker won't update it's items which should be displayed.
The code is as follows:
<template>
<Page>
<ActionBar title="Create Challenge" icon="">
<NavigationButton text="Back" android.systemIcon="ic_menu_back" #tap="goBack" />
</ActionBar>
<StackLayout>
<Label text="TOPIC" class="fab lblSubTitle"/>
<ListPicker :items="topics" v-model="selectedItem" />
<Button text="check" #tap="checkIt" />
</StackLayout>
</Page>
</template>
<script>
import {ObservableArray} from 'tns-core-modules/data/observable-array';
import {FirebaseService} from '../../services/firebase-service';
export default {
data() {
return {
selectedItem: 0,
topics: new ObservableArray(["some", "hardcoded", "items"])
};
},
methods: {
goBack() {
this.$navigateBack();
},
checkIt() {
this.topics.push("new item");
}
},
created() {
console.log("Create Challenge - Loading Topics")
// Fetch additional items from the Firebase DB
FirebaseService.fetchTopics().then(result => {
result.forEach(topic => {
this.topics.push(topic);
});
});
}
}
</script>
<style scoped>
.lblSubTitle {
font-size: 15;
margin: 10dp;
color: red;
}
</style>
So the FirebaseService.fetchTopics() returns an array of strings. This works perfektly fine and adds the received values to the ObserveableArray topics.
However the ListPicker only shows the hardcoded values. Not the dynamically added ones. Also the checkIt() method won't update the view.
I have tried to change topics to a conventional array with no effect.
Link to the Playground
NativeScript Version: 6.5.0
Android Device: Pixel 2 - Android 9
ListPicker doesn't listen to changes on ObservableArray. You must use a simple Array and mutate the changes
this.topics = [...this.topics, "new item"];
I need parse content from webview in my application,
is it possible? Now i can get only url page.
I tried search content in LoadFinished object, but i not found
WebView Component:
<template>
<Page class="webview-page">
<ScrollView>
<WebView height="100%" src="https://www.fl.ru/login/"
#loadFinished="parsePage" />
</ScrollView>
</Page>
</template>
<script>
export default {
methods: {
parsePage(webargs) {
//I need gets content here
console.log(webargs.url);
}
},
data() {
return {};
}
};
</script>
I expect get all html content from pageview
Try this,
<template>
<Page class="page">
<ActionBar title="Home" class="action-bar" />
<GridLayout>
<WebView src="https://www.nativescript.org/" #loadFinished="onLoadFinished"
:visibility="visibility"></WebView>
<ScrollView v-if="html">
<Label class="h3" :text="html" textWrap="true"></Label>
</ScrollView>
</GridLayout>
</Page>
</template>
<script>
import {
device
} from "platform";
export default {
data() {
return {
html: ""
};
},
computed: {
visibility: function() {
return this.html ? "hidden" : "visible";
}
},
methods: {
onLoadFinished: function(args) {
const that = this,
webView = args.object,
jsStr = "document.body.innerHTML";
if (webView.ios) {
webView.ios.evaluateJavaScriptCompletionHandler(jsStr,
function(
result,
error
) {
if (error) {
console.log("error...");
} else if (result) {
that.html = result;
}
});
} else if (webView.android) {
// Works only on Android 19 and above
webView.android.evaluateJavascript(
jsStr,
new android.webkit.ValueCallback({
onReceiveValue: function(html) {
that.html = html;
}
})
);
}
}
}
};
</script>
If you like support for older Android versions (API level 17 & 18), the implementation gets bit difficult. You will have to implement a #JavascriptInterface interface which can be written only in Java. There is already an issue reported on enabling the ability to access Java Annotation form JavaScript. You may have to write an Android library project where you implement the #JavascriptInterface and utilise it to track content as explained here.
Playground Sample
I have a form that display a Many-to-Many relationship between two entities Brand and Distribution.
I display the distributions through a CheckboxGroupInput component in a brand page. During the form edition (Edit component), I managed to pre-check distributions that have been checked previously during the creation (Create) (this is not specified in the documentation) but I can't check or uncheck any distributions.
Here's the edition form :
export class BrandEdit extends Component {
constructor(props) {
super(props)
this.state = {
isLoading: true
}
}
componentDidMount() {
//Find all distributions
restClient(GET_MANY, 'distribution', {
sort: {field: 'name', order: 'ASC'}
})
.then(response => {
return restClient(GET_ONE, 'brand', {
id: this.props.match.params.id
}).then(brandResult => {
let distributionsIds = []
brandResult.data.distributions.forEach(distribution => {
distributionsIds.push(distribution.id)
})
this.setState({
distributions: response.data,
distribution_checked_ids: distributionsIds,
isLoading: false,
})
});
})
}
render() {
const { isLoading, distributions, distribution_checked } = this.state
return (
<div>
{
<Edit {...this.props}>
<SimpleForm>
<DisabledInput source="id"/>
<TextInput label="Nom" source="name"/>
<CheckboxGroupInput
source="distributions"
input={{
value: this.state.distribution_checked_ids,
onChange: (checkedDistributionIds) => {
this.setState({ distribution_checked_ids: checkedDistributionIds });
}
}}
choices={distributions}
optionValue="id"
optionText="name"
/>
</SimpleForm>
</Edit>
}
</div>
);
}
}
Any ideas ?
Thanks
We need to pass an array of Distribution id to the component and not and array of Distribution objects.
Here's the component :
<CheckboxGroupInput
source="distributions"
choices={distributions}
optionValue="id"
optionText="name"
/>
Data should look like :
{
"id": 2115,
"name": "Test",
"distributions": [12, 13, 14]
}
Please remove the input prop. Why would you handle form state by yourself? AOR uses redux-form to handle that.