Skip to content

Commit db95e18

Browse files
committed
Merged PR 101002: Fixing bootstrap bug.
Fixing a bug where /report/embed message is sent before bootstrap is complete. **Repo:** 1) Call bootstrap. 2) Call report.embed immediately. **Timing:** 1) Bootstrap starts. 2) iframe is not loaded yet ('load') event is not fired to the SDK. 3) powerbi.embed starts 4) powerbi.embed calls embedExisting because iframe already exists - it's fine. 5) embedExisting calls embed::load method which sends /report/load message to the SDK 6) iframe is loaded (bootstrap is done). 7) ('load') event handler calls embed::load method which sends /report/load message to the SDK - AGAIN **Expected:** /report/load is called one time only. **Acutal** /report/load is called twice - first time before iframe is ready is a BUG. **Fix** Adding a flag which saves the state of the iframe (loaded/not loaded). If iframe is not loaded, avoid sending the postMessage. We need the load method to send the right config when it's called from 'load' event. For that, we save the last config per component, and post the load message with that config.
1 parent e81fb4d commit db95e18

File tree

9 files changed

+164
-79
lines changed

9 files changed

+164
-79
lines changed

dist/powerbi-client.d.ts

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*! powerbi-client v2.13.2 | (c) 2016 Microsoft Corporation MIT */
1+
/*! powerbi-client v2.13.3 | (c) 2016 Microsoft Corporation MIT */
22
declare module "util" {
33
import { HttpPostMessage } from 'http-post-message';
44
/**
@@ -259,6 +259,15 @@ declare module "embed" {
259259
* @hidden
260260
*/
261261
iframe: HTMLIFrameElement;
262+
/**
263+
* Saves the iframe state. Each iframe should be loaded only once.
264+
* After first load, .embed will go into embedExisting path which will send
265+
* a postMessage of /report/load instead of creating a new iframe.
266+
*
267+
* @type {boolean}
268+
* @hidden
269+
*/
270+
iframeLoaded: boolean;
262271
/**
263272
* Gets or sets the configuration settings for the Power BI embed component.
264273
*
@@ -376,7 +385,7 @@ declare module "embed" {
376385
* @param {boolean} phasedRender
377386
* @returns {Promise<void>}
378387
*/
379-
load(config: IEmbedConfigurationBase, phasedRender?: boolean): Promise<void>;
388+
load(phasedRender?: boolean): Promise<void>;
380389
/**
381390
* Removes one or more event handlers from the list of handlers.
382391
* If a reference to the existing handle function is specified, remove the specific handler.
@@ -1479,7 +1488,7 @@ declare module "visual" {
14791488
* @hidden
14801489
*/
14811490
constructor(service: service.Service, element: HTMLElement, baseConfig: embed.IEmbedConfigurationBase, phasedRender?: boolean, isBootstrap?: boolean, iframe?: HTMLIFrameElement);
1482-
load(baseConfig: embed.IEmbedConfigurationBase, phasedRender?: boolean): Promise<void>;
1491+
load(phasedRender?: boolean): Promise<void>;
14831492
/**
14841493
* Gets the list of pages within the report - not supported in visual embed.
14851494
*

dist/powerbi.js

+27-16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/powerbi.min.js

+4-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "powerbi-client",
3-
"version": "2.13.2",
3+
"version": "2.13.3",
44
"description": "JavaScript library for embedding Power BI into your apps. Provides service which makes it easy to embed different types of components and an object model which allows easy interaction with these components such as changing pages, applying filters, and responding to data selection.",
55
"main": "dist/powerbi.js",
66
"typings": "dist/powerbi-client.d.ts",

src/config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/** @ignore *//** */
22
const config = {
3-
version: '2.13.2',
3+
version: '2.13.3',
44
type: 'js'
55
};
66

src/embed.ts

+35-14
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,16 @@ export abstract class Embed {
175175
*/
176176
iframe: HTMLIFrameElement;
177177

178+
/**
179+
* Saves the iframe state. Each iframe should be loaded only once.
180+
* After first load, .embed will go into embedExisting path which will send
181+
* a postMessage of /report/load instead of creating a new iframe.
182+
*
183+
* @type {boolean}
184+
* @hidden
185+
*/
186+
iframeLoaded: boolean;
187+
178188
/**
179189
* Gets or sets the configuration settings for the Power BI embed component.
180190
*
@@ -244,6 +254,7 @@ export abstract class Embed {
244254
this.service = service;
245255
this.element = element;
246256
this.iframe = iframe;
257+
this.iframeLoaded = false;
247258
this.embedtype = config.type.toLowerCase();
248259

249260
this.populateConfig(config, isBootstrap);
@@ -358,27 +369,32 @@ export abstract class Embed {
358369
* @param {boolean} phasedRender
359370
* @returns {Promise<void>}
360371
*/
361-
load(config: IEmbedConfigurationBase, phasedRender?: boolean): Promise<void> {
362-
if (!config.accessToken) {
372+
load(phasedRender?: boolean): Promise<void> {
373+
if (!this.config.accessToken) {
374+
console.debug("Power BI SDK iframe is loaded but powerbi.embed is not called yet.");
375+
return;
376+
}
377+
378+
if (!this.iframeLoaded) {
379+
console.debug("Power BI SDK is trying to post /report/load before iframe is ready.");
363380
return;
364381
}
365382

366-
const path = phasedRender && config.type === 'report' ? this.phasedLoadPath : this.loadPath;
383+
const path = phasedRender && this.config.type === 'report' ? this.phasedLoadPath : this.loadPath;
367384
const headers = {
368385
uid: this.config.uniqueId,
369386
sdkSessionId: this.service.getSdkSessionId(),
370387
bootstrapped: this.config.bootstrapped,
371388
sdkVersion: sdkConfig.default.version
372389
};
373390

374-
return this.service.hpm.post<void>(path, config, headers, this.iframe.contentWindow)
391+
return this.service.hpm.post<void>(path, this.config, headers, this.iframe.contentWindow)
375392
.then(response => {
376-
utils.assign(this.config, config);
377393
return response.body;
378394
},
379-
response => {
380-
throw response.body;
381-
});
395+
response => {
396+
throw response.body;
397+
});
382398
}
383399

384400
/**
@@ -455,7 +471,7 @@ export abstract class Embed {
455471
* ```
456472
*/
457473
reload(): Promise<void> {
458-
return this.load(this.config);
474+
return this.load();
459475
}
460476

461477
/**
@@ -720,10 +736,15 @@ export abstract class Embed {
720736
}
721737
}
722738

723-
this.iframe.addEventListener('load', () => this.load(this.config, phasedRender), false);
739+
this.iframe.addEventListener('load', () => {
740+
this.iframeLoaded = true;
741+
this.load(phasedRender);
742+
}, false);
724743

725744
if (this.service.getNumberOfComponents() <= Embed.maxFrontLoadTimes) {
726-
this.frontLoadHandler = () => this.frontLoadSendConfig(this.config);
745+
this.frontLoadHandler = () => {
746+
this.frontLoadSendConfig(this.config);
747+
}
727748

728749
// 'ready' event is fired by the embedded element (not by the iframe)
729750
this.element.addEventListener('ready', this.frontLoadHandler, false);
@@ -806,9 +827,9 @@ export abstract class Embed {
806827
if (this.iframe.contentWindow == null)
807828
return;
808829

809-
return this.service.hpm.post<void>("/frontload/config", config, { uid: this.config.uniqueId }, this.iframe.contentWindow).then(response => {
810-
return response.body;
811-
},
830+
return this.service.hpm.post<void>("/frontload/config", config, { uid: this.config.uniqueId }, this.iframe.contentWindow).then(response => {
831+
return response.body;
832+
},
812833
response => {
813834
throw response.body;
814835
});

src/service.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,8 @@ export class Service implements IService {
401401
*/
402402
if(config.type === "report" && component.config.type === "create") {
403403
const report = new Report(this, element, config, /* phasedRender */ false, /* isBootstrap */ false, element.powerBiEmbed.iframe);
404-
report.load(config);
404+
component.populateConfig(config, /* isBootstrap */ false);
405+
report.load();
405406
element.powerBiEmbed = report;
406407

407408
this.addOrOverwriteEmbed(component, element);
@@ -413,7 +414,7 @@ export class Service implements IService {
413414
}
414415

415416
component.populateConfig(config, /* isBootstrap */ false);
416-
component.load(component.config, phasedRender);
417+
component.load(phasedRender);
417418

418419
return component;
419420
}

src/visual.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ export class Visual extends Report {
3131
super(service, element, baseConfig, phasedRender, isBootstrap, iframe);
3232
}
3333

34-
load(baseConfig: embed.IEmbedConfigurationBase, phasedRender?: boolean): Promise<void> {
35-
var config = <embed.IVisualEmbedConfiguration>baseConfig;
34+
load(phasedRender?: boolean): Promise<void> {
35+
var config = <embed.IVisualEmbedConfiguration>this.config;
3636

3737
if (!config.accessToken) {
3838
// bootstrap flow.
@@ -88,7 +88,8 @@ export class Visual extends Report {
8888
pagesLayout: pagesLayout
8989
};
9090

91-
return super.load(config, phasedRender);
91+
this.config = config;
92+
return super.load(phasedRender);
9293
}
9394

9495
/**

0 commit comments

Comments
 (0)