for clarify next part: payU is the Internet payment operator
I have a serious problem with integration my Angular app with payU payments. I won't store or pass credit card's data (security reasons) so I choose widget.
The first problem is how to place the widget in the code. Documentation says that I should place script in the following way:
<script
src="https://secure.payu.com/front/widget/js/payu-bootstrap.js"
pay-button="#pay-button"
merchant-pos-id="145227"
shop-name="Nazwa sklepu"
total-amount="9.99"
currency-code="USD"
success-callback="test"
sig="250f5f53e465777b6fefb04f171a21b598ccceb2899fc9f229604ad529c69532">
</script>
How you probably know, you can't set script in your code in this way in Angular so I decided use little walkaround:
ngAfterViewInit(): void {
this.script = document.createElement('script');
this.script.setAttribute('src', 'https://secure.payu.com/front/widget/js/payu-bootstrap.js');
this.script.setAttribute('pay-button', '#pay-button');
this.script.setAttribute('merchant-pos-id', '145227');
this.script.setAttribute('total-amount', '9.99');
this.script.setAttribute('currency-code', 'USD');
this.script.setAttribute('success-callback', 'test');
this.script.setAttribute('sig', '4752ce2b163684a9c27cc0923ad46068c04da5d34329f5669ce73dcf96394558');
this.renderer.appendChild(this.el.nativeElement, this.script);
}
I know it's not a perfect solution (if you know better way to do this, please let me know in comment.
But the main problem is pass name of callback function to success-callback attribute. I prepared function in my component, like:
test(arg: any) {
console.log(arg);
}
But I can't get this name. I was trying:
this.script.setAttribute('success-callback', this.test.name);
but property name is empty. Is there a simple way to get real name of method (after typescipt translating) in my component?
BTW.
Adding simple js script to index.html and providing its name works, but i need to call service within my function.
I'm using Angular v7.
Explanation:
Ok, let's start by explaining the script. Since the script is being added in the global namespace, the success callback refers to a global function with the name 'test' in your above code.
So we need a reference to the angular component's 'test' function in the global namespace of your app, so that it can be accessed on success callback.
In your component:
import {NgZone} from '#angular/core';
constructor(private zone:NgZone){
window.callbackComponentRef = {
testFn: (args) => {
this.zone.run(() => { this.test(args); })
}
};
}
test() {
//Whatever code you want to run
}
Then use in script addition code
this.script.setAttribute('success-callback', 'callbackComponentRef.testFn');
Related
We have a requirement that we'd like to develop a "mini" testing framework for use within our Angular application. The point of the framework is to be able to manipulate API calls to alter the response body and status code in order to trip various error handlers within our application and make sure the application is responding correctly to errored API calls.
To do this I have been researching how to call Angular services from outside of Angular. This article describes how it is possible to create a service and then trigger it from outside Angular by calling the window.fireAngularEvent('sampleEventName', args) function.
However when I tried to do this via the browser, I get the following: Uncaught TypeError: window.fireAngularEvent is not a function
Here is my Angular service
export class GlobalApiTestingFrameworkService {
constructor() {
window['fireAngularEvent'] = (eventName, args) => {
console.log('fireAngularEvent : ' + eventName + ' : ' + args);
}
}
}
What do I need to do to be able to call the window.fireAngularEvent function? Do I need to define it outside of Angular within its own JS file? If so, what exactly do I include within the function body to allow it to communicate with the service? The article I linked isn't very clear.
The problem is due to the space in window['fireAngularEvent '] replace the defintion with window['fireAngularEvent'] and you'll be able to call window.fireAngularEvent without any issue.
Make sure that your service is instantiated at least one time.
To be sure You can use for example your service in AppComponent like that:
export class AppComponent {
constructor(private globalApiTestingFrameworkService: GlobalApiTestingFrameworkService){
}
}
I'm getting rather confused as to if something is possible or not.
I create a module that contains the following:
export function logText(){
console.log('some text');
}
export class Example {
constructor(){
logText();
}
}
The intent is for the user to call new Example to start off the module logic.
import { logText, Example } from 'example';
// Do some magic here to modify the functionality of logText
new Example();
Is it possible for the end user to modify logText?
It would make sense for there to be a way for users to do something like this, or they'd have to take the entire module into their own repo just to make small functionality tweaks.
I frequently see repos with lots of functions exported that are useless without the users having to remake almost all the functionality manually, making it rather pointless to do. One good example is this repo whre theuy even call the exported functions their 'API'. In that example these are rather pointless exports and at worse would just cause issues if someone tried to use them in conjunction with the main function. But if you could modify them and have them still run then it would make sense to me.
Given this:
import { logText, Example } from 'example';
Is it possible for the end user to modify logText?
Since you aren't being very specific about what you mean by "modify logText", I'll go through several options:
Can you reassign some other function to the variable logText?
No. You cannot do that. When you use import, it creates a variable that is const and cannot be assigned to. Even if it wasn't const, it's just a local symbol that wouldn't affect the other module's use of its logText() anyway. The import mechanism is designed this way on purpose. A loader of your module is not supposed to be able to replace internal implementation pieces of the module that weren't specifically designed to be replaced.
Can you modify the code inside of the logText function from outside of the module that contains it?
No, you cannot. The code within modules lives inside it's own function scope which gives it privacy. You cannot modify code within that module from outside the module.
Can you replace the logText() function inside the module such that the implementation of Example inside that class will use your logText() function?
No, you cannot do that from outside the module. You would have to actually modify the module's code itself or someone would have to design the Example interface to have a replaceable or modifiable logText() function that the Example object used.
For example, logText() could be made a method on Example and then you could override it with your own implementation which would cause Example's implementation to use your override.
Code in the module that you do not modify:
export class Example {
constructor(){
this.logText();
}
logText() {
console.log('some text');
}
}
Code doing the import:
import { Example } from 'example';
class MyExample extends Example {
constructor() {
super();
}
logText() {
console.log("my own text");
}
}
let o = new MyExample();
Can you create your own version of logText and use it locally?
Sure, you can do that.
function myLogText() {
do your own thing
}
And, you could even NOT import logText so that you could use the symbol name logText() locally if you wanted. But, that won't affect what Example does at all.
Are there ways to design the example module so that that logText() can be easily replaced.
Yes, there are lots of ways to do that. I showed one above that makes logText a method that can be overriden. It could also be passed as an optional argument to the Example constructor.
There could even be an exported object that allowed the caller to replace properties on that object. For example:
export const api = {
logText: function logText(){
console.log('some text');
}
};
export class Example {
constructor(){
api.logText();
}
}
Then, use it like this:
import { api, Example } from 'example';
api.logText = function() {
console.log('my Text');
};
I would generally not recommend this because it sets you up for usage conflicts between multiple users of the same module where each one tries to modify it globally in ways that conflict with each other. The subclassing model (mentioned above) lets each use of the module customize in its own way without conflicting with other usages of the module.
Is it possible for the end user to modify logText?
No, that's not possible, import bindings are immutable, and function objects are basically immutable wrt the code they contain.
It would make sense for there to be a way for users to do something like this, or they'd have to take the entire module into their own repo just to make small functionality tweaks.
Why not make the log function an optional argument in the constructor? Usually when something is variable it becomes a parameter.
export class Example {
constructor(log=logText){
log();
}
}
Hi Guys i'm stuck with a small issue in angular 5, I'm trying to call a common session check method which is imported from a common ts file, i'm using the session check method on load of the page and on click of logout button to redirect the user to login page. but on load of the page it works fine, but on click of a button it gives an error of undefined, Please help thanks in advance.
Dashboard
Imported
this inside function() { ... } definition does not refer to your component, but to that function context. Use arrow function so this context will remain bound to your component:
logout() {
session.signOut().then(() => {
this.s.checkIfSignedIn();
});
}
Also you should remove the var keyword when declaring class variable u.
Bonus advices:
care about your editor/linter warnings (to suppress errors when accessing global objects like gapi, declare that first like this: declare const gapi: any;)
care about code indenting
try to make your variable names descriptive, do not use names like a or u, again so you do not get lost in the code so easily (btw you have utils variable there too, maybe you should use that instead of defining new one called u...)
You are declaring it with var and calling it with this. It's not a property of the component, that's why it's not working.
You have a different scope inside the promise.
logout() {
let self = this;
var session = gapi.auth2.getAuthInstance();
session.signOut().then( function() {
localStorage.clear();
self.s.checkIfSignedIn();
});
}
I have a js file in my Angular application, data.js . This js file has some variables declared in it, something like below.
var data = 'test'
Now I have to access these variables and their values in my component (app.component.ts).
I read some where that declaring them as exports make them into modules and those can be accessed anywhere, But I'm not sure how this can be done.
This is the structure of my application. I have data.js in assets->js folder.I need to modify the variable value in app.component.ts.
I'm very new to Angular. Is this even possible?
With the file in your assets, I am guessing you are declaring it on the window. You will need the include the script in your index.html, and then access it on the window within your component via window.data. This is not really the recommended way of doing this unless your use case dictates it. The module approach you mentioned is preferred.
Next to your app.component.ts, create a file called data.ts, with:
export let data: string = 'data';
In your app.component.ts, import it using:
import { data } from './data.ts';
If you plan to not mutate that data, consider using the const keyword instead (in data.ts).
Directory structure
/app.component.ts
/data.ts
/...
Edit: Show Global Approach
You will need to include your script outside of the context of the Angular application. If you bootstrapped your application using the Angular CLI, you can add a reference to it in the cli configuration file. See this documentation on the topic.
That file will be included and will be available for access within your component on the window. The tricky part comes with typing and the Window. And example may look like this.
class AppComponent extends Component {
private data: string;
constructor() {
// Explicitly cast window as an any type. Would be better to type this, but this should work for you.
this.data = (<any>window).data;
}
}
(referrring to https://stackoverflow.com/a/42682160)
first you have to include the script into your src/index.html like
< script src="/assets/js/data.js">< /script>
important is that the above statement is placed before your angular root component tags
(< root-component>< /root-component> or < ion-app>< /ion-app> or something like that)
then you can simply write (for example inside app.component.ts ngOnInit function)
let varFromJsFile = window["data"] // varFromJsFile = 'test'
You want the variable to be a member of a Component class, not just a variable declared anywhere within a module.
If this doesn't make sense right away, you need to look more carefully at some basic Angular code samples.
Also, as long as you're using Angular and therefore TypeScript, it's better the declare variables using let or const.
I thought I would create a very simple login form with component-bound username and password properties that would run through the following steps:
Submit credentials with a fetch() call
THEN obtain the Response result object's JSON content
THEN check that content for the result of the server-side check
If the credentials were wrong, it would change a component property that would tell Angular to apply a class to the username and password inputs to make them red temporarily (using setTimeout to change that back)
The problem I ran into is that Angular would not correctly apply the class, and I wasn't sure why. I decided to create a simplified test project to narrow down the problem and ended up with the inclusion of system-polyfills/when.js being cause.
This code goes through 1, 2, then 3, and sets that both in the component property and outputs it to the debug console. Angular correctly renders the component property unless system-polyfill is included, in which case it will stop at 1 and never change it to 2 or 3, even though the console shows that the property is in fact changed:
export class App {
private stateIndicator:string = "0";
public showState(state:string) {
console.log('State: ' + state);
this.stateIndicator = state;
}
public showFetchProblem() {
this.showState('1')
fetch('http://www.google.com/robots.txt',{mode:'no-cors'}).then((response:any) => {
this.showState('2')
response.text().then((value) => {
this.showState('3')
});
});
}
}
I created the following Plunker to demonstrate this behaviour: http://plnkr.co/edit/GR9U9fTctmkSGsPTySAI?p=preview
And yeah, the obvious solutions are:
Don't manually include system-polyfills, or
Manually include a differeny Promise polyfill before SystemJS if you need it
But I'm still curious why this is happening, and hopefully somebody can shed some light on this (and possibly help to resolve the base issue).
Edit: The original title of this was Why is Angular 2's template rendering misbehaving when using system-polyfills (when.js). Thanks to Thierry and Günter for contributing and pointing out that Angular 2's use of zones is at play here. For anybody who comes across this in the future, here are two excellent articles that explain zones in further detail and will enhance your understanding of this scenario should you run into it:
http://blog.thoughtram.io/angular/2016/01/22/understanding-zones.html
http://blog.thoughtram.io/angular/2016/02/01/zones-in-angular-2.html
Promise polyfill provides a custom implementation of Promise that seems to be executed outside the scope of Angular2 (i.e. not in a zone).
If you execute your code explicitly within a zone managed by Angular2 (with the NgZone class), it works when the system-polyfill.js file is included.
Here is a sample:
constructor(private zone:NgZone) {}
public showFetchProblem() {
this.showState('1')
this.zone.run(() => {
fetch('http://www.google.com/robots.txt',{mode:'no-cors'}).then((response:any) => {
this.showState('2')
response.text().then((value) => {
this.showState('3')
});
});
});
}
See the corresponding plunkr: http://plnkr.co/edit/EJgZKWVx6FURrelMEzN0?p=preview.
update
Not sure about the explanation anymore but the workaround works. But I think there is some other underlying issue.
See also https://github.com/angular/angular/issues/7792#issuecomment-211217775
original
I assume the problem is caused by fetch not being patched by Angulars zone
To work around make the code execute inside Angulars zone manually
export class App {
constructor(private zone:NgZone) {}
private stateIndicator:string = "0";
public showState(state:string) {
console.log('State: ' + state);
this.stateIndicator = state;
}
public showFetchProblem() {
this.showState('1')
fetch('http://www.google.com/robots.txt',{mode:'no-cors'}).then((response:any) => {
this.showState('2')
response.text().then((value) => {
this.zone.run(() => this.showState('3'));
});
});
}
}
Plunker example