Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions dist/minimongo-observable/meteor-observable.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,36 @@ var MeteorObservable = (function () {
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
var currentZone = Zone.current;
var argumentsArray = Array.prototype.slice.call(arguments);
var lastParam = argumentsArray[argumentsArray.length - 1];
if (lastParam && _.isFunction(lastParam)) {
throw new Error("Invalid MeteorObservable.call arguments:\n Your last param can't be a callback function, \n please remove it and use \".subscribe\" of the Observable!");
}
return rxjs_1.Observable.create(function (observer) {
var obs = rxjs_1.Observable.create(function (observer) {
Meteor.call.apply(Meteor, argumentsArray.concat([
function (error, result) {
if (error) {
observer.error(error);
observer.complete();
currentZone.run(function () {
observer.error(error);
observer.complete();
});
}
else {
observer.next(result);
observer.complete();
currentZone.run(function () { return observer.next(result); });
}
}
]));
});
obs.publish();
return obs;
};
MeteorObservable.subscribe = function (name) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
var currentZone = Zone.current;
var argumentsArray = Array.prototype.slice.call(arguments);
var lastParam = argumentsArray[argumentsArray.length - 1];
if (lastParam && _.isObject(lastParam) && (lastParam.onReady || lastParam.onError)) {
Expand All @@ -44,11 +49,13 @@ var MeteorObservable = (function () {
var handle = Meteor.subscribe.apply(Meteor, argumentsArray.concat([
{
onError: function (error) {
observer.error(error);
observer.complete();
currentZone.run(function () {
observer.error(error);
observer.complete();
});
},
onReady: function () {
observer.next();
currentZone.run(function () { return observer.next(); });
}
}
]));
Expand Down
8 changes: 5 additions & 3 deletions dist/minimongo-observable/to-observable.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
"use strict";
var observable_cursor_1 = require('./observable-cursor');
var _ = require('lodash');
var COLLECTION_EVENTS_DEBOUNCE_TIMEFRAME = 16;
function toObservable(cursor) {
var currentZone = Zone.current;
var observable = observable_cursor_1.ObservableCursor.create(function (observer) {
Copy link
Copy Markdown
Collaborator

@barbatus barbatus Aug 9, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dotansimha That do you think if save current zone here, i.e. before observable is created, and then use it below to run observer.next in? In this way we can choose what zone to use for observer.next (global or angular's one) by running toObserver in that particular zone, i.e. ngZone.run(toObservable(cursor)) or globalZone.run(toObservable(cursor)).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trying to get the Zone.current - but all I get is the root zone, and when trying to execute run on it, the view does not update... any idea? @barbatus

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Figured it out. the whole call stack comes from the subscription which should run next within the correct zone of execution.

var handleChange = function () {
observer.next(cursor.fetch());
};
var rawHandleChange = function () { return currentZone.run(function () { return observer.next(cursor.fetch()); }); };
var handleChange = _.debounce(rawHandleChange, COLLECTION_EVENTS_DEBOUNCE_TIMEFRAME);
var handler;
var isReactive = observable.isReactive();
observable._cursorRef = cursor;
Expand Down
25 changes: 17 additions & 8 deletions modules/minimongo-observable/meteor-observable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as _ from 'lodash';

export class MeteorObservable {
public static call<T>(name: string, ...args: any[]): Observable<T> {
const currentZone = Zone.current;
const argumentsArray: Array<any> = Array.prototype.slice.call(arguments);
const lastParam = argumentsArray[argumentsArray.length - 1];

Expand All @@ -14,22 +15,28 @@ export class MeteorObservable {
please remove it and use ".subscribe" of the Observable!`);
}

return Observable.create((observer: Subscriber<Meteor.Error | T>) => {
const obs = Observable.create((observer: Subscriber<Meteor.Error | T>) => {
Meteor.call.apply(Meteor, argumentsArray.concat([
(error: Meteor.Error, result: T) => {
if (error) {
observer.error(error);
observer.complete();
currentZone.run(() => {
observer.error(error);
observer.complete();
});
} else {
observer.next(result);
observer.complete();
currentZone.run(() => observer.next(result));
}
}
]));
});

obs.publish();

return obs;
}

public static subscribe<T>(name: string, ...args: any[]): ObservableMeteorSubscription<T> {
const currentZone = Zone.current;
const argumentsArray: Array<any> = Array.prototype.slice.call(arguments);
const lastParam = argumentsArray[argumentsArray.length - 1];

Expand All @@ -45,11 +52,13 @@ export class MeteorObservable {
let handle = Meteor.subscribe.apply(Meteor, argumentsArray.concat([
{
onError: (error: Meteor.Error) => {
observer.error(error);
observer.complete();
currentZone.run(() => {
observer.error(error);
observer.complete();
});
},
onReady: () => {
observer.next();
currentZone.run(() => observer.next());
}
}
]));
Expand Down
11 changes: 8 additions & 3 deletions modules/minimongo-observable/to-observable.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import {Subscriber} from 'rxjs/Rx';
import {ObservableCursor} from './observable-cursor';
import * as _ from 'lodash';

const COLLECTION_EVENTS_DEBOUNCE_TIMEFRAME = 16;

export function toObservable<T>(cursor : Mongo.Cursor<T>) : ObservableCursor<Array<T>> {
const currentZone = Zone.current;
const observable =
ObservableCursor.create((observer : Subscriber<Array<T>>) => {
const handleChange = () => {
observer.next(cursor.fetch());
};
const rawHandleChange = () => currentZone.run(() => observer.next(cursor.fetch()));
const handleChange = _.debounce(
rawHandleChange,
COLLECTION_EVENTS_DEBOUNCE_TIMEFRAME);

let handler;
let isReactive = observable.isReactive();
Expand Down
74 changes: 32 additions & 42 deletions tests/client/unit/meteor-observable.spec.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,47 @@
import {chai} from 'meteor/practicalmeteor:chai';
import {sinon} from 'meteor/practicalmeteor:sinon';
import {MeteorObservable, ObservableMeteorSubscription} from "angular2-meteor";
import {Observable} from "rxjs";
import {MeteorObservable, ObservableMeteorSubscription} from 'angular2-meteor';
import {Observable} from 'rxjs';

const expect = chai.expect;

describe('MeteorObservable', function () {
describe("call", function() {
describe('call', function() {

it("Should return RxJS Observable when using 'call'", function() {
let returnValue = MeteorObservable.call("testMethod");
it('Should return RxJS Observable when using "call"', function() {
let returnValue = MeteorObservable.call('testMethod');
expect(returnValue instanceof Observable).to.equal(true);
});

it("Should NOT run the actual 'call' method without subscribing to the result", function() {
let spy = sinon.spy(Meteor, "call");
MeteorObservable.call("testMethod");
it('Should NOT run the actual "call" method without subscribing to the result', function() {
let spy = sinon.spy(Meteor, 'call');
MeteorObservable.call('testMethod');
expect(spy.called).to.equal(false);
spy.restore();
});

it("Should run the actual 'call' method when subscribing to the result", function() {
let spy = sinon.spy(Meteor, "call");
let subscriptionHandler = MeteorObservable.call("testMethod").subscribe(function() {});
it('Should run the actual "call" method when subscribing to the result', function() {
let spy = sinon.spy(Meteor, 'call');
let subscriptionHandler = MeteorObservable.call('testMethod').subscribe(function() {});
expect(spy.calledOnce).to.equal(true);
spy.restore();
subscriptionHandler.unsubscribe();
});

it("Should trigger the RxJS Observable 'next' callback when got the server response", function(done) {
it('Should trigger the RxJS Observable "next" callback when got the server response', function(done) {
let spy = sinon.spy();
let subscriptionHandler = MeteorObservable.call("testMethod").subscribe((serverResponse) => {
let subscriptionHandler = MeteorObservable.call('testMethod').subscribe((serverResponse) => {
spy();
expect(spy.callCount).to.equal(1);
expect(serverResponse).to.equal("TEST_VALUE");
expect(serverResponse).to.equal('TEST_VALUE');
subscriptionHandler.unsubscribe();
done();
});
});

it("Should trigger the RxJS Observable 'complete' callback when got the server response", function(done) {
it('Should trigger the RxJS Observable "error" callback when got the server error', function(done) {
let spy = sinon.spy();
let subscriptionHandler = MeteorObservable.call("testMethod").subscribe(function() {}, function() {}, () => {
spy();
expect(spy.callCount).to.equal(1);
subscriptionHandler.unsubscribe();
done();
});
});

it("Should trigger the RxJS Observable 'error' callback when got the server error", function(done) {
let spy = sinon.spy();
let subscriptionHandler = MeteorObservable.call("NON_EXISTING_METHOD").subscribe(function() {}, (e) => {
let subscriptionHandler = MeteorObservable.call('NON_EXISTING_METHOD').subscribe(function() {}, (e) => {
spy();
expect(spy.callCount).to.equal(1);
expect(e instanceof Meteor.Error).to.equal(true);
Expand All @@ -61,46 +51,46 @@ describe('MeteorObservable', function () {
});
});

describe("subscribe", function() {
it("Should return RxJS Observable when using 'subscribe'", function() {
let returnValue = MeteorObservable.subscribe("test");
describe('subscribe', function() {
it('Should return RxJS Observable when using "subscribe"', function() {
let returnValue = MeteorObservable.subscribe('test');
expect(returnValue instanceof Observable).to.equal(true);
});

it("Should NOT run the actual 'subscribe' method without subscribing to the result", function() {
let spy = sinon.spy(Meteor, "subscribe");
MeteorObservable.subscribe("test");
it('Should NOT run the actual "subscribe" method without subscribing to the result', function() {
let spy = sinon.spy(Meteor, 'subscribe');
MeteorObservable.subscribe('test');
expect(spy.called).to.equal(false);
spy.restore();
});

it("Should run the actual 'subscribe' method when subscribing to the result", function() {
let spy = sinon.spy(Meteor, "subscribe");
let subscriptionHandler = MeteorObservable.subscribe("test").subscribe(function() {});
it('Should run the actual "subscribe" method when subscribing to the result', function() {
let spy = sinon.spy(Meteor, 'subscribe');
let subscriptionHandler = MeteorObservable.subscribe('test').subscribe(function() {});
expect(spy.called).to.equal(true);
spy.restore();
subscriptionHandler.unsubscribe();
});

it("Should call RxJS Observable 'next' callback when subscription is ready", function(done) {
it('Should call RxJS Observable "next" callback when subscription is ready', function(done) {
let spy = sinon.spy();

let subscriptionHandler = MeteorObservable.subscribe("test").subscribe(() => {
let subscriptionHandler = MeteorObservable.subscribe('test').subscribe(() => {
spy();
expect(spy.callCount).to.equal(1);
subscriptionHandler.unsubscribe();
done();
});
});

it("Should stop the Meteor subscription when unsubscribing to the RxJS Observable", function(done) {
it('Should stop the Meteor subscription when unsubscribing to the RxJS Observable', function(done) {
function getSubscriptionsCount() {
return Object.keys((<any>Meteor).default_connection._subscriptions).length;
}

let baseSubscriptionsCount = getSubscriptionsCount();

let subscriptionHandler = MeteorObservable.subscribe("test").subscribe(() => {
let subscriptionHandler = MeteorObservable.subscribe('test').subscribe(() => {
expect(getSubscriptionsCount()).to.equal(baseSubscriptionsCount + 1);
subscriptionHandler.unsubscribe();
expect(getSubscriptionsCount()).to.equal(baseSubscriptionsCount);
Expand All @@ -109,15 +99,15 @@ describe('MeteorObservable', function () {
});


it("Should stop the Meteor subscription when calling stop of the Observable", function(done) {
it('Should stop the Meteor subscription when calling stop of the Observable', function(done) {
function getSubscriptionsCount() {
return Object.keys((<any>Meteor).default_connection._subscriptions).length;
}

let baseSubscriptionsCount = getSubscriptionsCount();

let obs : ObservableMeteorSubscription<any> = MeteorObservable.subscribe("test");
let subscriptionHandler = obs.subscribe(() => {
let obs : ObservableMeteorSubscription<any> = MeteorObservable.subscribe('test');
obs.subscribe(() => {
expect(getSubscriptionsCount()).to.equal(baseSubscriptionsCount + 1);
obs.stop();
expect(getSubscriptionsCount()).to.equal(baseSubscriptionsCount);
Expand Down
Loading