Gumroad Embedded Widget ломается при навигации в угловых - PullRequest
0 голосов
/ 27 ноября 2018

Я хочу включить Gumroad's Embed Виджеты в мое угловое приложение.

Что я пробовал:

Я добавил это в свой index.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, виджет появится.Однако, если я перешел на /buy через какое-то routerLink в приложении, виджет не появляется, он просто продолжает отображать «Загрузка ...».

Я также подтвердил это, изменивrouterLink до href и все заработало.То есть проблема, вероятно, связана с удалением и повторным добавлением div.

В любом случае, мой вопрос: как правильно использовать виджет Gumroad Embed Widget внутри приложения Angular?

Примечания:

1- Может быть полезно взглянуть на gumroad-embed.js.У него есть что-то вроде MutationObserver, но я не могу следовать за ним.

2- Приложение Angular работает на electronicjs, на случай, если что-то изменится (из-за MutationObserver или чего-то еще).

1 Ответ

0 голосов
/ 28 ноября 2018

Пока кто-то не придумает лучшего ответа, вот решение, которое работает , в частности с текущим gumroad-embed.js.В будущем это может измениться, и ответ будет устаревшим.В любом случае, вот оно:

Решение

1) Добавьте файл edited-gumroad-embed.js под вашим /assets, содержащий следующий код:

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_super\b/:/.*/;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 , чтобы просто включить другой файл.Код этого другого файла является основой, на которой основан edited-gumroad-embed.js.edited-gumroad-embed.js - это, в основном, этот файл с двумя правками:

  1. Он вызывает createGumroadEmbed() вместо прямого вызова события load, потому что load запускается один раз только при начальной загрузкеdocument.
  2. Он комментирует прослушиватель событий для load, который раньше вызывал createGumroadEmbed().

Для файла компонента ts он в основном пытается симулировать загрузку скриптакак можно больше, как если бы это была первая загрузка.При неполном взгляде на код gumroad, кажется, что для выполнения действий window.GumroadEmbed должно быть undefined;вот почему он удаляет window['GumroadEmbed'].

Открытые вопросы и предостережения

Там достаточно открытых вопросов, чтобы пропустить верблюда.

  1. Все это даже нужноили должен был бы быть успешным исходный код Gumroad, если бы он запускался на Chrome вместо electronicjs?
  2. Я недостаточно изучил код gumroad, чтобы узнать, не имеет ли то, что я делаю, побочные эффекты.Например, есть ли утечки?есть слушатели событий, которые должны быть удалены?Как насчет MutationObserver (s)?
  3. Я не уверен, почему я вызываю код в ngAfterContentInit.Я пытаюсь заставить его работать после того, как div было добавлено в DOM, насколько это возможно.
  4. Если компонент используется повторно (например, в маршрутизации), нужно ли его заново создаватьscriptNode?В настоящее время у меня в любом случае отключено повторное использование маршрутизации по другим причинам.

Тем не менее, я, вероятно, не собираюсь его использовать в конце концов, но причина не связана с первоначальным вопросом.Причина в том, что я нашел много запросов на вкладке «Сеть» ко многим веб-сайтам (например, Facebook), и я не знаю, как это влияет на моих клиентов (например, в отношении конфиденциальности).Опять же: я не знаю, я не знаком с iframe s.Кроме того, я немного боюсь, что в моем решении могут быть утечки.

Я просто добавлю гиперссылку.

...