QML file include - or one monolithic file (structure QML code)? - qml

This is a QML newbie question. From the table view example I have code like this:
Column {
anchors.top: toolbar.bottom
.....
TabView {
id:frame
......
Tab {
title: "XmlListModel"
...
}
Tab { ...
As the qml file gets quite long, I wonder if I can have nested qml files
Column {
anchors.top: toolbar.bottom
.....
TabView {
id:frame
......
<include tab 1 qml file> <-- possible ????? -------
<include tab 2 qml file>
If such an include is not possible, how does a QML programmer structure his code? Even in the simple example there are already far too many lines to handle IMHO.
-- Edit --
After the answer I have found this readworthy:
http://qt-project.org/doc/qt-5.0/qtqml/qtqml-syntax-directoryimports.html
How to reuse code at QML

No, you can't do "includes" but you can put things into named objects.
For example, take your Tab #1 file, put it in a file called "Tab1" (or a better name that relates to what it's actually displaying; I don't know so can't help you with a name).
So in Tab1.qml we have:
import ...
Tab {
id: tab1
...
}
And then in the main file you can now reference it:
...
Tabview {
id: frame
Tab1 { id: tab1 }
}
You'll note that I included an id for it again, as the parent won't be
able to reference the id within the child without it. (they can be different names, but don't do that. Animals will cry. Actually, you can leave out the id in the child as well, but many people like being able to see it within a file.)

Related

Object reactivity of complex object

I have an issue with complex object reactivity.
I've read everything I can on stack to find a way to solve it, but nothing works. I've looked at object reactvity and array caveats on vuejs, but not working either.
So I'm asking some help please.
Let me explain the project:
I have 2 columns :
- on the left side, I CRUD my content
- on the right side, I display the results
I have my object, and I'm adding new elements on its "blocks" property (text, images, etc...)
[
{
"uid": 1573224607087,
"animation": "animationName",
"background": {
"bckColor": "#ff55ee",
...
},
"blocks": []
}
]
On click event, I add a new element via this method. Everything is ok, I can CRUD a block.
addBloc(el) {
if (el.type == "text") {
const datasA = {
type: "text",
uid: Date.now(),
slideId: this.pagination.currentPage,
content: el.content,
css: {
color: "#373737",
...
},
...
};
this.slides[this.pagination.currentPage].blocks.push(datasA);
this.$bus.$emit("newElement", datasA);
}
To modify the order of my elements on the display side, I added a drag and drop module to move my block on my DOM tree. Smooth dnd
The problem is, when I drang&drop my element, my object is updated correctly, but the DOM isn't. The dragged element goes back to its initial position.
What is strange, when I try to modify my block (the one I dragged), it modifies the other one.
I'me adding a small video, so you can see what's happening.
Small animation to show you what's going on
I add some more explainations.
I use event bus to communicate between my components, and the right side is using its own object!
I don't know how I can solve this issue.
Tell me if you need more information.
Thank you all !
EDIT 1 :
I added an id to each block to see what happens when I start Drag&Drop. ==> blocks are moving correctly. The problem is not coming from the method onDrop() but from my nested components if I understand well. They don't update. I'm going to search for this new issue.
I've added a new gif to show what's going on.
This is the nested structure
TheSidebar.vue => top container
<Container
:data-index="i"
#drop="onDrop(i,$event)"
:get-child-payload="itemIndex => getChildPayload(i, itemIndex)"
lock-axis="y"
>
<Draggable
v-show="pagination.currentPage === i"
v-for="(input, index) in slides[i].blocks"
:key="index.uid"
:id="'slideBlocksContainer'+index"
class="item"
>
blockId #{{input.uid}}
<AppContainer
v-if="input.type == 'text'"
:blocType="input.type"
:placeholder="input.content"
:id="index"
:slideId="i"
></AppContainer>
</Draggable>
</Container>
Then I have my AppContainer.vue file, which is a top level. In this I have the specific elements of each input type
And I have AppElement.vue file, which is common elements, I can use everywhere
Something like this
TheSidebar
--AppContainer
----AppElement
Know I don't know yet, how to force vue to update AppContainer.vue and AppElement.vue
EDIT 2 :
As suggested in this article I've changed the key of the component and now , when I drag and drop my elements, they stay where they are dropped.
What I see also, is that the AppElement inputs, are related to their own AppContainer. So everything is ok now, but I don't know if it is best practices.
The issue appears to be that the Smooth dnd library you are using is not updating the array of blocks that you are passing to it, it is likely making a copy of the array internally. So when you change the position of the blocks by dragging and dropping, you are not changing your blocks array, just the internal copy.
Looking at the Smooth dnd documentation, if you wanted to access the modified array you could try using the drag-end event handler:
onDragEnd (dragResult) {
const { isSource, payload, willAcceptDrop } = dragResult
}

Dynamically set Label's "id" property

I'm developing an application for SailfishOS using the QML language.
I want to dynamically set the id property of a Label by using an if condition.
This is my code:
Label {
id: {
if(myBool == false) {
thisText()
} else {
notThatText()
}
}
width: parent.width
horizontalAlignment: Text.AlignRight
text: ""
font.pixelSize: Theme.fontSizeLarge
}
This code is placed into my CoverPage.qml file, the one that display things on the application's cover while in background.
By doing this, the cover is simply black, nothing is displayed.
Is it possible in QML to do this?
Thanks in advance!
The Qt doc says this.
While it may look like an ordinary property, the id attribute is not an ordinary property attribute, and special semantics apply to it;
You cannot set the id of a QML component at runtime.(Correct me if I am wrong). You might find objectName property useful. But I don't understand why you are trying to assign dynamic id.
I have a use case where use a dynamic/specific id could be useful. The id could be view with Gammaray, it can help for debugging.
GridLayout {
id: layout
Repeater {
model: foo
Bar {
id: bar_X_Y // bar_{model.row}_{model.column}
}
}
}
But as far as I know, it's not possible.

How to refs on class attribute

I build an app for file system. Files from root directory will be load initially. After user taps a directory subfiles should be load and so on. Here is a problem: I dont know
how deep users file structure is.
Means I dont know how many views I will need.
Currently I want to make it this way(But I am happy for ideas to improve my app): User taps on a file. The controller should catch the tap event and should create a new View where loaded data should be placed in.
Thats the theory.
In praxis all my file views has a class. Lets call it 'fileStructureView'. And I have only one Controller for x-number of fileStructureViews.
In my contollers config I made it this way:
Code:
config: {
refs : {
fileStructureViews : 'list[class="fileStructureView"]'
},
control : {
fileStructureViews : {
onItem : 'onItem'
}
}
},
onItem : function() {
alert('Test');
}
In my view I set a handler on the items which fires an onItem event.
But my onItem Event will never executed.
If I choose view-ids in the refs it works, but because I have to create an unknown number of views I have to give classes to my views.
Thanks for help.
I suggest You to use routing in controller and dynamically create views. For example go to to sencha sdk and find project Touch Style.
Hope this helps

ComponentQuery for specific listitem

I want to create a controller with a ref to a specific item in a List, is this possible?
So really I need the ComponentQuery to reference a listitem. I am setting the data inline in the list:
Ext.define('MyApp.view.MyView', {
extend : 'Ext.List',
alias : 'widget.mylist',
config : {
title : 'Title',
itemTpl : ['{displayName}'].join(''),
data : [{
displayName : 'One',
id : 1
}, {
displayName : 'Two',
id : 2
}, {
displayName : 'Three',
id : 3
}, {
displayName : 'Four',
id : 4
}]
}
});
Then on in my controller (this is obviously not working):
refs : {
oneListItem: 'mylist.list[id=1]',
twoListItem: 'mylist.list[id=2]',
threeListItem: 'mylist.list[id=3]',
fourListItem: 'mylist.list[id=4]'
}
Hopefully this is possible. If it is not I can listen for an 'itemtap' on the list and call a method from there, however, I would prefer not have to do that for cleanliness.
Thanks in advance.
Brad
First off: adding id to the objects in your data section will not have any effect, that is just the data that's taken and substituted in the itemTpl you specified before.
As to your question: Sencha Touch 2 Documentation does not mention a way to have Ext.ComponentQuery select the n-th child of a Component, so, assuming you want to create your list objects with inline data, the only way I see is to use the id that Sencha automatically applies to each listItem on creation.
These ids are of the #ext-listitem-1 kind, see an example of how to use them in this Sencha Fiddle demo.
This is however bad practice, because you cannot rely on those id, if for example you add another list to your application they can change breaking your code.
I do not see any valid reason why you shouldn't use itemtap to act on the list, since with the event you get all the data you need to manipulate the proper list item:
itemtap( this, index, target, record, e, eOpts )
For completeness sake, I must mention that you could also add your items dynamically with Ext.dataview.List.add(), that way you should be able to create your dataitems with a custom id.
You might try Ext.select('.x-list-item-first'); or Ext.select('.x-list-item-last'); if you want a handle on the first or last item. I would encourage you to add an xtype set to "mylist" instead of using alias.

Reading n random files from a folder in Qt Quick

I'm writing my first Qt Quick application and I'm having lots of trouble doing things that are simple in other environments. I want to display n random images from a folder. For this purpose I'm using FolderListModel, but the trouble is that I don't see any methods to access list of files directly. So here's my hackish approach:
Use FolderListModel to read files
Use Text component as delegate
onTextChanged of Text component gets the filename (this part works) and adds it to some ListModel
randomize the ListModel and then use it to display files
I have many problems and questions, but first of all, what is the sane way to do this (please make it so that the list part doesn't have to be written in c++)?
There are 2 problems for now - I can't figure out how to access the ListModel from Text component; and I can' t figure out how to make the ListModel public/accessible from another component that will display the images.
Below is the code:
import QtQuick 1.0
import Qt.labs.folderlistmodel 1.0
ListView {
width: 200; height: 300
FolderListModel {
folder: "file:///C:/somefolder"
id: folderModel
nameFilters: ["*.jpg"]
}
Component {
id: fileDelegate
Text { id: intext
text: fileName
//the next line fails, Can't find variable: anotherModel
onTextChanged: anotherModel.append([{name: intext.text}]
)
}
}
model: folderModel
delegate: fileDelegate
ListModel {
id: anotherModel
}
}
Your code almost works for me. I get the error "QML ListModel: append: value is not an object". This is because you append arrays.
If you remove the brackets in line
anotherModel.append([{name: intext.text}])
it works. To make anotherModel a public property write it as
property ListModel anotherModel: ListModel {}