This is the first time I'm trying the bridging of react-native and a native iOS app.
In my react-native iOS project, I've created a swift file (that created a bridging header) and in that swift file I've created a sample method to test first:
import Foundation
#objc(MyModule)
class MyModule: NSObject {
#objc
func testFunctionWithPromiseResolve(frame: Frame,
resolver resolve: #escaping RCTPromiseResolveBlock,
rejecter reject: #escaping RCTPromiseRejectBlock) {
var resp = [String:Any]() //Init Dictionary
resp.updateValue(frame, forKey: "frame");
resolve(resp);
}
#objc
static func requiresMainQueueSetup() -> Bool {
return true
}
}
In the bridging header file I have imports only:
#import "React/RCTBridgeModule.h"
#import <VisionCamera/FrameProcessorPlugin.h>. //from react-native-vision-camera
#import <VisionCamera/Frame.h> //from react-native-vision-camera
Then I created an objective-c file named MyModule.m and in it, I've added:
#import <Foundation/Foundation.h>
#import "React/RCTBridgeModule.h"
#interface
RCT_EXTERN_MODULE(MyModule, NSObject);
RCT_EXTERN_METHOD(testFunctionWithPromiseResolve:
(Frame *)frame
resolver:(RCTPromiseResolveBlock *)resolve
rejecter:(RCTPromiseRejectBlock *)reject);
#end
Then in react-native, I have a Home.js where I'm going to access this method.
import React from 'react';
import { Text, StyleSheet, ScrollView, NativeModules } from 'react-native';
import { Camera, useCameraDevices, useFrameProcessor } from 'react-native-vision-camera';
import 'react-native-reanimated'
function Home(props) {
const devices = useCameraDevices();
const device = devices.back;
const { MyModule } = NativeModules;
const frameProcessor = useFrameProcessor((frame) => {
'worklet';
let res = MyModule.testFunctionWithPromiseResolve(frame);
console.log(res);
// .then((res) => {
// console.log(res);
// }).catch((e) => {
// console.log(e);
// })
})
//... My other code is just UI-related in which I'm calling the frameProcessor in <Camera... using its prop frameProcessor={frameProcessor} as per react-native-vision-camera documentation.
As per my understanding, we handle a Promise with then and catch as I assume this is what we'll be getting from RCTPromiseResolveBlock but that was not working so I just simply tried console.log(res); and it prints undefined.
The error I'm getting is:
Tried to synchronously call function {promiseMethodWrapper} from a different thread.
Possible solutions are:
a) If you want to synchronously execute this method, mark it as a Worklet
b) If you want to execute this method on the JS thread, wrap it using runOnJS
reanimated::REAIOSErrorHandler::raiseSpec()
REAIOSErrorHandler.mm:18
reanimated::ErrorHandler::raise()::'lambda'()::operator()()
decltype(static_cast<reanimated::ErrorHandler::raise()::'lambda'()&>(fp)()) std::__1::__invoke<reanimated::ErrorHandler::raise()::'lambda'()&>(reanimated::ErrorHandler::raise()::'lambda'()&)
void std::__1::__invoke_void_return_wrapper<void, true>::__call<reanimated::ErrorHandler::raise()::'lambda'()&>(reanimated::ErrorHandler::raise()::'lambda'()&)
std::__1::__function::__alloc_func<reanimated::ErrorHandler::raise()::'lambda'(), std::__1::allocator<reanimated::ErrorHandler::raise()::'lambda'()>, void ()>::operator()()
std::__1::__function::__func<reanimated::ErrorHandler::raise()::'lambda'(), std::__1::allocator<reanimated::ErrorHandler::raise()::'lambda'()>, void ()>::operator()()
std::__1::__function::__value_func<void ()>::operator()() const
std::__1::function<void ()>::operator()() const
invocation function for block in vision::VisionCameraScheduler::scheduleOnUI(std::__1::function<void ()>)
F14F0161-E0DE-3D9C-851E-AD12F95A3073
F14F0161-E0DE-3D9C-851E-AD12F95A3073
F14F0161-E0DE-3D9C-851E-AD12F95A3073
F14F0161-E0DE-3D9C-851E-AD12F95A3073
F14F0161-E0DE-3D9C-851E-AD12F95A3073
_pthread_wqthread
start_wqthread
I've worklet defined in the useFrameProcessor.
UPDATE:
I've updated the obj-c method to:
#objc(testFunctionWithPromiseResolve:resolver:rejecter:)
func testFunctionWithPromiseResolve(_ frame: Frame,
resolver resolve: #escaping RCTPromiseResolveBlock,
rejecter reject: #escaping RCTPromiseRejectBlock) {...
and the in JS I did:
let module = NativeModules.MyModule;
const frameProcessor = useFrameProcessor((frame) => {
'worklet';
//let module = NativeModules.MyModule; //didnt work either
console.log(module.testFunctionWithPromiseResolve(frame));
})
But I get the same error:
Luckily I found the problem.
Custom Frame Processor Plugins need to be completed according to the document, the document address is Creating Frame Processor Plugins, native-side customize code that you need to use the VISION_EXPORT_FRAME_PROCESSOR export definition plugin, and using RCT_EXTERN_MODULE and RCT_EXTERN_METHOD will fails.
You need to export custom plug-in methods on the js side, the document address is Expose your Frame Processor Plugin to JS, as follows:
import type { Frame } from 'react-native-vision-camera'
/**
* Scans QR codes.
*/
export function scanQRCodes(frame: Frame): string[] {
'worklet'
return __scanQRCodes(frame)
}
but now ,this will be __scanQRCodes:
module.exports = {
plugins: [
[
'react-native-reanimated/plugin',
{
globals: ['__scanQRCodes'],
},
],
you have to restart metro-bundler for changes in the babel.config.js file to take effect.
3: you must be restarted project to take effect.
My code is as follows:
I customized a Ojective-C frame Processor Plugins named MyModuleFrameProcessPlugin.m, the code is implemented as follows:
#import <VisionCamera/FrameProcessorPlugin.h>
#import <VisionCamera/Frame.h>
#interface MyModuleFrameProcessPlugin : NSObject
#end
#implementation MyModuleFrameProcessPlugin
static inline id myCustomPlugin(Frame* frame, NSArray* arguments) {
CVPixelBufferRef imageBuffer = CMSampleBufferGetImageBuffer(frame.buffer);
NSLog(#"myCustomPlugin: %zu x %zu Image. Logging %lu parameters:", CVPixelBufferGetWidth(imageBuffer), CVPixelBufferGetHeight(imageBuffer), (unsigned long)arguments.count);
for (id param in arguments) {
NSLog(#"myCustomPlugin: -> %# (%#)", param == nil ? #"(nil)" : [param description], NSStringFromClass([param classForCoder]));
}
return #{
#"myCustomPlugin_str": #"Test",
#"myCustomPlugin_bool": #true,
#"myCustomPlugin_double": #5.3,
#"myCustomPlugin_array": #[
#"Hello",
#true,
#17.38
]
};
}
VISION_EXPORT_FRAME_PROCESSOR(myCustomPlugin)
#end
Create a new js plugin named ExamplePlugin.ts, the code is as follows:
/* global __myCustomPlugin */
import type { Frame } from 'react-native-vision-camera';
declare let _WORKLET: true | undefined;
export function cusPlugin(frame: Frame): string[] {
'worklet';
if (!_WORKLET) throw new Error('my_custom_plugin must be called from a frame processor!');
// #ts-expect-error because this function is dynamically injected by VisionCamera
return __myCustomPlugin(frame, 'hello my_custom_plugin!', 'parameter2', true, 42, { test: 0, second: 'test' }, ['another test', 500]);
}
The babel.config.js file is as follows:
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
[
'react-native-reanimated/plugin',
{
globals: ['__myCustomPlugin']
}
]
],
};
The plugin import code is as follows:
import {cusPlugin} from './ExamplePlugin';
The calling code is as follows:
const frameProcessor = useFrameProcessor((frame) => {
'worklet';
const value = cusPlugin(frame);
console.log(`Return Values: ${JSON.stringify(value)}`);
}, []);
//Use in Camera is frameProcessor={frameProcessor}
I tested my code and it is ok, I hope it can help you.
Related
I am using Quasar with API composition and I have an issue.
I just want to get parameters from the current route so within onMounted, I try to use this.$route.params, but I always get "this is undefined" in console.
// src/pages/Level.vue
import { ref, onMounted, computed } from "vue";
import { useStore } from 'vuex'
import { useQuasar } from 'quasar';
export default {
name: "LevelPage",
setup () {
const $q = useQuasar();
const $store = useStore();
const level = ref(null);
onMounted(()=> {
console.log(this.$route.params)
});
return {
level,
}
},
};
I get the same error every time I use this in the setup.
So there must be something I'm not getting. Can you please help me?
I think you are looking for https://v3.vuejs.org/guide/composition-api-provide-inject.html#mutating-reactive-properties In your Level.vue:
import { provide } from 'vue';
export default {
setup() {
provide('appName', 'vue3')
}
}
And then in any child component where you want to access this variable, inject it:
import { inject } from 'vue'
export default {
setup() {
const appName = inject('appName');
}
}
OR
import { useRoute } from 'vue-router'
export default {
setup() {
const route = useRoute()
onMounted(() => {
const id = route.params.id
})
}
}
Wanted to check how many instances are running and control the number of instances running in one exe electron bundle. Let us say I wanted to allow only three instances running for the one exe bundle. I am not able to do this.
Current Behavior:
Only one and remaining can block. Or open for any number of instances. We need to control only three instances running, not more than that.
Example:
const { app } = require('electron')
let myWindow = null
const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) {
app.quit()
} else {
app.on('second-instance', (event, commandLine, workingDirectory) => {
// Someone tried to run a second instance, we should focus our window.
if (myWindow) {
if (myWindow.isMinimized()) myWindow.restore()
myWindow.focus()
}
})
// Create myWindow, load the rest of the app, etc...
app.on('ready', () => {
})
}
You can try with the following code to know how many windows have been opened.
const count = BrowserWindow.getAllWindows().length;
To check visible windows, you can try the following code
let count = BrowserWindow.getAllWindows()
.filter(b => {
return b.isVisible()
}).length
Once you get the number of instances, based upon the condition for number of instance, ie. if it is more than 3, you can quit using app.quit().
You can make each instance write to a file (increment a counter for example) when the instance starts and when it exits. (decrement the counter). You should check that file to see if the maximum number of instances are running
import { app } from "electron";
import path from "path";
import fs from "fs";
const MAX_APP_INSTANCES = 3;
const INSTANCE_COUNT_FILE_PATH = path.join(
app.getPath("userData"),
"numOfInstances"
);
// utils to read/write number of instances to a file
const instanceCountFileExists = () => fs.existsSync(INSTANCE_COUNT_FILE_PATH);
const readInstanceCountFile = () =>
parseInt(fs.readFileSync(INSTANCE_COUNT_FILE_PATH, "utf-8"));
const writeInstanceCountFile = (value) =>
fs.writeFileSync(INSTANCE_COUNT_FILE_PATH, value);
const incInstanceCountFile = () => {
const value = readInstanceCountFile() + 1;
writeInstanceCountFile(value.toString());
};
const decInstanceCountFile = () => {
const value = readInstanceCountFile() - 1;
writeInstanceCountFile(value.toString());
};
// logic needed to only allow a certain number of instances to be active
if (instanceCountFileExists() && readInstanceCountFile() >= MAX_APP_INSTANCES) {
app.quit();
} else {
if (!instanceCountFileExists()) {
writeInstanceCountFile("1");
} else {
incInstanceCountFile();
}
app.on("quit", () => decInstanceCountFile());
}
Note: this is solution is somewhat hacky. For example, the quit event is not guaranteed to fire when the Electron app exits
I'm new to flutter/iOS.
I'm using:
Flutter 1.22.6 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 9b2d32b605 • 2021-01-22 14:36:39 -0800
Engine • revision 2f0af37152
Tools • Dart 2.10.5
flutter_downloader: ^1.4.4
I have to correct an application that I did not code I'm trying to understand it. It downloads a pdf file and open it, but is not working in iOS.
All the configuration that I read in https://github.com/fluttercommunity/flutter_downloader is correct.
Flutter doctor is OK.
Below I show you parts of the code
main.dart
final _prefs = SharedPreferences();
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final prefs = SharedPreferences();
await prefs.initPrefs();
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown
]);
WidgetsFlutterBinding.ensureInitialized();
await FlutterDownloader.initialize(debug: true);
_prefs.uid = await getId();
runApp(MyApp());
}
pages/registry/facture.dart
List<Widget> _actionsCreateBar(BuildContext context) {
return <Widget>[
(document.id != null)
? IconButton(
icon: Icon(EvaIcons.downloadOutline),
onPressed: () async {
_downloadAction(); // This method is executed when user press download icon
},
color: primaryColor,
iconSize: 25,
)
: Container(),
];
}
void _downloadAction() async {
if (await utils.isInternetAvailable()) {
if (await _validateUrlRideBeforeDownload()) {
await _pdfBloc.downloadPdf(document.url_ride, Theme.of(context).platform);
setState(() {});
return;
}
_showDialogOk(
context, 'Download', 'Wait please');
} else {
_showDialogOk(context, 'Info',
'No conection');
}
}
bloc/pdf/pdfbloc.dart
class PdfBloc {
final _downloadingController = BehaviorSubject<bool>();
final _loadingController = BehaviorSubject<bool>();
final _progressStringController = BehaviorSubject<String>();
final _pdfProvider = DownloadProvider();
Stream<String> get progressStringStream => _progressStringController.stream;
Stream<bool> get loadingStream => _loadingController.stream;
Stream<bool> get downloadingStream => _downloadingController.stream;
Future<ResponseData> downloadPdf(String url, var platform) async {
_downloadingController.sink.add(true);
ResponseData resData = await _pdfProvider.downloadPdf(url, _progressStringController, platform);
_downloadingController.sink.add(false);
return resData;
}
dispose() {
_downloadingController.close();
_progressStringController.close();
_loadingController.close();
}
}
provider/download/downloadprovider.dart
class DownloadProvider {
Future<ResponseData> downloadPdf(String url, dynamic progressString, var platform) async {
ResponseData resData = ResponseData();
final _prefs = SharedPreferences();
try {
var path = await findLocalPath(platform) + '/';
FlutterDownloader.cancelAll();
final String taskId = await FlutterDownloader.enqueue(
url: url,
savedDir: path,
showNotification: true, // show download progress in status bar (for Android)
openFileFromNotification: true, // click on notification to open downloaded file (for Android)
headers: {HttpHeaders.authorizationHeader: _prefs.token, 'Content-type': 'application/json'},
);
// Last developer used this "while" to wait while a dialog is shown
// Android behaviour: flutter says "only success task can be opened" but then it works
// iOS behaviour: flutter says "only success task can be opened" infinitely and never
// shows the pdf
// In iOS this loop iterates forever
while(!await FlutterDownloader.open(taskId: taskId,)) {
// Last developer did this validation, but I don't know why
if (platform == TargetPlatform.iOS) {
await FlutterDownloader.open(taskId: taskId);
}
}
_setResponseData(resData, 'Completed', false);
return resData;
} catch(e) {
_setResponseData(resData, 'Error', true);
return resData;
}
}
_setResponseData(ResponseData resData, String message, bool state) {
resData.setData(message);
resData.setError(state);
}
}
Future<String> findLocalPath(var platform) async {
final directory = platform == TargetPlatform.android
? await getExternalStorageDirectory()
: await getApplicationDocumentsDirectory();
return directory.path;
}
I have tried several versions of ios and iphone without success.
Any ideas?
Please help me, I'm stuck.
Thanks.
I could to solve this problem. The previous developers committed a bad programming practice, which caused a race condition in ios when trying to force open a task without checking its status.
I had to change the "while" loop and within it, check the status and progress of the download task. Once it reached 100% progress and its status was complete, then we break the loop and finally open the task.
In provider/download/downloadprovider.dart
bool waitTask = true;
while(waitTask) {
String query = "SELECT * FROM task WHERE task_id='" + taskId + "'";
var _tasks = await FlutterDownloader.loadTasksWithRawQuery(query: query);
String taskStatus = _tasks[0].status.toString();
int taskProgress = _tasks[0].progress;
if(taskStatus == "DownloadTaskStatus(3)" && taskProgress == 100){
waitTask = false;
}
}
await FlutterDownloader.open(taskId: taskId);
open your ios project in Xcode
Add sqlite library.
Configure AppDelegate:
/// AppDelegate.h
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
#interface AppDelegate : FlutterAppDelegate
#end
// AppDelegate.m
#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
#include "FlutterDownloaderPlugin.h"
#implementation AppDelegate
void registerPlugins(NSObject<FlutterPluginRegistry>* registry) {
if (![registry hasPlugin:#"FlutterDownloaderPlugin"]) {
[FlutterDownloaderPlugin registerWithRegistrar:[registry registrarForPlugin:#"FlutterDownloaderPlugin"]];
}
}
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
[FlutterDownloaderPlugin setPluginRegistrantCallback:registerPlugins];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
#end
Completely disable ATS: (add following codes to your Info.plist file)
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key><true/>
</dict>
Configure maximum number of concurrent tasks: the plugin allows 3 download tasks running at a moment by default (if you enqueue more than 3 tasks, there're only 3 tasks running, other tasks are put in pending state). You can change this number by adding following codes to your Info.plist file.
<!-- changes this number to configure the maximum number of concurrent tasks -->
<key>FDMaximumConcurrentTasks</key>
<integer>5</integer>
Localize notification messages: the plugin will send a notification message to notify user in case all files are downloaded while your application is not running in foreground. This message is English by default. You can localize this message by adding and localizing following message in Info.plist file. (you can find the detail of Info.plist localization in this link)
<key>FDAllFilesDownloadedMessage</key>
<string>All files have been downloaded</string>
I'm working on a JHipster application that I'm trying to get functioning in Electron. I have Golden Layout for window/pane management and cross-pane communication. I am having several problems with the combination of technologies, including:
I can't pop out more than one pane at the same time into their own Electron windows. I instead get an Uncaught Error: Can't create config, layout not yet initialised error in the console.
Two thirds of the panes don't display anything when popped out into Electron windows, and I'm not sure what the reason is. Any ideas or suggestions for this? One example of content is a leaflet map, another is a "PowerPoint preview" that is really just divs that mock the appearance of slides.
I haven't made it this far yet, but I assume that I will have trouble communicating between popped-out Electron windows when I get more than one open. Right now, the panes communicate between each other using Golden Layout's glEventHub emissions. I have an avenue to explore when I cross that bridge, namely Electron ipcRenderer.
Some borrowed code is here (most of it I can't share because it's company confidential):
electron.js:
const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const path = require('path');
const isDev = require('electron-is-dev');
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({width: 900, height: 680});
mainWindow.loadURL(isDev ? 'http://localhost:9000' : `file://${path.join(__dirname, '../build/index.html')}`);
if (isDev) {
// Open the DevTools.
//BrowserWindow.addDevToolsExtension('<location to your react chrome extension>');
mainWindow.webContents.openDevTools();
}
mainWindow.on('closed', () => mainWindow = null);
}
app.on('ready', createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (mainWindow === null) {
createWindow();
}
});
goldenLayoutComponent.tsx, a patch for Golden Layout:
import React from "react";
import ReactDOM from "react-dom";
// import "./goldenLayout-dependencies";
import GoldenLayout from "golden-layout";
import "golden-layout/src/css/goldenlayout-base.css";
import "golden-layout/src/css/goldenlayout-dark-theme.css";
import $ from "jquery";
interface IGoldenLayoutProps {
htmlAttrs: {},
config: any,
registerComponents: Function
}
interface IGoldenLayoutState {
renderPanels: Set<any>
}
interface IContainerRef {
current: any
}
export class GoldenLayoutComponent extends React.Component <IGoldenLayoutProps, IGoldenLayoutState> {
goldenLayoutInstance = undefined;
state = {
renderPanels: new Set<any>()
};
containerRef: IContainerRef = React.createRef();
render() {
const panels = Array.from(this.state.renderPanels || []);
return (
<div ref={this.containerRef as any} {...this.props.htmlAttrs}>
{panels.map((panel, index) => {
return ReactDOM.createPortal(
panel._getReactComponent(),
panel._container.getElement()[0]
);
})}
</div>
);
}
componentRender(reactComponentHandler) {
this.setState(state => {
const newRenderPanels = new Set(state.renderPanels);
newRenderPanels.add(reactComponentHandler);
return { renderPanels: newRenderPanels };
});
}
componentDestroy(reactComponentHandler) {
this.setState(state => {
const newRenderPanels = new Set(state.renderPanels);
newRenderPanels.delete(reactComponentHandler);
return { renderPanels: newRenderPanels };
});
}
componentDidMount() {
this.goldenLayoutInstance = new GoldenLayout(
this.props.config || {},
this.containerRef.current
);
if (this.props.registerComponents instanceof Function)
this.props.registerComponents(this.goldenLayoutInstance);
this.goldenLayoutInstance.reactContainer = this;
this.goldenLayoutInstance.init();
}
}
// Patching internal GoldenLayout.__lm.utils.ReactComponentHandler:
const ReactComponentHandler = GoldenLayout["__lm"].utils.ReactComponentHandler;
class ReactComponentHandlerPatched extends ReactComponentHandler {
_container: any;
_reactClass: any;
_render() {
const reactContainer = this._container.layoutManager.reactContainer; // Instance of GoldenLayoutComponent class
if (reactContainer && reactContainer.componentRender)
reactContainer.componentRender(this);
}
_destroy() {
// ReactDOM.unmountComponentAtNode( this._container.getElement()[ 0 ] );
this._container.off("open", this._render, this);
this._container.off("destroy", this._destroy, this);
}
_getReactComponent() {
// the following method is absolute copy of the original, provided to prevent depenency on window.React
const defaultProps = {
glEventHub: this._container.layoutManager.eventHub,
glContainer: this._container
};
const props = $.extend(defaultProps, this._container._config.props);
return React.createElement(this._reactClass, props);
}
}
GoldenLayout["__lm"].utils.ReactComponentHandler = ReactComponentHandlerPatched;
Any help or insight into these issues would be appreciated. Thanks in advance!
If you are still looking for a solutions, 1 and 2 I have solved, if you want to see my solution you could see in this repository.
But it was basically this:
1: The window that popups has a different path than the main window, so I just had to put a try catch in my requires, and you have to set
nativeWindowOpen = true
when creating the Browser window.
2: Solves it's self after 1 I think
I'm trying to use Salesforce's sforce library to place an Ajax call to salesforce. Here is the example javascript that is working:
function setupPage() {
var state = { //state that you need when the callback is called
output : document.getElementById("output"),
startTime : new Date().getTime()};
var callback = {
//call layoutResult if the request is successful
onSuccess: layoutResults,
//call queryFailed if the api request fails
onFailure: queryFailed,
source: state};
sforce.connection.query(
"Select Id, Name, Industry From Account order by Industry",
callback);
}
function queryFailed(error, source) {
// not shown function code
}
function layoutResults(queryResult, source) {
// not shown function code
}
Here's my dart implementation:
import 'dart:html';
import 'package:js/js.dart' as js;
import 'dart:json';
void main() {
js.scoped(() {
var sforce = js.context.sforce;
var callbackSuccess = new js.Callback.once(success);
var callbackFailed = new js.Callback.once(failure);
var sfdc = new js.Proxy(sforce.connection.query("Select Id, Name, Industry From Account order by Industry"),
js.map({"onSuccess" : callbackSuccess, "onFailure" : callbackFailed}));
});
}
void success(queryResult) {
print("queryResult is: " + queryResult);
}
void failure(error) {
print("error is: " + error);
}
The Ajax call is being placed, as I see the POST request being made and returning data. However, I always seem to get this error (and I've tried countless different combinations):
Uncaught TypeError: object is not a function (program):370
construct (program):370
ReceivePortSync.dispatchCall darttest:178
$$._JsSendPortSync.callSync$1 minidartjs:4929
$.Proxy_Proxy$withArgList minidartjs:8194
$.Proxy_Proxy minidartjs:8183
$$.main_anon.call$0 minidartjs:6057
$.scoped minidartjs:8136
$.main minidartjs:8066
$$._IsolateContext.eval$1 minidartjs:276
$.startRootIsolate minidartjs:6533
(anonymous function)
Any help would be greatly appreciated, as I'm not sure where to turn at this point.
You get this error because you try to create a js.Proxy (sfdc) with the result of sforce.connection.query(...) . When you use new js.Proxy(f), f must be a js.Proxy of a js function. Here you get an object and that's why you get the error.
Here's the code that should work.
import 'dart:html';
import 'package:js/js.dart' as js;
import 'dart:json';
void main() {
js.scoped(() {
var sforce = js.context.sforce;
var callbackSuccess = new js.Callback.once(success);
var callbackFailed = new js.Callback.once(failure);
sforce.connection.query("Select Id, Name, Industry From Account order by Industry",
js.map({"onSuccess" : callbackSuccess, "onFailure" : callbackFailed}));
});
}
void success(queryResult, source) {
print("queryResult is: " + queryResult);
}
void failure(error, source) {
print("error is: " + error);
}