I am using ng-select as a custom form control and using controlvalueaccessor.
when i try to set value from parent component, the 'WriteValue' method in the child component is not getting the updated model value.
Parent component:
this.selectedEntityItems = ["a", "b", "c"];
<child-component
[config]="{ getPage: getSelectInfinitePage(), multiple: true }"
[(ngModel)]="selectedEntityItems" //the new values set to this variable is not getting updated in child component
[disabled]="!hasSubjectType"
[placeholder]="placeholderText"
name="selectInfinite"
(onChange)="onCustomItemChange()"
>
</child-component>
child-component:
publi writeValue(preselectedValues: string[]): void {
this.values = preselectedValues; //preselectedValues is always coming as undefined
this.onModelChange(this.values);
}
Related
I have a custom control child component. When i try to set a value updated in parent component, the child is not getting the updated value. Please let me know what i am doing wrong here.
parent .html
<child-component
[config]="{ getPage: getSelectInfinitePage(), multiple: true }"
[(ngModel)]="selectedEntityItems" //in the child compoennt, this always coming as empty
[disabled]="!hasSubjectType"
[placeholder]="placeholderText"
name="selectInfinite"
(onChange)="onCustomItemChange()"
>
</child-component>
parent .ts
this.selectedEntityItems = ['a', 'b', 'c'];
Child component .ts (custom control using ControlValueAccessor)
writeValue(preselectedValues: ValueType): void {
this.values = preselectedValues; //here it is always coming empty
this.onModelChange(this.values);
}
I have defined a MyElement element (in the MyElement.qml file) as the following:
Rectangle {
Item {
}
Component.onCompleted: {
console.warn(children.length)
}
}
Let's call the Item element defined within MyElement the internal child. Now, I'm using the MyElement element in the following way:
MyElement {
Item {
}
}
Here another one Item element is used as a child of MyElement. Let's call this Item element the external child. To understand my question below one should clearly understand the difference between internal and external children.
The output for the presented code will be 2, i.e. both Item elements are calculated as children.
In the future I want to iterate in the block Component.onCompleted only over external children, not over internal (external children go after internal). But for this I have to know a children index from which I have to start (in the given example this index is 1). Is there a way to get this index or, in other words, the number of internal children? Thanks.
There is no internal mechanism in Qt to distinguish internal children from those which are defined outside of the own QML definition.
My workaround is as follow
//MyElement.qml
Rectangle {
id: root
readonly property Item last_item: lastone
Item {
id: someitem
}
Item {
id: lastone
}
Component.onCompleted: {
var external_started = false;
for(var i = 0 ; i < root.children.length ; ++i)
{
if(external_started)
console.log(root.children[i].toString(), 'is external');
else if(root.children[i]===last_item)
external_started = true;
}
}
}
and
MyElement {
Item {
objectName: 'I am external'
}
}
It's a dirty hack but it works.
I'm saving a reference to the last item in a readonly property called last_item and that will distinguish my last item in qml definition.
Other items which are added outside of the qml file, will be placed after this item in the children list.
This is a simplification of the situation I am dealing with in main.qml file:
Component {
id: component1
property string stringIneedToPass: "Hello"
Text { text: stringIneedToPass }
}
Component {
id: component2
Rectangle {
id: myRectangle
property string stringIneedToReceive = component1.stringIneedToPass; //this doesn't work
}
}
Obviously my situation is more complicated. But in the end I just need to understand how this kind of transfer should be done!
Thank you all!
First of all, a Component element cannot have properties. Components are either loaded from files, or defined declaratively, in the latter case they can contain only one single root element and an id.
Second - you cannot do assignment in the body of an element, only bindings.
Third - you cannot reference properties defined inside an element inside a component from the outside, as that object doesn't exist until the component is instantiated. Such objects can only be referenced from inside.
Other than that, it will work as expected, if you can reference it, you can bind or assign it to a property, depending on what you want.
So you can simply have the string property external:
property string stringIneedToPass: "Hello"
Component {
id: component1
Text {
text: stringIneedToPass
}
}
Component {
id: component2
Rectangle {
id: myRectangle
property string stringIneedToReceive: stringIneedToPass
}
}
I have a string id of an object I need to find in QML tree.
For example:
var idToFind = "myBtnId"
Can I do something like the following?
var objectThatINeed = myMainWindow.findObjectById(idToFind)
As far as I understand I can use objectName for this purpose (at least from C++). Can I still reuse existing ids somehow without introducing the names?
I want to use this object as a parent for some other dynamically created controls.
No, you have to use objectName or some other property.
The id Attribute:
Once an object instance is created, the value of its id attribute cannot be changed. While it may look like an ordinary property, the id attribute is not an ordinary property attribute, and special semantics apply to it; for example, it is not possible to access myTextInput.id in the above example.
If you know the IDs of all items you want beforehand, you can create an object map for them and use that to look them up. For example:
Item {
property var idMap: ({ foo:foo, bar:bar })
Rectangle { id:foo }
Item { id:bar }
function findItemById(id) {
return idMap[id];
}
Component.onCompleted: console.log(findItemById('foo'), findItemById('bar'))
// qml: QQuickRectangle(0x1479e40) QQuickItem(0x148fbf0)
}
QJSValue MyEngine::getQmlObjectById(const QString& id) {
QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_JSEngine);
QV4::Scope scope(const_cast<QV4::ExecutionEngine *>(v4));
QV4::ScopedString name(scope, v4->newString(id));
return QJSValue(v4, v4->currentContext->getProperty(name));
}
Based on the answer from Phrogz, if you don't want an explicit map, you can assign the components to properties and then reference these with the [] operator:
Item {
id: page
property Component foo: Rectangle { color: "yellow" }
property Component bar: Item { }
Component.onCompleted: console.log(page["foo"], page["bar"])
//qml: QQuickRectangle(0x...) QQuickItem(0x...)
}
AA.qml
Item
{
id: drawLinesOnC
property string lineColour
property int lineDrawingSourceType
property variant startEndPointArray
}
main.qml
Loader
{
id: drawLineLoaderA
source: "AA.qml"
}
-
How to access the public properties of AA.qml page loaded through Loader drawLineLoaderA?
Solution is as follows:
drawLineLoaderA.source = "DrawLineLoader.qml"
if (drawLineLoaderA.status == Loader.Ready)
{
if (drawLineLoaderA.item && drawLineLoaderA.item.lineColour)
{
drawLineLoaderA.item.lineColour = "black"
drawLineLoaderA.item.lineDrawingSourceType = 2
}
}
In addition to what #TheIndependentAquarius said, you can declare property of the corresponding type in your loader:
Loader {
id: drawLineLoaderA
readonly property AA aa: item
source: "AA.qml"
}
And then use it like this:
if (drawLineLoaderA.aa) {
drawLineLoaderA.aa.color = "black"
}
Now you clearly stated that you deal with item of type AA and no other, and you'll get autocompletion on loaded item's properties as a bonus.
Note 1: Configuration of loaded item's properties should be done either in AA.qml itself (default values) or in Loader's onLoaded handler, as #troyane suggested.
Note 2: In your AA.qml you declared property string lineColour. You might be interested in color QML type. If you declare property color lineColour, QML will check that you assign valid values to this property. Moreover, color value is automatically converted to QColor when passed to C++ (and from QColor when passed from C++, of course).