I am trying to use void QWebFrame::addToJavaScriptWindowObject(const QString & name, QObject * object). My problem is when I try and call the function in the JavaScript
TypeError: Result of expression 'screen.valueChanged' [undefined] is not a function.
TimeSliceScreen::TimeSliceScreen(QWidget* parent)
:
QWidget( parent )
{
QVBoxLayout* layout = new QVBoxLayout( this );
_timeSlice = new QWebView( this );
_timeSlice->setMinimumSize( 200,200);
QSizePolicy policy = _timeSlice->sizePolicy();
policy.setVerticalStretch(3);
_timeSlice->setSizePolicy(policy);
_timeSlice->settings()->setAttribute( QWebSettings::JavascriptEnabled, true );
_timeSlice->settings()->setAttribute( QWebSettings::DeveloperExtrasEnabled, true );
layout->addWidget( _timeSlice );
layout->addStretch();
layout->addSpacing( 20 );
_timeSlice->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
interface = new WebPageInterface();
connect( _timeSlice->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()),
this, SLOT(populateJavaScriptWindowObject()) );
}
void TimeSliceScreen::populateJavaScriptWindowObject(){
_timeSlice->page()->mainFrame()->addToJavaScriptWindowObject(QString("screen"),
interface);
}
WebPageInterface is a very simple class that extends QObject and has one slot called valueChanged that is the function I am trying to call.
My JavaScript is:
function call() {
screen.valueChanged();
}
which gets called from
<input type="hidden" id="maxhid" name="maxhid" value="{maxSlider}" onchange="call()"/>
Everything I have read says that this is the way to do it, but it's not working for me.
I think screen is a reserved name in the js. Try changing the name to something else. Otherwise looks like it should work.
Related
I'm currently working on a node.js project that supports es6 classes and I need to add an optional parameter to a class constructor that is being called several times, but the problem is that the class constructor already has one optional parameter.
So my question is, is there a way I can add another optional parameter without having to refactor all places where the class is instantiated and still have a clear syntax?
The code I have so far looks like this:
// Error class
class MyError extends Error {
constructor( message = "default message") {
super(message)
}
}
...
// Being called as
throw new MyError()
And I would like to add another optional parameter:
class MyError extends Error {
constructor( message = "default message", code = 500) {
super(message);
this.code = code;
}
}
...
// But if I do it like this, then I always have to inform message when I instantiate MyError:
throw new MyError(undefined, 400)
Is there an approach to adding an optional parameter that would allow me to avoid refactoring previous calls and still allow me to omit the undefined when I create a new instance of MyError with a custom code?
I know for example that I could use object destructuring to have named parameters:
class MyError extends Error {
constructor( { message = "default message", code = 500 } = {} ) {
super(message);
this.code = code;
}
}
...
// But then I would have to refactor all places that call MyError with a message to
throw new MyError({ message: "OldMessage"})
As commented by others, it is best to keep with the parameter structure of Error from which this class inherits, and not be disturbed by new MyError(undefined, 400).
If however you really want to go ahead with the object parameter, then you could make it backwards compatible like this:
class MyError extends Error {
constructor(options = { message: "default message", code: 500 }) {
super(typeof options === "string" ? options : options?.message);
if (typeof options === "string") options = { message: options, code: 500 };
this.message = options?.message;
this.code = options?.code;
// rest of your code
}
}
This would allow old-style code like new MyError("OldMessage") to continue to work, while newer code can do any of these:
new MyError({ message: "NewMessage" });
new MyError({ code: 404 });
new MyError({ message: "NewMessage", code: 404 });
This gives you time to refactor the code at your convenience.
Given that you have already had to refactor this once, the best approach is going to be doing the work now so that you don't have to in the future.
You should go the route of object destructuring. If you are against refectoring everything now, you can add this as a new constructor and refactor at a later point, but it is highly recommended that you perform all this at once.
I am working on Angular 6 application. I have behaviour variable of Input, once I received data, I map to surveyInfo object. I have surveyInfoDataModel class as shown below; followed by I am trying to display this data by reading surveyInfo object in template but go error
error
ERROR TypeError: Cannot read property 'surveyId' of undefined
component
export class MyComponent implements OnInit {
#Input() surveySelectedToGetInfo: BehaviorSubject<any>;
ngOnInit() {
this.surveySelectedToGetInfo.subscribe(surveyDataItem=>{
debugger;
if(surveyDataItem!=null){
this.loadSurveyInformation(surveyDataItem);
}
});
}
private loadSurveyInformation(selectedSurveyObject:any):any{
var mappedObject = this.mapSurveyInfo(selectedSurveyObject);
}
private mapSurveyInfo(survey:any):SurveyInfoDataModel{
if(survey!=null){
this.surveyInfo = new SurveyInfoDataModel(
survey.consultationId,
survey.surveyId,
survey.surveyIdNum,
survey.surveyName
);
}
return this.surveyInfo;
}
Survey Info DataModel class
export class SurveyInfoDataModel{
surveyId:string;
surveyIdNum:string;
surveyName:string;
consultationId:string;
constructor(consultationId, surveyId, surveyIdNum, surveyName ){
this.consultationId =consultationId;
this.surveyId = surveyId;
this.surveyIdNum = surveyIdNum;
this.surveyName = surveyName;
}
}
html template
<div class="surveyListInfoBlock">
<div *ngIf="surveyInfo">
{{surveyInfo.surveyId}}
</div>
</div>
Try to change if(survey!=null) to if(!survey) return;. Looks like you going to return undefined if there is no survey cause return statement is outside the brackets. If it will work, you'll need to check all props of this object on undefined. Also you need to add typing to this object.
private mapSurveyInfo(survey:any):SurveyInfoDataModel{
if (!survey) return;
this.surveyInfo = new SurveyInfoDataModel(
survey.consultationId,
survey.surveyId,
survey.surveyIdNum,
survey.surveyName
);
return this.surveyInfo;
}
Survey in your case is undefined. Instead of testing if survey is null you can test for both null & undefined with this:
if(!!survey){
this.surveyInfo = new SurveyInfoDataModel(
survey.consultationId,
survey.surveyId,
survey.surveyIdNum,
survey.surveyName
);
}
I am using angular2 with typescript and i have defined a class
export class Example{
//.../
const self: all = this;
functionToCall(){
//.. Do somerthing
}
mainFunctionCall(){
somepromise.then(x => self.functionToCall('url/'+ x.name ) )
}
}
But it keeps throwing error about functionToCall being undefined.
Im quite new to typescript/angular#
is there any rule that prevents this to be correct? Or what is the correct way to invoke method of class inside another method ?
No need for such a hack here, since lambda functions capture the this reference of the outer scope. You can just simply write:
const somepromise = Promise.resolve({name:"noone"});
class Example {
functionToCall(x : string) {
console.log(x);
}
mainFunctionCall() {
somepromise.then(x => this.functionToCall('url/'+ x.name ) )
}
}
(new Example()).mainFunctionCall();
Edit Code snippet updated to include all details. Can be run in the typescript playground.
There is an existing solution for CefGlue: Call .Net from javascript in CefSharp 1 - wpf
I want exactly this, but for CefGlue: I want to communicate with the App using JavaScript. So when I click a button in my HTML site, I want the application to handle this (for example: start a tcp server).
I tried to register an own CefV8Handler but without success, the Execute function on the handler is never called. Here is what I do right now
protected override void OnWebKitInitialized()
{
Console.WriteLine("Registering testy extension");
Xilium.CefGlue.CefRuntime.RegisterExtension("testy", "var testy;if (!testy)testy = {};(function() {testy.hello = function() {};})();", new V8Handler());
base.OnWebKitInitialized();
}
My V8Handler code looks as follows:
public class V8Handler : Xilium.CefGlue.CefV8Handler
{
protected override bool Execute(string name, CefV8Value obj, CefV8Value[] arguments, out CefV8Value returnValue, out string exception)
{
if (name == "testy")
Console.WriteLine("CALLED TESTY");
else
Console.WriteLine("CALLED SOMETHING WEIRED ({0})", name);
returnValue = CefV8Value.CreateNull();
exception = null;
return true;
}
}
I'm in multiprocess mode, no console window shows "CALLED TESTY" nor "CALLED SOMETHING WEIRED".
Found a solution for that. The trick is to create a CefV8Value (CreateFunction) and assign it to a V8Handler. Then assign this value to the global context. This is what it looks like:
internal class RenderProcessHandler : CefRenderProcessHandler
{
protected override void OnContextCreated(CefBrowser browser, CefFrame frame, CefV8Context context)
{
CefV8Value global = context.GetGlobal();
CefV8Value func = CefV8Value.CreateFunction("magic", new V8Handler());
global.SetValue("magic", func, CefV8PropertyAttribute.None);
base.OnContextCreated(browser, frame, context);
}
}
Another problem came up: it was called in the renderer process, but I required the callback in the browser process. In the CefV8Handlers execute function i did this:
var browser = CefV8Context.GetCurrentContext().GetBrowser();
browser.SendProcessMessage(CefProcessId.Browser, CefProcessMessage.Create("ipc-js." + name));
This way I can retrive the message in the OnProcessMessageReceived function in the CefClient implementation.
I have started to dig in to C++ and Qt again, and have been mucking around with the WebKit Javascript/Qt bindings. I've got all the moving parts working, with the exception of my QObject subclass being "undefined" on the Javascript side. Here's the simple test app I'm having trouble with:
My main window implementation:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// instantiate a webview
QWebView *webview = new QWebView(this);
webview->setGeometry(0, 0, 400, 300);
webview->setUrl(QUrl("file://localhost/Users/kyle/Sites/tests/qt/index.html"));
// instantiate and attach our QObject
hello *h = new hello();
QWebFrame *frame = webview->page()->mainFrame();
frame->addToJavaScriptWindowObject("Hello", h);
// show the window
webview->show();
}
Hello.cpp
...snip...
QString hello::say()
{
return QString("Kyle");
}
Hello.h
...snip includes...
class hello : public QObject
{
Q_OBJECT
public:
hello();
Q_INVOKABLE QString say();
};
The above-mentioned index.html file does a simple alert(Hello.say()) call, but doing typeof Hello, I get undefined.
I'm a bit rusty with C++, and pretty new to Qt, so I'm sure this is a noob mistake, but I'm stumped.
Objects can't be inserted in the page at any time. You should put that line:
frame->addToJavaScriptWindowObject("Hello", h);
in a slot connected to the javaScriptWindowObjectCleared() signal of the QWebFrame and move some code around, so you can access the frame from that slot.
See also the Form Extractor example included with Qt.
The core of this is really implemented in two methods, which are
shown below:
void MyApi::setWebView( QWebView *view )
{
QWebPage *page = view->page();
frame = page->mainFrame();
attachObject();
connect(frame, &QWebFrame::javaScriptWindowObjectCleared, this, &MyApi::attachObject);
// old approach
//connect( frame, SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(attachObject()) );
}
void MyApi::attachObject()
{
frame->addToJavaScriptWindowObject( QString("MyApi"), this );
}
This code is all that you need in order to make all of the public slots of the
MyApi object visible to javascript. The MyApi class provides two public slots:
public slots:
void doSomething( const QString ¶m );
int doSums( int a, int b );
The first slot simply logs a message to the debug output, the second returns
the sum of its two arguments (yes, slots can return things!). They're called
from javascript like this:
MyApi.doSomething( 'Hello from JS page 2!!!!' );
sum = MyApi.doSums( 2, 3 );
alert( 'C++ says the sum is ' + sum );
The code above was tested in QT5.5, and please note all methods should be put in "public slots" section.