Throttling Search with UISearchController - ios

I have an Xamarin.iOS app which does some search on a long list that takes some time.I would like to introduce some throttling instead of searching for every single key stroke . Any idea ?

You can use Reactive extensions, asuming that you use C# style events instead of iOS Delegates.
Here is a bare bone sample code with UITextView:
var observable = Observable.FromEventPattern<EventArgs>
(
//Those are actions used to subscribe and unsubscribe for the event
eventHandler => TextView.Changed += eventHandler,
eventHandler => TextView.Changed -= eventHandler
)
.Sample(TimeSpan.FromMilliseconds(500)) //This will sample every 500 to check if the event has been raised
.Select(e => e.Sender as UITextView) //This is used so we can directly use the TextView in the subscribe method
observable.Subscribe(textView =>
{
//This will be executed if the event was raised the last 500 miliseconds
//This may not run on the UI thread
InvokeOnMainThread(() => Label.Text = textView.Text);
});
This will Sample the text every 500 miliseconds and if the event was rased at least once it will call the Action in the Subscribe method. Or you can use .Throttle instead of .Sample which will be called if the event was not raised for the interval which will be 500 miliseconds after the user stopped typing.
The cool thing is that you can filter the text with a:
.Where(textView => !String.IsNullOrWhiteSpace(textView.Text) || !IsCompleteNonSense(textView.Text));
after the Select method so it wont raise the event if the text is empty or if you want to make some other checks.
I came across this watching a presentation from Xamarin Evolve 2016 here is a link to the video. And here is a link to the source code.
The samples are on Xamarin.Forms but they will give you a good starting point.

Related

Accessing HTMLCollection

So I am trying to access all of a specific class name and then eventually ad an event listener to them. I'm doing it this way because I am building a hightcharts graph and cannot add click events specifically to the legend items. So after the graph is build I am trying to access the buttons and then add the event listener.
getButtons() {
let buttons = document.getElementsByClassName('legend-btn');
console.log(buttons);
console.log(buttons[0]);
},
The first console.log comes back with an HTMLCollection with a length of 48 (I know very long but for now it's more testing purposes than anything).
The second console.log comes back as undefined. Any ideas why? I was hoping to do something like this:
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function () {
changebackground(event);
});
}
Any suggestions for how I can add the click event for the newly built highcharts graph?
May I suggest that you try:
for (let button of buttons) {
button.addEventListener('click', changebackground);
}
I am not sure why you intended to call changebackground(event): in order to do that, you'd have to put event in the event listener's parentheses. Better pass changebackground directly (assuming it does take an event as a parameter).
I'm not quite sure about what I'm telling you: hard to tell without seeing more of the context.

How do you return a Future based off a Stream you don't control in Dart?

