QT5: Instantiate the same QML components multiple times - qml

I want to create component templates, meaning that I define my own MyButton type in a separate QML file, and I also want to define several singleton instances, like:
Predefined.qml:
pragma Singleton
[...]
property MyButton quitButton : quitButtonItem
MyButton {
id: quitButtonItem
text: qsTr("Quit")
imagesource : "qrc:/icons/quit.png"
}
then use it as:
Predefined { id: predefined }
Rectangle {
predefined.quitButton {
onClicked: console.log ("quit pressed.");
anchors.bottom : parent.bottom
anchors.horizontalCenter : parent.horizontalCenter
}
}
a.) I don't want to use Loaders for this -> overkill
b.) Don't really want to define as my QML files as many components I want to
clone (eg QuitButton.qml, BackButtonQml, etc.)
Any idea how to have it done?
Thanks

It can't be done.
The only way to instantiate an object declaratively from QML without a Loader is to create a new file for each component.
My suggestion for your usecase would simply be to create the files.
Alternatively, it appears you are doing some sort of navigation bar. What about unifying this in a single component?
I see two ways: have a single global nav bar for all your app, for example in ApplicationWindow's header, or have a commonbase type like YourPage.qml where you implement your bar, and then simply inherit from it for your actual content.
Personally, I adopted the first solution.

Related

Call parent component function

Struggling with some basic react-native stuff.
I have a master-detail scenario.
I push an edit button on a list-item to get to a detail-view, in that view I add a save-button to a navigation bar. Plain and simple.
Navigation to the detail-screen is done by:
this.props.navigator.push({
component: EditScreen,
passProps: {
id:myID,
},
onPress: this.onSave,
rightText: 'Save'
});
}
When tapping the save-button on the navigationbar, the onSave method of the parent component is called missing some context of the modifications.
How is this done in react-native?
I'm a react-native beginner, just want a clear view on how this stuff happens before moving to some flux-inspired libs ;-)
Update - Bind to this is not the problem
Flow:
I press a row which triggers an onEdit method passing in the ID of
the item i want to edit
onEdit pushes the EditScreen component on the navigator
onEdit adds the rightText and onPress bound to this (onSave is bound in the constructor as stated by some comments/answers)
When navigating to the EditScreen, the entity is fetched by its id
I edit the entity and press 'Save'
When pressing save, I return to the parent view and have no reference to the entity. That's my problem, not the binding of the method.
When using es6 classes, this is not automatically bound to the function, like React.createClass did. You have to manually bind this to the class functions. There are multiple ways of doing this.
You can use es6 fat arrows to bind this:
onPress: () => this.onSave()
You can use Function.prototype.bind():
onPress: this.onSave.bind(this)
You can also bind them on the constructor, so the method is created only once (most performant way, but won't matter very much if you are just building regular UI; if your onSave function is being called 1000 times per frame, then this might be a good idea):
constructor () {
super()
this.onSave = this.onSave.bind(this)
}

2 way databinding in Aurelia custom elements - bind custom element to parent viewmodel

In my application I have made a lot of "services" which I can inject in my viewmodels to save som redundancy and time.
Now I'm looking to take it 1 step further, and make those form elements (select, text, checkboxes - a select dropdown for starters) and turn them into custom elements, injecting the service in only the custom element.
I can get it working to some extent. The custom element (select in this case) is showing when I require it in the "parent" view, however when I change the selected value of the custom select element, it does not bind to the "parent" viewmodel, which is my requirement.
I want to be able to bind my selected value from the custom element to a property on the "parent" viewmodel via the bind attribute in it's template.
I'll update which a little code snippet in a few minutes.
create.js (what I refer to as parent viewmodel)
import {bindable} from 'aurelia-framework';
export class Create{
heading = 'Create';
#bindable myCustomElementValue = 'initial value';
}
create.html (parent view)
<template>
<require from="shared/my-custom-element"></require>
<my-custom selectedValue.bind="myCustomElementValue"></my-custom>
<p>The output of ${myCustomElementValue} should ideally be shown and changed here, as the select dropdown changes</p>
</template>
my-custom.js
import { inject, bindable} from 'aurelia-framework';
import MyService from 'my-service';
#inject(MyService )
export class MyCustomCustomElement {
#bindable selectedValue;
constructor(myService ) {
this.myService = myService ;
}
selectedValueChanged(value) {
alert(value);
this.selectedValue = value;
}
async attached() {
this.allSelectableValues = await this.myService.getAllValues();
}
}
What happens is initially the create.html view outputs "initial value", and as I change the value of the custom element select, the newly selected value gets alerted out, but it does not update the bound parent variable, which is still just displaying "initial value".
There are a couple of issues here:
You need to kebab-case any property names in the DOM due to case-insensitivity
selected-value.bind="property"
not
selectedValue.bind="property"
You need to bind two-way. When creating a #bindable using the decorator, one of the arguments is BindingMode which you use to set the default binding mode.
Aurelia sets some sensible defaults, e.g. the default for input value.bind=".." is two way without being explicit
If you don't want to set a default binding mode, you can just be explicit with your binding:
selected-value.two-way="prop"
Hope this helps :)
Edit: I think the API changed a little after this answer.
The #bindable decorator has the following sig:
bindable({
name:'myProperty',
attribute:'my-property',
changeHandler:'myPropertyChanged',
defaultBindingMode: bindingMode.oneWay,
defaultValue: undefined
})
One of the best places to check for quick reference is the Aurelia cheat-sheet in the docs:
http://aurelia.io/docs/fundamentals/cheat-sheet

