javascript - Angular 2 - bypassSecurityTrustHtml junto com bypassSecurityTrustScript
Eu tenho um botão (no lado esquerdo da navegação), ao clicar nele, quero exibir um template (.html) no lado direito (seção de conteúdo). O caminho do template (.html) será obtido de um serviço web e será diferente a cada vez (portanto, sem valores codificados).
Esse template (.html) pode ter suas próprias tags de script. Até o Angular 1.x era fácil com a tag ng-include, mas parece que o Angular 2 não suporta mais isso.
Sou capaz de exibir o conteúdo do HTML (bem como CSS) usando a solução alternativa abaixo -
this.sanitizer.bypassSecurityTrustHtml(url);
Mas os scripts não estão sendo carregados ou executados.
Agradeço antecipadamente por qualquer ajuda.
Resposta #1
A opção de [innerHTML] pode funcionar para você quando você tem uma única tag script no seu conteúdo HTML. Mas se você tiver várias tags Script para carregar com o conteúdo HTML, então não é uma opção viável, pois não garante a sequência na qual os scripts serão carregados e executados. Para isso, você terá que anexar seu HTML em algum elemento e carregar os scripts em sequência, um por um.
A solução abaixo está devidamente explicada aqui - https://ghinda.net/article/script-tags/
Seu HTML deve parecer com -
<div id="scripts"></div>
<div id="content"></div>
No primeiro div, todos os seus Scripts serão adicionados e no segundo, todo o seu conteúdo HTML será adicionado.
Primeiro converta seu HTML em string e coloque todas as tags de script em scriptArr[]
// run the scripts inside the dom node
if (scriptArr.length > 0) {
var $container = document.getElementById('scripts')
$container.innerHTML = scriptArr
runScripts($container)
}
/* helpers */
// runs an array of async functions in sequential order
function seq(arr, callback, index) {
// first call, without an index
if (typeof index === 'undefined') {
index = 0
}
arr[index](function () {
index++
if (index === arr.length) {
callback()
} else {
seq(arr, callback, index)
}
})
}
// trigger DOMContentLoaded
function scriptsDone() {
var DOMContentLoadedEvent = document.createEvent('Event')
DOMContentLoadedEvent.initEvent('DOMContentLoaded', true, true)
document.dispatchEvent(DOMContentLoadedEvent)
}
/* script runner */
function insertScript($script, callback) {
var s = document.createElement('script')
if (!s.type) {
s.type = 'text/javascript'
}
if ($script.src) {
s.onload = callback
s.onerror = callback
s.src = $script.src
} else {
s.textContent = $script.innerText
}
// re-insert the script tag so it executes.
document.head.appendChild(s)
// clean-up
$script.parentNode.removeChild($script)
// run the callback immediately for inline scripts
if (!$script.src) {
callback()
}
}
// https://html.spec.whatwg.org/multipage/scripting.html
var runScriptTypes = [
'application/javascript',
'application/ecmascript',
'application/x-ecmascript',
'application/x-javascript',
'text/ecmascript',
'text/javascript',
'text/javascript1.0',
'text/javascript1.1',
'text/javascript1.2',
'text/javascript1.3',
'text/javascript1.4',
'text/javascript1.5',
'text/jscript',
'text/livescript',
'text/x-ecmascript',
'text/x-javascript'
]
function runScripts($container) {
// get scripts tags from a node
var $scripts = $container.querySelectorAll('script')
var runList = []
var typeAttr
[].forEach.call($scripts, function ($script) {
typeAttr = $script.getAttribute('type')
// only run script tags without the type attribute
// or with a javascript mime attribute value
if (!typeAttr || (runScriptTypes ? runScriptTypes.indexOf(typeAttr) !== -1 : 'text/javascript')) {
runList.push(function (callback) {
insertScript($script, callback)
})
}
})
// insert the script tags sequentially
// to preserve execution order
seq(runList, scriptsDone)
}
let fragment = document.createRange()
.createContextualFragment(html);
// fragment.baseURI = 'http://toolsqaplp/publish/Dev_Testing/n1d/DS_MAC/en/CATMMGSkillets_en.edu/CATMMGEdc0020_en.doc/src/';
document.getElementById('content')
.appendChild(fragment);
Resposta #2
O documento oficial diz que bypassSecurityTrustHtml() deve funcionar no seu caso.
Também estou supondo que você deve estar usando o atributo [innerHTML] para preencher o conteúdo no contêiner de destino.
Eu tive um tipo de requisito semelhante, onde eu precisava de uma única tag HTML com uma única tag de script. Eu usei bypassSecurityTrustResourceUrl() para isso.
return this.sanitizer.bypassSecurityTrustResourceUrl(url)
Tenho quase certeza de que funcionará no seu caso também, já que você está buscando o código-fonte executável.
Você também pode tentar a combinação bypassSecurityTrustScript() e bypassSecurityTrustHtml().
