function is not getting called from a click function in angular 5 - javascript

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();
});
}

Related

How can I get component's method real name?

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');

How to transfer a object from JS to AngularJS?

I got an JS-object in my index.html namend user. In this object are some user with mail, name etc.
So now I want to use this object in my app.js (AngularJS). I need it there.
I tried to tranfer it like this in my app.js:
var tempArr = $scope.user;
But it does not work.
Is there a possibility to transfer it from JS in my index.html to app.js?
As You asked From JS to angularjs so anything that is in js is in global window scope of browser and accessible inside angularjs scope so no problem to use a variable from js to angularjs context.
In your, app.js you probably have something like
var app = angular.module(/*blah blah*/);
That same app variable should be (made) accessible from the outside i.e. in the global scope. That way you can just write
app.tempArr = $scope.user;
But, of course, this is just quick and dirty solution. What I usually do is to create one global object, like
var GLOBAL = {};
and attach everything of mine on it, like
GLOBAL.app = angular.module(/*blah blah*/);
GLOBAL.someHelperFunction = function someHelperFunction(){};
and even, if needed
GLOBAL.tempArr = $scope.user;
// even better if you just clone the user values
// not just attach it to GLOBAL

Mocking provider, injecting stuff

I have a somewhat complicated scenario here, I'll try my best to explain it, sorry if it's feels confusing.
Say, I have a contrived provider sitting in module aptly named core, let's call it actionProvider, it can register actions and then later invoke them.
And I use it like this:
// registering an action in m1 module
angular.module('m1').config((actionProvider)=> {
actionProvider.reg('myAction', { $fn: myActionFn });
myActionFn.$inject = ['$modal'];
function myActionFn($modal) {
$modal.open() // when action invoked it opens a modal dialog
}
})
// now somewhere else I invoke that previously registered action
angular.module('m2').controller('myCtrl', (action)=> {
action.invoke('myAction'); // and that calls $fn with $modal.open()
})
And this works perfectly. Now, let's say I have to test actionProvider in a module that has no access to source code of actionProvider. Means I completely need to mock it.
Ok. Let's try doing this:
angular.module('core', []).provider('action', function() {
let self = this;
self.actions = [];
self.$get = ()=> { return self }; // essential function that every provider has
// registering action just pushes it into the array of actions,
// remember this is a fake provider
self.reg = (action)=> {
self.actions.push(action)
};
// yet even though it's a fake it still needs to be able to invoke actions
self.invoke = (actionName) {
// so here I need to find specified action in the array and invoke it
};
})
Finding the right action in self.actions is easy. But how do I invoke its $fn correctly? How do I tell injector to find all the objects that's been injected (in the case of myAction it would be $modal service)
Finding what's injected into myActionFn is easy, you just need to inspect function's $inject property.
Next step is to simply invoke the function, passing into it what needs to be injected.
Using Function.prototype.apply in this case won't help, since we need to use angular's $injector. Creating an instance with angular.injector() wouldn't work either, because we need to use right instance of the injector.
The trick is to use angular.mock.injector to grab current injector instance.
so our invoke function should look like this:
self.invoke = (actionName) {
// instead of array it's probably better to use an object map for `actions`
let action = actions[actionName];
inject(($injector)=> {
$injector.invoke(action.$fn);
})
};

Circular dependencies in JavaScript OOP

