explicitly passing filters and query to visualize (#19172)

This commit is contained in:
Peter Pisljar 2018-06-13 04:35:07 -05:00 committed by GitHub
parent 43639cd9c0
commit 88bb0121d4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 216 additions and 249 deletions

View file

@ -27,3 +27,5 @@ export const updateIsFullScreenMode = createAction('UPDATE_IS_FULL_SCREEN_MODE')
export const updateUseMargins = createAction('UPDATE_USE_MARGINS');
export const updateHidePanelTitles = createAction('HIDE_PANEL_TITLES');
export const updateTimeRange = createAction('UPDATE_TIME_RANGE');
export const updateFilters = createAction('UPDATE_FILTERS');
export const updateQuery = createAction('UPDATE_QUERY');

View file

@ -160,9 +160,6 @@ app.directive('dashboardApp', function ($injector) {
timefilter.enableAutoRefreshSelector();
timefilter.enableTimeRangeSelector();
dash.searchSource.highlightAll(true);
dash.searchSource.version(true);
courier.setRootSearchSource(dash.searchSource);
updateState();

View file

@ -35,6 +35,8 @@ import {
updateHidePanelTitles,
updateTimeRange,
clearStagedFilters,
updateFilters,
updateQuery,
} from './actions';
import { stateMonitorFactory } from 'ui/state_management/state_monitor_factory';
import { createPanelState } from './panel';
@ -50,7 +52,9 @@ import {
getHidePanelTitles,
getStagedFilters,
getEmbeddables,
getEmbeddableMetadata
getEmbeddableMetadata,
getQuery,
getFilters,
} from '../selectors';
/**
@ -196,6 +200,17 @@ export class DashboardStateManager {
if (getDescription(state) !== this.getDescription()) {
store.dispatch(updateDescription(this.getDescription()));
}
if (!_.isEqual(
FilterUtils.cleanFiltersForComparison(this.appState.filters),
FilterUtils.cleanFiltersForComparison(getFilters(state))
)) {
store.dispatch(updateFilters(this.appState.filters));
}
if (getQuery(state) !== this.getQuery()) {
store.dispatch(updateQuery(this.getQuery()));
}
}
_handleStoreChanges() {

View file

@ -161,7 +161,9 @@ DashboardPanel.propTypes = {
]),
destroy: PropTypes.func.isRequired,
containerState: PropTypes.shape({
timeRange: PropTypes.object.isRequired,
timeRange: PropTypes.object,
filters: PropTypes.array,
query: PropTypes.object,
embeddableCustomization: PropTypes.object,
hidePanelTitles: PropTypes.bool.isRequired,
}),

View file

@ -26,6 +26,8 @@ import {
updateHidePanelTitles,
updateIsFullScreenMode,
updateTimeRange,
updateFilters,
updateQuery,
setVisibleContextMenuPanelId,
} from '../actions';
@ -47,6 +49,16 @@ export const view = handleActions({
timeRange: payload
}),
[updateFilters]: (state, { payload }) => ({
...state,
filters: payload
}),
[updateQuery]: (state, { payload }) => ({
...state,
query: payload
}),
[updateUseMargins]: (state, { payload }) => ({
...state,
useMargins: payload

View file

@ -158,6 +158,10 @@ export const getMaximizedPanelId = dashboard => dashboard.view.maximizedPanelId;
*/
export const getTimeRange = dashboard => dashboard.view.timeRange;
export const getFilters = dashboard => dashboard.view.filters;
export const getQuery = dashboard => dashboard.view.query;
/**
* @typedef {Object} DashboardMetadata
* @property {string} title
@ -205,6 +209,8 @@ export const getContainerState = (dashboard, panelId) => {
to: time.to,
from: time.from,
},
filters: getFilters(dashboard),
query: getQuery(dashboard),
embeddableCustomization: _.cloneDeep(getEmbeddableCustomization(dashboard, panelId) || {}),
hidePanelTitles: getHidePanelTitles(dashboard),
customTitle: getPanel(dashboard, panelId).title,

View file

@ -201,6 +201,15 @@ function discoverController(
.highlightAll(true)
.version(true);
// searchSource which applies time range
const timeRangeSearchSource = savedSearch.searchSource.new();
timeRangeSearchSource.set('filter', () => {
return timefilter.get($scope.indexPattern);
});
$scope.searchSource.inherits(timeRangeSearchSource);
const pageTitleSuffix = savedSearch.id && savedSearch.title ? `: ${savedSearch.title}` : '';
docTitle.change(`Discover${pageTitleSuffix}`);

View file

@ -21,6 +21,7 @@ import angular from 'angular';
import { Embeddable } from 'ui/embeddable';
import searchTemplate from './search_template.html';
import * as columnActions from 'ui/doc_table/actions/columns';
import { getTime } from 'ui/timefilter/get_time';
export class SearchEmbeddable extends Embeddable {
constructor({ onEmbeddableStateChanged, savedSearch, editUrl, loader, $rootScope, $compile }) {
@ -52,13 +53,20 @@ export class SearchEmbeddable extends Embeddable {
pushContainerStateParamsToScope() {
// If there is column or sort data on the panel, that means the original columns or sort settings have
// been overridden in a dashboard.
this.searchScope.columns = this.customization.columns || this.savedSearch.columns;
this.searchScope.sort = this.customization.sort || this.savedSearch.sort;
this.searchScope.sharedItemTitle = this.panelTitle;
this.filtersSearchSource.set('filter', this.filters);
this.filtersSearchSource.set('query', this.query);
}
onContainerStateChanged(containerState) {
this.customization = containerState.embeddableCustomization || {};
this.filters = containerState.filters;
this.query = containerState.query;
this.timeRange = containerState.timeRange;
this.panelTitle = '';
if (!containerState.hidePanelTitles) {
this.panelTitle = containerState.customTitle !== undefined ?
@ -74,11 +82,21 @@ export class SearchEmbeddable extends Embeddable {
initializeSearchScope() {
this.searchScope = this.$rootScope.$new();
this.pushContainerStateParamsToScope();
this.searchScope.description = this.savedSearch.description;
this.searchScope.searchSource = this.savedSearch.searchSource;
const timeRangeSearchSource = this.searchScope.searchSource.new();
timeRangeSearchSource.filter(() => {
return getTime(this.searchScope.searchSource.get('index'), this.timeRange);
});
this.filtersSearchSource = this.searchScope.searchSource.new();
this.filtersSearchSource.inherits(timeRangeSearchSource);
this.searchScope.searchSource.inherits(this.filtersSearchSource);
this.pushContainerStateParamsToScope();
this.searchScope.setSortOrder = (columnName, direction) => {
this.searchScope.sort = this.customization.sort = [columnName, direction];
this.emitEmbeddableStateChange(this.getEmbeddableState());

View file

@ -42,7 +42,7 @@ module.factory('SavedSearch', function (courier) {
hits: 0,
sort: [],
version: 1
}
},
});
this.showInRecenltyAccessed = true;

View file

@ -52,6 +52,8 @@ export const getMaximizedPanelId = state => DashboardSelectors.getMaximizedPanel
export const getUseMargins = state => DashboardSelectors.getUseMargins(getDashboard(state));
export const getHidePanelTitles = state => DashboardSelectors.getHidePanelTitles(getDashboard(state));
export const getTimeRange = state => DashboardSelectors.getTimeRange(getDashboard(state));
export const getFilters = state => DashboardSelectors.getFilters(getDashboard(state));
export const getQuery = state => DashboardSelectors.getQuery(getDashboard(state));
export const getTitle = state => DashboardSelectors.getTitle(getDashboard(state));
export const getDescription = state => DashboardSelectors.getDescription(getDashboard(state));

View file

@ -69,10 +69,9 @@
<visualize
saved-obj="savedVis"
ui-state="uiState"
app-state="state"
time-range="timeRange"
editor-mode="chrome.getVisible()"
show-spy-panel="chrome.getVisible()"
time-range="timeRange"
>
</visualize>

View file

@ -246,12 +246,14 @@ function VisEditor($scope, $route, timefilter, AppState, $window, kbnUrl, courie
// update the searchSource when filters update
$scope.$listen(queryFilter, 'update', function () {
$state.save();
$scope.fetch();
});
// update the searchSource when query updates
$scope.fetch = function () {
$state.save();
savedVis.searchSource.set('query', $state.query);
savedVis.searchSource.set('filter', $state.filters);
$scope.vis.forceReload();
};

View file

@ -98,6 +98,18 @@ export class VisualizeEmbeddable extends Embeddable {
this.timeRange = containerState.timeRange;
}
// Check if filters has changed
if (containerState.filters !== this.filters) {
updatedParams.filters = containerState.filters;
this.filters = containerState.filters;
}
// Check if query has changed
if (containerState.query !== this.query) {
updatedParams.query = containerState.query;
this.query = containerState.query;
}
const derivedPanelTitle = this.getPanelTitle(containerState);
if (this.panelTitle !== derivedPanelTitle) {
updatedParams.dataAttrs = {
@ -119,6 +131,8 @@ export class VisualizeEmbeddable extends Embeddable {
render(domNode, containerState) {
this.panelTitle = this.getPanelTitle(containerState);
this.timeRange = containerState.timeRange;
this.query = containerState.query;
this.filters = containerState.filters;
this.transferCustomizationsToUiState(containerState);
@ -127,6 +141,8 @@ export class VisualizeEmbeddable extends Embeddable {
// Append visualization to container instead of replacing its content
append: true,
timeRange: containerState.timeRange,
query: containerState.query,
filters: containerState.filters,
cssClass: `panel-content panel-content--fullWidth`,
// The chrome is permanently hidden in "embed mode" in which case we don't want to show the spy pane, since
// we deem that situation to be more public facing and want to hide more detailed information.

View file

@ -29,7 +29,6 @@ import { SearchSourceProvider } from './data_source/search_source';
import { requestQueue } from './_request_queue';
import { FetchSoonProvider } from './fetch';
import { SearchLooperProvider } from './looper/search';
import { RootSearchSourceProvider } from './data_source/_root_search_source';
import { SavedObjectProvider } from './saved_object';
import { RedirectWhenMissingProvider } from './_redirect_when_missing';
@ -42,9 +41,6 @@ uiModules.get('kibana/courier')
const fetchSoon = Private(FetchSoonProvider);
const searchLooper = self.searchLooper = Private(SearchLooperProvider);
// expose some internal modules
self.setRootSearchSource = Private(RootSearchSourceProvider).set;
self.SavedObject = Private(SavedObjectProvider);
self.indexPatterns = indexPatterns;
self.redirectWhenMissing = Private(RedirectWhenMissingProvider);

View file

@ -1,89 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { SearchSourceProvider } from './search_source';
export function RootSearchSourceProvider(Private, $rootScope, timefilter) {
const SearchSource = Private(SearchSourceProvider);
const globalSource = new SearchSource();
globalSource.inherits(false); // this is the final source, it has no parents
globalSource.filter(function (globalSource) {
// dynamic time filter will be called in the _flatten phase of things
const filter = timefilter.get(globalSource.get('index'));
// Attach a meta property to it, that we check inside visualizations
// to remove that timefilter again because we use our explicitly passed in one.
// This should be removed as soon as we got rid of inheritance in SearchSource
// across the boundary or visualization.
if (filter) {
filter.meta = { _globalTimefilter: true };
}
return filter;
});
let appSource; // set in setAppSource()
resetAppSource();
// when the route changes, clear the appSource
$rootScope.$on('$routeChangeStart', resetAppSource);
/**
* Get the current AppSource
* @return {Promise} - resolved with the current AppSource
*/
function getAppSource() {
return appSource;
}
/**
* Set the current AppSource
* @param {SearchSource} source - The Source that represents the applications "root" search source object
*/
function setAppSource(source) {
appSource = source;
// walk the parent chain until we get to the global source or nothing
// that's where we will attach to the globalSource
let literalRoot = source;
while (literalRoot._parent && literalRoot._parent !== globalSource) {
literalRoot = literalRoot._parent;
}
literalRoot.inherits(globalSource);
}
/**
* Sets the appSource to be a new, empty, SearchSource
* @return {undefined}
*/
function resetAppSource() {
setAppSource(new SearchSource());
}
return {
get: getAppSource,
set: setAppSource,
getGlobalSource: function () {
return globalSource;
}
};
}

View file

@ -75,7 +75,6 @@ import angular from 'angular';
import '../../promises';
import { NormalizeSortRequestProvider } from './_normalize_sort_request';
import { RootSearchSourceProvider } from './_root_search_source';
import { SearchRequestProvider } from '../fetch/request';
import { SegmentedRequestProvider } from '../fetch/request/segmented';
@ -227,11 +226,8 @@ export function SearchSourceProvider(Promise, PromiseEmitter, Private, config) {
* Get the parent of this SearchSource
* @return {undefined|searchSource}
*/
getParent(onlyHardLinked) {
const self = this;
if (self._parent === false) return;
if (self._parent) return self._parent;
return onlyHardLinked ? undefined : Private(RootSearchSourceProvider).get();
getParent() {
return this._parent || undefined;
}
/**
@ -282,6 +278,14 @@ export function SearchSourceProvider(Promise, PromiseEmitter, Private, config) {
return clone;
}
makeChild() {
return new SearchSource().inherits(this, { callParentStartHandlers: true });
}
new() {
return new SearchSource();
}
async getSearchRequestBody() {
const searchRequest = await this._flatten();
return searchRequest.body;

View file

@ -0,0 +1,50 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import _ from 'lodash';
import dateMath from '@kbn/datemath';
export function calculateBounds(timeRange, options = {}) {
return {
min: dateMath.parse(timeRange.from, { forceNow: options.forceNow }),
max: dateMath.parse(timeRange.to, { roundUp: true, forceNow: options.forceNow })
};
}
export function getTime(indexPattern, timeRange, forceNow) {
if (!indexPattern) {
//in CI, we sometimes seem to fail here.
return;
}
let filter;
const timefield = indexPattern.timeFieldName && _.find(indexPattern.fields, { name: indexPattern.timeFieldName });
if (timefield) {
const bounds = calculateBounds(timeRange, { forceNow });
filter = { range: {} };
filter.range[timefield.name] = {
gte: bounds.min.valueOf(),
lte: bounds.max.valueOf(),
format: 'epoch_millis'
};
}
return filter;
}

View file

@ -19,7 +19,7 @@
import _ from 'lodash';
import moment from 'moment';
import dateMath from '@kbn/datemath';
import { calculateBounds, getTime } from './get_time';
import '../state_management/global_state';
import '../config';
import { EventsProvider } from '../events';
@ -98,33 +98,6 @@ uiModules
$rootScope.$apply();
};
Timefilter.prototype.get = function (indexPattern, timeRange) {
if (!indexPattern) {
//in CI, we sometimes seem to fail here.
return;
}
let filter;
const timefield = indexPattern.timeFieldName && _.find(indexPattern.fields, { name: indexPattern.timeFieldName });
if (timefield) {
const bounds = timeRange ? this.calculateBounds(timeRange) : this.getBounds();
filter = { range: {} };
filter.range[timefield.name] = {
gte: bounds.min.valueOf(),
lte: bounds.max.valueOf(),
format: 'epoch_millis'
};
}
return filter;
};
Timefilter.prototype.getBounds = function () {
return this.calculateBounds(this.time);
};
Timefilter.prototype.getForceNow = function () {
const query = $location.search().forceNow;
if (!query) {
@ -138,13 +111,16 @@ uiModules
return new Date(ticks);
};
Timefilter.prototype.calculateBounds = function (timeRange) {
const forceNow = this.getForceNow();
Timefilter.prototype.get = function (indexPattern, timeRange) {
return getTime(indexPattern, timeRange ? timeRange : this.time, this.getForceNow());
};
return {
min: dateMath.parse(timeRange.from, { forceNow }),
max: dateMath.parse(timeRange.to, { roundUp: true, forceNow })
};
Timefilter.prototype.calculateBounds = function (timeRange) {
return calculateBounds(timeRange, { forceNow: this.getForceNow() });
};
Timefilter.prototype.getBounds = function () {
return this.calculateBounds(this.time);
};
Timefilter.prototype.getActiveBounds = function () {

View file

@ -25,21 +25,9 @@ import { calculateObjectHash } from '../lib/calculate_object_hash';
const CourierRequestHandlerProvider = function (Private, courier, timefilter) {
const SearchSource = Private(SearchSourceProvider);
/**
* TODO: This code can be removed as soon as we got rid of inheritance in the
* searchsource and pass down every filter explicitly.
* We are filtering out the global timefilter by the meta key set by the root
* search source on that filter.
*/
function removeSearchSourceParentTimefilter(searchSource) {
searchSource.addFilterPredicate((filter) => {
return !_.get(filter, 'meta._globalTimefilter', false);
});
}
return {
name: 'courier',
handler: function (vis, { appState, queryFilter, searchSource, timeRange, forceFetch }) {
handler: function (vis, { searchSource, timeRange, query, filters, forceFetch }) {
// Create a new search source that inherits the original search source
// but has the propriate timeRange applied via a filter.
@ -48,16 +36,17 @@ const CourierRequestHandlerProvider = function (Private, courier, timefilter) {
// Using callParentStartHandlers: true we make sure, that the parent searchSource
// onSearchRequestStart will be called properly even though we use an inherited
// search source.
const requestSearchSource = new SearchSource().inherits(searchSource, { callParentStartHandlers: true });
const timeFilterSearchSource = searchSource.makeChild();
const requestSearchSource = timeFilterSearchSource.makeChild();
// For now we need to mirror the history of the passed search source, since
// the spy panel wouldn't work otherwise.
Object.defineProperty(requestSearchSource, 'history', {
get() {
return requestSearchSource._parent.history;
return searchSource.history;
},
set(history) {
return requestSearchSource._parent.history = history;
return searchSource.history = history;
}
});
@ -69,57 +58,44 @@ const CourierRequestHandlerProvider = function (Private, courier, timefilter) {
return vis.onSearchRequestStart(searchSource, searchRequest);
});
// Add the explicit passed timeRange as a filter to the requestSearchSource.
requestSearchSource.filter(() => {
timeFilterSearchSource.set('filter', () => {
return timefilter.get(searchSource.get('index'), timeRange);
});
removeSearchSourceParentTimefilter(requestSearchSource);
requestSearchSource.set('filter', filters);
requestSearchSource.set('query', query);
if (queryFilter && vis.editorMode) {
searchSource.set('filter', queryFilter.getFilters());
searchSource.set('query', appState.query);
}
const shouldQuery = () => {
const shouldQuery = (requestBodyHash) => {
if (!searchSource.lastQuery || forceFetch) return true;
if (!_.isEqual(_.cloneDeep(searchSource.get('filter')), searchSource.lastQuery.filter)) return true;
if (!_.isEqual(_.cloneDeep(searchSource.get('query')), searchSource.lastQuery.query)) return true;
if (!_.isEqual(calculateObjectHash(vis.getAggConfig()), searchSource.lastQuery.aggs)) return true;
if (!_.isEqual(_.cloneDeep(timeRange), searchSource.lastQuery.timeRange)) return true;
if (searchSource.lastQuery !== requestBodyHash) return true;
return false;
};
return new Promise((resolve, reject) => {
if (shouldQuery()) {
requestSearchSource.onResults().then(resp => {
searchSource.lastQuery = {
filter: _.cloneDeep(searchSource.get('filter')),
query: _.cloneDeep(searchSource.get('query')),
aggs: calculateObjectHash(vis.getAggConfig()),
timeRange: _.cloneDeep(timeRange)
};
searchSource.rawResponse = resp;
return _.cloneDeep(resp);
}).then(async resp => {
for (const agg of vis.getAggConfig()) {
if (_.has(agg, 'type.postFlightRequest')) {
const nestedSearchSource = new SearchSource().inherits(requestSearchSource);
resp = await agg.type.postFlightRequest(resp, vis.aggs, agg, nestedSearchSource);
return requestSearchSource.getSearchRequestBody().then(q => {
const queryHash = calculateObjectHash(q);
if (shouldQuery(queryHash)) {
requestSearchSource.onResults().then(resp => {
searchSource.lastQuery = queryHash;
searchSource.rawResponse = resp;
return _.cloneDeep(resp);
}).then(async resp => {
for (const agg of vis.getAggConfig()) {
if (_.has(agg, 'type.postFlightRequest')) {
const nestedSearchSource = new SearchSource().inherits(requestSearchSource);
resp = await agg.type.postFlightRequest(resp, vis.aggs, agg, nestedSearchSource);
}
}
}
searchSource.finalResponse = resp;
resolve(resp);
}).catch(e => reject(e));
searchSource.finalResponse = resp;
resolve(resp);
}).catch(e => reject(e));
courier.fetch();
} else {
resolve(searchSource.finalResponse);
}
courier.fetch();
} else {
resolve(searchSource.finalResponse);
}
});
});
}
};

View file

@ -167,13 +167,6 @@ describe('visualize directive', function () {
assertParam({ forceFetch: true });
});
it('should be true if triggered via fetch event', async () => {
$scope.$emit('fetch');
await waitForFetch();
sinon.assert.calledOnce(requestHandler);
assertParam({ forceFetch: true });
});
it('should be false if triggered via resize event', async () => {
$el.width(400);
$el.height(500);

View file

@ -55,6 +55,12 @@ export class EmbeddedVisualizeHandler {
if (params.hasOwnProperty('timeRange')) {
this._scope.timeRange = params.timeRange;
}
if (params.hasOwnProperty('filters')) {
this._scope.filters = params.filters;
}
if (params.hasOwnProperty('query')) {
this._scope.query = params.query;
}
// Apply data- attributes to the element if specified
if (params.dataAttrs) {

View file

@ -52,6 +52,8 @@ import { EmbeddedVisualizeHandler } from './embedded_visualize_handler';
* will be set to the root visuzalize element.
* @property {object} dataAttrs An object of key-value pairs, that will be set
* as data-{key}="{value}" attributes on the visualization element.
* @property {array} filters Specifies the filters that should be applied to that visualization.
* @property {object} query The query that should apply to that visualization.
*/
const VisualizeLoaderProvider = ($compile, $rootScope, savedVisualizations) => {
@ -62,6 +64,8 @@ const VisualizeLoaderProvider = ($compile, $rootScope, savedVisualizations) => {
scope.appState = params.appState;
scope.uiState = params.uiState;
scope.timeRange = params.timeRange;
scope.filters = params.filters;
scope.query = params.query;
scope.showSpyPanel = params.showSpyPanel;
const container = angular.element(el);

View file

@ -3,6 +3,8 @@
app-state="appState"
ui-state="uiState"
time-range="timeRange"
filters="filters"
query="query"
show-spy-panel="showSpyPanel"
render-complete
></visualize>

View file

@ -19,7 +19,6 @@
import _ from 'lodash';
import { uiModules } from '../modules';
import { stateMonitorFactory } from '../state_management/state_monitor_factory';
import visualizeTemplate from './visualize.html';
import { VisRequestHandlersRegistryProvider } from '../registry/vis_request_handlers';
import { VisResponseHandlersRegistryProvider } from '../registry/vis_response_handlers';
@ -55,6 +54,8 @@ uiModules
appState: '=?',
uiState: '=?',
timeRange: '=?',
filters: '=?',
query: '=?',
},
template: visualizeTemplate,
link: async function ($scope, $el) {
@ -92,21 +93,16 @@ uiModules
// fetching new data and rendering.
if (!$scope.vis.initialized || !$scope.savedObj || destroyed) return;
// TODO: This should ALWAYS be passed into this component via the loader
// in the future. Unfortunately we need some refactoring in dashboard
// to make this working and correctly rerender, so for now we will either
// use the one passed in to us or look into the timefilter ourselfs (which
// will be removed in the future).
const timeRange = $scope.timeRange || timefilter.time;
$scope.vis.filters = { timeRange };
$scope.vis.filters = { timeRange: $scope.timeRange };
const handlerParams = {
appState: $scope.appState,
uiState: $scope.uiState,
queryFilter: queryFilter,
searchSource: $scope.savedObj.searchSource,
timeRange: timeRange,
timeRange: $scope.timeRange,
filters: $scope.filters,
query: $scope.query,
forceFetch,
};
@ -147,14 +143,12 @@ uiModules
});
}, 100);
//todo: clean this one up as well
const handleVisUpdate = () => {
if ($scope.editorMode) {
if ($scope.appState.vis) {
$scope.appState.vis = $scope.vis.getState();
$scope.appState.save();
} else {
$scope.fetch();
}
$scope.fetch();
};
$scope.vis.on('update', handleVisUpdate);
@ -166,32 +160,10 @@ uiModules
$scope.vis.on('reload', reload);
// auto reload will trigger this event
$scope.$on('courier:searchRefresh', reload);
// dashboard will fire fetch event when it wants to refresh
$scope.$on('fetch', reload);
const handleQueryUpdate = ()=> {
$scope.fetch();
};
queryFilter.on('update', handleQueryUpdate);
if ($scope.appState) {
const stateMonitor = stateMonitorFactory.create($scope.appState);
stateMonitor.onChange((status, type, keys) => {
if (keys[0] === 'vis') {
if ($scope.appState.vis) $scope.vis.setState($scope.appState.vis);
$scope.fetch();
}
if ($scope.vis.type.requiresSearch && ['query', 'filters'].includes(keys[0])) {
$scope.fetch();
}
});
$scope.$on('$destroy', () => {
stateMonitor.destroy();
});
}
$scope.$watch('filters', $scope.fetch, true);
$scope.$watch('query', $scope.fetch, true);
$scope.$watch('timeRange', $scope.fetch, true);
// Listen on uiState changes to start fetching new data again.
// Some visualizations might need different data depending on their uiState,
@ -199,16 +171,13 @@ uiModules
// checking if anything changed, that actually require a new fetch or return
// cached data otherwise.
$scope.uiState.on('change', $scope.fetch);
resizeChecker.on('resize', $scope.fetch);
// visualize needs to know about timeFilter
$scope.$listen(timefilter, 'fetch', $scope.fetch);
resizeChecker.on('resize', $scope.fetch);
$scope.$on('$destroy', () => {
destroyed = true;
$scope.vis.removeListener('reload', reload);
$scope.vis.removeListener('update', handleVisUpdate);
queryFilter.off('update', handleQueryUpdate);
$scope.uiState.off('change', $scope.fetch);
resizeChecker.destroy();
});