my javascript function does not load on initial page load - javascript

I'm building an app with an Angular2 frontend and a web API backend using the asp.net Angular2 template. I have an inline script in my index.html file that does not get loaded on the initial page load, but when i refresh the page it gets loaded. Below is the code.
<script>
$(function () {
$('.toggle-nav').click(
function () {
$('#body-holder').toggleClass('show-nav');
return false;
});
});
</script>
I tried loading it in my component.ts file within the ngOnInit function but i get a cannot find $ error when i try that.

import { Component, OnInit } from '#angular/core';
declare var $:any; // this is required
#Component({
selector: 'app-root',
template: `Something`
})
export class AppComponent implements OnInit {
ngOnInit() {
$('.toggle-nav').click(function () {
$('#body-holder').toggleClass('show-nav');
});
}
}

Related

unable to call javascript function from angular 13 component

There is a recent change introduced in Angular 13 where the old way of calling javascript functions from angular components isn't working anymore.
I am facing an issue with calling a javascript function from a specific component.
I already tried the usual way and here is my approach.
FILE: src\assets\js\main.js
(function($) {
"use strict";
function animatedProgressBar () {
$(".progress").each(function() {
var skillValue = $(this).find(".skill-lavel").attr("data-skill-value");
$(this).find(".bar").animate({
width: skillValue
}, 1500, "easeInOutExpo");
$(this).find(".skill-lavel").text(skillValue);
});
}
})(jQuery);
FILE: src\app\about-me\about-me.component.ts
import { Component, OnInit } from '#angular/core';
declare function animatedProgressBar(): any;
#Component({
selector: 'app-about-me',
templateUrl: './about-me.component.html',
styleUrls: ['./about-me.component.css']
})
export class AboutMeComponent implements OnInit {
//declare animatedProgressBar: any;
constructor() {}
ngOnInit(): void {
animatedProgressBar();
}
}
This code snippet throws an error: ERROR ReferenceError: animatedProgressBar is not defined
I checked the answer on This StackOverflow topic but it didn't work for me.
Looking forward to some valuable inputs on this issue.

Angular functions are performed twice when called in the html file

I tried to start an angular project, I've created a simple component and started a console.log in it but I have Confusing problem. when I calling a function in html file from ts file its run twice
TS:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-hello',
templateUrl: './hello.component.html',
styleUrls: ['./hello.component.less']
})
export class HelloComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
log(val)
{
console.log(val);
}
test() {
let time = new Date()
console.log(time.getSeconds());
}
}
html :
hello works!
{{log('test')}}
{{test()}}
image log:
enter image description here
Where and how often do you call your component?
Can you produce a simple example on stackblitz?
Without any further info, we can just guess what it is.
You propably have called your component via '' twice.
Each instance will call all your template and hence its calling functions.

(Angular 2/4/5/6) Adding tawk widget to specific pages in Angular 6

