BB Cascade- How to change a variable of an attached object? - qml

so I'm new to the world of BB developement, QML, and C++ so please bear with me as I try to explain my issue.
I'm in the process of making an application that will seach for people from some far away API that returns an XML file that I will then parse and show the information in a list.
So in the first page I have two textfields that take in first name and last name. I also have a button that goes to the results page and is supposed to also send along the URL for contacting the API. Here's the code for the first page:
import bb.cascades 1.0
NavigationPane {
id: nav
Page {
content: Container {
TextField {
id: firstName
hintText: "First Name"
inputMode: TextFieldInputMode.Text
input {
flags: TextInputFlag.PredictionOff | TextInputFlag.AutoCorrectionOff
}
validator: Validator {
id: firstNameValidator
mode: ValidationMode.Immediate
errorMessage: "You must enter at least three characters."
onValidate: {
if (firstName.text.length >= 3 || firstName.text.length == 0)
state = ValidationState.Valid;
else
state = ValidationState.Invalid;
}
}
}
TextField {
id: lastName
hintText: "Last Name"
inputMode: TextAreaInputMode.Text
input {
flags: TextInputFlag.PredictionOff | TextInputFlag.AutoCorrectionOff
onSubmitted: {
if (firstNameValidator.state == ValidationState.Valid
&& lastNameValidator.state == ValidationState.Valid) {
theButton.text = "Valid enter!"
} else {
theButton.text = "Invalid enter!!!!"
}
}
}
validator: Validator {
id: lastNameValidator
mode: ValidationMode.Immediate
errorMessage: "You must enter at least three characters."
onValidate: {
if (lastName.text.length >= 3 || lastName.text.length == 0)
state = ValidationState.Valid;
else
state = ValidationState.Invalid;
}
}
}
Button {
id: theButton
text: "Search"
onClicked: {
if (firstNameValidator.state == ValidationState.Valid
&& lastNameValidator.state == ValidationState.Valid) {
text = "Valid button press!"
pushed.theUrl = link to url
pushed.dataSource.source = link to url
nav.push(pushed)
} else {
text = "Invalid button press!!!"
}
}
}
}
}
attachedObjects: [
AdvancedResult {
id: pushed
}
]
}
And here's the code for the second page:
import bb.cascades 1.0
import bb.data 1.0
Page {
property string theUrl
property alias dataSource: dataSource
content: ListView {
id: myListView
// Associate the list view with the data model that's defined in the
// attachedObjects list
dataModel: dataModel
listItemComponents: [
ListItemComponent {
type: "item"
// Use a standard list item to display the data in the data
// model
StandardListItem {
imageSpaceReserved: false
title: ListItemData.FIRST + " " + ListItemData.LAST
description: ListItemData.JOB
status: ListItemData.PHONE
}
}
]
onTriggered: {
var selectedItem = dataModel.data(indexPath);
// Do something with the item that was tapped
}
}
attachedObjects: [
GroupDataModel {
id: dataModel
grouping: ItemGrouping.None
},
DataSource {
id: dataSource
// Load the XML data from a remote data source, specifying that the
// "item" data items should be loaded
source: theUrl
query: some query
type: DataSourceType.Xml
onDataLoaded: {
// After the data is loaded, clear any existing items in the data
// model and populate it with the new data
dataModel.clear();
if (data[0] == undefined) {
dataModel.insert(data);
} else {
dataModel.insertList(data);
}
}
}
]
onCreationCompleted: {
// When the top-level Page is created, direct the data source to start
// loading data
dataSource.load();
}
}
As you can see in the first page, I try attaching the source for the data source object in two different ways: by pushing it directly to the global string theURL and by pushing it directly to the source variable of the datasource object but both ways don't seem to be working. What am I doing wrong? This is driving me crazy as I haven't been able to figure it out yet it seems like such an easy answer.
EDIT: Okay this is seriously so unintuitive! So I was able to fix it by simply adding theUrl: "link to url" under the "pushed" object in the main page and for the second page on the top adding "property string theUrl: gibberish". But now that I'm trying to make it so that the url is different depending on the user input, it just doesn't work.
So here's the current code I have so far:
import bb.cascades 1.0
NavigationPane {
id: nav
property string firstName: ""
property string lastName: ""
property string jobTitle: ""
property string test: url1
Page {
content: Container {
TextField {
id: firstNameField
hintText: "First Name"
inputMode: TextFieldInputMode.Text
input {
flags: TextInputFlag.PredictionOff | TextInputFlag.AutoCorrectionOff
}
validator: Validator {
id: firstNameValidator
mode: ValidationMode.Immediate
errorMessage: "You must enter at least three characters."
onValidate: {
if (firstNameField.text.length >= 3 || firstNameField.text.length == 0) {
state = ValidationState.Valid;
firstName = firstNameField.text;
} else {
state = ValidationState.Invalid;
}
}
}
}
TextField {
id: lastNameField
hintText: "Last Name"
inputMode: TextAreaInputMode.Text
input {
flags: TextInputFlag.PredictionOff | TextInputFlag.AutoCorrectionOff
onSubmitted: {
if (firstNameValidator.state == ValidationState.Valid
&& lastNameValidator.state == ValidationState.Valid) {
theButton.text = "Valid enter!"
} else {
theButton.text = "Invalid enter!!!!"
}
}
}
validator: Validator {
id: lastNameValidator
mode: ValidationMode.Immediate
errorMessage: "You must enter at least three characters."
onValidate: {
if (lastNameField.text.length >= 3 || lastNameField.text.length == 0) {
state = ValidationState.Valid;
lastName = lastNameField.text;
} else {
state = ValidationState.Invalid;
}
}
}
}
Button {
id: theButton
text: "Search"
onClicked: {
if (firstNameValidator.state == ValidationState.Valid
&& lastNameValidator.state == ValidationState.Valid) {
//text = "Valid button press!"
pushed.theUrl = url2
text = pushed.theUrl;
nav.push(pushed)
} else {
text = "Invalid button press!!!"
}
}
}
}
}
attachedObjects: [
AdvancedResult {
id: pushed
theUrl: test
}
]
}
So I'm currently using two different urls for testing: url1 and url2. url1 is set to test and test is set to theUrl under pushed. That works fine but when i add the line pushed.theUrl = url2; the page still returns the result for url1. So what I did is add the line text = pushed.theUrl; for testing purposes where "text" is the text shown on the button and when I run the app, even though it doesn't return to me a page, "text" is in fact set to url2 meaning that pushed.theUrl is url2. This has all been very unintuitive and not an enjoyable experience at all so far. What I'm trying to do here (get the input from a textfield, add it to a link and send it off to another page) would have taken me an hour at most on Android to code.

