Types for observable - rxjs

I would like to have an type for my observable (just for learning purposes). How can I do it for the follwoing lines of code? I get compiler errors and don't understand them.
type Color = "white" | "green" | "red" | "blue";
type Logo = "fish" | "dog" | "bird" | "cow";
const color$ = new Subject<Color>();
const logo$ = new Subject<Logo>();
const observabl1:<Subject<Color>, Subject<Logo>> = combineLatest([color$, logo$]);
observabl1.subscribe(([color, logo]) =>
console.log(`${color} ${logo}`)
);
and
const observable2$:Observable<string> = from(["white", "green", "red", "blue"]);
Update: I found the error. I imported import { Observable } from 'rx'; Changing it to import { Observable } from 'rxjs'; fixed the problem

I get compiler errors and don't understand them
Kindly provide the errors you want to discuss.
const observabl1:<Subject, Subject> = combineLatest([color$, logo$]);
combineLatest provides a stream with an array of the data.
const observabl1: Observable<[Color, logo]> = combineLatest([color$, logo$]);

Related

Set alternative colors when using the propertyFields on series.columns.template

Im having a problem to set an alternative color for a label on a series column template using the propertyField.
For example. I use this:
let columnTemplate = series.columns.template;
columnTemplate.propertyFields.fill = 'color';
Where 'color' is comming from DataItem, as a data from backend system.
And later on I want to add a label on the column. And I want a contrast so I want to use the alternative color: https://www.amcharts.com/docs/v4/concepts/colors/#Getting_contrasting_color
I make up my label as:
let label = columnTemplate.createChild(am4core.Label);
Then I will set the alternative color from the series.columns.template.
But I dont get it work. I tried for example:
label.fill = series.columns.template.fill.alternative;
Dosent work.
This:
label.fill = am4core.color(series.columns.template.propertyFields.fill).alternative;
Dont work.
This:
label.adapter.add('fill', (value, target, key) => {
const dataContext: { [key: string]: string } | undefined = target.dataItem?.dataContext as {
[key: string]: string;
};
return am4core.color(dataContext?.color).alternative;
});
Throws an error...
Is there anyone who know a solution for this? Please help.
I,m not sure this is a good solution but it works:
export function setContrastColor(value: any, target: any) {
const dataContext = (target.dataItem?.dataContext ?? {}) as Record<string, string | undefined>;
if (dataContext.color) {
return am4core.color(`${dataContext?.color}`).alternative;
} else {
return am4core.color('#FFF');
}
}
And calling it:
label.adapter.add('fill', setContrastColor);

Gatsby: How Do I Make a List of All Nodes?

