variable can't assign from subscribe method - javascript

Within the method itself Shows the value, but outside it shows undefined message
Ubigeo.service.ts
lisProvinciaByDepartamento( ubigeoId: string): Observable<Ubigeo[]> {
let ubigeo$ = this.http
.get(`${this.baseUrl}/provincia/${ubigeoId}`, {headers: this.getHeaders()})
.map(mapUbigeos)
.catch(handleError);
return ubigeo$;
}
Detail.component.ts
cargaPronvicia(ubigeoId: string) {
this._ubigeoService
.lisProvinciaByDepartamento(ubigeoId)
.subscribe((p) => {
this.provinciaSeleccionada = p
.find(item =>
item.ubigeoId.substr(2, 2) === ubigeoId.substr(2, 2));
this.provincias = p;
console.log(this.provinciaSeleccionada); //2
});
console.log(this.provinciaSeleccionada); //1
}
undefined in console

Its because the data retrieval is relatively async when you are using it as observalbes.
So never use the variable directly,
Good practices
Skip subscription if it is empty.
cargaPronvicia(ubigeoId: string) {
this._ubigeoService
.lisProvinciaByDepartamento(ubigeoId)
////////////////////////////////////////////////////////////////
.skipWhile((p) => p === undefined)
////////////////////////////////////////////////////////////////
.subscribe((p) => {
this.provinciaSeleccionada = p
.find(item =>
item.ubigeoId.substr(2, 2) === ubigeoId.substr(2, 2));
this.provincias = p;
console.log(this.provinciaSeleccionada); //2
});
console.log(this.provinciaSeleccionada); //1
}
Where ever you are using the subscribed variables, check if it contains data, an example in your case would be
if(this.provinciaSeleccionada){
console.log(this.provinciaSeleccionada);
}
Update 1:
Use promises with most care, reason is when you are using promises in constructor, your component will not load until the promise is resolved.
By this speed of the application will be as expected.
Angular2's reactive nature is not utilized.

Related

Can method chaining be implemented the way built-in functions in Javascript are implemented?

