Skip to content

Commit c77d7b6

Browse files
committed
fix initial dashboard widget loading
1 parent c31f0fa commit c77d7b6

3 files changed

Lines changed: 110 additions & 44 deletions

File tree

core/Widget/WidgetConfig.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ public function getAction()
126126
* Sets (overwrites) the parameters of the widget. These parameters will be added to the URL when rendering the
127127
* widget. You can access these parameters via `Piwik\Common::getRequestVar(...)`.
128128
*
129+
* This applies to widgets rendered through their controller/action request. Client-rendered widgets do not receive
130+
* these parameters automatically and should instead derive request state from the browser context or load data via
131+
* API requests.
132+
*
129133
* @param array $parameters eg. ('urlparam' => 'urlvalue')
130134
* @return static
131135
*/
@@ -139,6 +143,9 @@ public function setParameters($parameters)
139143
/**
140144
* Add new parameters and only overwrite parameters that have the same name. See {@link setParameters()}
141145
*
146+
* Like {@link setParameters()}, these parameters are only used for widgets rendered through their
147+
* controller/action request and are not forwarded automatically to client-rendered widgets.
148+
*
142149
* @param array $parameters eg. ('urlparam' => 'urlvalue')
143150
* @return static
144151
*/
@@ -357,9 +364,13 @@ public function getMiddlewareParameters()
357364
/**
358365
* Marks this widget as client-rendered by a Vue component exported by the given plugin bundle.
359366
*
367+
* Client-rendered widgets do not execute the widget controller/action in a separate request before rendering.
368+
* They should derive dynamic state from the current browser request or load data through API requests instead.
369+
*
360370
* @param string $plugin eg 'Transitions'
361371
* @param string $component eg 'TransitionsPage'
362372
* @return static
373+
* @since 5.10.0
363374
*/
364375
public function setClientSideComponent(string $plugin, string $component)
365376
{
@@ -373,17 +384,23 @@ public function setClientSideComponent(string $plugin, string $component)
373384

374385
/**
375386
* Returns the configured client-rendered component definition.
387+
* @return array{}|array{plugin: string, name: string}
388+
* @since 5.10.0
376389
*/
377390
public function getClientSideComponent(): array
378391
{
379392
return $this->clientSideComponent;
380393
}
381394

382395
/**
383-
* Sets props that should be passed to the client-rendered Vue widget.
396+
* Sets static props that should be passed to the client-rendered Vue widget.
397+
*
398+
* Use this for configuration known when the widget is registered. Request-specific widget parameters are not
399+
* forwarded to client-rendered widgets through this mechanism.
384400
*
385401
* @param array $props
386402
* @return static
403+
* @since 5.10.0
387404
*/
388405
public function setClientSideProps(array $props)
389406
{
@@ -394,6 +411,8 @@ public function setClientSideProps(array $props)
394411

395412
/**
396413
* Returns props configured for the client-rendered Vue widget.
414+
* @return array
415+
* @since 5.10.0
397416
*/
398417
public function getClientSideProps(): array
399418
{

plugins/CoreHome/vue/src/Widget/ClientWidgetRenderer.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ export default defineComponent({
107107
108108
this.componentToRender = markRaw(component as Component);
109109
} catch (e) {
110+
console.error(e);
110111
this.loadingFailed = true;
111112
} finally {
112113
this.loading = false;

plugins/Dashboard/javascripts/widgetMenu.js

Lines changed: 89 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ widgetsHelper.firstGetAvailableWidgetsCall = null;
2020
*/
2121
widgetsHelper.getAvailableWidgets = function (callback) {
2222

23+
if (!widgetsHelper.availableWidgets && widgetsHelper.firstGetAvailableWidgetsCall) {
24+
widgetsHelper.firstGetAvailableWidgetsCall.then(function () {
25+
if (callback) {
26+
callback(widgetsHelper.availableWidgets);
27+
}
28+
});
29+
30+
return widgetsHelper.firstGetAvailableWidgetsCall;
31+
}
32+
2333
function mergeCategoriesAndSubCategories(availableWidgets)
2434
{
2535
var categorized = {};
@@ -113,6 +123,8 @@ widgetsHelper.getAvailableWidgets = function (callback) {
113123
callback(widgetsHelper.availableWidgets);
114124
}
115125
});
126+
127+
return promise;
116128
};
117129

118130
/**
@@ -121,6 +133,7 @@ widgetsHelper.getAvailableWidgets = function (callback) {
121133
*/
122134
widgetsHelper.clearAvailableWidgets = function () {
123135
delete widgetsHelper.availableWidgets;
136+
widgetsHelper.firstGetAvailableWidgetsCall = null;
124137
};
125138

126139
/**
@@ -131,17 +144,25 @@ widgetsHelper.clearAvailableWidgets = function () {
131144
*/
132145
widgetsHelper.getWidgetObjectFromUniqueId = function (uniqueId, callback) {
133146
widgetsHelper.getAvailableWidgets(function(widgets){
134-
for (var widgetCategory in widgets) {
135-
var widgetInCategory = widgets[widgetCategory];
136-
for (var i in widgetInCategory) {
137-
if (widgetInCategory[i]["uniqueId"] == uniqueId) {
138-
callback(widgetInCategory[i]);
139-
return;
140-
}
147+
callback(widgetsHelper.findWidgetObjectFromUniqueId(uniqueId, widgets));
148+
});
149+
};
150+
151+
widgetsHelper.findWidgetObjectFromUniqueId = function (uniqueId, widgets) {
152+
if (!widgets) {
153+
return false;
154+
}
155+
156+
for (var widgetCategory in widgets) {
157+
var widgetInCategory = widgets[widgetCategory];
158+
for (var i in widgetInCategory) {
159+
if (widgetInCategory[i]["uniqueId"] == uniqueId) {
160+
return widgetInCategory[i];
141161
}
142162
}
143-
callback(false);
144-
});
163+
}
164+
165+
return false;
145166
};
146167

147168
/**
@@ -174,51 +195,76 @@ widgetsHelper.loadWidgetAjax = function (widgetUniqueId, widgetParameters, onWid
174195

175196
widgetParameters['widget'] = 1;
176197

177-
var clientWidgetRequest = {
178-
abort: function () {}
198+
var widgetRequest = {
199+
aborted: false,
200+
ajaxRequest: null,
201+
abort: function () {
202+
this.aborted = true;
203+
if (this.ajaxRequest) {
204+
this.ajaxRequest.abort();
205+
}
206+
}
179207
};
180208

181-
var clientWidget = null;
182-
if (widgetsHelper.availableWidgets) {
183-
for (var widgetCategory in widgetsHelper.availableWidgets) {
184-
if (!widgetsHelper.availableWidgets.hasOwnProperty(widgetCategory)) {
185-
continue;
209+
function loadLegacyWidget() {
210+
if (widgetRequest.aborted) {
211+
return;
212+
}
213+
214+
var ajaxRequest = new ajaxHelper();
215+
ajaxRequest.addParams(widgetParameters, 'get');
216+
ajaxRequest.setCallback(function () {
217+
if (widgetRequest.aborted) {
218+
return;
186219
}
187220

188-
var widgets = widgetsHelper.availableWidgets[widgetCategory];
189-
for (var index in widgets) {
190-
if (widgets.hasOwnProperty(index) && widgets[index]["uniqueId"] == widgetUniqueId) {
191-
clientWidget = widgets[index];
192-
break;
221+
onWidgetLoadedCallback.apply(this, arguments);
222+
});
223+
if (onWidgetErrorCallback) {
224+
ajaxRequest.setErrorCallback(function () {
225+
if (widgetRequest.aborted) {
226+
return;
193227
}
194-
}
195228

196-
if (clientWidget) {
197-
break;
198-
}
229+
onWidgetErrorCallback.apply(this, arguments);
230+
});
199231
}
232+
ajaxRequest.setFormat('html');
233+
ajaxRequest.send();
234+
widgetRequest.ajaxRequest = ajaxRequest;
200235
}
201236

202-
if (clientWidget && clientWidget.clientComponent) {
203-
clientWidget = $.extend(true, {}, clientWidget);
204-
clientWidget.parameters = $.extend({}, clientWidget.parameters, widgetParameters);
237+
var metadataReady = widgetsHelper.availableWidgets
238+
? Promise.resolve()
239+
: widgetsHelper.getAvailableWidgets();
205240

206-
var html = '<div vue-entry="CoreHome.Widget"'
207-
+ ' widget="' + piwikHelper.htmlEntities(JSON.stringify(clientWidget)) + '"'
208-
+ ' widgetized="true"></div>';
209-
onWidgetLoadedCallback(html);
210-
return clientWidgetRequest;
211-
}
241+
metadataReady.then(function () {
242+
if (widgetRequest.aborted) {
243+
return;
244+
}
212245

213-
var ajaxRequest = new ajaxHelper();
214-
ajaxRequest.addParams(widgetParameters, 'get');
215-
ajaxRequest.setCallback(onWidgetLoadedCallback);
216-
if (onWidgetErrorCallback) {
217-
ajaxRequest.setErrorCallback(onWidgetErrorCallback);
218-
}
219-
ajaxRequest.setFormat('html');
220-
ajaxRequest.send();
221-
return ajaxRequest;
246+
var clientWidget = widgetsHelper.findWidgetObjectFromUniqueId(
247+
widgetUniqueId,
248+
widgetsHelper.availableWidgets
249+
);
250+
251+
if (clientWidget && clientWidget.clientComponent) {
252+
clientWidget = $.extend(true, {}, clientWidget);
253+
clientWidget.parameters = $.extend({}, clientWidget.parameters, widgetParameters);
254+
255+
var html = '<div vue-entry="CoreHome.Widget"'
256+
+ ' widget="' + piwikHelper.htmlEntities(JSON.stringify(clientWidget)) + '"'
257+
+ ' widgetized="true"></div>';
258+
onWidgetLoadedCallback(html);
259+
return;
260+
}
261+
262+
loadLegacyWidget();
263+
}).catch(function () {
264+
loadLegacyWidget();
265+
});
266+
267+
return widgetRequest;
222268
};
223269

224270
(function ($, require) {

0 commit comments

Comments
 (0)