// Main class
function App() {
this.task = new Task(this); // pass the instance of this class to Task so
// it has access to doSomething
}
App.prototype.doSomething = function () {
alert("I do something that Task() needs to be able to do!");
};
function Task(app) {
// This class needs access to App()'s doSomething method
this.appInstance = app;
this.appInstance.doSomething(); // Great, now Task can call the method
}
var app = new App();
The aim of the code above is to give Task access to one of App's methods called doSomething. The code is the current way I'd go about it and I'm posting this to see if it's the best way...
To give Task access I simply pass the whole instance of App, is this efficient or is there a better way to go about it? Is the code above general practice in going about doing something like this?
Yes, what you have is fine. It is a circular dependency, however because of JavaScript's dynamic nature there aren't really any issues.
Another way you could reference App from Task would be a Singleton pattern or something similar, but that would probably be harder to test.
jsFiddle Demo
Generally bind would be used in this scenario assuming that the Task "class" didn't also setup other facilities which were not shown here.
Bind allows for the context to be provided for a function. This could be done in app's constructor. At which point only a function task would be required to call "someMethod".
function task(){
return this["someMethod"]();
}
function App(){
task.bind(this)();
}
App.prototype.someMethod = function(){
alert("Task needed access to this");
};
var a = new App();
However, if task must be a "class", and have other responsibilities then the prototype function could be shared.
function Task(){}
function App(){}
App.prototype.someMethod = Task.prototype.someMethod = function(){
alert("Task needed access to this");
};
var a = new App();
a.task();//->"Task needed access to this"
var t = new Task();
t.someMethod();//->"Task needed access to this"
Your app instances and task instances are tightly bound. App instances have tasks and this can be fine.
A design of loosely coupled objects is more flexible and easier to extend but more complicated to initially create. One such pattern is using a mediator/publish subscriber and have app raise an event/publish message any other object function can listen to this and take action on the event.
For example: your app creates an Ajax instance and when that instance is done it raises some event (fetchedData for example). A listener could be DomDependent.updateView function but later you may want to add/remove/change the order of tasks to do after data is fetched. This can all be configured in a app.init function or per procedure in a controller that kicks of certain procedures (like log in, search, ...).
Instead of creating a whole bunch of specific functions in Ajax (fetchUserPrefs, login, search, ...) you can create one general function and have the controller add listeners or pass the next event when fetchData is complete to run the correct next function.
Here is some pseudo code:
var app = {
init:function(){
mediator.add("updateLogin",domDependent.updateView);
mediator.add("updateLogin",app.loadUserPrefs);
mediator.add("failLogin",domDependent.updateView);
},
login: function(){
mediator.trigger("loadingSometing",{type:"login"});
ajax.fetch({
onComplete:"updateLogin",//what listens to updateLogin you decided in init
onFail:"failLogin",
loginDetails:domDependent.getLogin(),
url:settings.loginUrl,
type:"post"
});
}
}
var ajax = {
fetch:function(data){
data = data || {};
//simple check for onComplete, it's mandatory
var complete = data.onComplete || app.raiseError("ajax.fetch needs onComplete");
//other code to validate data and making ajax request
onSuccess:function(resp){
//mutate data object as the mediator will pass it to
// whatever other function is called next
// you don't hard code domDependent.updateView and
// app.loadUserPrefs because fetch can be used generally and
// success may have to do completely different things after its done
// and you want to define procedures in init, not all over your code
data.response=resp;
//trigger event to do whatever needs to be done next
mediator.trigger(complete,data);
}
}
}
As you can see it gets complicated and maybe doesn't look like code you're used to but it's highly configurable.
I may have misunderstood the advantages of the mediator pattern to loose couple and if so please comment. I use it to:
Make methods more general instead of copying a lot of logic only
because what to do after it's done is different. In fetch the ajax
object just fetches, this would be the same for login or getting
user preferences, the only thing different is what function to call
next/on error when it's done.
A procedure like login involves multiple functions in multiple
objects if this function chain hard code what to do next once a
particular function is done your procedure of login is defined all
over your code. When defining it in init/config you can easily change the
order or add/remove functions in the chain.

Handling Page Refresh with AngularJS Service and Views

I have a simple SPA with two views: a list view and a detail view. I use a service called StateService to pass data between the two controllers.
I am trying to handle the case where the user refreshes the browser page--when this happens, the StateService gets reinitialized and the detail view can no longer work. I want to detect when this happens and return the user to the list view.
Here is a simplified version of my State Service. The idea is that I would set isInitialized to true when I switch to the detail view so that I can detect when the service has not been properly initialized.
var StateService = function () {
var isInitialized = false;
};
This is what I have tried in the first few lines of my controller. The StateService is being successfully injected into the controller.
//always returns [Object], on refresh or navigating from list page
alert(StateService);
// this next line always returns undefined. Should be false since I am initializing
// the value to false?
alert(StateService.isInitialized);
//One of the many combinations I have tried . . .
if (!StateService.isInitialized | StateService.isInitialized == false) {
$location.path('/');
}
I don't know if this is a gap in my understanding of javascript or angular, but any thoughts on how I can get the above code to work, or better ideas on what to do when a user refreshes the page?
Edit
Using console.log as recommended by nycynik I see the following:
c {} [StateService]
undefined [StateService.isInitialized]
So it seems that StateService itself is just an empty object when this code gets hit. I get the same results from my other controller (the one that handles the list view).
As noted in the comments, the service seems to otherwise work as expected.
I think you have a problem with scoping. variables in javascript have function scope.
isInitialized is scoped only to your StateService Function, so you can't get at it outside of your StateService Function.
not sure exactly how you're getting this thing into your controller, but maybe these help:
if you're using an angular's module.service() to use StateService as a constructor to inject a (new StateService) into your controller then you need to set isInitialized on the instance
var StateService = function () {
this.isInitialized = false;
};
This way (new StateService).isInitialized === false
If you are just using module.factory() or something else that doesn't use new, then you need to put your isInitialized value somewhere else you can actually get at it.
var StateService = function () {
};
StateService.isInitialized = false
Hope that helps.

Categories