Related

iOS16 - How to navigate through hidden links with an optional value?

I previously used an optional value to click a hidden link to navigate in my app. Something like this example in Hacking With Swift:
#State private var selection: String? = nil
var body: some View {
....
NavigationLink(destination: Text("View A"), tag: "A", selection: $selection) { EmptyView() }
NavigationLink(destination: Text("View B"), tag: "B", selection: $selection) { EmptyView() }
Button("Tap to show A") {
selection = "A"
}
Button("Tap to show B") {
selection = "B"
}
With iOS 16 this is deprecated. I am currently setting an optional value and when it's not nil I want a link to open. I can't figure out how to do it with the new NavigationLink/Value/Destination combination. Has anyone else figured out how to do it?
I created a new projects and switched ContentView to the following:
private enum Destinations: Hashable {
case empty
case general
case myQuestionView(String)
}
struct ContentView: View {
#State private var selection: Destinations?
#State private var mySelectedString: String?
var body: some View {
NavigationSplitView {
List(selection: $selection) {
NavigationLink(value: Destinations.general) {
Text("Example")
}
if mySelectedString != nil {
NavigationLink(value: Destinations.myQuestionView(mySelectedString!)) {
Text("String: \(mySelectedString ?? "no name")")
}
}
Button(action: {
mySelectedString = "A Name"
}, label: {
Text("Set the value")
})
}
} detail: {
NavigationStack {
switch selection ?? .empty {
case .empty: Text("Please select an option to continue.")
case .general: Text("Result of this option")
case .myQuestionView(let aString): Text("Hello \(aString)")
}
}
}
}
}
Here the Set the value button sets the selectedString which makes the link appear but I can't make it automatically navigate AND, ideally, it would never appear and would navigate when the value is set.
Okay... the answer is super simple and leaving the question and this answer up in case it helps someone else.
You don't need to have an invisible NavigationLink. You just need to set the selection to the new destination: selection = .myQuestionView("this name instead"). Or, if I was using the navigation path NavigationStack(path: $myPath) I'd just append it.
So in this specific example ContentView would now be:
private enum Destinations: Hashable {
case empty
case general
case myQuestionView(String)
}
struct ContentView: View {
#State private var selection: Destinations?
var body: some View {
NavigationSplitView {
List(selection: $selection) {
NavigationLink(value: Destinations.general) {
Text("Example")
}
Button(action: {
selection = .myQuestionView("this name instead")
}, label: {
Text("Set the value")
})
}
} detail: {
NavigationStack {
switch selection ?? .empty {
case .empty: Text("Please select an option to continue.")
case .general: Text("Result of this option")
case .myQuestionView(let aString): Text("Hello \(aString)")
}
}
}
}
}

How to change button property when starting a vuex-electron app?

I am working on a vuex-electron application.
There are several buttons on the main page, when clicked, a file is stored in a specific folder, and at the same time, the property of the button turns to [saved], when this [saved] button is clicked again, a message pops up, asking whether to overwrite or not the old file.
But the problem is : when the app is restarted, all of the buttons' property is initialized to [not saved], so, even though the same file has already been stored, when button is clicked, the old file is overwritten without any pop-ups asking whether to overwrite the existing file or not.
I want to change the feature as below:
when the app is restarted, check file existence first, then based on the result, change button property to [saved].
Is this possible?
If possible, where should I add the logic in.
I am a complete beginner on vue.js.
Have been looking up for a while, but did not find any useful information.
From the following thread, learned that state data is stored in a json file, does this mean I need to change the state? Currently, button properties are not saved in this json file.
Where is the state of a Electron-Vue application stored?
//code
v-for="(btn,i) in buttons" :key="i" #click="save(btn)"
computed: {
buttons() {
let r = this.$store.state.App.aData.filter(x=> {
if (this.aType==='abc') {
return x.video.toString() === '1'
} else {
return !x.video
}
}).map(x => {
let y = this.$copy(x)
y.saved = this.savedData.includes(x.index)
y.disabled = this.appState !== 'def'
return y
})
let index = this.aType==='abc'?998:999
r.push({
index,
a_name:'others',
e_number:this.aType==='abc'?'N':'999',
disabled: this.appState !== 'def',
saved: this.savedData.includes(index),
video:''
})
return r
},
},
methods:{
save(btn) {
if (btn.disabled) {
return
}
if (btn.saved) {
this.$buefy.dialog.confirm({
message: '??',
confirmText: 'overwrite?',
cancelText: 'cancel',
type: 'is-danger',
hasIcon: true,
onConfirm: () => {
if (this.aType==='xyz') {
this.saveXyz(btn)
}
if (this.aType==='abc') {
this.saveAbc(btn)
}
}
})
return
}
if (this.aType==='xyz') {
this.saveXyz(btn)
}
if (this.shootingType==='abc') {
this.saveAbc(btn)
}
},
saveXyz(data) {
if (this.xyzBuffer) {
//create a file and store to a folder
this.savedData.push(data.index)
let idx = this.$store.state.App.aData.findIndex(x=>x.index===data.index)
if (idx > -1) {
idx++
if (idx < this.$store.state.App.aData.length) {
this.selectedData = idx
this.aType = this.$store.state.App.aData[this.selectedData].video ? 'abc' : 'xyz'
this.scrollTo(this.selectedData)
}
}
this.cancel()
})
}
}
},
saveAbc(data) {
if (this.recording) {
return
}
//create a file and store to a folder
this.savedData.push(data.index)
let idx = this.$store.state.App.aData.findIndex(x=>x.index===data.index)
if (idx > -1) {
idx++
if (idx < this.$store.state.App.aData.length) {
this.selectedData = idx
this.aType = this.$store.state.App.aData[this.selectedData].video ? 'abc' : 'xyz'
this.scrollTo(this.selectedData)
}
}
this.cancel()
}
})
},
},

