Why does QQmlComponent::create() return nullptr? - qtquick2

Under what conditions does QQmlComponent::create(QQmlContext *) fail by returning nullptr? The documentation only says " Returns nullptr if creation failed" without further detail. My backend C++ code tries to instantiate a MessageDialog from the following qml:
import QtQuick 2.0
import QtQuick.Dialogs 1.1
MessageDialog {
id: messageDialog
title: "My message"
text: "Fill in this message from C++"
onAccepted: {
console.log("Knew you'd see it my way!")
// Hide the dialog
visible = false
}
Component.onCompleted: visible = true
}
And here's a fragment of my backend constructor:
QQmlApplicationEngine engine;
// Note that component resource is local file URL,
// not network - no need to wait before calling create()?
QQmlComponent *component =
new QQmlComponent(&engine,
QStringLiteral("ui-components/MessageDialog.qml"));
// Following call returns nullptr
QObject *childItem = component->create();
Anyone know why? Thanks!

I don't think it is finding your qml file. Presuming your "ui-components/MessageDialog.qml" is in a qrc file, try:
new QQmlComponent(&engine, QStringLiteral("qrc:/ui-components/MessageDialog.qml"));

Related

AEM Workflow custom input data

I need to create a workflow in AEM that for a page (specified as payload) finds all the assets used on the page and uploads a list of them to an external service. So far I have most of the code ready, but business process requires me to use a special code for each of the pages (different for each run of the workflow), so that the list is uploaded to correct place.
That is when I have a question - Can you somehow add more input values for an AEM workflow? Maybe by extending the starting dialog, or adding some special step that takes user input? I need to be able to somehow specify the code when launching the workflow or during its runtime.
I have read a lot of documentation but as this is my first time using workflows, I might be missing something really obvious. I will be grateful for any piece of advice, including a link to a relevant piece of docs.
Yes, that is possible. You need to implement a dialog step in your workflow: https://docs.adobe.com/content/help/en/experience-manager-64/developing/extending-aem/extending-workflows/workflows-step-ref.html#dialog-participant-step
You could:
Create a custom menu entry somewhere in AEM (e.g. Page Editor, /apps/wcm/core/content/editor/_jcr_content/content/items/content/header/items/headerbar/items/pageinfopopover/items/list/items/<my-action>, see under libs for examples)
Create a client-library with the categories="[cq.authoring.editor]". So it is loaded as part of the page editor (and not inside the iframe with your page)
Create a JS-Listener, that opens a dialog if the menu-entry was clicked (see code). You can either use plain Coral UI dialogs, or my example misused a Granite page dialog (Granite reads the data-structure in cq:dialog, and creates a Coral UI component edit-dialog out of it - while Coral is the plain JS UI-framework)
Create a Java-Servlet, that catches your request, and creates the workflow. You could theoretically use the AEM servlet. But I often have to write my own, because it lacks some features.
Here is the JS Listener:
/*global Granite,jQuery,document,window */
(function ($, ns, channel, window) {
"use strict";
var START_WORKFLOW_ACTIVATOR_SELECTOR = ".js-editor-myexample-activator";
function onSuccess() {
ns.ui.helpers.notify({
heading: "Example Workflow",
content: "successfully started",
type: ns.ui.helpers.NOTIFICATION_TYPES.SUCCESS
});
}
function onSubmitFail(event, jqXHR) {
var errorMsg = Granite.I18n.getVar($(jqXHR.responseText).find("#Message").html());
ns.ui.helpers.notify({
heading: "Example Workflow",
content: errorMsg,
type: ns.ui.helpers.NOTIFICATION_TYPES.ERROR
});
}
function onReady() {
// add selector for special servlet to form action-url
var $form = ns.DialogFrame.currentFloatingDialog.find("form");
var action = $form.attr("action");
if (action) {
$form.attr("action", action + ".myexample-selector.html");
}
// register dialog-fail event, to show a relevant error message
$(document).on("dialog-fail", onSubmitFail);
// init your dialog here ...
}
function onClose() {
$(document).off("dialog-fail", onSubmitFail);
}
// Listen for the tap on the 'myexample' activator
channel.on("click", START_WORKFLOW_ACTIVATOR_SELECTOR, function () {
var activator = $(this);
// this is a dirty trick, to use a Granite dialog directly (point to data-structure like in cq:dialog)
var dialogUrl = Granite.HTTP.externalize("/apps/...." + Granite.author.ContentFrame.getContentPath());
var dlg = new ns.ui.Dialog({
getConfig: function () {
return {
src: dialogUrl,
loadingMode: "auto",
layout: "auto"
}
},
getRequestData: function () {
return {};
},
"onSuccess": onSuccess,
"onReady": onReady,
"onClose": onClose
});
ns.DialogFrame.openDialog(dlg);
});
}(jQuery, Granite.author, jQuery(document), window));
And here is the servlet
#Component(service = Servlet.class,
property = {
SLING_SERVLET_RESOURCE_TYPES + "=cq:Page",
SLING_SERVLET_SELECTORS + "=myexample-selector",
SLING_SERVLET_METHODS + "=POST",
SLING_SERVLET_EXTENSIONS + "=html"
})
public class RequestExampleWorkflowServlet extends SlingAllMethodsServlet {
#Override
protected void doPost(#Nonnull SlingHttpServletRequest request, #Nonnull SlingHttpServletResponse response) throws IOException {
final Page page = request.getResource().adaptTo(Page.class);
if (page != null) {
Map<String, Object> wfMetaData = new HashMap<>();
wfMetaData.put("workflowTitle", "Request Translation for " + page.getTitle());
wfMetaData.put("something", "Hello World");
try {
WorkflowSession wfSession = request.getResourceResolver().adaptTo(WorkflowSession.class);
if (wfSession != null) {
WorkflowModel wfModel = wfSession.getModel("/var/workflow/models/example-workflow");
WorkflowData wfData = wfSession.newWorkflowData(PayloadInfo.PAYLOAD_TYPE.JCR_PATH.name(), page.getPath());
wfSession.startWorkflow(wfModel, wfData, wfMetaData);
MyServletUtil.respondSlingStyleHtml(response, HttpServletResponse.SC_OK, "Triggered Example Workflow");
} else {
throw new WorkflowException("Cannot retrieve WorkflowSession");
}
} catch (WorkflowException e) {
MyServletUtil.respondSlingStyleHtml(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
}
} else {
MyServletUtil.respondSlingStyleHtml(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Internal error - cannot get page");
}
}
}

In AEM how to add a new node called “listeners” in the component dialog ,without using dialog.xml file

Actaully i am using "before submit" listener to do some validation for my selection box
I have reffered the following link:
https://helpx.adobe.com/experience-manager/using/classic_dialog_validation.html.
But "before submit" method calling only when i place ,
dialog listener in the dialog root level only.
how to place dialog listener in dialog root level(I checked in my project there is no dialog.xml file ,they using only java code to construct component dialog).
Can anyone please help me in this ?enter image description here
Dialog property construction code :
#DialogField(name ="./validateProgram",
fieldLabel = "Validate Program",
fieldDescription = "(synchronized)",
additionalProperties = {
#Property(renderIn = Property.RenderValue.TOUCH,
name = "validation",
value = "validation-program")
},
listeners = {
#Listener(name ="beforesubmit",
value = "function(dialog){" +
"return programValidation.beforeSubmit(dialog);"+
"}")
})
#Selection(
type ="select",
optionsProvider = " ",
dataSource = "/resourcetype/data")
public final String validateProgram;
Java Script code:
window.onload = function() {
programValidation.init();
};
var programValidation= programValidation|| (function($) {
function initialize() {
};
function validate() {
alert("inside validate method");
var res = true;
return res;
};
return {
beforeSubmit: validate,
init: initialize
}
})(jQuery);
You are using the cq component maven plugin this a very vital piece of information to get your question answered.
I have not used this plugin before, but in your case, I assume you are looking for the Listener annotation where you can set the name as beforesubmit and the value as function(){alert(1)}
you'll probably have to set the annotation on a local variable similar to how you would annotate a dialog field '#DialogField', find more docs in the plugin's usage page here: http://code.digitalatolson.com/cq-component-maven-plugin/usage.html
Hope this helps.
Thanks for your support. Found the following way to solve the issue .
I added ValidateFields method from within the 2 listeners (FIELD_LISTENER_LOAD_CONTENT and FIELD_LISTENER_SELECTION_CHANGED)
function ValidateFields(dialog) {
dialog.on("beforesubmit", function(e) {
if(<condtion failed>)
CQ.Ext.Msg.alert(CQ.I18n.getMessage("Error"), CQ.I18n.getMessage("<error message>"));
return false;
} else {
return true;
}
}, this);
}

