Are ngXS selectors fired on the page init, without any changes of the state slice that returned? - select

I have a selector and I subscribe to them on ngOnInit; but the code inside the subscribe is executed every time when the page is initialized (refreshed).
#Select(SurveysSelectors.deleteSurveys) deleteSurveys$: Observable<IDeleteSurveys>;
.
.
.
ngOnInit(): void {
this.deleteSurveys$.pipe(takeUntil(this.destroy$), debounceTime(600)).subscribe((result: IDeleteSurveys) => {
if (!result.surveyDeleteResult.esriUpdate) {
return;
}
this.esriUpdate(result.surveyIds, result.surveyDeleteResult.iotFunc);
});
}
Is this normal? I expected that the code inside subscribe to run only when a change is made on the slice of state that selector returns.

this is expected since your ngOnInit will run every time your component is initialized. In NGXS the selectors are hot and always will emit on subscription the last value, in this case the initial value. A possible workaround would be to use a skip(1) to be sure you only react to changes that happen after you subscribed.
Example below:
this.deleteSurveys$.pipe(takeUntil(this.destroy$), skip(1), debounceTime(600)).subscribe((result: IDeleteSurveys) => {
if (!result.surveyDeleteResult.esriUpdate) {
return;
}
this.esriUpdate(result.surveyIds, result.surveyDeleteResult.iotFunc);
});
I hope this can help you.

Related

res.data won't save to state correctly

I have a MERN app which pulls data from a collection in MongoDB to render a timer component in the DOM. Currently in my collection, I have three timers titled first timer, another timer and even another. When I make a get request and run console.log(res.data), I see all the timers and their relevant data logged to the console. However, when I try to set state of timers using the useState hook, only the last timer is saved to state. Here is the code of my component:
function Wrapper() {
const [timers, setTimers] = useState([]);
const [title, setTitle] = useState('');
useEffect(() => {
axios
.get('http://localhost:3001/')
.then((res) => {
console.log(res.data);
res.data.map((timer) => {
let newTimer = (
<Timer title={timer.title} id={timer._id} time={timer.time} />
);
let allTimers = timers.slice();
allTimers.push(newTimer);
setTimers(allTimers);
console.log(allTimers);
});
})
.catch((err) => {
console.log(err);
});
}, []);
Here I am making my get request and mapping through the res.data to create a new timer component for each iteration. Then, I make a copy of timers (since state is immutable), push my new timer to allTimers variable and finally run setTimers(allTimers). Here is what React renders:
I expect allTimers to contain one, two and then all three of the timers in my database when logged to the consol on line 17. However, only the most recent timer shows up so it seems like I am setting state incorrectly but I'm not sure how. Anyone have any suggestions?
Because you set useEffect to Only runs on initial render, it always refers to the initial state of timers, which is an empty array, and even as you try to update it with useState but on the next loop it still refers to the empty array. The only update to have real effect is the last one, pushing the third timer into the empty array.
You can move your entire map loop to the return statement of the functional component to render an element for each timer.

Angular Ag Grid - Has anyone figured out a way to wait for a cell node update to happen and THEN fire a function, like a callback?

I am utilizing cellChanged.node.setDataValue(fieldChanged, oldValue) inside of the (cellValueChanged) event emitter, and I'm having trouble figuring out how to call a function once the setDataValue function has finished executing. I need to do this to do a check to see if a user has the permission to update a cell.
Here is the full code that checks:
if(this.showPaywallNotification) {
// Okay, so the budget is above what we allow HOWEVER...
if(budget > BUDGET_AMOUNT) {
this.showPaywallNotification = false;
cellChanged.node.setDataValue(fieldChanged, oldValue)
// Note: This timeout is in place to prevent the infinite updating bug
// This is problematic because if the user changes the cells fast enough, they can get around the paywall. If I change the timeout to be smaller, the resulting change triggers the update, which ends up creating an infinite loop of updates.
setTimeout(() => {
this.showPaywallNotification = true;
}, 230)
}
}
Is there a way I can replace my setTimeout() function with something better that can always ensure the user can't get around my paywall by just updating the cell faster than the timeout can execute?
You don't have to do polling. setDataValue is a not an async function.
Also, onCellValueChanged won't get called again if you call node.setDataValue.
Have a look at this plunk: Cell Editing - Revert to old value. Try updating any Age value to negative.
onCellValueChanged($event) {
if ($event.colDef.field === 'age' && $event.newValue < 0) {
// debugger
$event.node.setDataValue('age', $event.oldValue);
console.log('value reverted');
}
}
Let me know if something is not clear, or this is not sufficient.

Meteor subscription ready

I'am building a react/meteor app. I'm having a problem with subscriptions. There's a component that is showed when the subscriotion.ready() is false. When it's turned to true the component is replaced by a table with data, but it takes a few seconds between the ready and the data from find().fetch(), showing another component for a while.
Any suggestion ?
Thanks
If you are using react-meteor-data you can get the subscription status in ready property. Then you can send this property to the presentation component and update it accordingly.
Sample code snippet from the package's documentation:
import { createContainer } from 'meteor/react-meteor-data';
export default PresenterContainer = createContainer(props => {
// Do all your reactive data access in this method.
// Note that this subscription will get cleaned up when your component is unmounted
const handle = Meteor.subscribe('publication_name');
return {
isReady: ! handle.ready(),
list: CollectionName.find().fetch(),
};
}, PresenterComponent);
Explanation:
The first argument to createContainer is a reactive function that will get re-run whenever its reactive inputs change.
The PresenterComponent component will receive {isReady, list} as props. So, you can render your component according to the status of isReady
Addition:
Write your render method of the presenter component like this:
render(){
if(!this.isReady) return <LoadingComponent/>
else if(this.props.list.length() != 0) return <TableComponent/>
else return <NoDataFoundComponent/>
}

How to observe added events in Meteor without firing on initialization?

There is a lot of information on this topic out there, but I can't seem to get it working for myself. I am using toastr to display notifications of events to the user in the top right hand corner of my app. I need to add an observer for the 'added' event to a collection, and create the toastr notification when an item is added. The problem is that the observer fires when the collection is initialized. I've tried about a half dozen different ways trying to check of the collection is ready() before I allow the observer code to continue through and show notifications, but I can't get it working consistently, especially when changing pages. Here is some sample code:
MainController = RouteController.extend({
before: [
function() {
deviceEventsInitializing = true;
var alerts = this.subscribe("alerts", Meteor.user()._id);
if (alerts.ready()) {
deviceEventsInitializing = false;
}
Alerts.find().observeChanges({
added: function(id, doc) {
if (deviceEventsInitializing || deviceEventsInitializing == undefined) {
return;
}
doToastrStuff();
}
});
this.next();
}
],
});
This is just my latest attempt. The flow goes like this:
1.) Subscription happens, all the items in the collection hit the observer but deviceEventsInitializing is true so it does nothing.
2.) alerts.ready() fires and deviceEventsInitilizing is set to false.
3.) The added trigger fires again for all the events in the collection, causing toastr to be called for every item.
All I'm interested in is the following:
1.) Some trigger or event where I can set a variable that says the subscription is reloading the collection.
2.) Some trigger or event that tells me that this reloading of the collection is complete so I can set a variable indicating that.
I think you're on the right track, but trying to manage reactivity like this using solely IronRouter can be a nightmare. I've tried and failed before.
Instead, leverage Mongo to limit your reactivity to only alerts you care about. Let's imagine your alerts database looks something like:
{
_id: 1,
hasNotified: false,
...
}
Now, Mongo is deciding what is new vs. not new instead of trying to determine state based on IronRouter timing. In fact, because in Meteor any alerts cursor is natively reactive, you don't even need to observeChanges:
MainController = RouteController.extend({
waitOn: function() {
return this.subscribe("alerts", Meteor.userId());
},
data: function() {
var newAlerts = Alerts.find({hasNotified: false}).forEach(function(doc) {
doToastrStuff();
Alerts.update({_id: doc._id}, {$set: {hasNotified: true}});
});
}
})
With this kind architecture, navigating to other routes, reloading the page, etc. will not re-fire any of your alerts because Mongo stores your alert state.

