Debouncing ember search input - search

I want to add debounce to my search input in ember app.
I'm having problems with using debounce method.
Here's how my search looks like:
{{input type="search"
placeholder="Search by XYZ"
value=search
id="search"
}}
My route file:
export default Ember.Route.extend({
queryParams: {
search:{refreshModel: true}
}
My controller file:
export default Ember.Controller.extend({
search: "",

Implementing your desired behaviour debouncing with existing input helper is quite difficult since it will update value immediately.
So I will encourage you to use normal input html element and use keyUp event to listen and manually set value inside debounce method.
Inside controller,
import Ember from 'ember';
export default Ember.Controller.extend({
queryParams:['easyFilter'],
easyFilter:'',
setEasyFilterLazily(value){
this.set('easyFilter',value);
},
actions:{
handleEasyFilterEntry(value){
Ember.run.debounce(this, this.setEasyFilterLazily,value, 500);
}
}
});
Created twiddle for demonstration.
One more interesting options are using ember-concurrency addon.
Need to use perform helper instead of action helper,
<input type="text" value={{filterValue}} oninput={{perform triggerToUpdateFilter value='target.value'}}>
and
triggerToUpdateFilter: task(function*(value) {
yield timeout(1000); //here it will wait for 1000 ms before setting
this.set('easyFilter',value);
}).restartable(),
As this task is restartable so it will restart when you type fast within 1000 ms.

search: computed('', {
set(key, value) {
debounce(this, this.setValue, value, 500);
}
})
This worked for me, looks like this overrides the default setter for search.

Related

passing function between separate components in react not between child and parent

I have a small question regarding passing functions between components that are not in parent/child relationship.
My structure inside App.
function App() {
return (
<div className="App">
<Header/>
<Pfl />
<Sdc/>
<Checkscan/>
</div>
);
}
Those 3 components have an on click function attached to a button i want the button from the pfl component to trigger all 3 on click functions.
When i click on the button in the pfl component i want to trigger the function running in the pfl component and the functions that are inside the Sdc,Checkscan component.
Whats the best way to do it and pass the functions from the other components so when i click the button inside the pfl component it will trigger all the methods from the other 2 components(Sdc,Checkscan)?
Or if I make a container that looks like this
export default function Apicontainer() {
return (
<React.Fragment>
<Pfl />
<Sdc />
<Checkscan />
<Button variant="contained">Start</Button>
</React.Fragment>
)
}
and in app.js i only have the Apicontainer.
How do i transfer all the functions to work in that button click Component
I just wrote some quick and dirty example code to show how you can share things between components via a parent component:
export default function Apicontainer() {
const [sharedState, setSharedState] = useState({sdc: null, checkScan: null})
function pflFunction() {
console.log('pflFunction')
// do your stuff here. I would update state with some reasonable data, and then pass
// the relevant data to the component that needs it. This is just an example.
setSharedState({sdc: 'sdcData', checkScan: 'checkScanData'})
}
return (
<React.Fragment>
<Pfl onClick={pflFunction} />
<Sdc data={sharedState.sdc}/>
<Checkscan data={sharedState.checkScan} />
<Button variant="contained">Start</Button>
</React.Fragment>
)
}
// Example of how to trigger a function inside a component (separate file):
export default function Sdc({data}){
const sdcFunction = useCallback(() => {
// implement the function you want to call here. useCallback makes sure to keep
// a stable reference to the function, so that you can rely on it in a useEffect
}, [])
useEffect(() => {
if(data){
// do something. This effect will run whenever the data or sdcFunction changes.
sdcFunction()
}
}, [data, sdcFunction])
return (
<div>your Sdc view code here</div>
)
}
For the record: If fplFunction is anything else than an onClick handler, you should make sure the function has a stable reference, using useCallback (as in the last component)

Method “simulate” is meant to be run on 1 node. 0 found instead. jest/enzyme

I even tried it by giving classnames and removing div and adding it back. Can somene help?
//Page1.js
<div className="email" >
<MyInput className="input1" name="email" type="email" onChange={formikProps.handleChange}/>
</div>
//Page1.test.js
describe("testing email",()=>{
test("email check",()=>{
let wrapper = shallow(<Page1/>)
wrapper.find('MyInput[name="email"]').simulate('onChange', {target: {
name: 'email',
value: 'ale#gmail.com'
}})
expect(wrapper.state('email')).toEqual('ale#gmail.com')
})
})
The first argument to simulate has to be the event name to be simulated not the name of the event handler.
wrapper.find('MyInput[name="email"]').simulate('change', {target: {
name: 'email', value: 'ale#gmail.com' }})
Shallow doesn't render child components. It will render just the react wrapper. For testing events and interactions, use mount. Also you can use console.log(wrapper.debug()) to see the content of wrapper. It will help you to identify the problem

How can I add a class to iron router default template?

I've been searching all over and haven't really found an answer to this seemingly very simply problem.
I have an app that has a default template
Router.configure({
layoutTemplate: 'appLayout',
});
<template name="appLayout">
<div id="container">
{{> yield}}
</div>
</template>
Then I have a new route called 'reservations'
Router.route('/reservations',{
name:'reservations',
onAfterAction: function(){
}
});
What I'd like to do is add a class to the default container that uses css transitions to animate background color from white to black.
What I've tried so far is onAfterAction
Router.route('/reservations',{
name:'reservations',
onAfterAction: function(){
$('#container').addClass('black');
}
});
Does nothing, no errors, no anything. So I've tried
Template.reservations.rendered = function () {
setTimeout(function(){
$('#container').addClass('black');
});
};
Which works, but flashes and creates routing problems. You can see the routing issues at crawfish.meteor.com. How can I simply toggle a class after the template is rendered?
You can use the rendered event:
Template.reservations.rendered = function() {
if( ! this.rendered) {
$('#container').addClass('black');
this.rendered = true;
}
};
Note that we check and set rendered. The Template instance then has a 'rendered' variable that we use to prevent re-triggering on re-render (which can run multiple times)

How to put a delay on AngularJS instant search?

I have a performance issue that I can't seem to address. I have an instant search but it's somewhat laggy, since it starts searching on each keyup().
JS:
var App = angular.module('App', []);
App.controller('DisplayController', function($scope, $http) {
$http.get('data.json').then(function(result){
$scope.entries = result.data;
});
});
HTML:
<input id="searchText" type="search" placeholder="live search..." ng-model="searchText" />
<div class="entry" ng-repeat="entry in entries | filter:searchText">
<span>{{entry.content}}</span>
</div>
The JSON data isn't even that large, 300KB only, I think what I need to accomplish is to put a delay of ~1 sec on the search to wait for the user to finish typing, instead of performing the action on each keystroke. AngularJS does this internally, and after reading docs and other topics on here I couldn't find a specific answer.
I would appreciate any pointers on how I can delay the instant search.
UPDATE
Now it's easier than ever (Angular 1.3), just add a debounce option on the model.
<input type="text" ng-model="searchStr" ng-model-options="{debounce: 1000}">
Updated plunker:
http://plnkr.co/edit/4V13gK
Documentation on ngModelOptions:
https://docs.angularjs.org/api/ng/directive/ngModelOptions
Old method:
Here's another method with no dependencies beyond angular itself.
You need set a timeout and compare your current string with the past version, if both are the same then it performs the search.
$scope.$watch('searchStr', function (tmpStr)
{
if (!tmpStr || tmpStr.length == 0)
return 0;
$timeout(function() {
// if searchStr is still the same..
// go ahead and retrieve the data
if (tmpStr === $scope.searchStr)
{
$http.get('//echo.jsontest.com/res/'+ tmpStr).success(function(data) {
// update the textarea
$scope.responseData = data.res;
});
}
}, 1000);
});
and this goes into your view:
<input type="text" data-ng-model="searchStr">
<textarea> {{responseData}} </textarea>
The mandatory plunker:
http://plnkr.co/dAPmwf
(See answer below for a Angular 1.3 solution.)
The issue here is that the search will execute every time the model changes, which is every keyup action on an input.
There would be cleaner ways to do this, but probably the easiest way would be to switch the binding so that you have a $scope property defined inside your Controller on which your filter operates. That way you can control how frequently that $scope variable is updated. Something like this:
JS:
var App = angular.module('App', []);
App.controller('DisplayController', function($scope, $http, $timeout) {
$http.get('data.json').then(function(result){
$scope.entries = result.data;
});
// This is what you will bind the filter to
$scope.filterText = '';
// Instantiate these variables outside the watch
var tempFilterText = '',
filterTextTimeout;
$scope.$watch('searchText', function (val) {
if (filterTextTimeout) $timeout.cancel(filterTextTimeout);
tempFilterText = val;
filterTextTimeout = $timeout(function() {
$scope.filterText = tempFilterText;
}, 250); // delay 250 ms
})
});
HTML:
<input id="searchText" type="search" placeholder="live search..." ng-model="searchText" />
<div class="entry" ng-repeat="entry in entries | filter:filterText">
<span>{{entry.content}}</span>
</div>
In Angular 1.3 I would do this:
HTML:
<input ng-model="msg" ng-model-options="{debounce: 1000}">
Controller:
$scope.$watch('variableName', function(nVal, oVal) {
if (nVal !== oVal) {
myDebouncedFunction();
}
});
Basically you're telling angular to run myDebouncedFunction(), when the the msg scope variable changes. The attribute ng-model-options="{debounce: 1000}" makes sure that msg can only update once a second.
<input type="text"
ng-model ="criteria.searchtext""
ng-model-options="{debounce: {'default': 1000, 'blur': 0}}"
class="form-control"
placeholder="Search" >
Now we can set ng-model-options debounce with time and when blur, model need to be changed immediately otherwise on save it will have older value if delay is not completed.
For those who uses keyup/keydown in the HTML markup.
This doesn't uses watch.
JS
app.controller('SearchCtrl', function ($scope, $http, $timeout) {
var promise = '';
$scope.search = function() {
if(promise){
$timeout.cancel(promise);
}
promise = $timeout(function() {
//ajax call goes here..
},2000);
};
});
HTML
<input type="search" autocomplete="off" ng-model="keywords" ng-keyup="search()" placeholder="Search...">
Debounced / throttled model updates for angularjs : http://jsfiddle.net/lgersman/vPsGb/3/
In your case there is nothing more to do than using the directive in the jsfiddle code like this:
<input
id="searchText"
type="search"
placeholder="live search..."
ng-model="searchText"
ng-ampere-debounce
/>
Its basically a small piece of code consisting of a single angular directive named "ng-ampere-debounce" utilizing http://benalman.com/projects/jquery-throttle-debounce-plugin/ which can be attached to any dom element. The directive reorders the attached event handlers so that it can control when to throttle events.
You can use it for throttling/debouncing
* model angular updates
* angular event handler ng-[event]
* jquery event handlers
Have a look : http://jsfiddle.net/lgersman/vPsGb/3/
The directive will be part of the Orangevolt Ampere framework (https://github.com/lgersman/jquery.orangevolt-ampere).
Just for users redirected here:
As introduced in Angular 1.3 you can use ng-model-options attribute:
<input
id="searchText"
type="search"
placeholder="live search..."
ng-model="searchText"
ng-model-options="{ debounce: 250 }"
/>
I believe that the best way to solve this problem is by using Ben Alman's plugin jQuery throttle / debounce. In my opinion there is no need to delay the events of every single field in your form.
Just wrap your $scope.$watch handling function in $.debounce like this:
$scope.$watch("searchText", $.debounce(1000, function() {
console.log($scope.searchText);
}), true);
Another solution is to add a delay functionality to model update. The simple directive seems to do a trick:
app.directive('delayedModel', function() {
return {
scope: {
model: '=delayedModel'
},
link: function(scope, element, attrs) {
element.val(scope.model);
scope.$watch('model', function(newVal, oldVal) {
if (newVal !== oldVal) {
element.val(scope.model);
}
});
var timeout;
element.on('keyup paste search', function() {
clearTimeout(timeout);
timeout = setTimeout(function() {
scope.model = element[0].value;
element.val(scope.model);
scope.$apply();
}, attrs.delay || 500);
});
}
};
});
Usage:
<input delayed-model="searchText" data-delay="500" id="searchText" type="search" placeholder="live search..." />
So you just use delayed-model in place of ng-model and define desired data-delay.
Demo: http://plnkr.co/edit/OmB4C3jtUD2Wjq5kzTSU?p=preview
I solved this problem with a directive that basicly what it does is to bind the real ng-model on a special attribute which I watch in the directive, then using a debounce service I update my directive attribute, so the user watch on the variable that he bind to debounce-model instead of ng-model.
.directive('debounceDelay', function ($compile, $debounce) {
return {
replace: false,
scope: {
debounceModel: '='
},
link: function (scope, element, attr) {
var delay= attr.debounceDelay;
var applyFunc = function () {
scope.debounceModel = scope.model;
}
scope.model = scope.debounceModel;
scope.$watch('model', function(){
$debounce(applyFunc, delay);
});
attr.$set('ngModel', 'model');
element.removeAttr('debounce-delay'); // so the next $compile won't run it again!
$compile(element)(scope);
}
};
});
Usage:
<input type="text" debounce-delay="1000" debounce-model="search"></input>
And in the controller :
$scope.search = "";
$scope.$watch('search', function (newVal, oldVal) {
if(newVal === oldVal){
return;
}else{ //do something meaningful }
Demo in jsfiddle: http://jsfiddle.net/6K7Kd/37/
the $debounce service can be found here: http://jsfiddle.net/Warspawn/6K7Kd/
Inspired by eventuallyBind directive http://jsfiddle.net/fctZH/12/
Angular 1.3 will have ng-model-options debounce, but until then, you have to use a timer like Josue Ibarra said. However, in his code he launches a timer on every key press. Also, he is using setTimeout, when in Angular one has to use $timeout or use $apply at the end of setTimeout.
Why does everyone wants to use watch? You could also use a function:
var tempArticleSearchTerm;
$scope.lookupArticle = function (val) {
tempArticleSearchTerm = val;
$timeout(function () {
if (val == tempArticleSearchTerm) {
//function you want to execute after 250ms, if the value as changed
}
}, 250);
};
I think the easiest way here is to preload the json or load it once on$dirty and then the filter search will take care of the rest. This'll save you the extra http calls and its much faster with preloaded data. Memory will hurt, but its worth it.

How do you post data to CouchDB both with and without using JavaScript

I have a show which displays a form with fields populated from a document. I'd like to change the values in the field and then save the updated document.
I'm having trouble finding a clear, concise example of how to do this.
Seriously, just finishing this example would work wonders for so many people (I'm going to leave a lot of stuff out to make this concise).
Install Couchapp
This is outside the scope of my question, but here are the instructions for completeness.
Create a couchapp
Again, this is kind outside the scope of my question. Here is a perfectly concise tutorial on how to create a couchapp.
Create a template
Create a folder in the root of your couchapp called templates. Within the templates folder create an HTML page called myname.html. Put the following in it.
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<form method='post' action='#'>
<fieldset>
Hello <input type='text' name='name' value='{{ name }}'>
<input type='submit' name='submit' value='submit'>
</form>
</body>
</html>
Create a show
See the tutorial above for hwo to do this.
Add this code to a show called myname.
function(doc, req) {
if (doc) {
var ddoc = this
var Mustache = require("vendor/couchapp/lib/mustache");
var data = {
title: "The Name",
name: "Bobbert"
}
return Mustache.to_html(ddoc.templates.myname, data)
} else {
return ('nothing here baby')
}
}
Update the document with a new name by ...
So who can complete this step via both the client side and the server side?
Please don't point me to the guide, I need to read it in your words.
Thanks.
Edit:
Although the return value isn't pretty, just posting a form to the update handler will update the document.
You will probably want to look into update handler functions.
An update handler handles granular document transformations. So you can take 1 form, that has one distinct purpose, and only update the relevant fields in your document via the update handler.
Your update handler will need to take a PUT request from your form. A browser can't do this directly, so you'll need some javascript to handle this for you. If you're using jQuery, this plugin can take your form and submit it seamlessly via AJAX using PUT for you.
Inside the function, you can take the fields you are accepting, in this case name and apply that directly to the document. (input validation can be handled via the validate_doc_update function)
Update Handler (in your Design Document)
{
"updates": {
"name": function (doc, req) {
doc.name = req.form.name;
return [doc, "Name has been updated"];
}
}
}
HTML
<form id="myForm" action="/db/_design/ddoc/_update/name/doc_id">...</form>
JavaScript
$(document).ready(function() {
$('#myForm').ajaxForm({
type: "PUT",
success: function () {
alert("Thank you");
}
});
});
Once you've gotten this basic example up and running, it's not much more difficult to add some more advanced features to your update handlers. :)

Resources