I think there is something that i'm missing about method chaining. To me it feels incomplete.
Method chaining works by having each method return this so that another method on that object can be called. However, the fact that the return value is this and not the result of the function seems inconvenient to me.
Here is a simple example.
const Obj = {
result: 0,
addNumber: function (a, b) {
this.result = a + b;
return this;
},
multiplyNumber: function (a) {
this.result = this.result * a;
return this;
},
}
const operation = Obj.addNumber(10, 20).multiplyNumber(10).result
console.log(operation)
key points:
Every method in the chain Obj.addNumber(10, 20).multiplyNumber(10) returns this.
The last part of the chain .result is the one that returns a value other than this.
The problem with this approach is that it require you to tack on a property / method to get a value at the end other thanthis.
Compare this with built-in functions in JavaScript.
const str = " SomE RandoM StRIng "
console.log(str.toUpperCase()) // " SOME RANDOM STRING "
console.log(str.toUpperCase().trim()) // "SOME RANDOM STRING"
console.log(str.toUpperCase().trim().length) // 18
key points:
Each function in the chain returns the result of the function not this (maybe this is done under the hood)
No property / method is required at the end of the chain just to get the result.
Can we implement method chaining to behave the way built-in functions in Javascript behave?
First of all, each of your console.log doesn't return properly:
console.log(str.toUpperCase.trim) //undefined
It returns undefined because str.toUpperCase returns the function object and does not execute the function itself so it won't work
The only correct usage is
console.log(str.toUpperCase().trim()
Now about your question, it is pretty easy to do it without a result and it is much more efficient.
Everything in javascript has a method called valueOf(), here is my example of calling everything like that for numbers, though I prefer just making functions instead of Objects.
const Obj = {
addNumber: function (a = 0) {
return a + this.valueOf();
},
multiplyNumber: function (a = 1) {
return a*this.valueOf();
},
}
const nr = 2;
Object.keys(Obj).forEach(method => {
Number.prototype[method] = Obj[method];
})
console.log(Number.prototype); // will print out addNumber and multiplyNumber
// Now You can call it like this
console.log(nr.addNumber().multiplyNumber()); // Prints out 2 because it becomes (nr+0)*1
console.log(nr.addNumber(3).multiplyNumber(2)) // Prints out 10;
I think you are misunderstanding what method chaining actually is. It is simply a shorthand for invoking multiple methods without storing each intermediate result in a variable. In other words, it is a way of expressing this:
const uppercase = " bob ".toUpperCase()
const trimmed = uppercase.trim()
as this
const result = " bob ".toUpperCase().trim()
Nothing special is happening. The trim method is simply being called on the result of " bob ".toUpperCase(). Fundamentally, this boils down to operator precedence and the order of operations. The . operator is an accessor, and is evaluated from left to right. This makes the above expression equivalent to this (parens used to show order of evaluation):
const result = (" bob ".toUpperCase()).trim()
This happens regardless of what is returned by each individual method. For instance, I could do something like this:
const result = " bob ".trim().split().map((v,i) => i)
Which is equivalent to
const trimmed = " bob ".trim()
const array = trimmed.split() //Note that we now have an array
const indexes = array.map((v,i) => i) //and can call array methods
So, back to your example. You have an object. That object has encapsulated a value internally, and adds methods to the object for manipulating the results. In order for those methods to be useful, you need to keep returning an object that has those methods available. The simplest mechanism is to return this. It also may be the most appropriate way to do this, if you actually are trying to make the object mutable. However, if immutability is an option, you can instead instantiate new objects to return, each of which have the methods you want in the prototype. An example would be:
function MyType(n) {
this.number = n
}
MyType.prototype.valueOf = function() {
return this.number
}
MyType.prototype.add = function(a = 0) {
return new MyType(a + this)
}
MyType.prototype.multiply = function(a = 1) {
return new MyType(a * this)
}
const x = new MyType(1)
console.log(x.add(1)) // { number: 2 }
console.log(x.multiply(2)) // { number: 2 }
console.log(x.add(1).multiply(2)) // { number: 4 }
console.log(x.add(1).multiply(2) + 3) // 7
The key thing to note about this is that you are still using your object, but the valueOf on the prototype is what allows you to directly utilize the number as the value of the object, while still making the methods available. This is shown in the last example, where we directly add 3 to it (without accessing number). It is leveraged throughout the implementation by adding this directly to the numeric argument of the method.
Method chaining is the mechanism of calling a method on another method of the same object in order to get a cleaner and readable code.
In JavaScript method chaining most use the this keyword in the object's class in order to access its method (because the this keyword refers to the current object in which it is called)
When a certain method returns this, it simply returns an instance of the object in which it is returned, so in another words, to chain methods together, we must make sure that each method we define has a return value so that we can call another method on it.
In your code above, the function addNumber returns the current executing context back from the function call. The next function then executes on this context (referring to the same object), and invokes the other functions associated with the object. it's is a must for this chaining to work. each of the functions in the function chaining returns the current Execution Context. the functions can be chained together because the previous execution returns results that can be processed further on.
This is part of the magic and uniqueness of JavaScript, if you're coming from another language like Java or C# it may look weird for you, but the this keyword in JavaScript behaves differently.
You can avoid the necessity of this and be able to return a value implicitly, using a Proxy object with a get-trap.
Here you find a more generic factory for it.
const log = Logger();
log(`<code>myNum(42)
.add(3)
.multiply(5)
.divide(3)
.roundUp()
.multiply(7)
.divide(12)
.add(-1.75)</code> => ${
myNum(42)
.add(3)
.multiply(5)
.divide(3)
.roundUp()
.multiply(7)
.divide(12)
.add(-1.75)}`,
);
log(`\n<code>myString(\`hello world\`)
.upper()
.trim()
.insertAt(6, \`cruel coding \`)
.upper()</code> => ${
myString(`hello world`)
.upper()
.trim()
.insertAt(6, `cruel coding `)
.upper()
}`);
log(`<br><code>myString(\`border-top-left-radius\`).toUndashed()</code> => ${
myString(`border-top-left-radius`).toUndashed()}`);
// the proxy handling
function proxyHandlerFactory() {
return {
get: (target, prop) => {
if (prop && target[prop]) {
return target[prop];
}
return target.valueOf;
}
};
}
// a wrapped string with chainable methods
function myString(str = ``) {
const proxyHandler = proxyHandlerFactory();
const obj2Proxy = {
trim: () => nwProxy(str.trim()),
upper: () => nwProxy(str.toUpperCase()),
lower: () => nwProxy(str.toLowerCase()),
insertAt: (at, insertStr) =>
nwProxy(str.slice(0, at) + insertStr + str.slice(at)),
toDashed: () =>
nwProxy(str.replace(/[A-Z]/g, a => `-${a.toLowerCase()}`.toLowerCase())),
toUndashed: () => nwProxy([...str.toLowerCase()]
.reduce((acc, v) => {
const isDash = v === `-`;
acc = { ...acc,
s: acc.s.concat(isDash ? `` : acc.nextUpcase ? v.toUpperCase() : v)
};
acc.nextUpcase = isDash;
return acc;
}, {
s: '',
nextUpcase: false
}).s),
valueOf: () => str,
};
function nwProxy(nwStr) {
str = nwStr || str;
return new Proxy(obj2Proxy, proxyHandler);
}
return nwProxy();
}
// a wrapped number with chainable methods
function myNum(n = 1) {
const proxyHandler = proxyHandlerFactory();
const obj2Proxy = {
add: x => nwProxy(n + x),
divide: x => nwProxy(n / x),
multiply: x => nwProxy(n * x),
roundDown: () => nwProxy(Math.floor(n)),
roundUp: () => nwProxy(Math.ceil(n)),
valueOf: () => n,
};
function nwProxy(nwN) {
n = nwN || n;
return new Proxy(obj2Proxy, proxyHandler);
}
return nwProxy();
}
// ---- for demo ---- //
function Logger() {
const report =
document.querySelector("#report") ||
document.body.insertAdjacentElement(
"beforeend",
Object.assign(document.createElement("pre"), {
id: "report"
})
);
return (...args) => {
if (!args.length) {
return report.textContent = ``;
}
args.forEach(arg =>
report.insertAdjacentHTML(`beforeEnd`,
`<div>${arg.replace(/\n/g, `<br>`)}</div>`)
);
};
}
body {
font: 12px/15px verdana, arial;
margin: 0.6rem;
}
code {
color: green;
}

Maybe monad sample code in javascript explanation

I'm starting or trying to learn functional programming monads.
So the first is Maybe. I'm trying to convert the code with maybe monad.
function(fieldName, vals, fields) {
var newValue = vals[fieldName];
if (typeof(newValue) == 'undefined') {
var elemFrom = fields[fieldName];
if (elemFrom) {
newValue = fields[fieldName]
}
}
if (typeof (newValue) != 'undefined') {
return newValue
}
}
Here I have a bunch of checks for undefined which i think is good use of monay.
My problem is that I read that you pass value to the maybe monad and map function.
However in my case I replace the value inside the monad.
If I pass null the map method will do nothig since the value is undefined.
I'm not using a framework, i want simple implementation so I can understand it.
Should I add "else" method in the maybe monad class (function).
I have the opposite case "Do something if the value is undefined"
Can you suggest how to solve the issue
Thank you
So the function you posted could be rewritten as
const f = (a, b, c) => b[a] === undefined ? c[a] : b[a];
It isn't clear to me that this needs to be a function at all rather than being inlined wherever you want to use the relevant object properties, but maybe you're partially applying it or something, I'm not judging.
As for Maybe, a (very simple) implementation might look something like this:
class Maybe {
static of (value) {
return new Maybe(value);
}
// Proper solution here should be recursive to handle
// nesting properly, but I'm lazy
static equals (a, b) {
return a.chain(x => x) === b.chain(x => x);
}
constructor(value) {
this._value = value;
}
map (f) {
// Does not distinguish null from undefined, but YMMV. Note
// that if the Maybe value is null or undefined we never touch
// f, that's the null propagation thing.
return this._value == null ? this : new Maybe(f(this._value));
}
chain (f) {
const result = this._value == null ? this : f(this._value);
console.assert(result instanceof Maybe);
return result;
}
}
Now we can test that it obeys the Monad laws:
const a = 3;
const f = x => Maybe.of(x * x);
Maybe.of(a).chain(f) === f(a) // left identity
Maybe.equals(Maybe.of(5).chain(Maybe.of), Maybe.of(5)); // right identity
And that it's a valid Functor
Maybe.equals(Maybe.of(3).map(x => x), Maybe.of(3)); // identity
Maybe.equals( // composition
Maybe.of(3).map(x => x + 2).map(x => x * 3),
Maybe.of(3).map(compose(x => x * 3, x => x + 2))
);
Sweet.
So now, to your function. It would be rewritten as
const f = (a, b, c) => {
return b[a] === undefined ? Maybe.of(c[a]) : Maybe.of(b[a]);
}
Perhaps you see now the reason for my confusion, Maybe isn't really saving you much here. But if I were using Maybe I'd rewrite the whole thing like this:
const or = (a, b) => {
return Maybe.of(a == null ? b : a);
}
And then I would just pass in the property accesses:
const obj1 = { a: 2, c: 3 };
const obj2 = { b: 4 };
const prop = "a"
const result = or(obj1["prop"], obj2["prop"]); // Maybe(2)
Update
Credit to #Bergi in the comments reminding me about Alternative. You could add a method to the Maybe class above like so:
alt (x) {
if (!(x instanceof Maybe)) {
throw new TypeError("Expected a Maybe");
}
return this.chain(x => x) == null ? x : this;
}
// semantics
Maybe.of(null).alt(Maybe.of(3)); // Maybe(3)
Maybe.of(2).alt(Maybe.of(4)); // Maybe(2)
// usage
Maybe.of(obj1[prop]).alt(Maybe.of(obj2[prop]));
Note that this doesn't totally satisfy as an implementation of Alternative (you'd also need a zero/empty method) but you can read here and here for more details. This is probably the best replacement for the function you posted.

