I have a problem with Cordova's android application based on Angular 5+. I've found that window.history.back() and similar native JS back functions make two problems:
when going back, a page is flashing. It seems like first, all HTML content loaded and after it CSS
In one page on a back action, my layout is broken (screens below)
Orginal view:
After back button:
What's curious - after changing phone orientation all backs to normal.
I've found a solution - instead of using vanilla JS back functions I've created mine using Angular Router:
I've subscribe on router's events and save all routes:
this._subs.push(this._router.events.subscribe((e) => {
if (e instanceof NavigationEnd) {
this._cordovaService.saveRoute(e.url);
}
}));
And if I want back, I use navigateByUrl function:
back(): void {
const lastRoute = this._routingHistory[this._routingHistory.length - 2];
if (lastRoute) {
this._router.navigateByUrl(lastRoute);
this._routingHistory.pop();
}
}
After implementing this functionality for my inApp back buttons all work fine - there is no flashing or breaking layout.
Although, after implemented this function for my physical back button, the error is the same - layout breaking or flashing. Below my implementation:
Service:
this.deviceReady = Observable.fromEvent(document, 'deviceready').publishReplay(1);
(this.deviceReady as ConnectableObservable<Event>).connect();
this.restore = Observable.fromEvent(document, 'resume').publishReplay();
(this.restore as ConnectableObservable<Event>).connect();
this.backbutton = Observable.fromEvent(document, 'backbutton').publishReplay();
(this.backbutton as ConnectableObservable<Event>).connect();
Using it:
this._subs.push(this._cordovaService.deviceReady.subscribe(
() => {
document.addEventListener('backbutton', function (e) {
e.preventDefault();
e.stopPropagation();
this._cordovaService.back();
}.bind(this), false);
}
)
);
I'm sure that function in backbutton is executed (I've logged some information) but the problem still occurs.
More information:
I'm using cordova version 8.0.0
I'm using the following plugins:
https://github.com/TheMattRay/cordova-plugin-wkwebviewxhrfix.git" />
Some hints:
Once, I've built Cordova's android applications which work great (with native JS back function) but after next build, all come back. In hockeyapp I see that in good working version lowest available Android version was 4.1. In new apps, it is 4.4.
I've tried to downgrade Cordova/android engine version but without any positive results.
Additionally, I want to work with the newest libraries available.
Thanks for any help in that case.
I've finally found the solution, based on the following blog's post: http://weblogs.thinktecture.com/thomas/2017/02/cordova-vs-zonejs-or-why-is-angulars-document-event-listener-not-in-a-zone.html, I've added below script just before cordova.js import:
<script>
(function () {
'use strict';
window.addEventListener = function () {
EventTarget.prototype.addEventListener.apply(this, arguments);
};
window.removeEventListener = function () {
EventTarget.prototype.removeEventListener.apply(this, arguments);
};
document.addEventListener = function () {
EventTarget.prototype.addEventListener.apply(this, arguments);
};
document.removeEventListener = function () {
EventTarget.prototype.removeEventListener.apply(this, arguments);
};
})();
</script>
<script type="text/javascript" src="cordova.js"></script>
More about why this error occurring you can read in this GitHub issue: https://github.com/angular/angular/issues/22509
Related
I use Laravel with Livewire. I wanted to react to an event in js. Once its emitted, an js alert should appear.
The problem seems to be in app.js were I have my js function. I receive an error, Livewire is not available. I use Laravel Mix to compile it, and it compiles all fine.
Error: Uncaught TypeError: Livewire.on is not a function
app.js:
import Livewire from '../../vendor/livewire/livewire/dist/livewire';
Livewire.on('testEvent', function () {
alert("test");
});
It looks like an import problem. I've removed the package and tried it again and I also thought its a timing problem, so I executed on document load but still the same problem.
I've then decided to push it directly to the stack through a components blade file, and here it works all fine.
#push('scripts')
<script>
Livewire.on('testEvent', function () {
alert("test");
});
</script>
#endpush
Pushing it to the stack should not be required as I've seen other code snippets without it and the error seems to be a simple import problem?
Use the browser Inspector and look if #livewireStyles and #livewireScripts are loaded in page. Be sure that Livewire already is installed
Please note that there are some js codes which #livewireScripts puts after the actual livewire.js file. so ...
make sure loading Livewire inside global window object for later accesses.
window.Livewire = require("../../public/vendor/livewire/livewire.js");`
create a new file called livewire.js with these lines
if (window.livewire) {
console.warn(
"Livewire: It looks like Livewire's #livewireScripts JavaScript assets have already been loaded. Make sure you aren't loading them twice."
);
}
window.livewire = new window.Livewire();
window.livewire.devTools(true);
window.Livewire = window.livewire;
window.livewire_app_url = "";
window.livewire_token = "ZgcGN7YWdBf2wfwJQoDJ3pUHfwhBmAAlSoeBpQXD";
/* Make sure Livewire loads first. */
if (window.Alpine) {
/* Defer showing the warning so it doesn't get buried under downstream errors. */
document.addEventListener("DOMContentLoaded", function () {
setTimeout(function () {
console.warn(
"Livewire: It looks like AlpineJS has already been loaded. Make sure Livewire's scripts are loaded before Alpine.\\n\\n Reference docs for more info: http://laravel-livewire.com/docs/alpine-js"
);
});
});
}
/* Make Alpine wait until Livewire is finished rendering to do its thing. */
window.deferLoadingAlpine = function (callback) {
window.addEventListener("livewire:load", function () {
callback();
});
};
let started = false;
window.addEventListener("alpine:initializing", function () {
if (!started) {
window.livewire.start();
started = true;
}
});
document.addEventListener("DOMContentLoaded", function () {
if (!started) {
window.livewire.start();
started = true;
}
});
Make sure you are using the latest version of livewire and use your own updated version of #livewireScripts js codes which automatically gets appended after the actual livewire.js script tag.
Does the Electron application framework have built-in text search?
The quick-start application doesn't provide any apparent search functionality (e.g. using Ctrl-F or from the menu options). I would have expected this to be a BrowserWindow option (or an option of its WebContents), but I don't see anything helpful in the docs.
I know this is an old thread, but might still be relevant for people out there.
Had the same problem, and first fixed by using electron-in-page-search, but this component doesn't work properly with Electron 2 or greater.
Then finally found electron-find resolved my problem. Using with Electron 4.
You just add the component to your project:
npm install electron-find --save
Add a global shortcut in your Electron main process to send an event to the renderer in a ctrl+f:
globalShortcut.register('CommandOrControl+F', () => {
window.webContents.send('on-find');
});
And then you can add this to your page (the renderer process)
const remote = require('electron').remote;
const FindInPage = require('electron-find').FindInPage;
let findInPage = new FindInPage(remote.getCurrentWebContents());
ipcRenderer.on('on-find', (e, args) => {
findInPage.openFindWindow()
})
Hope that helps.
Try webContents.findInPage just added in the latest version.
There is an issue with the solution Robson Hermes offered. globalShortcut is, by definition, global, so the shortcut will be detected even when the app is not focused. This will result in the Ctrl+F shortcut being "stolen" from everywhere else.
I have found no ideal solution (see this issue on the electron repository), but a hacky one can be achieved by doing what Robson said and adding
win.on('focus', () => {
globalShortcut.register('CommandOrControl+F', () => windows.main.send('on-find'))
})
win.on('blur', () => {
globalShortcut.unregister('CommandOrControl+F')
}
Note that as seen here, this is not ideal and can lead to several issues:
Other applications can get a lock on the shortcut when you lose focus, i.e. the shortcut will magically stop working when you switch back to the app later.
Some apps can appear on screen without taking focus (spotlight I believe has this behavior) and during the app's appearance the shortcuts will still be captured by your application.
There's also gonna be those weird one in a thousand situations where somehow you switch focus and the shortcut is not removed.
Instead of using global shortcuts , use Accelerators ( normal Keyboard shortcut )
{
label : 'help',
click : function(){.
electron.shell.openExternal('http://....').
},
accelerator : 'CmdOrCtrl+ Shift + H'
}
The above shown is just an example of How to use accelerator
I am a backend developer who got a little project to fix it.
So my boss gives me an electron project which runs on touch devices.
I know that I can listen any key events in Javascript if I use the document object, but in electron it does not work, it says the docuemnt cannot be found.
So implemented this when I or other support guy press the F12 button then the dev tools be rendered out in the electron app.
mainWindow = new BrowserWindow({
'web-preferences': {'web-security': false}
});
mainWindow.onkeydown = function (e) {
console.log("Key down");
if (e.which === 123) {
console.log("Key is F12");
mainWindow.webContents.openDevTools();
}
};
But this code is not working to me. I have no idea how I can listen the F12 button is pressed.
Unfortunately I cannot render out button to the UI which can show the devtools. Because of the customers mustn't press it.
Sometimes I need to see the realtime console tab in devtools on the device.
There is a known issue in Electron ( which has lately been marked as wontfix ) that prevents the usual approach to catch key events using the traditional JS approach.
There also is a small library called electron-localshortcut that circumvents this issue by hijacking the Electron global shortcuts API when the window is active.
Use like this in your main.js:
const electronLocalshortcut = require('electron-localshortcut');
electronLocalshortcut.register(mainWindow, 'F12', () => {
// Open DevTools
});
Without additional libraries you can use "globalShortcut" of electron
const { app, BrowserWindow, globalShortcut } = require("electron");
globalShortcut.register("CmdOrCtrl+F12", () => {
mainWindow.isFocused() && mainWindow.webContents.toggleDevTools();
});
I think F12 is preserved so I use ctrl+f12 which is not far off
You can use the library mousetrap to add global short cut, because it can be installed through node and could bypass the problem of electron mentioned in the accepted answer.
A code example in the render process would be:
var Mousetrap = require('mousetrap');
Mousetrap.bind('4', function() { console.log('4'); });
I have defined a library in Javascript that works great when I am in a browser but whose name isn't found when running under PhoneGap on my device.
The library is defined as so:
(function(bsdi, $) {
bsdi.SomeName = "XYZ";
bsdi.addDays = function (date, days) { ...stuff here...}
....
}(bsdi = window.bsdi || {}, jQuery));
Later, in a .js file that is loaded last, I have:
function knockoutFn() {
var self = this;
if (bsdi.SomeName == "XYZ") { <<--- CRASHES HERE, "bsdi not defined" but only on Device
...stuff here...
}
}
// Happens to use Knockout...
var koFn = new knockoutFn();
ko.applyBindings(koFn);
function init() {
if (isPhoneGap) {
document.addEventListener("deviceready", onDeviceReady, false);
}
else {
koFn.InitPage();
}
}
function onDeviceReady() {
// Now safe to use the Cordova API
koFn.InitPage();
}
What happens is that a normal web browser handles this just fine. However, when I download to my iPhone using the PhoneGap Build app, it gets to "bsdi.SomeName" and crashes because bsdi is not defined. I thought that my method for defining the bsdi library was correct but, obviously, there is something in PhoneGap that doesn't like this. Note that "isPhoneGap" is true and we do use the addEventListener on the device.
Any ideas are greatly appreciated!
UPDATE: On a hunch, I tried moving the bsdi object into the same .js file as the code that uses it. In this case, it finds the object and uses it correctly. When it is an external file, however, it fails. And yes, I have triple-checked that the file exists and is at the correct location. Again, it works fine in a browser!
If window.bsdi isn't defined, then (as posted in your question) your initialization code never ensures that window.bsdi is defined by the time the code is finished. All it does is add those properties to the new empty object passed in, but that won't have any effect on anything once the initialization function is finished.
I'm using Ionic to build an app for Android, and when testing from a device, the call I make using the org.apache.cordova.file-transfer plugin always yields the same response.
Here's the code used to fire off the download:
$scope.getFile = function(){
var filePath = cordova.file.dataDirectory + "beer.json";
var uri = encodeURI('https://stormy-sierra-8448.herokuapp.com/api/?q=stones+pale+ale');
var options = {};
$cordovaFileTransfer.download(uri, filePath, options, true)
.then(function(result) {
$scope.status = result;
alert('success');
}, function(err) {
console.log(err);
}, function (progress) {
$timeout(function () {
$scope.downloadProgress = (progress.loaded / progress.total) * 100;
})
});
}
And then the response (from the console)
FileTransferError {
code: 3,
source: "https://stormy-sierra-8448.herokuapp.com/api/?q=stones+pale+ale",
target: "documents/beer.txt",
http_status: 401,
body: null…
}
body: nullcode: 3exception: nullhttp_status: 401source: "https://stormy-sierra-8448.herokuapp.com/api/?q=stones+pale+ale"
target: "documents/beer.txt"
__proto__: FileTransferError
My environment looks like this:
Cordova v5.0.0
Ionic 1.3.20
I've seen others post that downgrading the plugin made it work, but when I go below the current version I'm using (0.5), the app doesn't build. When I use the newest version (1.0), the app builds, but after it launches, the console says:
Uncaught module cordova-plugin-file.ProgressEvent not found - cordova.js:59
The device has a connection and verified with the 'device' plugin.
Please help!
In the end, I bootstrapped Angular to the device ready event in my main index.html file. Once the event fired, I didn't have to worry about plugins not being initialized and ready for use, which I think is one of the reasons the FileTransfer plugin was failing me.
Another thing I did was install and use the $iconic command for everything. Before, I was mixing $cordova [cmd] and $ionic [cmd]. Not sure that it really matters, but everything seems to be working properly now.
The bootstrapping logic is below. I personally think this is how every ionic project should be set up from the get-go. Just remember to remove the ng-app from your body tag or wherever you've placed it.
angular.element(document).ready(function() {
if (window.cordova) {
document.addEventListener('deviceready', function() {
angular.bootstrap(document.body, ['myApp']);
}, false);
} else {
angular.bootstrap(document.body, ['myApp']);
}
});
Still using the latest versions of Cordova and Ionic. :)