Conversation
|
Thanks for your contribution! The pull request is marked to be Document changes are required in this PR. Please also make a PR to apache/echarts-doc for document changes and update the issue id in the PR description. When the doc PR is merged, the maintainers will remove the |
|
The changes brought by this PR can be previewed at: https://echarts.apache.org/examples/editor?version=PR-20705@21c5ffc |
src/model/Global.ts
Outdated
| setTheme(theme: object) { | ||
| this._theme = new Model(theme); | ||
| // Merge theme with current option directly | ||
| mergeTheme(this.option, this._theme.option); |
There was a problem hiding this comment.
The next sentence this._resetOption('recreate', null); will recreate ecModel.option, which makes this mergeTheme ineffective.
I think it's OK to just remove this mergeTheme.
And the next sentence will call initBase to recreate a ecModel.option and merge theme to it.
There was a problem hiding this comment.
OK. I tested it works without mergeTheme. Thanks.
src/core/echarts.ts
Outdated
| this._model.setTheme(this._theme); | ||
| // Force refresh to apply theme changes | ||
| updateMethods.prepareAndUpdate.call(this, { | ||
| type: 'themeChanged' |
There was a problem hiding this comment.
I think the type: 'themeChanged' is ineffective. It won't tigger an event. it is probably inappropriate to trigger an event in an explicit API call (which might lead to dead loop).
If needing to call updateMethods.prepareAndUpdate or updateMethods.update, it's OK to use null as the 2nd parameter.
There was a problem hiding this comment.
Do you mean updateMethods.prepareAndUpdate.call(this);? This causes optionChanged: payload.newOption != null to have error since payload is undefined. It works if I add payload && as:
updateMethods.update.call(this, payload, payload && {
// Needs to mark option changed if newOption is given.
// It's from MagicType.
// TODO If use a separate flag optionChanged in payload?
optionChanged: payload.newOption != null
});There was a problem hiding this comment.
I've changed my understanding, it's OK to use
{
type: 'themeChanged'
}But there is more details need to be considered, see comments below.
| updateMethods.prepareAndUpdate.call(this, { | ||
| type: 'themeChanged' | ||
| }); | ||
| } |
There was a problem hiding this comment.
If we provide an API setTheme, I think the behavior should better be similar to setOption. that is, it modifies the model, and causes re-render either immediately or in a lazy way.
But in the current implementation, re-render will not be triggered.
There was a problem hiding this comment.
By calling updateMethods.prepareAndUpdate, it will re-render as soon as calling setTheme. See the demo of test case:
onclick: function () {
const riverTheme = {
backgroundColor: '#ccf',
color: ['#00f'],
title: {
textStyle: {
color: '#00f'
}
}
};
chart.setTheme(riverTheme); // renders the chart at once
}Anything else I need to change?
There was a problem hiding this comment.
After reviewing the implementation of ECharts#resize, I updated by understanding.
- Whether to trigger a event when an API called? The current approach is that a parameter
silentis provided, which is supported by all of theECharts#setOption,ECharts#resizeandECharts#dispatchAction. So insetThemewe'd better follow the convention. - Whether to call
zr.flush()to perform zr render before the API return?- The current impl is not consistent:
ECharts#setOption: if not ssr, callzr.flush()immediatelyECharts#dispatchActionprovide an parameterflushto enable customization it.ECharts#resizedoes not callzr.flush().
- If we do not intend to figure this inconsistency this time, I think that it's OK to follow what
ECharts#resizedoes.
- The current impl is not consistent:
- Whether to use a nested-call-guard: a flag
IN_MAIN_PROCESS_KEYis used to prevent from nested calling in bothECharts#setOption,ECharts#resizeandECharts#dispatchAction. So I think we can follow that if we do not want to renew the impl.
So IMO the impl of setTheme can be (modified based on the impl of ECharts#resize):
export interface SetThemeOpts {
silent?: boolean // by default false.
};
setTheme(theme: string | ThemeOption, opts?: SetThemeOpts): void {
if (this[IN_MAIN_PROCESS_KEY]) {
if (__DEV__) {
error('`setTheme` should not be called during the main process.');
}
return;
}
if (this._disposed) {
disposedWarning(this.id);
return;
}
const ecModel = this._model;
if (!ecModel) {
return;
}
let silent = opts && opts.silent;
let updateParams = null as UpdateLifecycleParams;
// There is some real cases that:
// chart.setOption(option, { lazyUpdate: true });
// chart.setTheme('some_theme');
if (this[PENDING_UPDATE]) {
if (silent == null) {
silent = (this[PENDING_UPDATE] as ECharts[PENDING_UPDATE]).silent;
}
updateParams = (this[PENDING_UPDATE] as ECharts[PENDING_UPDATE]).updateParams;
this[PENDING_UPDATE] = null;
}
this[IN_MAIN_PROCESS_KEY] = true;
try {
this._updateTheme(theme);
ecModel.setTheme(this._theme);
prepare(this);
updateMethods.update.call(this, {type: 'setTheme'}, updateParams);
}
catch (e) {
this[IN_MAIN_PROCESS_KEY] = false;
throw e;
}
this[IN_MAIN_PROCESS_KEY] = false;
flushPendingActions.call(this, silent);
triggerUpdatedEvent.call(this, silent);
}BTW, it seems that there is some defects in the impl of ECharts#resize: this[PENDING_UPDATE].updateParams is omitted in lazyUpdate scenario. I've no idea whether it does that deliberately or to be a mistake.
There was a problem hiding this comment.
Thanks a lot for the help. This deepens my understanding a lot.
| prepareAndUpdate(this: ECharts, payload: Payload): void { | ||
| prepare(this); | ||
| updateMethods.update.call(this, payload, { | ||
| updateMethods.update.call(this, payload, payload && { |
There was a problem hiding this comment.
I kept this change to make the code more robust, although we are not calling this function now.
|
Congratulations! Your PR has been merged. Thanks for your contribution! 👍 |
|
@Ovilia I have installed in my project Apache ECharts 5.6.0. I see that in following files setTheme is marked as private. Is that ok ? |
@Ovilia Any comments here ? :) |
Brief Information
This pull request is in the type of:
What does this PR do?
Add support for dynamically changing themes after chart initialization.
Fixed issues
Details
Before: What was the problem?
Currently, themes can only be set before chart initialization.
There's no way to change themes dynamically after the chart is created.
The deprecated
setThememethod in ECharts 3.0 was removed without a replacement.After: How does it behave after the fixing?
Now users can:
chart.setTheme('themeName')orchart.setTheme(themeObject)for registered themesthemeNameis used, both the themes registered before or after the chart was created can be applied.setOption.Example usage:
Document Info
One of the following should be checked.
Misc
ZRender Changes
Related test cases or examples to use the new APIs
test/theme.htmlOthers
Merging options
Other information
This PR is a pre-requisite for design token feature, with which we can dynamically change themes using design tokens.