TS - assign value from a callback function using return

Im getting data of class People array with each person having attendance list as Observable<any[]>.
// Class person
class Person {
id: number;
name: string;
attendance: Observable<any[]>; // An Observable array
}
// People array (I only use array for simplicity sake)
// NOTE: Every person has a list of attendance
people = Person[] = this.someService.getPeople();
Now I'm validating if some of the people may had 0 length of attendance assuming that attendance: Observable<any[]> is always an empty array [] if nothing is found, return false otherwise true to validateAttendance().
validateAttendance(): boolean {
// Loop to each person and get attendance list.
people.forEach(p => {
// Get the list using a callback function.
const func = (callback: (data) => void) => {
// Get attendance using subscribe() method
p.attendance.subscribe(list => {
callback(list);
});
}
// An attempt to get the list from a callback function
// Failed because return is undefined.
const result = func(res => {
console.log(res); // Got list of attendance of a given person.
return res;
});
if (result.length <= 0) {
alert(`${p.name} has no attendance, must be a lazy one please check.`);
return false;
}
});
return true;
}
In order to do that i attempted to create a callback function inside the validateAttendance() method and return the attendance list from it. HOWEVER the result variable is undefined!
It is possible to return a value from a callback function this way?
There are several things wrong with your code:
It is asynchronous, so the function needs to be async too (using Promise)
func() does not return a value, so the result will always be undefined.
You have alert() deep in your code. Are you going to pop up for each person with no attendance? It is better to separate logic and UI.
I have modified your code to the following:
function validateAttendance(people): Promise<boolean> {
return getPeopleWithNoAttendance(people)
.then(peopleWithNoAttendance => {
if (peopleWithNoAttendance.length > 0) {
// or loop though them
alert(`${peopleWithNoAttendance[0].name} has no attendance, must be a lazy one please check.`);
return false
}
return true
})
}
function getPeopleWithNoAttendance(people) {
return Promise.all(people.map(person => {
return new Promise(r => {
person.attendance.subscribe(r)
}).then(attendance => {
return attendance.length <= 0 ? person : undefined
})
})).then(listOfPeopleOrUndefined => {
const listOfPeople = listOfPeopeleOrUndefined.filter(p => p)
return listOfPeople
})
}
Also changed is having the people variable passed in. If this is a method in a class, feel free to change that.

