Ok, I have on-device remote store debugging working with Ionic 2. Unfortunately, time-travel and state import doesn't work with store-devtools
yet (see ngrx/store-devtools#33 and ngrx/store-devtools#31), but at least the Inspector, Log Monitor and Graph is working remotely. Here's how:
First, add https://github.com/zalmoxisus/remotedev to the project:
> npm install --save-dev remotedev
Then add these devtools proxy wrapper classes to the project. They provide the same interface as the browser extension so store-devtools will think it's just talking to the chrome extension. I left in the trace debug logging so you can clearly see what's happening in the console, but it's easy to remove if you want.
import moment from 'moment';
import {
ReduxDevtoolsExtension,
ReduxDevtoolsExtensionConnection
} from '@ngrx/store-devtools/src/extension';
import { connect, extractState } from 'remotedev/lib/devTools';
export class RemoteDevToolsConnectionProxy implements ReduxDevtoolsExtensionConnection {
className = 'RemoteDevToolsConnectionProxy';
constructor(
public remotedev: any,
public instanceId: string
) {
}
subscribe(
listener: (change: any) => void
): any {
const logContext = `${this.className} - subscribe`;
console.log(`[${logContext}] listener: ${listener.toString()}`);
const listenerWrapper = (change: any) => {
console.log(`[${logContext} - listenerWrapper] change: ${
JSON.stringify(change, null, 4)}`);
// extractState handles circular references, unlike JSON.parse directly. See:
// https://github.com/zalmoxisus/remotedev/issues/5#issuecomment-257009656
const state = extractState(change);
console.log(`[${logContext} - listenerWrapper] parsed state: ${
JSON.stringify(state, null, 4)}`);
// WARNING: @ngrx/store-devtools does NOT handle the time-travel changes
// correctly, so this doesn't actually work yet! See the following issues:
// https://github.com/ngrx/store-devtools/issues/33
// https://github.com/ngrx/store-devtools/issues/31
listener(change);
};
this.remotedev.subscribe(listenerWrapper);
}
unsubscribe(
): any {
// HACK a bug in remotedev ignores the real instanceId. See:
// https://github.com/zalmoxisus/remotedev/issues/4
// UPDATE: fixed - quickly! - in [email protected]
//const instanceId = 0; // internal array index instead of actual this.instanceId;
const instanceId = this.instanceId;
console.log(`[${this.className} - unsubscribe] instanceId: ${instanceId}`);
// HACK fix bug in @ngrx/store-devtools that calls this instead of returning
// a lambda that calls it when their Observable wrapper is unsubscribed.
return () => this.remotedev.unsubscribe(instanceId);
}
// NOTE: THIS IS NEVER CALLED - see send() on RemoteDevToolsProxy below
send(
action: any,
state: any
): any {
console.log(`[${this.className} -send]\n` +
`action: ${JSON.stringify(action, null, 4)},\n` +
`state: ${JSON.stringify(state, null, 4)}`);
this.remotedev.send(action, state);
}
}
export class RemoteDevToolsProxy implements ReduxDevtoolsExtension {
className = 'RemoteDevToolsProxy';
remotedev: any = null;
defaultOptions = {
realtime: true,
hostname: 'localhost',
port: 8000,
autoReconnect: true,
connectTimeout: 20000,
ackTimeout: 10000,
secure: true,
};
constructor(
defaultOptions: Object
) {
this.defaultOptions = Object.assign(this.defaultOptions, defaultOptions);
}
connect(
options: {
shouldStringify?: boolean;
instanceId: string;
}
): ReduxDevtoolsExtensionConnection {
const logContext = `${this.className} - connect`;
console.log(`[${logContext}] options: ${JSON.stringify(options, null, 4)}`);
const connectOptions = Object.assign(this.defaultOptions, options);
console.log(`[redux extension - connect] connectOptions: ${
JSON.stringify(connectOptions, null, 4)}`);
this.remotedev = connect(connectOptions);
console.log(`[${logContext}] remotedev:`);
console.log(this.remotedev);
const connectionProxy = new RemoteDevToolsConnectionProxy(
this.remotedev, connectOptions.instanceId);
return connectionProxy;
}
send(
action: any,
state: any,
shouldStringify?: boolean,
instanceId?: string
): any {
console.log(`[${this.className} - send]\n` +
`action: ${JSON.stringify(action, null, 4)},\n` +
`state: ${JSON.stringify(state, null, 4)},\n` +
`shouldStringify: ${shouldStringify}, instanceId: ${instanceId}`
);
this.remotedev.send(action, state);
}
}
Then in your app.module.ts
, just add an instance to window.devToolsExtension
(legacy) and/or window.__REDUX_DEVTOOLS_EXTENSION__
(current) if you're not in a browser where it already exists (see deprecation of window.devToolsExtension
in zalmoxisus/redux-devtools-extension#220). Then call StoreDevtoolsModule.instrumentOnlyWithExtension()
, as usual.
// ...
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { RemoteDevToolsProxy } from './remote-devtools-proxy'; // our new wrapper class
// ...
// Register our remote devtools if we're on-device and not in a browser
if (!window['devToolsExtension'] && !window['__REDUX_DEVTOOLS_EXTENSION__']) {
let remoteDevToolsProxy = new RemoteDevToolsProxy({
connectTimeout: 300000, // extend for pauses during debugging
ackTimeout: 120000, // extend for pauses during debugging
secure: false, // dev only
});
// support both the legacy and new keys, for now
window['devToolsExtension'] = remoteDevToolsProxy;
window['__REDUX_DEVTOOLS_EXTENSION__'] = remoteDevToolsProxy;
}
// ...
@NgModule({
imports: [
// ...
// the devtools looks for a window.devToolsExtension to attach to,
// which we registered above if there wasn't one already.
StoreDevtoolsModule.instrumentOnlyWithExtension(),
// ...
],
// ...
})
export class AppModule {}
Whew, ok now that we have the remote client set up, let's run a server for it to talk to. I just installed remotedev-server
globally and ran it like this:
> npm install -g remotedev-server
> remotedev --hostname=my.local.ip.address --port=8000
Make sure the hostname matches what was configured in app.module.ts
. The default is localhost
.
Once that's up and listening for connections, we can use the Chrome Redux DevTools extension to connect to the remotedev server via the Remote
button in the interface.
Now, run the app on the device. For an Android device connected via USB, I use Chrome's port forwarding feature to forward port 8000 to my dev box.
@rob3c How about nativescript?