Use variable for json answer in QML BlackBerry

I have a project in QML, MomenticsID (BlackBerry10).
I need to use the variable for json answer.
I can not find some manual for this problem.
When I use var result = data.item_01[0] everything is OK and program results in correct value.
But when I use var result = data.variable I won't get an answer.
main.qml
import bb.cascades 1.4
import bb.data 1.0
Page {
id: page
property string variable: "item_01[0]"
Container {
Label {
id: label
}
}
attachedObjects: [
DataSource {
id: dataSource
type: DataSourceType.Json
source: "asset:///data.json"
onDataLoaded: {
var result = data.item_01[0] // it's OK, result is "a"
//var result = data.variable // it's NOK, nothing result
label.text = result.answer_01
}
}
]
onCreationCompleted: {
dataSource.load()
}
}
data.json
{
"item_01": [
{
"answer_01": "a"
}
]
}

Load more functionality using SwiftUI

i have used ScrollView with HStack, now i need to load more data when user reached scrolling at last.
var items: [Landmark]
i have used array of items which i am appeding in HStack using ForEach
ScrollView(showsHorizontalIndicator: false) {
HStack(alignment: .top, spacing: 0) {
ForEach(self.items) { landmark in
CategoryItem(landmark: landmark)
}
}
}
What is the best possible solution to manage load more in SwiftUI without using custom action like loadmore button.
It's better to use ForEach and List for this purpose
struct ContentView : View {
#State var textfieldText: String = "String "
private let chunkSize = 10
#State var range: Range<Int> = 0..<1
var body: some View {
List {
ForEach(range) { number in
Text("\(self.textfieldText) \(number)")
}
Button(action: loadMore) {
Text("Load more")
}
}
}
func loadMore() {
print("Load more...")
self.range = 0..<self.range.upperBound + self.chunkSize
}
}
In this example each time you press load more it increases range of State property. The same you can do for BindableObject.
If you want to do it automatically probably you should read about PullDownButton(I'm not sure if it works for PullUp)
UPD:
As an option you can download new items by using onAppear modifier on the last cell(it is a static button in this example)
var body: some View {
List {
ForEach(range) { number in
Text("\(self.textfieldText) \(number)")
}
Button(action: loadMore) {
Text("")
}
.onAppear {
DispatchQueue.global(qos: .background).asyncAfter(deadline: DispatchTime(uptimeNanoseconds: 10)) {
self.loadMore()
}
}
}
}
Keep in mind, that dispatch is necessary, because without it you will have an error saying "Updating table view while table view is updating). Possible you may using another async way to update the data
If you want to keep using List with Data instead of Range, you could implement the next script:
struct ContentView: View {
#State var items: [Landmark]
var body: some View {
List {
ForEach(self.items) { landmark in
CategoryItem(landmark: landmark)
.onAppear {
checkForMore(landmark)
}
}
}
}
func checkForMore(_ item: LandMark) {
guard let item = item else { return }
let thresholdIndex = items.index(items.endIndex, offsetBy: -5)
if items.firstIndex(where: { $0.id == item.id }) == thresholdIndex {
// function to request more data
getMoreLandMarks()
}
}
}
Probably you should work in a ViewModel and separate the logic from the UI.
Credits to Donny Wals: Complete example

Invocation from function in Blackberrry 10 qml?

I have invoked invite to BBM while clicking a button in qml,but i need to send the invitation for the contacts how to do this?my code
Button {
text: "Invite"
onClicked: {
invokeQuery.uri = "pin:210000A"
invokeQuery.updateQuery();
}
attachedObjects: [
Invocation {
id: invokeShare
query: InvokeQuery {
id: invokeQuery
}
onArmed: {
trigger("bb.action.INVITEBBM");
}
}
]
}
Can anyone send me some solutions to solve this.?
Thanks
Invocation query is immutable object which means that values in the query are not dynamic. If you want to update query in dynamic you need to do it via control signals or variables.
For example, you have a component called Inviter with the pin property exposed:
import bb.cascades 1.0
Container {
property string pin
Button {
text: "Invite to BBM"
onClicked: {
query.uri = "pin:" + pin
invoke.trigger("bb.action.INVITEBBM")
}
}
attachedObjects: [
Invocation {
id: invoke
query: InvokeQuery {
id: query
invokeTargetId: "sys.bbm.sharehandler"
onQueryChanged: {
invoke.query.updateQuery()
}
}
}
]
}
Then you can use it this way:
import bb.cascades 1.0
Page {
Container {
layout: DockLayout {
}
TextArea {
id: pinEditor
hintText: "Enter PIN to invite"
onTextChanged: {
inviter.pin = text
}
input.submitKey: SubmitKey.Send
}
Inviter {
id: inviter
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
}
}
}
Also, don't forget to enable "Blackberry Messenger" permission in your bar-descriptor.xml and add following libraries to your .pro file:
LIBS += -lbbplatformbbm
LIBS += -lbbsystem