I want to make a list of all unique node values and render them on my page. This is what I have:
import { graphql, Link } from "gatsby"
import React from "react"
import Layout from "../layouts/Layout"
const _ = require("lodash")
export default props => {
const majorNodes = props.item.edges.node
let majors = []
let major = []
majorNodes.map(majorNode => majors.push(majorNode.major))
majors = majors.concat(major)
majors = _.uniq(majors)
return (
<Layout>
<div>{majors}</div>
</Layout>
)
}
export const query = graphql`
query majors {
item: allContentfulPortfolio {
edges {
node {
major
}
}
}
}
`
However, this gives me the following error:
TypeError: Cannot read property 'edges' of undefined
_default
D:/Gatsby/archives/src/pages/majors.js:7
4 | const _ = require("lodash")
5 |
6 | export default props => {
> 7 | const majorNodes = props.item.edges.node
8 | let majors = []
9 | let major = []
10 | majorNodes.map(majorNode => majors.push(majorNode.major))
How do I fix this? Additionally, is this how I should pass the array majors for rendering? I think that is where I am going wrong.
Your nodes are stored inside props.data so:
const majorNodes = props.item.edges.node
Should become:
const majorNodes = props.data.item.edges.node
Additionally, is this how I should pass the array majors for
rendering? I think that is where I am going wrong.
majors is an array so printing it directly as:
<div>{majors}</div>
Won't work.
You should loop through each element with something like:
<div>{majors.map(major => <p> Major element is {major}</p> )}</div>

BufferTime with leading option

I have some events that I'd like to buffer but I'd like to buffer only after the first element.
[------bufferTime------]
Input over time:
[1, 2, 3, -------------|---4, 5, 6 ----------------]
Output over time:
[1]-----------------[2,3]---[4]------------------[5,6]
is there a way to do this?
I think this can be solved by dividing your stream into two, firstValue$ and afterFirstValue$, and then merging them.
import { merge } from 'rxjs';
import { take, skip, bufferTime } from 'rxjs/operators';
...
firstValue$ = source$.pipe(
take(1)
);
afterFirstValue$ = source$.pipe(
skip(1),
bufferTime(5000)
);
merge(firstValue$, afterFirstValue$)
.subscribe(result => {
// Do something
});
Answer to follow up question concerning subject
So I have done it so that the original source is a subject here. It is not exactly how you described it, but I think maybe this is what you want.
import { merge, Subject } from 'rxjs';
import { take, skip, bufferTime } from 'rxjs/operators';
import { Source } from 'webpack-sources';
...
source$ = new Subject();
firstValue$ = source$.pipe(
take(1)
);
afterFirstValue$ = source$.pipe(
skip(1),
bufferTime(5000)
);
merge(firstValue$, afterFirstValue$)
.subscribe(result => {
// Do something
});
source$.next(1);
source$.next(1);
source$.next(1);
You can use multicast to split the stream into two and just pass the first value through.
import { concat, Subject } from “rxjs”;
import { multicast, take, bufferCount } from “rxjs/operators”;
source.pipe(
multicast(
new Subject(),
s => concat(
s.pipe(take(1)),
s.pipe(bufferCount(X)),
)
),
);
I got really good answers that enlightened my view of the problem and made me come up with the real thing that I was needing, that was something like this:
function getLeadingBufferSubject (bufferTimeArg) {
const source = new Subject()
const result = new Subject()
let didOutputLeading = false
const buffered$ = source
.pipe(bufferTime(bufferTimeArg))
.pipe(filter(ar => ar.length > 0))
.pipe(map(ar => [...new Set(ar)]))
buffered$.subscribe(v => {
didOutputLeading = false
const slicedArray = v.slice(1)
// emits buffered values (except the first) and set flag to false
if (.length > 0) result.next(v.slice(1))
})
// emits first value if buffer is empty
source.subscribe(v => {
if (!didOutputLeading) {
didOutputLeading = true
result.next(v)
}
})
// call .next(value) on "source"
// subscribe for results on "result"
return {
source,
result
}
}
I had the same problem and after playing around with it, I found this additional solution:
source$.pipe(
buffer(source$.pipe(
throttleTime(bufferTime, asyncScheduler, {leading: true, trailing: true}),
delay(10) // <-- This here bugs me like crazy though!
)
)
Because throttle already features a leading option, you can just use it to trigger buffer emits manually.
I would really like to get rid of that delay here though. This is necessary because the inner observable is triggered first causing the buffer to emit prematurely.

RxJs emit after delay or next event on the stream

I've pairs of events: add1/add2/etc and remove1/remove2/etc. I'd like the following:
when an add1 is emitted on the stream
if DELAY transpires with no new add* emissions
emit remove1
if add* is emitted
emit remove1 for add1 immediately
emit remove* for add* after DELAY
This should continue for all emissions of add* on the stream.
Here's a test I've written using RxJS marble testing for this case:
import test from 'tape'
import { set, lensPath } from 'ramda'
import { TestScheduler } from 'rxjs/testing'
import hideAfterDelay from '../another/file'
import { actionCreators } from '../another/dir'
const prefix = 'epics -> notifications'
test(`${prefix} -> hideAfterDelay`, t => {
t.plan(1)
const scheduler = new TestScheduler(t.deepEqual)
const actionMap = {
a: createAddAction('hello!'),
b: createAddAction('goodbye!'),
x: actionCreators.notifications.remove('hello!'),
y: actionCreators.notifications.remove('goodbye!')
}
scheduler.run(({ cold, expectObservable }) => {
const actionStream = cold('a-------a-b-a------', actionMap)
const expected = '-----x-----x-y----x'
const actual = hideAfterDelay(5)(actionStream)
expectObservable(actual).toBe(expected, actionMap)
})
})
function createAddAction (name) {
const action = actionCreators.notifications.add(name)
const lens = lensPath(['payload', 'id'])
return set(lens, name, action)
}
I think the test is representative of the behavior I described above and that I want.
How can I write this observable? I've tried using timer and race but I haven't been able to get this working...
This is an epic using redux-observable, btw.
Using RxJS v6
Ok, I think I got a working solution using a closure and slightly modifying my test assertion.
First, the expected marble diagram should look like this
// input: a-------a-b-a------
// - expected: -----x-----x-y----x
// + expected: -----x----x-y----x
//
// Note above that the middle x and y emit at the same time as new
// `add*` actions on the source stream instead of one frame later
With that small change—which still feels consistent with my description in the question—I was able to get my test passing with the following:
import { of, timer, empty } from 'rxjs'
import { switchMap, mapTo, tap, merge } from 'rxjs/operators'
import { ofType } from '../operators'
import actionTypes from '../../actionTypes/notifications'
import { actionCreators } from '../..'
export default (delay = 3000) => actionStream => {
let immediateRemove
return actionStream.pipe(
ofType(actionTypes.ADD),
switchMap(action => {
let obs = empty()
if (immediateRemove) {
obs = of(immediateRemove)
}
const remove = actionCreators.notifications.remove(action.payload.id)
immediateRemove = remove
return obs.pipe(
merge(
timer(delay).pipe(
tap(() => {
immediateRemove = null
}),
mapTo(remove)
)
)
)
})
)
}
I've no idea if this is the best or right way to solve it, but I'm fairly certain it is a way.

redux observable map not invoked

I have this code, and failing to understand why I am not getting inside the map function (where I have the comment "I AM NEVER GETTING TO THIS PART OF THE CODE"):
export const fiveCPMonitoringLoadEpic = (action$, store) =>
action$
.ofType(
FIVE_CP_MONITORING_ACTION_TYPES.LOAD_FIVE_CP_MONITORING_DATA_STARTED
)
.debounceTime(250)
.switchMap(action => {
const params = action.params;
const siteId = { params };
// getting site's EDC accounts (observable):
const siteEdcAccount$ = getSiteEDCAccountsObservable(params);
const result$ = siteEdcAccount$.map(edcResponse => {
// getting here - all good so far.
const edcAccount = edcResponse[0];
// creating another observable (from promise - nothing special)
const fiveCPMonitoringEvent$ = getFiveCPAndTransmissionEventsObservable(
{
...params,
edcAccountId: edcAccount.utilityAccountNumber
}
);
fiveCPMonitoringEvent$.subscribe(x => {
// this is working... I am getting to this part of the code
// --------------------------------------------------------
console.log(x);
console.log('I am getting this printed out as expected');
});
return fiveCPMonitoringEvent$.map(events => {
// I NEVER GET TO THIS PART!!!!!
// -----------------------------
console.log('----- forecast-----');
// according to response - request the prediction (from the event start time if ACTIVE event exists, or from current time if no active event)
const activeEvent = DrEventUtils.getActiveEvent(events);
if (activeEvent) {
// get event start time
const startTime = activeEvent.startTime;
// return getPredictionMeasurementsObservable({...params, startTime}
const predictions = getPredictionMock(startTime - 300);
return Observable.of(predictions).delay(Math.random() * 2000);
} else {
// return getPredictionMeasurementsObservable({...params}
const predictions = getPredictionMock(
DateUtils.getLocalDateInUtcSeconds(new Date().getTime())
);
return Observable.of(predictions).delay(Math.random() * 2000);
}
});
can someone please shed some light here?
why when using subscribe it is working, but when using map on the observable it is not?
isn't map suppose to be invoked every time the observable fires?
Thanks,
Jim.
Until you subscribe to your observable, it is cold and does not emit values. Once you subscribe to it, the map will be invoked. This is a feature of rxjs meant to avoid operations that make no change (= no cunsumer uses the values). There are numerous blog posts on the subject, search 'cold vs hot obserables' on google

Resources