Custom QML element - Item or Component

When declaring custom QML element in separate file (to be reusable across project(s)), which option is better, to declare it as Item or as Component and what are pros and cons for them?
Once I had the same doubts and the following example helped me. I put it there, hoping it can help you.
Here is the component defined in a file somewhere:
// FooBar.qml
// import whatever.you.need
Rectangle { }
Here a possible use as involved as main item of another component definition:
// ...
Component {
id: myFooBar
FooBar { }
}
// ...
Well, what about if the first one was as it follows?
// FooBar.qml
// import whatever.you.need
Component {
Rectangle { }
}
Actually it doesn't make much sense, besides the official documentation. Does it?
That's why I've never tried it... But I've also read the documentation, of course, as kindly pointed out by someone else!! It's far more helpful. :-)

accessing dojo attach point outside templated widget

I have a dojo attach point for list item which is inside a templated widget.I need to access the dojo attach point outside the widget in order to assign onclick to the list item created by the template.How do I do it?
Well, if you want to attach an event handler to it, you have to provide a function. You can override functions/properties from outside using getters and setters.
I also suggest using data-dojo-attach-event if you only need the node for attaching event handlers. For example by using: data-dojo-attach-event="onclick: myFunction". By doing this, it needs a function called myFunction in your templated widget, provide a default function (in your widget) for example:
myFunction: function() {
/** Stub */
},
And then you can do something like this from outside:
myWidget.set("myFunction", function(evt) {
console.log("Someone clicked on the list item");
});
Because the myFunction event handler is overriden, it will execute the function provided in the setter.
You could also directly access the attach points from outside using:
myWidget.listItemNode
When you have a data-dojo-attach-point="listItemNode". However, I don't think it's recommended to use it this way because now your widget is tightly coupled (you use the internal functionality of the widget).
HTML template:-
<div data-dojo-attach-point="infoDialog" >
</div>
Save this as "Template.html"
---------------------------------------------
load this html file using "dojo\text" plugin in your widget i.e.
"dojo/text!./templates/Template.html",
and store as template in widget main function
-----------------------------------------------
assign it as template string to the widget.
templateString: template,
now this template attachpoint(infoDialog) will be the part of your current widget scope.
-----------------------------------------------
attach event:-
this.infoDialog.onclick=function(){
alert("hello world")
};

ComponentQuery for a parent with ExtJS4?

Is there a way to query "up"? I'm in a Component and want to register listeners to it's parents events with control(). This requires a Query which gets me the parent of my main view.
In ExtJS4, you can use 'up()' from an Ext Element.
The params are a string of the parent element you wish to find e.g:
var parentEl = Ext.get('childID').up('div.parentClass');
If you provide some details about the structure of your components/elements I can give a specific example which should fit.
EDIT: To show going 'up' from a component
var myComponent = // however you've got it
var theParentEl = myComponent.getEl().up('div.parentClass');
Usually up('PARENTCLASS') is enough for what you're trying to do. Here is what I do all over the code so elements generates event for the form they are in:
items: [
...
{ xtype: 'checkbox', listeners: {
change: function() { this.up('window').fireEvent('checkboxchanged'); }
}}
...
]
As I understand, you want to listen to events dispatched by a component's parent from the child component's controller control function specifically.
There is not a query selector like 'parent < component' which you can put in the child controller's control function to listen to parent events.
Normally I would just add the parent view to the child's controller, then you could listen to it's events. But I assume you are not doing this because you are trying to delegate to different controllers or something.
You could fire an event in the child component whenever that parent event occurs. Inside the parent controller you could do it like this:
var child = parent.down('child');
child.fireEvent('myOwnEventName', arg1, arg2, arg3, etc);
Then you would add a handler for 'myOwnEventName' in the child controller's control function to run the logic you wanted for it.
If the parent doesn't have a controller then you should just add the parent component as a view in the child's controller.
The Sencha help says "Member expressions from candidate Components may be tested. If the expression returns a truthy value, the candidate Component will be included in the query:" in the http://docs.sencha.com/ext-js/4-0/#!/api/Ext.ComponentQuery help.
Took me a while to realize I can do the following in my controller:
this.control({
'window{down("testcomp")}[down]': { beforedestroy: this.doNotCloseIfUnsaved }
});
Using the {} operation, we can call any arbitrary code. Really bad from an efficiency standpoint, but it got the job done. I had to add the [down] because it runs component queries from right to left, so we have to be sure down() exists before we try running it (on every component). Thus, I was able to attach an event to whatever window holds my component.
Of course, other functions can be used too, like child() instead of down() if you want to ensure it is the immediate child rather than just anywhere below.