RxJS: debounce a stream only if distinct

I want to debounce a stream - but only if the source value is the same as before. How would I do this with RxJS 5?
I do not want to emit a value if the value is the same and I emitted it previously within a specified time window. I should be able to use the value from the stream - or compare function similar to distinctUntilChanged.
It depends on what you're trying to do; I came upon this question when I was trying to do something similar, basically debouncing but with different debounces for different values of an object.
After trying the solution from jayphelps I couldn't get it to behave as I wanted. After much back and forth, turns out there is an in built easy way to do it: groupby.
const priceUpdates = [
{bid: 10, id: 25},
{bid: 20, id: 30},
{bid: 11, id: 25},
{bid: 21, id: 30},
{bid: 25, id: 30}
];//emit each person
const source = Rx.Observable.from(priceUpdates);
//group by age
const example = source
.groupBy(bid => bid.id)
.mergeMap(group$ => group$.debounceTime(500))
const subscribe = example.subscribe(val => console.log(val));
Output:
[object Object] {
bid: 11,
id: 25
}
[object Object] {
bid: 25,
id: 30
}
Jsbin: http://jsbin.com/savahivege/edit?js,console
This code will group by the bid ID and debounce on that, so therefore only send the last values for each.
I'm not aware of any way to do this with without creating your own operator because you need to maintain some sort of state (the last seen value).
One way looks something like this:
// I named this debounceDistinctUntilChanged but that might not be
// the best name. Name it whatever you think makes sense!
function debounceDistinctUntilChanged(delay) {
const source$ = this;
return new Observable(observer => {
// Using an object as the default value
// so that the first time we check it
// if its the same its guaranteed to be false
// because every object has a different identity.
// Can't use null or undefined because source may
// emit these!
let lastSeen = {};
return source$
.debounce(value => {
// If the last value has the same identity we'll
// actually debounce
if (value === lastSeen) {
return Observable.timer(delay);
} else {
lastSeen = value;
// This will complete() right away so we don't actually debounce/buffer
// it at all
return Observable.empty();
}
})
.subscribe(observer);
});
}
Now that you see an implementation you may (or may not) find it differs from your expectations. Your description actually left out certain details, like if it should only be the last value you keep during the debounce time frame or if it's a set--basically distinctUntilChanged vs. distinct. I assumed the later.
Either way hopefully this gives you a starting point and reveals how easy it is to create custom operators. The built in operators definitely do not provide solutions for everything as-is, so any sufficiently advanced app will need to make their own (or do the imperative stuff inline without abstracting it, which is fine too).
You can then use this operator by putting it on the Observable prototype:
Observable.prototype.debounceDistinctUntilChanged = debounceDistinctUntilChanged;
// later
source$
.debounceDistinctUntilChanged(400)
.subscribe(d => console.log(d));
Or by using let:
// later
source$
.let(source$ => debounceDistinctUntilChanged.call($source, 400))
.subscribe(d => console.log(d));
If you can, I recommend truly understanding what my code does, so that in the future you are able to easily make your own solutions.
Providing an answer for RxJS 6+ with the method suggested by #samberic in an ngrx effect to group actions coming from a same source id with RxJS 6.
this.actions$.pipe(
ofType(actionFoo, actionBar), // Two different ngrx action with an id property
groupBy(action => action.id), // Group by the id from the source
mergeMap(action => action.pipe(
debounceTime(5000)
))
).pipe(
// Do whatever it is that your effect is supposed to do!
)
Here is my RXJS 6+ version in typescript that works 100% as originally requested. Debounce (restart timer) on every new source value. Emit value only if the new value is different from the previous value or the debounce time has expired.
// custom rxjs operator to debounce while the source emits the same values
debounceDistinct<T>(delay: number) {
return (source: Observable<T>): Observable<T> => {
return new Observable(subscriber => {
let hasValue = false;
let lastValue: T | null = null;
let durationSub: Subscription = null;
const emit = () => {
durationSub?.unsubscribe();
durationSub = null;
if (hasValue) {
// We have a value! Free up memory first, then emit the value.
hasValue = false;
const value = lastValue!;
lastValue = null;
subscriber.next(value);
}
};
return source.subscribe(
(value: T) => {
// new value received cancel timer
durationSub?.unsubscribe();
// emit lastValue if the value has changed
if (hasValue && value !== lastValue) {
const value = lastValue!;
subscriber.next(value);
}
hasValue = true;
lastValue = value;
// restart timer
durationSub = timer(delay).subscribe(() => {
emit();
});
},
error => {
},
() => {
emit();
subscriber.complete();
lastValue = null;
});
});
}
}
Another possibility, not sure if supported with rxjs5 though:
source$.pipe(
pairwise(),
debounce(([a, b]) => {
if (a === b) {
return interval(1000)
}
return of();
}),
map(([a,b]) => b)
)
.subscribe(console.log);
https://stackblitz.com/edit/typescript-39nq7f?file=index.ts&devtoolsheight=50
update for rxjs 6 :
source$
.pipe(
// debounceTime(300), optionally un-comment this to add debounce
distinctUntilChanged(),
)
.subscribe(v => console.log(v))
This rxjs6+ operator will emit when the source 'value' has changed or when some 'delay' time has passed since last emit (even if 'value' has not changed):
export function throttleUntilChanged(delay: number) {
return (source: Observable<any>) => {
return new Observable(observer => {
let lastSeen = {};
let lastSeenTime = 0;
return source
.pipe(
flatMap((value: any) => {
const now = Date.now();
if (value === lastSeen && (now - lastSeenTime) < delay ) {
return empty();
} else {
lastSeen = value;
lastSeenTime = now;
return of(value);
}
})
)
.subscribe(observer);
});
};
}