Ignore multiple button taps after first one on iPhone webapp using jQuery Mobile?

Assume button A in an HTML5 webapp built with jQuery Mobile.
If someone taps button A, we call foo(). Foo() should get called once even if the user double taps button A.
We tried using event.preventDefault(), but that didn't stop the second tap from invoking foo(). event.stopImmediatePropagation() might work, but it also stops other methods further up the stack and may not lead to clean code maintenance.
Other suggestions? Maintaining a tracking variable seems like an awfully ugly solution and is undesirable.
You can set a flag and check if it's OK to run the foo() function or unbind the event for the time you don't want the user to be able to use it and then re-bind the event handler after a delay (just a couple options).
Here's what I would do. I would use a timeout to exclude the subsequent events:
$(document).delegate('#my-page-id', 'pageinit', function () {
//setup a flag to determine if it's OK to run the event handler
var okFlag = true;
//bind event handler to the element in question for the `click` event
$('#my-button-id').bind('click', function () {
//check to see if the flag is set to `true`, do nothing if it's not
if (okFlag) {
//set the flag to `false` so the event handler will be disabled until the timeout resolves
okFlag = false;
//set a timeout to set the flag back to `true` which enables the event handler once again
//you can change the delay for the timeout to whatever you may need, note that units are in milliseconds
setTimeout(function () {
okFlag = true;
}, 300);
//and now, finally, run your original event handler
foo();
}
});
});
I've created a sample here http://jsfiddle.net/kiliman/kH924/
If you're using <a data-role="button"> type buttons, there is no 'disabled' status, but you can add the appropriate class to give it the disabled look.
In your event handler, check to see if the button has the ui-disabled class, and if so, you can return right away. If it doesn't, add the ui-disabled class, then call foo()
If you want to re-enable the button, simply remove the class.
$(function() {
$('#page').bind('pageinit', function(e, data) {
// initialize page
$('#dofoo').click(function() {
var $btn = $(this),
isDisabled = $btn.hasClass('ui-disabled');
if (isDisabled) {
e.preventDefault();
return;
}
$btn.addClass('ui-disabled');
foo();
});
});
function foo() {
alert('I did foo');
}
});