Trying to mock Ionic Native Network in browser. To do so, I added the class
in my app.module.ts
as follows:
...
import { Network } from '@ionic-native/network';
...
...
export class NetworkMock extends Network{
type = 'none';
}
@NgModule({ ....
providers: [
...
...
{ provide: ErrorHandler, useClass: IonicErrorHandler },
{provide: Network, useClass: NetworkMock}
With the above, when I call the following function in a component:
check_network() {
console.log(this.net.type);
}
I get null
in the browser console
How can I get it working correctly ? Am i not supposed to get 'none'
?
The following is the output from ionic-info :
global packages:
@ionic/cli-utils : 1.4.0
Cordova CLI : 6.5.0
Ionic CLI : 3.4.0
local packages:
@ionic/app-scripts : 1.3.0
@ionic/cli-plugin-cordova : 1.4.0
@ionic/cli-plugin-ionic-angular : 1.3.1
Cordova Platforms : none
Ionic Framework : ionic-angular 3.0.1
System:
Node : v6.9.1
OS : Windows 7
Xcode : not installed
ios-deploy : not installed
ios-sim : not installed
npm : 3.10.8
What is going wrong ? Please help.
I prefer to do things a little bit different when it comes to using a cordova plugin in the browser. Even though your approach works great, you still need to manually tell Angular which class should be used when it sees a Network
parameter in some component's constructor. You do that in this line:
{ provide: Network, useClass: NetworkMock }
So if you want to run the app on a mobile device, you'd need to manually change that line (and all the lines related to some other cordova plugins as well).
That's why I like to use a factory provider instead. This is how I work with the Network
cordova plugin in the browser:
// ------------------------------------------------------------
// File: /providers/cordova-plugins/network/network.provider.ts
// ------------------------------------------------------------
// Ionic
import { Platform } from 'ionic-angular';
// Ionic native
// http://ionicframework.com/docs/v2/native/network/
import { Network } from '@ionic-native/network';
// Browser implementation
export class BrowserNetworkProvider extends Network {
public get type(): string {
return this.isOnline() ? 'wify' : 'none';
}
private isOnline(): boolean {
return navigator.onLine;
}
}
// Mobile implementation
// Is empty in this case since I don't to override anything, but in
// some other plugins, I like to add some console.log() before calling
// the methods in the plugin (by using super.nameOfTheMethod();)
export class MobileNetworkProvider extends Network {}
// ------------------------------------------------------------
// Network factory
// parameters: dependencies of the target service
// returns: instance of the service (for real devices or the browser)
// ------------------------------------------------------------
export function networkFactory(platform: Platform) {
return platform.is('cordova') ? new MobileNetworkProvider() : new BrowserNetworkProvider();
}
// networkProvider: used to import the service in the NgModule declaration
export let networkProvider =
{
provide: Network,
useFactory: networkFactory,
deps: [Platform]
};
And then in the app.module.ts
file:
import { networkProvider } from '../providers/plugins/network/network.provider';
@NgModule({
declarations: [
MyApp,
//...
],
imports: [
// ...
],
bootstrap: [IonicApp],
entryComponents: [
// ...
],
providers: [
// Cordova plugins
networkProvider, // <- I'm using our custom provider here! :)
// ...
]
})
export class AppModule { }
As you can see, Angular will use the Platform
information to know which extension of the Network
class should be used in our app.
As we talked about in the comments:
1) Regarding the getter to override the type
property, I don't know that much of Typescript, but for some reason I was not able to set the value of a property when extending a class (that's why I suggested using the getter instead). Since a getter is called just like if it were a property, I though about doing it like that and seems to be working!
2) Why extending the Network
class for the browser, when we could also use a class that has nothing to do with the Network
class (like when you omit the extend
keyword in your example? Well, you could do that, since after all this is just Javascript (and you can fix any Typescript complain using the (<any>propertyName)
cast. But since you're telling Angular which implementation of the Network
class should be used, why not extending that class, overriding what we want to change, but keeping everything consistent? If later the plugin add some methods that works on the browser, you won't need to change anything, since you're already using the real implementation of the plugin for all the methods that you don't override.
I just tried the following alternative and it worked.
...
import { Network } from '@ionic-native/network';
...
...
export class NetworkMock{
// I omitted the Extend keyword and avoided overriding the actual class
type = 'none';
}
@NgModule({ ....
providers: [
...
...
{ provide: ErrorHandler, useClass: IonicErrorHandler },
{provide: Network, useClass: NetworkMock}
With the code above, now the browser console correctly prints 'none' or whatever value I put in the type property of the NetworkMock
class.
To my understanding, the Inheritance is Javascript is tricky in nature and comes up with undesirable circumstances. So I avoided it completely. However, in the providers
array I referenced my NetworkMock
in useClass
property.
Any further insight will be highly appreciated.