Toggle between data streams and make them pause/play with RxJs - rxjs

I have getUserData$(userId) which returns user data based on dropdown select and if not provided with userId returns a default stream,
pauser reflects pause/play status,
var clicks = Observable.fromEvent(document, 'click')
.map(function() {
var pauseStatus= state.get('dashboard').get('pauseStatus')
return pauseStatus;
});
var pauser = new Subject();
clicks.subscribe(pauser);
I want to toggle between different streams and make them pausable as well:
var sources = [];
var newSub =()=>{return new Subject();}
sources.push(newSub());
getUserData$().subscribe(sources[0]);
//if new user is selected from dropdown
if(user_id){
sources.push(newSub());
getUserData$(user_id).subscribe(sources[sources.length-1]);
}
var pausable = pauser.switchMap(paused => paused ? Observable.empty() : sources[sources.length-1]);
pausable.subscribe(function(result){...})
but it keeps jumping between two streams rendering both ,what I am doing wrong?

Related

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

My Throttable EventStream implementation - is it redundant?

Subject: I have a stream (actually combined stream from Bacon.interval and buttons clicks EventStreams) wich fires ajax request and solve task of manual and automatic data refresh.
Problem: After manual events (buttons clicks) I need reset timer because two immediate updates looks ugly.
My solution: I've created my own Bacon.interval implementation where event polls can be reseted http://jsfiddle.net/cvvkxveq/1/:
Bacon.dynInterval = function(time,resetter){
if(!time) return Bacon.once(new Bacon.Error("Invalid args"));
var ivId, lastTime = Date.now();
return time < 1 ? Bacon.once(new Bacon.Error("Invalid time")) : Bacon.fromBinder(function(sink) {
function setUpInterval(){
if(ivId) clearInterval(ivId);
ivId = setInterval(function(){
var n = Date.now();
var tdx = n - lastTime;
lastTime = n;
sink(new Bacon.Next(tdx));
},time);
}
setUpInterval();
if(resetter) resetter.onValue(setUpInterval);
return function() {
clearInterval(ivId);
sink(new Bacon.End())
}
})
}
Question: Is such behaivour can be done without custom event stream?
Update (thanks #raimohanska's answer) basing on #raimohanska's answer I've also converded my ouUiE event sream (manualTriggerE) to property with initial value to accomplish immediate updates starts.
var quotesService = Bacon.constant({url:"quotes.php"});
var onUiE = $("#next_stock, #prev_stock, #refresh_quotes").clickE().map(".currentTarget.id");
var onUiP = onUiE.toProperty("");
var periodicUpdateE = onUiP.flatMapLatest(function(){ return Bacon.interval(3000)});
var manualPlusPeriodicP = onUiP.toEventStream().merge(periodicUpdateE);
var quotesStream = quotesService.sampledBy(manualPlusPeriodicP).ajax();
If you have a stream manualTriggerE, you can add periodic updates that are reseted on each manual trigger like this:
let periodicUpdateE = manualTriggerE.flatMapLatest(() => Bacon.interval(1000))
let manualPlusPeriodicE = manualTrigger.merge(periodicUpdateE)
The trick is flatMapLatest: it restarts the periodic updates whenever a manual trigger occurs.

Assigning functions to multiple slickgrids

Please help to solve this very annoying problem. I am using a for loop to iterate over an array of data and create multiple grids. It is working well but the filter function is not binding properly (it only binds to the LAST grid created) Here is the code:
// this function iterates over the data to build the grids
function buildTables() {
// "domain" contains the dataset array
for (i = 0; i < domains.length; i++) {
var dataView;
dataView = new Slick.Data.DataView();
var d = domains[i];
grid = new Slick.Grid('#' + d.name, dataView, d.columns, grids.options);
var data = d.data;
// create a column filter collection for each grid - this works fine
var columnFilters = columnFilters[d.name];
// this seems to be working just fine
// Chrome console confirms it is is processed when rendering the filters
grid.onHeaderRowCellRendered.subscribe(function (e, args) {
$(args.node).empty();
$("<input type='text'>")
.data("columnId", args.column.id)
.val(columnFilters[args.column.id])
.appendTo(args.node);
});
// respond to changes in filter inputs
$(grid.getHeaderRow()).delegate(":input", "change keyup", function (e) {
var columnID = $(this).data("columnId");
if (columnID != null) {
// this works fine - when the user enters text into the input - it
// adds the filter term to the filter obj appropriately
// I have tested this extensively and it works appropriately on
// all grids (ie each grid has a distinct columnFilters object
var gridID = $(this).parents('.grid').attr('id');
columnFilters[gridID][columnID] = $.trim($(this).val());
dataView.refresh();
}
});
//##### FAIL #####
// this is where things seem to go wrong
// The row item always provides data from the LAST grid populated!!
// For example, if I have three grids, and I enter a filter term for
// grids 1 or 2 or 3 the row item below always belongs to grid 3!!
function filter(row) {
var gridID = $(this).parents('.grid').attr('id');
for (var columnId in grids.columnFilters[gridID]) {
if (columnId !== undefined && columnFilters[columnId] !== "") {
var header = grid.getColumns()[grid.getColumnIndex(columnId)];
//console.log(header.name);
}
}
return true;
}
grid.init();
dataView.beginUpdate();
dataView.setItems(data);
dataView.setFilter(filter); // does it matter than I only have one dataView instance?
dataView.endUpdate();
grid.invalidate();
grid.render();
In summary, each function seems to be binding appropriately to each grid except for the filter function. When I enter a filter term into ANY grid, it returns the rows from the last grid only.
I have spent several hours trying to find the fault but have to admit defeat. Any help would be most appreciated.
yes, it matters that you have only one instance of dataView. and also sooner or later you will come up to the fact that one variable for all grids is also a bad idea
so add a var dataView to your loop, it should solve the problem

Cannot update label on Google Apps Script GUI Builder Interface at runtime

I have an interface that calls a script for spreadsheet creation using data taken from other spreadsheet. I want the interface to update its labels at runtime in order to give visual feedback to the user and let him know the script is running and it's not stuck. When I try to update the label I put in the interface, it doesn't update the first time, but updates correctly after myFunction() reaches its end. Which means I can see the message "Creation Completed", but the message "Creating file..." is never shown. Also, the button buttonCompile is never disabled so it seems that the instructions before myFunction() are not executed at all. How can I get the labels updated and the button disabled before myFunction() starts executing? (I already double-checked variable references)
function doGet() {
var app = UiApp.createApplication();
app.add(app.loadComponent("File creation"));
var buttonCreate = app.getElementById('createBtn');
var handlerCrea = app.createServerHandler('createClickHandler');
buttonCreate.addClickHandler(handlerCreate);
return app;
}
function createClickHandler(e) {
var app = UiApp.getActiveApplication();
var label = app.getElementById('createLbl');
label.setText("Creating file...");
var buttonCompile = app.getElementById('compileBtn');
buttonCompile.setEnabled(false);
myFunction();
label.setText("Creation completed.");
buttonCompile.setEnabled(true);
app.close();
return app;
}
The cause of this behavior is that the GUI is updated only after leaving a handler. A workaround is to use two handlers. The 1st one sets the label text to Creating file... and disables the button, the 2nd one executes the myFunction function, changes the text to Creation completed, and eanbles the button. Here is an example. It disables/enables the button and the worker handler simply waits 5 seconds.
function doGet(e) {
var app = UiApp.createApplication();
var container = app.createHorizontalPanel().setId('container');
var btnPerformance = app.createButton("Performance Demo").setId('btnPerformance');
var handlerPerformance = app.createServerHandler('onBtnPerformanceClick');
var handlerWait = app.createServerHandler('onWait');
btnPerformance.addClickHandler(handlerPerformance);
btnPerformance.addClickHandler(handlerWait);
container.add(btnPerformance);
app.add(container);
return app;
}
function enableControls(enable) {
var lstControls = [ 'btnPerformance' ];
var app = UiApp.getActiveApplication();
for (var i = 0; i < lstControls.length; i++) {
var ctl = app.getElementById(lstControls[i]);
ctl.setEnabled(enable);
}
}
function onWait(e) {
enableControls(false);
return UiApp.getActiveApplication();
}
function onBtnPerformanceClick(e) {
Utilities.sleep(5000);
enableControls(true);
return UiApp.getActiveApplication();
}

Store & retrieve the identifiers of a multipliable widget's instances

The aim is to remove only the last row at any time and only by the last remove button.
There is a user interface which building up as a multiplication of the same row. The number of rows are controlled by 'Add' & 'Remove' buttons which are also elements of the row. The problem is that the hidden widgets - that are applied for each row to distinguish the instances by storing their row numbers - are storing the very same number which is the last one. Except the first (0) hidden widget which stores the proper number (0). Where am I missing the point? How should this be resolved?
As per the remove buttons have two different purposes (not detailed here), we use a cacheService to distinguish the last row from all the others. Only the last row should be removed at any time.
var cache = CacheService.getPrivateCache();
we clear the cache and create the first instance
function doGet() {
var app = UiApp.createApplication();
app.add(app.createVerticalPanel().setId('mainContainer'));
cache.removeAll([]);
ui(0);
cache.put('numberOfInstances',0);
return app; }
each instance is held by a horizontal panel which contains the mentioned hidden widget, a label which informs about the instance number, and the Add & Remove buttons.
function ui(instance) {
var app = UiApp.getActiveApplication();
var eventContainer = app.createHorizontalPanel()
.setId('eventContainer' + instance);
var instanceContainer = app.createHidden('instanceContainer',instance);
var showInstance = app.createLabel(instance)
.setId('showInstance' + instance);
var addButton = app.createButton('Add')
.setId('add' + instance)
.addClickHandler(app.createClientHandler()
.forEventSource().setEnabled(false)) //avoiding multiple click during server response
.addClickHandler(app.createServerHandler('add')
.addCallbackElement(instanceContainer));
var removeButton = app.createButton('X')
.addClickHandler(app.createServerHandler('remove')
.addCallbackElement(instanceContainer));
app.getElementById('mainContainer')
.add(eventContainer
.add(instanceContainer)
.add(showInstance)
.add(addButton)
.add(removeButton));
return app; }
and the event handling...
function add(inst) {
var app = UiApp.getActiveApplication();
var instance = Number(inst.parameter.instanceContainer);
ui(instance+1);
cache.put('numberOfInstances',instance+1);
return app; }
function remove(inst) {
var app = UiApp.getActiveApplication();
var instance = Number(inst.parameter.instanceContainer);
var numberOfInstances = cache.get('numberOfInstances')
if( (instance != 0) && (instance = numberOfInstances) ) {
app.getElementById('mainContainer').remove(app.getElementById('eventContainer' + instance));
cache.put('numberOfInstances',instance-1);
app.getElementById('add' + (instance-1)).setEnabled(true); } //avoiding multiple click during server response
return app; }
The aim is to remove only the last row at any time and only by the last remove button.
Many Thanks.
Why don't you simply use a clientHandler just as you did on the 'add' button? You could target the preceding 'remove' button and disable it each time you create a new one and change /update each time you remove one row.
EDIT : I can suggest you something, feel free to have a look, I changed a bit the approach but it is working and I hope you'll find it at least interesting ;-)
Link to the online test
function doGet() {
var app = UiApp.createApplication();
var counter = app.createHidden().setName('counter').setId('counter').setValue('1');
var mainContainer = app.createVerticalPanel().setId('mainContainer')
app.add(mainContainer.add(counter));
var event1Container = app.createHorizontalPanel()
var showInstance = app.createLabel('1')
var addButton = app.createButton('Add')
.setId('add1')
.addClickHandler(app.createClientHandler()
.forEventSource().setEnabled(false)) //avoiding multiple click during server response
.addClickHandler(app.createServerHandler('add')
.addCallbackElement(mainContainer));
var removeButton = app.createButton('X')
.setId('remove1')
.addClickHandler(app.createServerHandler('remove')
.addCallbackElement(mainContainer));
mainContainer.add(event1Container
.add(showInstance)
.add(addButton)
.add(removeButton));
return app; }
function add(inst) {
var app = UiApp.getActiveApplication();
var hiddenVal =inst.parameter.counter;
var counterVal = Number(hiddenVal);
var mainContainer = app.getElementById('mainContainer')
var counter = app.getElementById('counter')
++ counterVal
counter.setValue(counterVal.toString())
var eventContainer = app.createHorizontalPanel().setId('eventContainer'+counterVal)
var showInstance = app.createLabel(counterVal.toString())
var addButton = app.createButton('Add')
.setId('add'+counterVal)
.addClickHandler(app.createClientHandler()
.forEventSource().setEnabled(false)) //avoiding multiple click during server response
.addClickHandler(app.createServerHandler('add')
.addCallbackElement(mainContainer));
var removeButton = app.createButton('X')
.setId('remove'+counterVal)
.addClickHandler(app.createServerHandler('remove')
.addCallbackElement(mainContainer));
app.add(eventContainer
.add(showInstance)
.add(addButton)
.add(removeButton));
if(counterVal>1){app.getElementById('remove'+(counterVal-1)).setEnabled(false)}
return app; }
function remove(inst) {
var app = UiApp.getActiveApplication();
var counterVal = Number(inst.parameter.counter);
var counter = app.getElementById('counter')
if(counterVal ==1) {return app}
var maincontainer = app.getElementById('mainContainer')
app.getElementById('eventContainer' + counterVal).setVisible(false)
--counterVal
counter.setValue(counterVal.toString())
app.getElementById('add'+counterVal).setEnabled(true)
app.getElementById('remove'+counterVal).setEnabled(true)
return app;
}
NOTE : I didn't make use of .remove(widget) since this is a fairly new method and I don't know exactly how it works... I'll test it later. Until then I used setVisible(false) instead, sorry about that :-)
Note 2 : I didn't use the cache since the hidden widget is sufficient to keep track of what is going on... if you needed it for something else then you could always add it back .

Resources