Multiple subscriptions to Observable

I create my own Observable and subscribed two functions to it. I would expect to have both functions executed for each element in the sequence but only the last one is.
let observer = null
const notificationArrayStream = Rx.Observable.create(function (obs) {
observer = obs;
return () => {}
})
function trigger(something) {
observer.next(something)
}
notificationArrayStream.subscribe((x) => console.log('a: ' + x))
notificationArrayStream.subscribe((x) => console.log('b: ' + x))
trigger('TEST')
Expected output
a: TEST
b: TEST
Actual output
b: TEST
Here's the JSBin: http://jsbin.com/cahoyey/edit?js,console
Why is that? How can I have multiple functions subscribed to a single Observable?
Subject
In your case, you could simply use a Subject. A subject allows you to share a single execution with multiple observers when using it as a proxy for a group of subscribers and a source.
In essence, here's your example using a subject:
const subject = new Subject();
function trigger(something) {
subject.next(something);
}
subject.subscribe((x) => console.log('a: ' + x));
subject.subscribe((x) => console.log('b: ' + x));
trigger('TEST');
Result:
a: TEST
b: TEST
Pitfall: Observers arriving too late
Note that the timing of when you subscribe and when you broadcast the data is relevant. If you send a broadcast before subscribing, you're not getting notified by this broadcast:
function trigger(something) {
subject.next(something);
}
trigger('TEST');
subject.subscribe((x) => console.log('a: ' + x));
subject.subscribe((x) => console.log('b: ' + x));
Result: (empty)
ReplaySubject & BehaviorSubject
If you want to ensure that even future subscribers get notified, you can use a ReplaySubject or a BehaviorSubject instead.
Here's an example using a ReplaySubject (with a cache-size of 5, meaning up to 5 values from the past will be remembered, as opposed to a BehaviorSubject which can remember only the last value):
const subject = new ReplaySubject(5); // buffer size is 5
function trigger(something) {
subject.next(something);
}
trigger('TEST');
subject.subscribe((x) => console.log('a: ' + x));
subject.subscribe((x) => console.log('b: ' + x));
Result:
a: TEST
b: TEST
To have multiple functions subscribe to a single Observable, just subscribe them to that observable, it is that simple. And actually that's what you did.
BUT your code does not work because after notificationArrayStream.subscribe((x) => console.log('b: ' + x)) is executed, observer is (x) => console.log('b: ' + x)), so observer.next will give you b: TEST.
So basically it is your observable creation which is wrong. In create you passed an observer as parameter so you can pass it values. Those values you need to generate somehow through your own logic, but as you can see your logic here is erroneous. I would recommend you use a subject if you want to push values to the observer.
Something like:
const notificationArrayStream = Rx.Observable.create(function (obs) {
mySubject.subscribe(obs);
return () => {}
})
function trigger(something) {
mySubject.next(something)
}
Every time you subscribe, you are overriding the var observer.
The trigger function only reference this one var, hence no surprise there is only one log.
If we make the var an array it works as intended: JS Bin
let obs = [];
let foo = Rx.Observable.create(function (observer) {
obs.push(observer);
});
function trigger(sth){
// console.log('trigger fn');
obs.forEach(ob => ob.next(sth));
}
foo.subscribe(function (x) {
console.log(`a:${x}`);
});
foo.subscribe(function (y) {
console.log(`b:${y}`);
});
trigger(1);
trigger(2);
trigger(3);
trigger(4);
A cleaner solution would be to use Subject, as suggested above.
Observables are not multicasting; unless you use any kind of Subject. You can of course create Subject, pipe the Observable output into like other answers propose.
However if you already have an Observalbe, it is way more convenient to use share() that turns Observable into Subject or shareReplay(n) which would be equivalent for ReplaySubject(n):
import {share} from 'rxjs/operators';
let observer = null
const notificationArrayStream = new Observable(obs => {
observer = obs;
}).pipe(share());
function trigger(something) {
observer.next(something)
}
notificationArrayStream.subscribe((x) => console.log('a: ' + x))
notificationArrayStream.subscribe((x) => console.log('b: ' + x))
trigger('TEST')
That's pretty much it.
You can build wrapper class Subscribable<> based on ReplaySubject. It would be cleaner than managing Subject and Observable:
export class Subscribable<T> {
private valueSource: Subject = new ReplaySubject(1);
public value: Observable;
private _value: T;
constructor() {
this.value = this.valueSource.asObservable();
}
public set(val: T) {
this.valueSource.next(val);
this._value = val;
}
public get(): T {
return this._value;
}
}
Usage:
let arrayStream : Subscribable<TYPE> = new Subscribable<TYPE>();
…
public setArrayStream (value: TYPE) {
this.set(value);
}
Handle value change:
arrayStream.value.subscribe(res => { /*handle it*/ });
Original article: http://devinstance.net/articles/20170921/rxjs-subscribable
Instead of using a Subject, it is also possible to use the publishReplay() + refCount() combo to allow an observable to multicast to multiple subscribers:
const notificationArrayStream = Rx.Observable.create(function (obs) {
observer = obs;
return () => {}
}).pipe(publishReplay(), refCount())
const subs = []
const ob = new Observable((s) => {
console.log('called')
subs.push(s)
})
const trigger = (v) => {
subs.forEach((sub) => {
sub.next(v)
})
}
ob.subscribe((v) => {
console.log('ob1', v)
})
ob.subscribe((v) => {
console.log('ob2', v)
})
trigger(1)
Change your code into something like this, and it will work. The point here is that each subscription is updated through its corresponding subscriber, if you have multiple subscriptions, you have to notify multiple subscribers. And in your case, you just notified the last one.

Categories