BBOS 10 File picker not returning signals properly

I implemented a native File Picker on BlackBerry 10, after a bit of messing around it finally recognised the class, it opens fine and returns the file Address on the console but it looks like two signals are not working properly, baring in mind this is pretty much a straight copy of code from BlackBerry 10 docs.
using namespace bb::cascades::pickers;
void Utils::getFile() const{
FilePicker* filePicker = new FilePicker();
filePicker->setType(FileType::Music);
filePicker->setTitle("Select Sound");
filePicker->setMode(FilePickerMode::Picker);
filePicker->open();
// Connect the fileSelected() signal with the slot.
QObject::connect(filePicker,
SIGNAL(fileSelected(const QStringList&)),
this,
SLOT(onFileSelected(const QStringList&)));
// Connect the canceled() signal with the slot.
QObject::connect(filePicker,
SIGNAL(canceled()),
this,
SLOT(onCanceled()));
}
I wanted it to return the file url to qml with this (works fine with QFileDialog but that wouldn't recognise on my SDK) var test=utils.getFile()
if(test=="") console.debug("empty")
else console.debug(test)
But I'm getting these messages from the console: Object::connect: No such slot Utils::onFileSelected(const QStringList&) in ../src/Utils.cpp:27
Object::connect: No such slot Utils::onCanceled() in ../src/Utils.cpp:33
It is returning undefined from the else in the qml function when it opens,
Does anyone know where I cocked up or how I could get QFileDialog class to be found by the SDK?
I just wanted to give you a bit of an explanation in case you're still having some troubles. The concept's in Qt were a little foreign to me when I started in on it as well.
There are a couple ways you can do this. The easiest would probably be the pure QML route:
import bb.cascades 1.2
import bb.cascades.pickers 1.0
Page {
attachedObjects: [
FilePicker {
id: filePicker
type: FileType.Other
onFileSelected: {
console.log("selected files: " + selectedFiles)
}
}
]
Container {
layout: DockLayout {
}
Button {
id: launchFilePicker
text: qsTr("Open FilePicker")
onClicked: {
filePicker.open();
}
}
}
}
When you click the launchFilePicker button, it will invoke a FilePicker. Once a file is selected, the fileSelected signal will be fired. The slot in this case is the onFileSelected function (predefined), which logs the filepaths of the files that were selected (a parameter from the signal) to the console.
The C++ route is a little more work, but still doable.
If your class file was called Util, then you'd have a Util.h that looks something like this:
#ifndef UTIL_H_
#define UTIL_H_
#include <QObject>
class QStringList;
class Util : public QObject
{
Q_OBJECT
public:
Util(QObject *parent = 0);
Q_INVOKABLE
void getFile() const;
private Q_SLOTS:
void onFileSelected(const QStringList&);
void onCanceled();
};
#endif /* UTIL_H_ */
Note the Q_INVOKABLE getFile() method. Q_INVOKABLE will eventually allow us to call this method directly from QML.
The corresponding Util.cpp would look like:
#include "Util.h"
#include <QDebug>
#include <QStringList>
#include <bb/cascades/pickers/FilePicker>
using namespace bb::cascades;
using namespace bb::cascades::pickers;
Util::Util(QObject *parent) : QObject(parent)
{
}
void Util::getFile() const
{
FilePicker* filePicker = new FilePicker();
filePicker->setType(FileType::Other);
filePicker->setTitle("Select a file");
filePicker->setMode(FilePickerMode::Picker);
filePicker->open();
QObject::connect(
filePicker,
SIGNAL(fileSelected(const QStringList&)),
this,
SLOT(onFileSelected(const QStringList&)));
QObject::connect(
filePicker,
SIGNAL(canceled()),
this,
SLOT(onCanceled()));
}
void Util::onFileSelected(const QStringList &stringList)
{
qDebug() << "selected files: " << stringList;
}
void Util::onCanceled()
{
qDebug() << "onCanceled";
}
To make your Q_INVOKABLE getFile() method available to QML, you'd need to create an instance and set it as a ContextProperty. I do so in my applicationui.cpp like so:
Util *util = new Util(app);
QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(this);
qml->setContextProperty("_util", util);
Then, you can call this Q_INVOKABLE getFile() method from QML:
Page {
Container {
layout: DockLayout {}
Button {
id: launchFilePicker
text: qsTr("Open FilePicker")
onClicked: {
_util.getFile();
}
}
}
}
Like Richard says, most of the documentation covers how to create signals/slots, so you could review that, but also have a look at some Cascades-Samples on Git.
Hope that helps!!!

AddChart() throws when presentation is opened with WithWindow = false

I'm not sure if this is "intended", or if a workaround (other than showing the presentation) exists.
#if DEBUG
var withWindow = Microsoft.Office.Core.MsoTriState.msoTrue;
#else
// using msoTrue fixes the error
var withWindow = Microsoft.Office.Core.MsoTriState.msoFalse;
#endif
presentation = app.Presentations.Open(templateFileName,
Microsoft.Office.Core.MsoTriState.msoTrue, // readonly
Microsoft.Office.Core.MsoTriState.msoFalse, // untitled (?)
withWindow // with window: visible or not
);
// some time later...
slide.Shapes.AddChart(); // Error HRESULT E_FAIL has been returned from a call to a COM component. ErrorCode: -214747259
Not being able to have the PowerPoint presentation hidden when automating is pretty annoying. Are there any workarounds for this issue?

Using a javascript XPCOM component to create a custom autocomplete box in a FireFox add-on

I've spent a few days reading over all manner of tutorials, MDN entries, and S.O. posts, and I've come to suspect that I'm missing something obvious, but I'm too inexperienced with XPCOM to spot it. I'm about 80% sure there error is somewhere in my custom component (components/fooLogin.js).
Problem: When the add-on initializes (when I call loadData() from chrome/content/foologin.js), I get an error saying:
TypeError: Components.classes['#foo.com/foologinautocomplete;1'] is undefined
Am I maybe trying to create the component before the class has been registered? Is there something else I need to do to register it? Any tips would be appreciated.
Relevant Code: (happy to supply any additional code, if need be)
components/fooLogin.js:
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
function fooLoginAutoComplete(){
this.wrappedJSObject = this;
}
fooLoginAutoComplete.prototype = {
classID: Components.ID("loginac#foo.com"),
contractID: "#foo.com/foologinautocomplete;1",
classDescription: "Auto complete for foo",
QueryInterface: XPCOMUtils.generateQI([]),
complete: function(str){ // Autocomplete functionality will in this function
return null;
}
};
var NSGetFactory = XPCOMUtils.generateNSGetFactory([fooLoginAutoComplete]);
chrome/content/foologin.js:
let fooLogin = {
dataLoaded : false,
searchFilter = null,
...
loadData : function(){
...
try{
alert(1); // This alert fires
this.searchFilter = Components.classes['#foo.com/foologinautocomplete;1']
.getService().wrappedJSObject;
alert(2); // I get the error before this alert
}catch(e){alert(e);}
this.dataLoaded = true;
}
}
window.addEventListener("load", function(){
if(!fooLogin.dataLoaded) fooLogin.loadData();
}
chrome.manifest:
content foologin chrome/content/
content foologin chrome/content/ contentaccessible=yes
skin foologin classic chrome/skin/
locale foologin en-US chrome/locale/en-US/
component loginac#foo.com components/fooLogin.js
contract #foo.com/foologinautocomplete;1 loginac#foo.com
overlay chrome://browser/content/browser.xul chrome://foologin/content/foologin.xul
In your chrome.manifest, you have this:
component loginac#foo.com components/fooLogin.js
contract #foo.com/foologinautocomplete;1 loginac#foo.com
and in fooLogin.js you have:
classID: Components.ID("loginac#foo.com"),
loginac#foo.com is not a valid class ID for a component.
They have to be of the form:
{00000000-0000-0000-0000-000000000000}
Only add-ons can have a foo#bar.com format.