英文:
Trouble passing filter (viz-filter) value from Angular application to embedded web-component Tableau report
问题
I'm having trouble embedding and configuring a Tableau report inside of an Angular 15 app. What I want to do is just pass an initial filter value, and then show the report. Here is a code sample of a report running properly in regular old HTML/JS:
这里我在嵌入和配置Tableau报告到Angular 15应用程序时遇到了问题。我想要做的是只传递一个初始筛选值,然后显示报告。以下是在常规的HTML/JS中正确运行的报告示例:
This works fine. The report loads, filtered to the proper channelGrouping. However, if I bring this into an Angular context, I cannot get the dynamic value I want to pass to viz-filter
to bind. This has gotta be something silly, but I can't figure it out.
这个工作得很好。报告加载并进行了适当的channelGrouping筛选。然而,如果将其引入到Angular上下文中,我无法获取要绑定到 viz-filter
的动态值。这肯定是一些愚蠢的问题,但我想不出来。
In my Angular application, I'm including this in my index.html
page (note that it's different from the referenced version in the above example):
在我的Angular应用程序中,我在index.html
页面中包含了这个(注意,这与上面示例中引用的版本不同):
<script type="module" src="https://embedding.tableauusercontent.com/tableau.embedding.3.1.0.min.js"></script>
...and then this is the Angular component:
然后这是Angular组件:
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core';
@Component({
selector: 'app-tableau-embed',
standalone: true,
imports: [CommonModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `<tableau-viz
id="tableau1"
src="https://public.tableau.com/views/DigitalMarketingWebTraffic/Cockpit?:language=en-US&:display_count=n&:origin=viz_share_link">
</tableau-viz>`,
styleUrls: [],
})
export class TableauEmbedComponent {}
This works fine.
这个工作得很好。
But as soon as I introduce viz-filter
, I start to run into weird problems. For example, this works:
但是,一旦我引入 viz-filter
,我开始遇到奇怪的问题。例如,这个可以工作:
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core';
@Component({
selector: 'app-tableau-embed',
standalone: true,
imports: [CommonModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `<tableau-viz
id="tableau1"
src="https://public.tableau.com/views/DigitalMarketingWebTraffic/Cockpit?:language=en-US&:display_count=n&:origin=viz_share_link">
<viz-filter field="ga:channelGrouping" value="Direct" />
</tableau-viz>`,
styleUrls: [],
})
export class TableauEmbedComponent {}
However, if I try to bind the value
attribute to an Angular class variable, like this:
然而,如果我尝试将 value
属性绑定到一个Angular类变量,就像这样:
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core';
@Component({
selector: 'app-tableau-embed',
standalone: true,
imports: [CommonModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `<tableau-viz
id="tableau1"
src="https://public.tableau.com/views/DigitalMarketingWebTraffic/Cockpit?:language=en-US&:display_count=n&:origin=viz_share_link">
<viz-filter field="ga:channelGrouping" [value]="channelGrouping" />
</tableau-viz>`,
styleUrls: [],
})
export class TableauEmbedComponent {
channelGrouping = 'Direct';
}
...it doesn't work. In the DOM inspector, what I see is literally this:
...但是它不起作用。在DOM检查器中,我看到的实际上是这样的:
The entire value
attribute just doesn't show up. I've tried this:
整个 value
属性都没有显示出来。我尝试过这个:
<viz-filter field="user_id" [attr.value]="channelGrouping" />
...which seems like it does the right thing, in that the DOM inspector shows the value
attribute, with the proper value passed along:
...看起来似乎做了正确的事情,因为DOM检查器显示了带有正确值的 value
属性:
...but Tableau doesn't appear to like that because the Tableau report ignores that value, which makes me think it's not really getting passed to Tableau, for whatever reason. (Again, if I hard-code that value in viz-filter
, then Tableau behaves properly.)
...但是Tableau似乎不喜欢这样做,因为Tableau报告忽略了该值,这让我觉得它实际上没有传递给Tableau,不管出于什么原因。 (再次强调,如果我在 viz-filter
中硬编码该值,那么Tableau会正常工作。)
I've also tried this:
我还尝试过这个:
<viz-filter field="ga:channelGrouping" value="{{ channelGrouping }}" />
and:
和:
<viz-filter field="ga:channelGrouping" attr.value="{{ channelGrouping }}" />
...but I get the same issue where I just see <viz-filter field="ga:channelGrouping" />
.
...但是我遇到了同样的问题,我只看到 <viz-filter field="ga:channelGrouping" />
。
And for the sake of completion, if I do this:
出于完整性的考虑,如果我这样做:
<tableau-viz
id="tableauViz"
src="https://public.tableau.com/views/DigitalMarketingWebTraffic/Cockpit?:language=en-US&:display_count=n&:origin=viz_share_link">
<viz-filter field="ga:channelGrouping" [value]="channelGrouping" />
<div [id]="channelGrouping"></div>
</tableau-viz>
英文:
I'm having trouble embedding and configuring a Tableau report inside of an Angular 15 app. What I want to do is just pass an initial filter value, and then show the report. Here is a code sample of a report running properly in regular old HTML/JS:
<!DOCTYPE html>
<html lang="en">
<head>
<script type="module" src="https://public.tableau.com/javascripts/api/tableau.embedding.3.latest.js"></script>
</head>
<body>
<tableau-viz id="tableauViz" src="https://public.tableau.com/views/DigitalMarketingWebTraffic/Cockpit?:language=en-US&:display_count=n&:origin=viz_share_link">
<viz-filter field="ga:channelGrouping" value="Social" />
</tableau-viz>
</body>
</html>
This works fine. The report loads, filtered to the proper channelGrouping. However, if I bring this into an Angular context, I cannot get the dynamic value I want to pass to viz-filter
to bind. This has gotta be something silly, but I can't figure it out.
In my Angular application, I'm including this in my index.html
page (note that it's different from the referenced version in the above example):
<script type="module"
src="https://embedding.tableauusercontent.com/tableau.embedding.3.1.0.min.js"></script>
...and then this is the Angular component:
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core';
@Component({
selector: 'app-tableau-embed',
standalone: true,
imports: [CommonModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `<tableau-viz
id="tableau1"
src="https://public.tableau.com/views/DigitalMarketingWebTraffic/Cockpit?:language=en-US&:display_count=n&:origin=viz_share_link">
</tableau-viz>`,
styleUrls: [],
})
export class TableauEmbedComponent {}
This works fine.
But as soon as I introduce viz-filter
, I start to run into weird problems. For example, this works:
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core';
@Component({
selector: 'app-tableau-embed',
standalone: true,
imports: [CommonModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `<tableau-viz
id="tableau1"
src="https://public.tableau.com/views/DigitalMarketingWebTraffic/Cockpit?:language=en-US&:display_count=n&:origin=viz_share_link">
<viz-filter field="ga:channelGrouping" value="Direct" />
</tableau-viz>`,
styleUrls: [],
})
export class TableauEmbedComponent {}
However, if I try to bind the value
attribute to an Angular class variable, like this:
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core';
@Component({
selector: 'app-tableau-embed',
standalone: true,
imports: [CommonModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `<tableau-viz
id="tableau1"
src="https://public.tableau.com/views/DigitalMarketingWebTraffic/Cockpit?:language=en-US&:display_count=n&:origin=viz_share_link">
<viz-filter field="ga:channelGrouping" [value]="channelGrouping" />
</tableau-viz>`,
styleUrls: [],
})
export class TableauEmbedComponent {
channelGrouping = 'Direct';
}
...it doesn't work. In the DOM inspector, what I see is literally this:
The entire value
attribute just doesn't show up. I've tried this:
<viz-filter field="user_id" [attr.value]="channelGrouping" />
...which seems like it does the right thing, in that the DOM inspector shows the value
attribute, with the proper value passed along:
...but Tableau doesn't appear to like that because the Tableau report ignores that value, which makes me think it's not really getting passed to Tableau, for whatever reason. (Again, if I hard-code that value in viz-filter
, then Tableau behaves properly.)
I've also tried this:
<viz-filter field="ga:channelGrouping" value="{{ channelGrouping }}" />
and:
<viz-filter field="ga:channelGrouping" attr.value="{{ channelGrouping }}" />
...but I get the same issue where I just see <viz-filter field="ga:channelGrouping" />
.
And for the sake of completion, if I do this:
<tableau-viz
id="tableauViz"
src="https://public.tableau.com/views/DigitalMarketingWebTraffic/Cockpit?:language=en-US&:display_count=n&:origin=viz_share_link">
<viz-filter field="ga:channelGrouping" [value]="channelGrouping" />
<div [id]="channelGrouping"></div>
</tableau-viz>
I see this:
...where the class variable channelGrouping
is clearly bound to the id attribute of the div
tag which is a sibling to the viz-filter
tag.
Here's the start of a Stackblitz which has all the code that should be required for this to run--but of course for some reason this code is not actually working in Stackblitz.
https://stackblitz.com/edit/angular-uch2bk?file=src%2Ftableau-embed.ts
答案1
得分: 0
以下是您要翻译的代码部分:
For anybody who stumbles across this, I've figured out one approach that works. I can't say with 100% certainty that this is the "right" way to do this, though it probably is.
The issue is that Angular treats the `value` attribute on `viz-filter` differently than the `src` and `id` attributes on `tableau-viz`. In short, you can't use data binding on `value` in `viz-filter`, and so need to use `Renderer2` to grab that component's native element, and then call `setAttribute` against that. Here's the complete code of the Angular component I'm using to embed a Tableau dashboard:
import { CommonModule } from '@angular/common';
import {
AfterViewInit,
CUSTOM_ELEMENTS_SCHEMA,
Component,
ElementRef,
HostListener,
Input,
OnInit,
Renderer2,
SimpleChanges,
ViewChild,
inject,
} from '@angular/core';
@Component({
selector: 'app-tableau-embed',
standalone: true,
imports: [CommonModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `<div #wrapper>
<tableau-viz
[id]="vizIndex"
[src]="url"
width="{{ screenWidth }}"
hide-tabs
toolbar="hidden">
<viz-filter #vizFilterUserId field="user_id"></viz-filter>
</tableau-viz>
</div>`,
styleUrls: [],
})
export class TableauEmbedComponent implements AfterViewInit, OnInit {
private renderer = inject(Renderer2);
@Input() dashboardIndex = 0;
@Input() toolbar = 'hidden';
@Input() url = '';
@Input() user_id = '';
@ViewChild('wrapper') wrapperElement?: ElementRef<HTMLElement>;
@ViewChild('vizFilterUserId', { static: false }) vizFilterUserId?: ElementRef;
@HostListener('window:resize', ['$event'])
onWindowResize() {
this.calculateDashboardSize();
}
initialized = false;
screenWidth: number = 0;
vizIndex = `Tableau-Viz-${this.dashboardIndex}`;
calculateDashboardSize = () => {
const bufferSize = 25;
this.screenWidth =
this.wrapperElement?.nativeElement.offsetWidth || 0 - bufferSize;
};
ngOnInit(): void {
this.calculateDashboardSize();
}
ngAfterViewInit(): void {
if (this.vizFilterUserId) {
this.renderer.setAttribute(
this.vizFilterUserId.nativeElement,
'value',
this.user_id
);
}
}
}
英文:
For anybody who stumbles across this, I've figured out one approach that works. I can't say with 100% certainty that this is the "right" way to do this, though it probably is.
The issue is that Angular treats the value
attribute on viz-filter
differently than the src
and id
attributes on tableau-viz
. In short, you can't use data binding on value
in viz-filter
, and so need to use Renderer2
to grab that component's native element, and then call setAttribute
against that. Here's the complete code of the Angular component I'm using to embed a Tableau dashboard:
import { CommonModule } from '@angular/common';
import {
AfterViewInit,
CUSTOM_ELEMENTS_SCHEMA,
Component,
ElementRef,
HostListener,
Input,
OnInit,
Renderer2,
SimpleChanges,
ViewChild,
inject,
} from '@angular/core';
@Component({
selector: 'app-tableau-embed',
standalone: true,
imports: [CommonModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `<div #wrapper>
<tableau-viz
[id]="vizIndex"
[src]="url"
width="{{ screenWidth }}"
hide-tabs
toolbar="hidden">
<viz-filter #vizFilterUserId field="user_id"></viz-filter>
</tableau-viz>
</div>`,
styleUrls: [],
})
export class TableauEmbedComponent implements AfterViewInit, OnInit {
private renderer = inject(Renderer2);
@Input() dashboardIndex = 0;
@Input() toolbar = 'hidden';
@Input() url = '';
@Input() user_id = '';
@ViewChild('wrapper') wrapperElement?: ElementRef<HTMLElement>;
@ViewChild('vizFilterUserId', { static: false }) vizFilterUserId?: ElementRef;
@HostListener('window:resize', ['$event'])
onWindowResize() {
this.calculateDashboardSize();
}
initialized = false;
screenWidth: number = 0;
vizIndex = `Tableau-Viz-${this.dashboardIndex}`;
calculateDashboardSize = () => {
const bufferSize = 25;
this.screenWidth =
this.wrapperElement?.nativeElement.offsetWidth || 0 - bufferSize;
};
ngOnInit(): void {
this.calculateDashboardSize();
}
ngAfterViewInit(): void {
if (this.vizFilterUserId) {
this.renderer.setAttribute(
this.vizFilterUserId.nativeElement,
'value',
this.user_id
);
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论