Gumroad嵌入式小工具以角度中断导航



我想在我的Angular应用程序中包括Gumroad的嵌入小工具。

我尝试过的:

我把它添加到我的索引中。html:

<script src="https://gumroad.com/js/gumroad-embed.js"></script>

我把它添加到我的一个组件模板中:

<div class="gumroad-product-embed" data-gumroad-product-id="demo"><a href="https://gumroad.com/l/demo">Loading...</a></div>

问题:

比方说/buy页上的组件。如果打开/buy,则会出现小部件。然而,如果我已经通过应用程序中的一些routerLink导航到/buy,那么小部件不会出现,它只是不断显示"正在加载…"。

我还通过将routerLink更改为href来证实这一点,并且它起到了作用。也就是说,这个问题可能与删除和重新添加分区有关

无论如何,我的问题是:如何在Angular应用程序中正确使用Gumroad的嵌入式小工具?

注意:

1-查看gumroad-embed.js可能很有用。它有类似MutationObserver的东西,但我无法跟上。

2-Angular应用程序在electronjs上运行,以防产生影响(由于MutationObserver或其他原因(。

在有人给出更好的答案之前,这里有一个解决方案,特别适用于当前的gumroad-embed.js。在未来,它可能会改变,答案将是过时的。不管怎样,它在这里:

解决方案

1( 在/assets下添加一个文件edited-gumroad-embed.js,其中包含以下代码:

function createGumroadEmbed(){window.GumroadEmbed||(window.GumroadEmbed=new GumroadEmbedManager)}function receiveMessage(t){var e={};if(t.data)try{e=JSON.parse(t.data)}catch(r){}if("GumroadEmbedMessage"===e.type&&GumroadEmbed){var i=GumroadEmbed.findEmbed(e.args.id)||GumroadEmbed.findEmbed(e.args.unique_id);i&&("setHeight"===e.action?i.setHeight(e.args.height):"scrollToTop"===e.action&&i.scrollToTop())}}!function(){var n=!1,a=/xyz/.test(function(){})?/b_superb/:/.*/;this._GumroadClass=function(){},_GumroadClass.extend=function(t){function e(){!n&&this.init&&this.init.apply(this,arguments)}var o=this.prototype;n=!0;var i=new this;for(var r in n=!1,t)i[r]="function"==typeof t[r]&&"function"==typeof o[r]&&a.test(t[r])?function(i,r){return function(){var t=this._super;this._super=o[i];var e=r.apply(this,arguments);return this._super=t,e}}(r,t[r]):t[r];return e.prototype=i,(e.prototype.constructor=e).extend=arguments.callee,e}}();var GumroadClass=_GumroadClass.extend({setEnvironment:function(){this.environment="production",this.domain="https://gumroad.com",this.isMobile=navigator.userAgent.match(/Mobile[/; ]/i)||navigator.userAgent.match(/Opera (Mini|Mobi)/i)||navigator.userAgent.match(/IEMobile/i),this[this.environment]=!0,this.origin=window.location.protocol+"//"+window.location.hostname+(window.location.port?":"+window.location.port:"")},startNodeAdditionObserver:function(){MutationObserver&&(this.nodeAdditionObserver=new MutationObserver(function(t){for(var e=0;e<t.length;e++)for(var i=0;i<t[e].addedNodes.length;i++)this.nodeAdditionCallback&&this.nodeAdditionCallback(t[e].addedNodes[i])}.bind(this)),this.nodeAdditionObserver.observe(document.body,{childList:!0,subtree:!0}))}}),GumroadEmbedElement=GumroadClass.extend({init:function(t,e){this.manager=e;var i=t.getAttribute("data-gumroad-product-id");i&&(this.div=t,this.id=i,this.opts={as_embed:"true",referrer:document.referrer,origin:this.manager.origin},this.manager.embeds.push(this),this.show())},buildUrl:function(){var t=(this.manager.domain||"")+"/l/"+this.id+"?";for(var e in this.outboundEmbed&&(this.opts.outbound_embed="true"),this.opts)this.opts.hasOwnProperty(e)&&(t+="&"+e+"="+this.opts[e]);return t},createIframe:function(){this.iframe=document.createElement("iframe"),this.iframe.allowtransparency=!0,this.iframe.setAttribute("allowFullScreen","allowfullscreen"),this.iframe.className="gumroad-embed-iframe",this.iframe.scrolling="no",this.iframe.width="100%",this.iframe.height=0,this.iframe.id="gumroad-embed-iframe-"+this.id,this.iframe.setAttribute("style","display: block !important; border: none !important; margin: 0 auto !important; padding: 0 !important; max-width: 676px !important;"),this.div.parentNode.insertBefore(this.iframe,this.div)},scrollToTop:function(){this.iframe&&this.manager.isMobile&&window.scrollTo(0,this.iframe.offsetTop)},setHeight:function(t){this.div.style.display="none",this.iframe.setAttribute("height",t)},show:function(){this.iframe||this.createIframe();this.id=this.div.getAttribute("data-gumroad-product-id"),this.outboundEmbed=!!this.div.getAttribute("data-outbound-embed"),this.iframe.setAttribute("src",this.buildUrl())}}),GumroadEmbedManager=GumroadClass.extend({init:function(){this.setEnvironment(),this.createEmbeds()},createEmbeds:function(){this.embeds=[];for(var t=document.getElementsByClassName("gumroad-product-embed"),e=0;e<t.length;e++)new GumroadEmbedElement(t[e],this)},findEmbed:function(t){for(var e=0;e<this.embeds.length;e++)if(this.embeds[e].id==t)return this.embeds[e];return!1},gotMessage:function(t){var e={};try{e=JSON.parse(t.data)}catch(i){}this[e.action]&&this[e.action](e.args)},reload:function(){for(var t=0;t<this.embeds.length;t++){var e=this.embeds[t].iframe;e&&e.parentNode&&(e.parentNode.removeChild(e),this.embeds[t].div.style.display="")}this.createEmbeds()},scrollToTop:function(t){var e=this.findEmbed(t);e&&e.scrollToTop()},setHeight:function(t,e){var i=this.findEmbed(t);i&&i.setHeight(e)}});window.addEventListener?(window.addEventListener("message",receiveMessage,!1)/*,window.addEventListener("load",createGumroadEmbed)*/):window.attachEvent&&(window.attachEvent("onmessage",receiveMessage,!1)/*,window.attachEvent("onload",createGumroadEmbed)*/);createGumroadEmbed();

2( 在模板包含<div class="gumroad-product-embed" data-gumroad-product-id="demo"><a href="https://gumroad.com/l/demo">Loading...</a></div>的组件中,添加以下代码:

import {AfterContentInit, Component, OnDestroy} from '@angular/core';
@Component({
selector: 'app-buy',
templateUrl: './buy.component.html'
})
export class BuyComponent implements OnDestroy, AfterContentInit {
readonly scriptNode: HTMLScriptElement;
constructor() {
this.scriptNode = document.createElement('script')
this.scriptNode.setAttribute('src','/assets/edited-gumroad-embed.js')
}
ngAfterContentInit() {
document.getElementsByTagName('head')[0].appendChild(this.scriptNode)
}
ngOnDestroy() {
this.scriptNode.remove()
delete window['GumroadEmbed']
}
}

3( 成功!

解释

通过查看gumroad-embed.js,截至2018年11月,似乎只是包含了另一个文件。这个其他文件的代码是edited-gumroad-embed.js的基础。edited-gumroad-embed.js基本上是这个文件,有两个编辑:

  1. 它调用createGumroadEmbed(),而不是直接在load事件上调用它,因为load似乎只在文档的初始加载时触发一次
  2. 它对用于调用createGumroadEmbed()load的事件侦听器进行了注释

对于组件ts文件,它基本上试图尽可能多地模拟脚本的加载,就像它是第一次加载一样。通过对gumroad的代码进行非彻底的检查,似乎要使操作发生,window.GumroadEmbed需要是undefined;这就是它删除CCD_ 24的原因。

开放性问题和注意事项

有足够多的悬而未决的问题让骆驼通过。

  1. 如果Gumroad的原始代码在Chrome而不是electrojs上运行,那么所有这些都需要吗
  2. 我没有对gumroad的代码进行足够的研究,以确定我所做的是否没有副作用。例如,是否存在泄漏?是否存在应该删除的事件侦听器?MutationObserver
  3. 我不知道为什么要调用ngAfterContentInit中的代码。我试图让它在div尽可能多地添加到DOM之后运行
  4. 如果组件被重用(例如,在路由中(,是否需要重新创建scriptNode?由于其他原因,我目前禁用了路由重用

也就是说,我可能根本不打算使用它,但原因与最初的问题无关。原因是我在"网络"选项卡中发现了许多网站(如Facebook(的请求,我不知道这对我的客户有什么影响(如隐私方面(。再次:我不知道,我不熟悉iframes。此外,我有点担心我的解决方案可能会有任何泄漏。

我只添加一个超链接。

我在React中遇到了同样的问题,并提出了这个简单的解决方案:

index.html:

<script src="https://gumroad.com/js/gumroad-embed.js"></script>
<div style="display:none;" id="gumroad-product-embed" class="gumroad-container">
<div class="gumroad-product-embed" data-gumroad-product-id="gwIwi">
<a href="https://gumroad.com/l/gwIwi">Loading...</a>
</div>
</div>
<script defer>
const embed = document.getElementById('gumroad-product-embed')
window.GUMROAD = embed
</script>

<gumroadcomponent>.js:

const gumroad = window.GUMROAD
const container = React.createRef()
export class BuyNUNISYNTH extends React.Component {
componentDidMount() {
gumroad.style.display = "block"
container.current.appendChild(gumroad)
}
render() {
return <div>
<MyNav no_buy_button/>
<div ref={container}></div>
</div>
}
}

这使Gumroad嵌入React之外,因此它只需要加载一次。是React的重新渲染造成了问题。

最新更新