От известно време, в професионалната си работа, разработвам софтуерни приложения и на Angular. В няколко случая съм се сблъсквал с въпроси как едно или друго нещо би могло да се случи и не съм откривал бърз и еднозначен отговор за себе си. В този пост ще дискутирам две интересни неща от този характер:
- Как динамично от Angular да „сваляме“ в приложението third-party JavaScript библиотеки
- Как да се „закачим“ за процеса на инициализация на Angular приложението и да изпълним наш собствен скрипт
За илюстрация съм написал едно примерно приложение, което ще цитирам на няколко места в поста.
Кодът на примерното приложение е достъпен на: https://github.com/agyonov/NgInitGA
Самото приложение е живо на: https://agyonov.github.io/NgInitGA
Динамичен download на third-party JavaScript библиотека
За повечето библиотеки, които бихте искали да използвате, някой вече е създал, развива и поддържа NPM пакет със съответния(те) Angular модули и Вие бихте могли (а и би трябвало) да ги ползвате на готово. Но понякога няма готов NPM пакет или този, който е наличен не ви харесва и тогава можете директно да закачите исканата от вас JavaScript библиотека към вашето Angular приложение и да я използвате.
Има два лесни подхода, които са описани в документацията на Angular и примерно в тази на Google Analytics. За съжаление, те не винаги вършат работа по една или друга причина.
- Първото, изцяло Angular, решение е да свалите съответния .js файл от интернет. Да го добавите към Вашия проект и да поставите референция към .js библиотеката в масива scripts, на angular.json конфигурационния файл. Например:
…
"scripts": [
"src/scripts/analytics.js"
]
…
Както подробно е описано в документацията на Angular.
Това е ОК като подход и ще работи.
Съображение срещу евентуалното му използване, е че при този подход .js скрипта ще се download-ва от вашия хостинг сървър, а не от оригиналния сървър (в случая https://www.google-analytics.com/analytics.js). И въпроса не е толкова в увеличения трафик към Вашия сайт и скоростта, а в това, че автора на библиотеката (Google) би могъл да променя разни неща в нея, да има нови версии и т.н., а вие няма да имате тази нова версия, докато не re-build-нете и не пре-публикувате вашето приложение.
2. Втория подход е да се приложи инструкцията от страницата на Google Analytics:
<script>
window.ga=window.ga || function(){(ga.q=ga.q||[]).push(arguments)};
ga.l=+new Date;
ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');
</script>
<script async src='https://www.google-analytics.com/analytics.js'>
</script>
И горния код да се постави директно в index.html файла инициализиращ вашето Angular приложение. По-специално последния ред от горната извадка код.
Това е ОК като подход и също ще работи в частта му за download на .js библиотеката. Освен това тук възраженията, които бяха валидни за първото решение отпадат по естествен начин.
Съображение срещу евентуалното му използване, е стилистично – не е „чисто“ Angular решение.
Останалата част от отрязъка код от Google Analytics, виден по-горе, изобщо не ни върши работа! Защото при Angular имаме Single Page Application (SPA) при които, за разлика от традиционните уеб приложения, реално само първата страница се зарежда от уеб сървъра, а в последствие всеки преход от страница в страница, се случва само вътре в SPA приложението в браузера. Също ако имаме и някакви специални събития (например натискане на бутон) и те се случват вътре в Angular приложението и няма да се отразят на данните в Google Analytics.

Тези проблеми са детайлно обсъдени в самата документация на Google Analytics под темата за Single Page Application Tracking.
3. Да се върнем към въпроса с динамичния download на JavaScript библиотека. Има и трети способ, които имено ще разгледам. Понеже примера е с използване на Google Analytics – ще го разширя да се ползва и за проследяване действията на потребителите в Angular приложение.
NB: Що се касае до track-не на потребители в Angular приложение, то има чудесни, готови библиотеки, като angulartics2, които можете да ползвате. Тук въпроса не е да създавам поредната такава библиотека, а с пример да покажа възможно решение на двата въпроса зададени в началото на този пост!
Та, динамичния download – да видим извадка от следния Angular сервиз:
…
// Typescript extention of Window interface
declare global {
interface Window { ga: any; }
}
@Injectable({
providedIn: 'root'
})
export class GoogleAnalyticsService {
// The source for the load
private googleAnalyticsScript = {
loaded: false,
url: 'https://www.google-analytics.com/analytics.js'
};
constructor(@Inject(PLATFORM_ID) private platformId: Object,
@Inject(DOCUMENT) private dom: Document) {
}
…
// Init the GA infrastructure
public loadScript(): void {
// Check already loaded
if (!this.googleAnalyticsScript.loaded) {
// Check if we are at browser
if (isPlatformBrowser(this.platformId)) {
// Create new scipt element
const scriptElm: HTMLScriptElement = this.dom.createElement('script');
scriptElm.src = this.googleAnalyticsScript.url;
scriptElm.type = 'text/javascript';
scriptElm.async = true;
scriptElm.onload = () => {
// Prevent from load second time
this.googleAnalyticsScript.loaded = true;
};
// Define GA object
window.ga = window.ga || function () { (window.ga.q = window.ga.q || []).push(arguments); };
window.ga.l = +new Date;
// Set some Google Analytics stuff
window.ga('create', environment.propertyID, 'auto');
// Add to document
this.dom.body.appendChild(scriptElm);
}
}
}
}
По същество това е преписване на TypeScript, на инструкцията от страницата на Google Analytics, която я има малко по-горе в поста. По важните моменти са:
- Съвсем в началото се разширява TypeScript Window интерфейса с ново поле – ‚ga‘
- В метода ‚loadScript‘ се извършва същинската работа:
- Създава се скрипт елемента scriptElm и се добавя динамично към DOM-а на web странцита. Това е същото като да се постави
<script async src='https://www.google-analytics.com/analytics.js'></script>
в index.html-ла.
- Дефинира се полето ‚ga‘, с което е разширен Window интерфейса, по начина указан ни от Google Analytics. Това е същото като да се постави
<script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};
ga.l=+new Date;
ga('create', 'UA-XXXXX-Y', 'auto');
</script>
И готово!
😊 Е, не съвсем… все пак трябва да се извика метода ‚loadScript‘, някъде от Angular приложението, за да се изпълни и да накара браузера да започне download-а на JavaScript библиотеката. И тук стигаме до втория въпрос:
„Закачане“ за инициализирането на Angular приложение
Но този пост стана малко дълъг. Май е време да завърша тази първа част. За „закачането“ към инициализирането на на Angular приложението, ще пиша във втората част.
Автор: Александър Гьонов
Очаквайте част 2