I'm currently working on a Ionic/Cordova application, a to-do application. I am using the ngCordova library for an easier use of the cordova plugins. I'm also using the Sqlite plugin by litehelpers (GitHub page).
My problem is about the correct understanding of the JavaScript's promises behavior. I have a Angular service called "Projects", which make use of the SQLite plugin, and a "Tasks" controller which is delegated to it.
angular.module('myapp.services', [])
.factory("Projects", ["$ionicPlatform", "$cordovaSQLite", "$window", "$q",
function($ionicPlatform, $cordovaSQLite, $window, $q) {
return {
// SOME FUNCTIONS..
getCurrentProject: function() {
var q = $q.defer();
$ionicPlatform.ready(function() {
$cordovaSQLite.execute(db,
"SELECT id_project, name FROM projects WHERE active = 1").then(
function(res) {
q.resolve(res.rows.item(0));
}, function(err) {
q.reject(err);
console.error(err.message);
});
});
return q.promise;
}
};
}
]);
I want to make this function return an object, which is the result of my query.
The promises and callback keywords are confusing me.
I've tried a few ways to solve my problem, but when I call (in my controller):
var currentProject = Projects.getCurrentProject();
And then trying to check its values with:
console.log("ID -> " + currentProject.id_project);
console.log("NAME -> " + currentProject.name);
I always get undefined. How do I handle a promise in a Ionic/Cordova Application?
Solved
#bardzusny's answer totally made me realize where i was wrong, thank you all :)
Promises are designed to be chainable with error handling and final behavior (optional), like so:
Projects.getCurrentProject()
.then (project) -> currentProject = project
.catch (err) -> console.error err
.finally () -> wrapUpFn()
So you would need to use .then to make the assignment. Otherwise you're assigning currentProject to the promise object, not the result of the promise object (which is what you want).
EDIT
Now in javascript :)
var currentProject
Projects.getCurrentProject()
.then(function(project) {
currentProject = project
})
.catch(function(err) {
console.error(err)
})
.finally(function() {
wrapUpFn()
})
As an aside, promise objects are great because you can just keep on chaining down the line with .thens, and still keep your error handling and final behaviors clear. This lets you create simple flows that are ordered within the crazy world of javascript.
Related
I'm in need of your expertise in React Native.
I'm trying to use expo-local-authentication for local fingerprint authentication for my application.
My project was created using expo init command.
I have done the setup as per the documentation and still running into a strange issue:
Below is the error I'm facing for LocalAuthentication.authenticateAsync(options):
Native method ExpoLocalAuthentication.authenticateAsync expects 0
arguments but received 1
Here is the required part of my code:
import * as LocalAuthentication from 'expo-local-authentication';
const authenticate = async () => {
const hasHardwareAsync = await LocalAuthentication.hasHardwareAsync();
if (hasHardwareAsync) {
const supportedAuthentications = await LocalAuthentication.supportedAuthenticationTypesAsync();
if (supportedAuthentications.indexOf(1) !== -1) {
// Finger print supported
const isFingerprintEnrolled = await LocalAuthentication.isEnrolledAsync();
if (isFingerprintEnrolled) {
const options = {
promptMessage: 'Authenticate yourself',
};
try {
// Also tried with await but it throws the same error
// await LocalAuthentication.authenticateAsync(options)
LocalAuthentication.authenticateAsync(options).then(result => {
// I never get inside this block
console.warn(result)
})
.catch(error => {
console.warn('Authentication Error: ', error)
})
} catch (error) {
console.warn(error)
}
}
}
}
}
Not sure what I'm missing. Seems like there is no information available about the error. I also tried to run the LocalAuthentication.authenticateAsync() without any arguments but it still throws the same error.
Any help on what could be the root cause of the issue and how can I resolve it or any other alternative for local authentication would be highly appreciated.
Update your app to the latest version of expo (38 in my case) and to the latest version of expo-local-authentication, and the error goes away.
I am exporting JSON by fetch from the URL. I think I have a binding issue if take from local data file working I'm not completely sure on how I should proceed to bind my function.
Data.js
const Json = require('./one.js'); // not working or const Json = require('./two.json'); // working
export default Json;
one.js
function getvals(){
return fetch('http://xxxxxx')
.then((response) => response.json())
.then((json) => {
return json.products;
})
.catch((error) => {
console.error(error);
});
}
getvals().then(response => response);
two.json
[{"id":"1","category":"kkk","title":"sss"}]
Nothing in one.js exports anything. With CommonJS-style modules, you export something by assigning it to a property on exports (or by reassigning the exports variable entirely).
But note that since what you're getting is only available asynchronously, other modules may request one.js's default export before the asynchronous process has completed. The usual solution to that is export the promise from fetch:
module.exports = fetch('http://xxxxxx')
.then((response) => {
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response.json();
})
.then((data) => {
return data.products;
});
Also note that you need to check for HTTP success (the footgun in the fetch API) and you don't want to hide errors; let the users of the module know if the fetch fails.
Code using that would need to use the promise, e.g.:
require("./one.js")
.then(data => {
// ...use the products...
})
.catch(error => {
// ...handle the fact the fetch failed and the data won't be coming...
});
I don't know the React Native ecosystem, but if you can to switch to JavaScript modules ("ESM" = ECMAScript Modules) instead (and if you want to), someday you'd be able to use a new feature called top-level await. The V8 engine has it (behind a flag), presumably JavaScriptCore will at some stage. That would let you suspend module evaluation until the fetch completed, and directly export the result:
// With top-level `await` in an ESM module
export default await fetch('http://xxxxxx')
.then((response) => {
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response.json();
})
.then((data) => {
return data.products;
});
Modules using it would be able to get the products directly:
import products from "./one.js";
If you're using Webpack (again, I don't know the React Native ecosystem), it has experimental support for it, too.
I'm writing an app using React Native and I came across an issue when trying to update an Object with the useState method. Here's my code:
const Screen = ({route}) => {
var roomKey = route.params;
const [room, setRoom] = useState({});
db.ref('rooms').on('value', (data) => {
setRoom(() => (data.val())[roomKey]);
});
console.log(room);
// rest of the code ...
My code works as follows: first it takes a key outputted by another screen with react navigation, and then calls the firebase database with the ref method (db is defined as Firebase.initializeApp(config).database(), where config is the object with all the firebase datas needed). If I log out the data.val())[roomKey] it correctly outputs the object I'm downloading from database, however the room object is not updated by the useState method (it outputs undefined, making the following part of the code crash). What am I doing wrong?
In your code, setRoom is a function to change the value of room state. So, you have to pass the value into the setRoom.
Please check the following code.
const Screen = ({route}) => {
var roomKey = route.params;
const [room, setRoom] = useState({});
db.ref('rooms').on('value', (data) => {
setRoom(data.val()[roomKey]);
});
console.log(room);
// rest of the code ...
I am trying to implement a feature to let the user upload a file in my NativeScript Angular Project. NativeScript does not seem to have a native implementation of a file picker and there are limited plugins available that can do the job. Plus they have their own set of problems. The closest I have come to a workable solution is using the nativescript-mediafilepicker and that opens a blank page like the one below instead of the file explorer.
I exactly followed the documentation and can't figure out why it's not working. Here is the service I wrote:
payload.service.ts
import { Injectable } from '#angular/core';
import { Mediafilepicker, ImagePickerOptions, VideoPickerOptions, AudioPickerOptions,
FilePickerOptions } from 'nativescript-mediafilepicker';
#Injectable({
providedIn: 'root'
})
export class PayloadService {
constructor() { }
pickFile(){
console.log('Pick File Payload Service requested');
const extensions = ['pdf'];
let options: FilePickerOptions = {
android: {
extensions: extensions,
maxNumberFiles: 1
},
ios: {
extensions: extensions,
multipleSelection: false
}
};
let mediafilepicker = new Mediafilepicker();
mediafilepicker.openFilePicker(options);
mediafilepicker.on("getFiles", function (res) {
let results = res.object.get('results');
console.dir('File Pick Success: ',results);
});
mediafilepicker.on("error", function (res) {
let msg = res.object.get('msg');
console.log('File Pick Error: ',msg);
});
mediafilepicker.on("cancel", function (res) {
let msg = res.object.get('msg');
console.log('File Pick Cancel: ',msg);
});
}
}
Can someone help me fix this or rather provide me with a native implementation? I don't need much customization options and user will only upload one file at a time.
I have a Sencha Touch 2 project and everything works great in the web browser. No errors in the console, and everything looks good. Once I package it with Phonegap and run it on a mobile device, however, things don't work as well.
I am using ext.device.notification.show in two places in my application. At first, I was doing requires: 'Ext.device.*' and while it worked in web, the app wouldn't run on mobile and eclipse would give me the error message Uncaught TypeError: Cannot read property 'name' of undefined. I switched over to requires: Ext.device.Notification (exact spelling and capitalization) and now the app runs but when I click a button that should create a message box, I get the error Uncaught TypeError: Cannot call method 'confirm' of undefined. The problem is I have no method called confirm. In one case I have a method called confirmItem, but for the second button that should be invoking a message box I have no method remotely close to "confirm."
I'll post one of the controllers below (this one has the confirmItem method):
Ext.define('MyApp.controller.MainController',
{
extend: 'Ext.app.Controller',
requires: ['Ext.device.Notification'],
config:
{
refs:
{
mainView: 'mainview',
btnConfirm: 'mainview button[action=confirmItem]',
},
control:
{
'btnConfirm':
{
tap: 'confirmItem'
},
mainView:
{
onSignOffCommand: 'onSignOffCommand'
}
}
},
// Transitions
getSlideLeftTransition: function ()
{
return {
type: 'slide',
direction: 'left'
};
},
getSlideRightTransition: function ()
{
return {
type: 'slide',
direction: 'right'
};
},
onSignOffCommand: function ()
{
var me = this;
console.log('Signed out.');
loginView = this.getLoginView();
//MainView.setMasked(false);
Ext.Viewport.animateActiveItem(loginView, this.getSlideRightTransition());
},
confirmItem: function ()
{
Ext.device.Notification.show(
{
title: 'Confirm',
message: 'Would you like to Confirm?',
buttons: ['No', 'Yes'],
callback: function (button)
{
if (button == "Yes")
{
MyApp.app.getController('MainController')
.confirmPickup();
}
else
{
console.log('Nope.');
}
}
});
},
confirmPickup: function ()
{
var me = this;
var loginStore = Ext.getStore('LoginStore');
mainView = this.getMainView();
mainView.setMasked(
{
xtype: 'loadmask',
message: ' '
});
if (null != loginStore.getAt(0))
{
var user_id = loginStore.getAt(0).get('id');
var name = loginStore.getAt(0).get('name');
var winner = loginStore.getAt(0).get('winner');
}
if (winner === 1)
{
console.log('success');
}
else
{
console.log('fail');
}
}
});
I only assume this is a problem because whenever I push the button that should be calling confirmItem I get the error. Am I using Ext.device.Notification correctly, or Have I missed something needed to make it work in Phonegap?
I found the solution! Everything was fine from a Sencha Touch point of view in terms of using requires: Ext.device.Notification but some things were missing on the Phonegap side. Specifically, I needed to install the appropriate plugins.
Open a terminal and type: Phonegap local plugin list to see your currently installed plugins. I had none. I went ahead and installed:
org.apache.cordova.device
org.apache.cordova.dialogs
org.apache.cordova.vibration
by using the following reference: http://docs.phonegap.com/en/3.0.0/cordova_device_device.md.html and selecting options from the menu on the left.