RxJs Interval with takeUntil to publish last value - angular6

I have some code which polls until a task is complete
See below
this.simulationStatus =
interval(2000).pipe(
switchMap(
() => from(this.simulationService.getSimulationStatus(this.route.snapshot.paramMap.get('jobId')))),
takeUntil(this.stopPoll),
tap(simulation => {
if (simulation && simulation.complete) {
if (this.stopCount == 1) {
// Get once after complete
this.stopPoll.next(true);
}
this.stopCount++;
}
})
);
I have tried using takeUntil and takeWhile the problem is that that the last value is never published once the task is complete.
To get around this I have to include the tap method to with the stopPoll subject and incrementing the stopCount to get the last value.
So the above works but just feels a bit messy, I'm sure there must be a better way of achieving this?
I would have expected takeUntil to publish the last value or have an override to tell it to e.g takeUntil(observable, {publishLast: true})
BTW Update, the observable is subscribed to by an Angular 6 template
Thanks in advance

One thing you can do is use a custom takeWhile-like operator like this:
const completeWith = <T>(predicate: (arg: T) => boolean) => (
source: Observable<T>,
) =>
new Observable<T>(observer =>
source.subscribe(
value => {
observer.next(value);
if (predicate(value)) {
observer.complete();
}
},
error => observer.error(error),
() => observer.complete(),
),
);
It doesn't seem like a good idea to see it as a variation of takeWhite because it's not just taking values while a condition holds, but also emits an extra value.
It might be that a more elegant solution would be make the simulation status observable emit two kinds of values: next notifications and completion notifications, similarly to how materialize/dematerialize operators work.

This has in the meantime been implemented in rxjs as takeWhile(condition, ?inclusive):
timer(0, 10).pipe(
takeWhile((x) => x < 3, true)
)
emits 0, 1, 2, 3

You can also create subject and emit using next() if you want to complete the observable.
this.stopPoll: Subject<any> = new Subject<any>();
If you want to do complete the subscription. you can call this.stopPoll.next(true);
you can access the data in subscribe()
this.simulationStatus.subscribe(success=>{}, failure=>{}, complete=>{});

Related

Displaying Node MySQL Results in React Using State

My node.js MySQL query returns a single row wrapped in [RowPacketData] which I can normally access the ID field using results[0].ID.
However, when I store the result in React state (using hooks) it does not work. I can access the result object, but not fields within it.
function MyReactComponent() {
const [dbEntry, setDbEntry] = useState();
useEffect(() => {
const fetchData = async () => {
const result = await queryFunc(`SELECT * FROM table LIMIT 1`);
console.log(result[0]); // <-- Works (shows [RowDataPacket] object)
console.log(result[0].ID); // <-- Works (shows ID)
setDbEntry(result);
};
fetchData();
}, []);
console.log(dbEntry[0]); // <-- Works (shows [RowDataPacket] object)
console.log(dbEntry[0].ID); // <-- TypeError: Cannot read property '0' of undefined
return (
<p>
{dbEntry[0].ID} // <-- How do I render here?
</p>
)
}
What's going on here? I have a feeling React is coercing the result object somehow, but I can't figure it out...
When you need to display data that comes from an async font(API calls for example), it's possible (actually almost certain) that it won't be available by the time the first render occurs, to solve that there is actually a few things you could do:
Placeholder state
You could have a model of what the data will look like described as your initial state, so properties won't be undefined anymore:
const [state, setState] = useState({
data:[
{name: ''}
]
})
Assuming that your data will have this format accessing state.data[0].name won't throw an error. This could be useful in some cases but I personally don't like the approach.
Conditional Render
At each render you should check for a condition and only if satisfied render the piece of code:
return(
<>
<div>Title</div>
{Boolean(state.data.length) && <div>{state.data[0].name}</div>}
</>
)
Suspense
That one is brand new, if you have a component tha't need to perform side effects before render it's content, you should have a fallback content to be displayed while the async action is being perform.
<Suspense fallback={<span>Loading</span>}>
<MYAsyncComponent />
</Suspense>

Integers are marked as dirty attributes no matter what

I need to check if a model has been updated and what attributes have changed when saving.
I'm using dirtyAttributes and filter intval as the docs suggests.
The values are coming from an API and are type-cast as they come in, so in theory the filter is redundant.
Model rules
public function rules()
{
return [
[['contract_date', 'order_date'], 'integer'],
[['contract_date', 'order_date'], 'filter', 'filter' => 'intval'],
];
}
This is some of the code currently running:
// Add the changed status variables to the job log
$dirty_attributes = array_keys($model->dirtyAttributes);
if($model->save()) foreach ($dirty_attributes as $attribute)
{
$data[$attribute] = $model->getOldAttribute($attribute).' ('.gettype($model->getOldAttribute($attribute)).')'. ' => '. $model->$attribute.' ('.gettype($model->$attribute).')';
}
var_dump($data);
This produces:
["contract_date"]=>
string(44) "1559669638 (integer) => 1559669638 (integer)"
["order_date"]=>
string(44) "1559669638 (integer) => 1559669638 (integer)"
There is probably something obvious I'm missing, but I can understand what.
After saving model all "oldAttributes" are updated to store new values so comparing them like you do makes no sense. If you want to check which attributes have been changed after saving you can override afterSave() method in your model like:
public function afterSave($insert, $changedAttributes)
{
// $changedAttributes -> this is it
parent::afterSave(); // call parent to trigger event
}
or listen for ActiveRecord::EVENT_AFTER_INSERT / ActiveRecord::EVENT_AFTER_UPDATE event where this data is also passed.