I have a situation where I'm expecting a single value from a Stream, but because it's one provided by the browser I can't rely on simply calling streamSub.single. Currently, I'm creating an explicit StreamController so that I can easily generate a Future from it while guaranteeing that it will only get a single response. However, that ends up being a lot more overhead than I was expecting to have to set up, making me think I'm missing something. The current code follows:
StreamController<String> streamCtrlr = new StreamController<String>();
var popup = window.open(targetUrl, "Auth Window");
//The popup above will call window.opener.postMessage, so listen for messages
StreamSubscription sub = window.onMessage.listen(null);
sub.onData((Event){
/* Logic goes here */
sub.cancel();
popup.close();
streamCtrlr.add(Event.data);
streamCtrlr.close();
});
return streamCtrlr.stream.single;
How can this be re-written so that the intermediary StreamController isn't required?
Why can't you rely on calling streamSub.single? Is it because there might be more than one message?
Your example code picks the first event in all cases, so to get the same behavior, you can use window.onMessage.first instead of window.onMessage.single.
It will still cause an error if there is no first event (but I don't think that can happen with DOM event handlers - they never send a done event), and otherwise it will give a future that is completed with the first event.
You also want to extract the event data, so you will probably want:
return window.onMessage.first.then((event) {
/* Logic goes here */
popup.close();
return event.data;
});

Dart web-ui not updated when data received from network

I have the following fragment in a web component:
<div id="mycodes">
<template iterate='code in codeList'>
{{code}}
</template>
</div>
And in a Dart file, codeList is populated when the user clicks on a button:
void onMyButtonClick(Event event) {
HttpRequest.getString('http://getData').then((response) {
mylist = json.parse(response);
for(var code in mylist){
codeList.add(code['c']);
}
}
The problem is that I don't see data on first click. I need to click the button twice to see data.
But if I fill codeList manually (not from network data) as shown below, then I see the data on first click:
void onMyButtonClick(Event event) {
codeList.add("data 1");
codeList.add("data 2");
}
}
I need the template to iterate after the network data is available. It appears that event loop has already done its job of painting a page before the network data becomes available through future object.
Is there a way to refresh the page after model is updated in dart?
The reason your codeList currently populates if you add it with the on-click event is because the current web_ui has 'watchers' which automatically are called when an event happens. You then populate the list synchronously. However one of the downfalls of watchers is exactly your use case, when the data is updated asynchronously then the watchers don't reflect changes in time.
As a result the watchers are being phased out and replaced with observables. Observables allow us to flag a variable to be watched for reassignment and when that happens it will cause the view to change. For example:
#observable int x = 0;
// ...
x = 1;
When the x = 1 is called later in the code it automatically triggers the views to update. This leaves us with one problem however. When you are adding to a list, you are not reassigning the value itself. As such, observables also offer a function to convert a list to an observable list (this also works for maps).
For instance if you changed your declaration of codeList to something like the following, then when you add to the list later it will update accordingly.
var codeList = toObservable([]); // Assuming it starts with an empty list
// or
var codeList = toObservable(_startCodeList); // if you already have a list
Also see the Dart Tutorial: Target 7 for more information on using #observable and toObservable.
For more in-depth information, check out the article on Observables and Data Binding
You need to mark the fields you want WebUi to monitor with the #observable annotation. Otherwise you only get the initial value not any subsequent updates.
You can do this either directly on the object declaration or you can make the entire class as observable and all its fields will then be observed.
For an example see http://www.dartlang.org/docs/tutorials/custom-elements/#using-two-way-data-binding

Why isn't the keydown event firing?

I'm trying to attach an event handler to the keyDown event in a canvas element. Here is a simplified version of my code.
class CanvasFun{
CanvasElement canvas;
CanvasFun(this.canvas){
print("Game is loading!");
this.canvas.onKeyDown.listen(handleInput);
}
void handleInput(e)
{
//breakpoint is never hit
print(e.keyCode);
}
}
I've removed some of the drawing code. In my main function I simply query the canvas element and pass it to my CanvasFun constructor.
I've also tried doing it this way:
void main() {
var canvas = query("#Game");
canvas.onKeyDown.listen(handleInput);
var canvasFun = new CanvasFun(canvas);
}
void handleInput(e)
{
print(e.keyCode);
}
The reason why the event is not firing is because the focus is on the document (or some other element like an input, for example). And in fact, canvas element even when focused does not fire an event. Some elements do, like input elements.
The solution is to listen to key down events from the document or window:
window.onKeyDown.listen(handleInput);
document.onKeyDown.listen(handleInput); // You already noticed this worked.
John McCutchan has written a nice Dart package to help handle keyboard input. You can read more about it here: http://dartgamedevs.org/blog/2012/12/11/keyboard-input/
Note that this library helps you handle input "correctly". You do not want to do any "work" in the input handling, instead you simply want to register that a key was pressed. You can check the state of any key presses inside of your requestAnimationFrame callback.
Hope that helps!
There exists a workaround to get the canvas-element accept KeyboardEvents:
Problems handling KeyboardEvents on DartFlash
Once you add the tabindex-attribute to your canvas-element, it can get the focus and then it will receive KeyboardEvents.
It looks like I can get it to work if I register the event on the document rather than the canvas element.
document.onKeyDown.listen(handleInput);

Ajax queue Backbone js

I am running Backbone js 0.9.2 on Rails 3.2.2,I have a page for adding cost rows.A cost have 3 TextFields: title, description and price.
I am saving each cost on blur.
model.save() gets called multiple times with very short intervals. Which issues one create(post) request then one update(put) request shortly there after. The problem I am experiencing is that PUT request sometimes reaches the server before the POST, the result being that model gets created and persisted twice(duplicates).
To save on blur is the requested behavior, so I need a way to queue up requests.
I have read something about Spine js, and that they solve it by some kind of queue. I've also looked in to this, but can't seem to figure this out.
It feels like this should be a common issue, working with "single-page-apps" but can't find anything about it.
You could override the save method and create a queue with a deferred object . For example,
var MDef = Backbone.Model.extend({
url: "/echo/json/?delay=3",
initialize: function() {
this.queue = $.Deferred();
this.queue.resolve();
},
save: function(attrs,options) {
var m = this;
console.log("set "+JSON.stringify(attrs));
// this.queue = this.queue.pipe with jquery<1.8
this.queue = this.queue.then(function() {
console.log("request "+JSON.stringify(attrs));
return Backbone.Model.prototype.save.call(m, attrs, options);
});
}
});
var m = new MDef();
m.save({title: "a title"});
m.save({description: "a description"});
m.save({price: "a price"});
And a Fiddle : http://jsfiddle.net/nikoshr/8nEUm/
User debounce from underscore.js.
Creates and returns a new debounced version of the passed function that will postpone its execution until after wait milliseconds have elapsed since the last time it was invoked.
This way it will only fire once after the last blur event.

Resources