Solution:
I got my solution here How to load external scripts dynamically in Angular?
And I checked for my path in app component to load the script accordingly.
import { Component, OnInit } from "#angular/core";
import { NavigationStart, Router } from "#angular/router";
import { Subscription } from "rxjs";
import { ScriptService } from "./shared/script.service";
import { Location } from "#angular/common";
#Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.scss"],
})
export class AppComponent implements OnInit {
constructor(
router: Router,
private location: Location,
public script: ScriptService,
) {}
ngOnInit() {
if (!this.location.path().includes("admin")) {
// load script for tawk widget
this.script.load("tawk").catch(error => console.log(error));
}
}
}
Previous Question:
I have difficulties adding the tawk widget to specific components in the angular 6 project. If I add it to index.html page below app-root, it shows up in all pages of the project. Is there any other way of doing it?
Sample code:
<!--Start of Tawk.to Script-->
<script type="text/javascript">
var Tawk_API=Tawk_API||{}, Tawk_LoadStart=new Date();
(function(){
var s1=document.createElement("script"),s0=document.getElementsByTagName("script")[0];
s1.async=true;
s1.src='https://embed.tawk.to/17f35g40afc2c34e96e75909/default';
s1.charset='UTF-8';
s1.setAttribute('crossorigin','*');
s0.parentNode.insertBefore(s1,s0);
})();
</script>
<!--End of Tawk.to Script-->
Sample code taken from here --->
Integrating tawk.to into Angular 6 (Angular 2) app
I've tried the following site,
Adding JavaScript File to Angular 4 Component but it still shows up for every page. Appreciate your help!
the easiest way would be to create a component, and than inside your component's .ts, to append script to the body like so:
onInit(): void {
const s = this._renderer.createElement('script');
s.text = `var Tawk_API = Tawk_API || {}, Tawk_LoadStart = new Date();
(function () {
var s1 = document.createElement("script"), s0 = document.getElementsByTagName("script")[0];
s1.async = true;
s1.src = 'https://embed.tawk.to/${id}/default';
s1.charset = 'UTF-8';
s1.setAttribute('crossorigin', '${cors}');
s0.parentNode.insertBefore(s1, s0);
})();
this._renderer.appendChild(this._document.body, s);
}
Of course you'll need to inject Document and renderer into constructor
constructor(
private _renderer: Renderer2,
#Inject(DOCUMENT) private _document,
) {}
and make sure you set your twalk.to id and cors (${id}, ${cors})

angular click() event not working after rendering dynamic html

Few html elements loaded from server side and angular click() event also part of it. but event is not firing after rendering the elements. I understand that i need to notify the DOM. I can not do this.
component.ts
mport { Component} from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: '../Views/Dashboard.html',
providers: []
})
export class DashboardComponent {
public deviceData="<div (click)="Go()">Click</div>"; //e.x: loaded from server
constructor(){
}
Go():void{
alert("Go");
}
}
html
<div [innerHTML]="deviceData"></div>
You can bind event using
<div class="classname" [innerHTML]="deviceData"></div>
this.deviceData = "<a herf='javascript:void(0);'>Click</a>";
ngOnInit()
{
var obj=this;
$(document).on('click', '.classname', function () {
obj.Go();
});
}
Go()
{
alert("Go");
}
A click function (or any Angular specific tag) to be rendered as html is not best practice. Try to only render bare basic html and put the click function in separately.

How to access functions created in the Component outside of Angular2 using JavaScript [duplicate]

I am using a javascript Object that has a callback. Once the callback is fired I want to call a function inside an Angular2 component.
example
HTML file.
var run = new Hello('callbackfunction');
function callbackfunction(){
// how to call the function **runThisFunctionFromOutside**
}
<script>
System.config({
transpiler: 'typescript',
typescriptOptions: { emitDecoratorMetadata: true },
packages: {'js/app': {defaultExtension: 'ts'}}
});
System.import('js/app/main')
.then(null, console.error.bind(console));
</script>
My App.component.ts
import {Component NgZone} from 'angular2/core';
import {GameButtonsComponent} from './buttons/game-buttons.component';
#Component({
selector: 'my-app',
template: ' blblb'
})
export class AppComponent {
constructor(private _ngZone: NgZone){}
ngOnInit(){
calledFromOutside() {
this._ngZone.run(() => {
this.runThisFunctionFromOutside();
});
}
}
runThisFunctionFromOutside(){
console.log("run");
}
How can i call the function runThisFunctionFromOutside which is inside App.component.ts
I basically followed this answer, but I didn't want my "outside" code to know anything about NgZone. This is app.component.ts:
import {Component, NgZone, OnInit, OnDestroy} from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: 'app.component.html'
})
export class AppComponent implements OnInit, OnDestroy {
constructor(private ngZone: NgZone) {}
ngOnInit() {
window.my = window.my || {};
window.my.namespace = window.my.namespace || {};
window.my.namespace.publicFunc = this.publicFunc.bind(this);
}
ngOnDestroy() {
window.my.namespace.publicFunc = null;
}
publicFunc() {
this.ngZone.run(() => this.privateFunc());
}
privateFunc() {
// do private stuff
}
}
I also had to add a definition for TypeScript to extend the window object. I put this in typings.d.ts:
interface Window { my: any; }
Calling the function from the console is now as simple as:
my.namespace.publicFunc()
See also How do expose angular 2 methods publicly?
When the component is constucted make it assign itself to a global variable. Then you can reference it from there and call methods.
Don't forget to use zone.run(() => { ... }) so Angular gets notified about required change detection runs.
function callbackfunction(){
// window['angularComponentRef'] might not yet be set here though
window['angularComponent'].zone.run(() => {
runThisFunctionFromOutside();
});
}
constructor(private _ngZone: NgZone){
window['angularComponentRef'] = {component: this, zone: _ngZone};
}
ngOnDestroy() {
window.angularComponent = null;
}
Plunker example1
In the browser console you have to switch from <topframe> to plunkerPreviewTarget.... because Plunker executes the code in an iFrame. Then run
window['angularComponentRef'].zone.run(() => {window['angularComponentRef'].component.callFromOutside('1');})
or
window.angularComponentRef.zone.run(() => {window.angularComponentRef.componentFn('2');})
An alternative approach
would be to dispatch events outside Angular and listen to them in Angular like explained in Angular 2 - communication of typescript functions with external js libraries
Plunker example2 (from the comments)
Below is a solution.
function callbackfunction(){
window.angularComponent.runThisFunctionFromOutside();
}
<script>
System.config({
transpiler: 'typescript',
typescriptOptions: { emitDecoratorMetadata: true },
packages: {'js/app': {defaultExtension: 'ts'}}
});
System.import('js/app/main')
.then(null, console.error.bind(console));
</script>
My App.component.ts
import {Component NgZone} from 'angular2/core';
import {GameButtonsComponent} from './buttons/game-buttons.component';
#Component({
selector: 'my-app',
template: ' blblb'
})
export class AppComponent {
constructor(private _ngZone: NgZone){
window.angularComponent = {runThisFunctionFromOutside: this.runThisFunctionFromOutside, zone: _ngZone};
}
runThisFunctionFromOutside(){
console.log("run");
}
}
An other approach without using global variables is to use pass a control object and bind its properties to the variables and methods to expose.
export class MyComponentToControlFromOutside implements OnChanges {
#Input() // object to bind to internal methods
control: {
openDialog,
closeDialog
};
ngOnChanges() {
if (this.control) {
// bind control methods to internal methods
this.control.openDialog = this.internalOpenDialog.bind(this);
this.control.closeDialog = this.internalCloseDialog;
}
}
internalOpenDialog(): Observable<boolean> {
// ...
}
internalCloseDialog(result: boolean) {
// ...
}
}
export class MyHostComponent {
controlObject= {};
}
<my-component-to-control [control]="controlObject"></my-component-to-control>
<a (click)="controlObject.open()">Call open method</a>
I had a similar situation when using the callback 'eventClick' of the fullCalendar library, whose callbacks are returning from outside the angular zone, causing my application to have partial and unreliable effects. I was able to combine the zone approach and a closure reference to the component as seen below in order to raise an output event. Once I started executing the event inside of the zone.run() method the event and it's effects were once again predictable and picked up by angular change detection. Hope this helps someone.
constructor(public zone: NgZone) { // code removed for clarity
}
ngOnInit() {
this.configureCalendar();
}
private configureCalendar() {
// FullCalendar settings
this.uiConfig = {
calendar: { // code removed for clarity
}
};
this.uiConfig.calendar.eventClick = this.onEventClick();
}
private onEventClick() {
const vm = this;
return function (event, element, view) {
vm.zone.run(() => {
vm.onSequenceSelected.emit(event.sequenceSource);
});
return false;
};
}
Just adding to #Dave Kennedy:
Calling the function from the console is now as simple as:
my.namespace.publicFunc()
1) If we try to access our component's public method from a different domain you will get caught into CORS issue (the cross origin problem, can be solved if both server and client code resides in same machine).
2) if you were to call this method from server using javascript, you will have to use window.opener.my.namespace.publicFunc() instead of window.my.namespace.publicFunc():
window.opener.my.namespace.publicFunc();

Categories