Unable to add new key-value pair dynamically to a STATE JSON Array in React App

I am trying to add a new key-value pair to the already loaded JSON Array. I am adding the new key-value pair to customize the header column cells in react bootstrap table but getting the below errors. Can any one please help?
'Columns' in the below state is where I wanted to add new key-value pair
state = {
data: MYResult.Products || [],
actualData: MYResult.Products || [],
columns: MYResult.ParametricList_Attributes || [],
isCompareClicked: false,
isDisabled: true,
selected: []
};
This is how I am adding the key-value pair -
componentDidMount(){
checkbox = (column, colIndex) => {
return (
<h5>{ column.text }<checkbox/></h5>
);
}
console.log(this.state.columns) ;
newColumn = this.state.columns.map((column) => {
return {...column, headerFormatter: checkbox};
});
this.setState({columns: newColumn });
}
Full code here - https://codesandbox.io/s/o1r988qkz Please uncomment the componentDidMount() to see the issue
Firstly, there's a typo in dcolumn and column.
And regarding the not defined error, you need to define it using const. Use like:
const checkbox = (column, colIndex) => {
return (
<h5>{column.text}<checkbox /></h5>
);
}
JavaScript variables need to be declared when they are used. Public class syntax can not be used everywhere. The error you're getting is self-evident - 'checkbox is not defined'.
Refer this on how to use it: https://tylermcginnis.com/javascript-private-and-public-class-fields/
I simply declared the undeclared variables in your example and the code worked.

Declare a variable and store retrieve data inside

I have managed to retrieve data from Firebase, however, I am unable to store the data into a variable I have declared. I want to store in a variable is so that I am able to use at another method.
Please help.
These are the codes that I have tried.
1st method, rest api
retrieveUser(){
this.restProvider.retrieveUser(this.emailAdd, this.pw)
.subscribe(listUser => {
this.users = userList.results //trying to store to users variable
console.log(listUser);
},
err => {
console.log(err);
},
() => console.log('success')
);}
2nd method to use for some IF-ELSE
loginBtnPress(event){
this.retrieveUser();
console.log(this.users);
}
You are doing it wrong. this.users is getting updated using observables. So, if you try to retrieve its value before the value has been updated, you wont get it as expected. You can call it as below,
retrieveUser(){
this.restProvider.retrieveUser(this.emailAdd, this.pw)
.subscribe(listUser => {
this.users = userList.results //trying to store to users variable
callYourMethodWithUpdatedValue(this.users); // <-- Pass value here
},
err => {
console.log(err);
},
() => console.log('success')
);}
The callYourMethodWithUpdatedValue is called every time(and as soon as) the value this.users is updated
Update 1
You can't retrieve async value in one method and expect that value to be used in some other method on button click. Reason being, your are expecting value on that button click , but since its a async call (can take n secs), you can't be assured that the value will be populated when the value is used through some button call.
That's wrong implementation. You need to change the architecture to either of below:
Don't route to that page unless the user value has been resolved (received). Check canActivate and canActivateChildren.
Change the UX where you are trying to retrieve the use async variable on button click, and activate the button when the value has been received (Here you can use Subject ). Something like this demo

In Apollo GraphQL, how do I setup a resolver to not loop values?

I'm using Sequelize in Node.js with Apollo-Server and Express.js.
When making queries that go deeper and deeper, GraphQL is looping my models and doing a separate query by ID on each of those.
For example, if I get user(userId) > playthroughs > scores, this will do a lookup for that user (no problem), then a lookup for all the playthroughs with that userId (still no a big deal), but then to get the scores, it loops each playthroughId and does a completely separate query on each. This is ridiculously inefficient and causes my queries to take way longer than they should.
Instead of looping:
SELECT scoreValue
FROM scores
WHERE playthroughId = id
I'd really like to grab the array myself and do that loop like this:
SELECT scoreValue
FROM scores
WHERE playthroughId IN (...ids)
This also happened when I used the reference GraphQL from Facebook last year, so I don't think it's specific to Apollo's implementation.
I'd like to know how I can tweak these queries so they're not taking such a performance hit.
Example resolvers:
const resolvers = {
Query: {
user: (_, values) => User.findOne(formatQuery(values))
.then(getDataValues),
},
Playthrough: {
score: ({ playthroughId }) => Score.findOne(formatQuery({ playthroughId }))
.then(getDataValues),
},
User: {
playthroughs: ({ userId }, { take }) => Playthrough.findAll(formatQuery({ userId, take, order: 'playthroughId DESC' }))
.then(getAllDataValues),
},
}
In addition to graphql, facebook has also released a much lesser known project, dataloader.
What it does it batch several requests in the same tick into one. So your code would be something like
scoreLoader = new Dataloader(keys => {
return Score.findAll({ where: { id: keys } }).then(() => {
//Map the results back so they are in the same order as keys
})
});
score: ({ playthroughId }) => scoreLoader.load(playthroughId).then(getDataValues)
Of course, having a load for each field is going to be tedious. So instead you can use dataloader-sequelize, which wrap all calls to association.get (i.e. Playthrough.getScores()) and calls to findOne / findById to dataloader calls, so several calls are batched in one.
Since you are building a graphql API backed by sequelize, you might also be interested in https://github.com/mickhansen/graphql-sequelize/, which provides sequelize specific helpers for grahpql, and uses dataloader-sequelize below the hood