Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Composite Javelin Typeahead Datasource

Summary: Refactor typeahead to support a composite datasource.

Reviewers: epriestley, mroch, tomo
Reviewed By: epriestley

CC: ry

Test Plan: Build a typeahead that composes two Preloaded datasources, and also one that composes
a preloaded with an ondemand datasource. Verify 'complete' event triggered at the right time.

Revert Plan: OK

Differential Revision: 407

Signed-off-by: Marshall Roch <mroch@fb.com>
  • Loading branch information...
commit 1a153b16a032408a8543a099d987ebbb1cc5aed0 1 parent bbae4f8
@aran aran authored mroch committed
View
37 src/lib/control/typeahead/Typeahead.js
@@ -129,6 +129,7 @@ JX.install('Typeahead', {
_stop : false,
_focus : -1,
_display : null,
+ _datasource : null,
/**
* Activate your properly configured typeahead. It won't do anything until
@@ -139,23 +140,40 @@ JX.install('Typeahead', {
*/
start : function() {
this.invoke('start');
+ if (__DEV__) {
+ if (!this._datasource) {
+ throw new Error(
+ "JX.Typeahead.start(): " +
+ "No datasource configured. Create a datasource and call " +
+ "setDatasource().");
+ }
+ }
},
/**
* Configure a datasource, which is where the Typeahead gets suggestions
* from. See @{JX.TypeaheadDatasource} for more information. You must
- * provide a datasource.
+ * provide exactly one datasource.
*
* @task datasource
* @param JX.TypeaheadDatasource The datasource which the typeahead will
* draw from.
*/
setDatasource : function(datasource) {
+ if (__DEV__) {
+ if (this._datasource) {
+ throw new Error(
+ "JX.Typeahead.setDatasource(): " +
+ "Typeahead already has a datasource.");
+ }
+ }
+ datasource.listen('waiting', JX.bind(this, this.waitForResults));
+ datasource.listen('resultsready', JX.bind(this, this.showResults));
datasource.bindToTypeahead(this);
+ this._datasource = datasource;
},
-
/**
* Override the <input /> selected in the constructor with some other input.
* This is primarily useful when building a control on top of the typeahead,
@@ -187,9 +205,8 @@ JX.install('Typeahead', {
/**
* Show a given result set in the typeahead's dropdown suggestion menu.
- * Normally, you only call this method if you are implementing a datasource.
- * Otherwise, the datasource you have configured calls it for you in
- * response to the user's actions.
+ * Normally, you don't call this method directly. Usually it gets called
+ * in response to events from the datasource you have configured.
*
* @task control
* @param list List of ##<a />## tags to show as suggestions/results.
@@ -218,14 +235,7 @@ JX.install('Typeahead', {
}
this._value = this._control.value;
- if (!this.invoke('change', this._value).getPrevented()) {
- if (__DEV__) {
- throw new Error(
- "JX.Typeahead._update(): " +
- "No listener responded to Typeahead 'change' event. Create a " +
- "datasource and call setDatasource().");
- }
- }
+ this.invoke('change', this._value);
},
/**
* Show a "waiting for results" UI in place of the typeahead's dropdown
@@ -237,6 +247,7 @@ JX.install('Typeahead', {
waitForResults : function() {
// TODO: Build some sort of fancy spinner or "..." type UI here to
// visually indicate that we're waiting on the server.
+ // Wait on the datasource 'complete' event for hiding the spinner.
this.hide();
},
View
75 src/lib/control/typeahead/source/TypeaheadCompositeSource.js
@@ -0,0 +1,75 @@
+/**
+ * @requires javelin-install
+ * javelin-typeahead-source
+ * javelin-util
+ * @provides javelin-typeahead-composite-source
+ * @javelin
+ */
+
+JX.install('TypeaheadCompositeSource', {
+
+ extend : 'TypeaheadSource',
+
+ construct : function(sources) {
+ JX.TypeaheadSource.call(this);
+ this.sources = sources;
+
+ for (var ii = 0; ii < this.sources.length; ++ii) {
+ var child = this.sources[ii];
+ child.listen('waiting', JX.bind(this, this.childWaiting));
+ child.listen('resultsready', JX.bind(this, this.childResultsReady));
+ child.listen('complete', JX.bind(this, this.childComplete));
+ }
+ },
+
+ members : {
+ sources : null,
+ results : null,
+ completeCount : 0,
+
+ didChange : function(value) {
+ this.results = [];
+ this.completeCount = 0;
+ for (var ii = 0; ii < this.sources.length; ++ii) {
+ this.sources[ii].didChange(value);
+ }
+ },
+
+ didStart : function() {
+ for (var ii = 0; ii < this.sources.length; ++ii) {
+ this.sources[ii].didStart();
+ }
+ },
+
+ childWaiting : function() {
+ if (!this.results.length) {
+ this.invoke('waiting');
+ }
+ },
+
+ childResultsReady : function(nodes) {
+ this.results = this.mergeResults(this.results, nodes);
+ this.invoke('resultsready', this.results);
+ },
+
+ childComplete : function() {
+ this.completeCount++;
+ if (this.completeCount == this.sources.length) {
+ this.invoke('complete');
+ }
+ },
+
+ /**
+ * Overrideable strategy for combining results.
+ * By default, appends results as they come in
+ * so that results don't jump around.
+ */
+ mergeResults : function(oldResults, newResults) {
+ for (var ii = 0; ii < newResults.length; ++ii) {
+ oldResults.push(newResults[ii]);
+ }
+ return oldResults;
+ }
+ }
+});
+
View
5 src/lib/control/typeahead/source/TypeaheadOnDemandSource.js
@@ -40,9 +40,6 @@ JX.install('TypeaheadOnDemandSource', {
haveData : null,
didChange : function(value) {
- if (JX.Stratcom.pass()) {
- return;
- }
this.lastChange = new Date().getTime();
value = this.normalize(value);
@@ -54,8 +51,6 @@ JX.install('TypeaheadOnDemandSource', {
JX.bind(this, this.sendRequest, this.lastChange, value),
this.getQueryDelay());
}
-
- JX.Stratcom.context().kill();
},
sendRequest : function(when, value) {
View
1  src/lib/control/typeahead/source/TypeaheadPreloadedSource.js
@@ -36,7 +36,6 @@ JX.install('TypeaheadPreloadedSource', {
this.lastValue = value;
this.waitForResults();
}
- JX.Stratcom.context().kill();
},
didStart : function() {
View
10 src/lib/control/typeahead/source/TypeaheadSource.js
@@ -14,6 +14,8 @@ JX.install('TypeaheadSource', {
this.setNormalizer(JX.TypeaheadNormalizer.normalize);
},
+ events : ['waiting', 'resultsready', 'complete'],
+
properties : {
/**
@@ -65,11 +67,9 @@ JX.install('TypeaheadSource', {
members : {
_raw : null,
_lookup : null,
- _typeahead : null,
_normalizer : null,
bindToTypeahead : function(typeahead) {
- this._typeahead = typeahead;
typeahead.listen('change', JX.bind(this, this.didChange));
typeahead.listen('start', JX.bind(this, this.didStart));
},
@@ -117,7 +117,7 @@ JX.install('TypeaheadSource', {
},
waitForResults : function() {
- this._typeahead.waitForResults();
+ this.invoke('waiting');
return this;
},
@@ -181,7 +181,9 @@ JX.install('TypeaheadSource', {
}
}
- this._typeahead.showResults(this.renderNodes(value, hits));
+ var nodes = this.renderNodes(value, hits);
+ this.invoke('resultsready', nodes);
+ this.invoke('complete');
},
renderNodes : function(value, hits) {
Please sign in to comment.
Something went wrong with that request. Please try again.