I am writting an angular 5 app using the CLI.
I am using gapi to retrieve user data in order to populate a form.
The client script is included in the index.html file :
<head>
<meta charset="utf-8">
<script src="https://apis.google.com/js/client.js"></script>
...
</head>
Here is my component with the call :
userProfileModel: UserProfileModel = new UserProfileModel();
ngOnInit() {
this.form = this.toFormGroup();
this.onFormChanges();
this.userService
.getUserById(this.userId)
.then(usr => {
this.mapModel(usr.result['profile']);
})
.then(() => {
this.form.patchValue(this.userProfileModel);
});
}
And the userService's method :
declare var gapi: any;
export class UserService {
getUserById(id: number) {
return gapi.client.request({
method: 'GET',
'path': this.constants['endpoint_user_getbyid'] + '/' + id,
'root': this.constants['api_url']
});
}
...
}
Problem is, gapi seems to do not be initialized right after my component has finished loading : I have to set a >500ms timeout to be able to use it, and it's ugly.
This code gives me the following error :
ERROR TypeError: Cannot read property 'request' of undefined
Please not :
1- I haven't installed anything with npm / yarn, I am simply using the script with the gapi var declaration.
2 - after every build, the code works without any error and populates my form the first time the page is loaded, then after one refresh, it fails everytime.
How can I tell angular to load client.js at startup before all the components ?
Thanks !
Thanks to #ADarnal I finally found a solution by reading this topic :
How to pass parameters rendered from backend to angular2 bootstrap method
I followed the same exact process described in computeiro's answer.
My equivalent of the "BackendRequestClass" class is a GapiService.
In this Gapi service, the load method allows me to load gapi before any other call is executed :
/* GapiService */
loadGapi() {
return new Promise((resolve, reject) => {
gapi.load('client', () => {
gapi.client.init({apiKey: 'AIzaSyD5Gl9...'}).then(() => {
resolve(gapi.client);
})
});
});
}
// Method used in any component
getUserById(id: number) {
return gapi.client.request({
method: 'GET',
'path': this.constants['endpoint_user_getbyid'] + '/' + id,
'root': this.constants['api_url']
});
}
Finally; in my component, i inject this gapiService and I am able to use client.request on component init !
ngOnInit() {
this.gapiService
.getUserById(5066549580791808)
.then(
...
});
Related
I'm trying to use Keycloak with JavaScript and these are the steps that I followed.
I create a client inside KeyCloak admin panel.
Link to image
I copy the .json file to my apache folder.
{
"realm": "master",
"auth-server-url": "http://localhost:8080/auth",
"ssl-required": "external",
"resource": "test",
"public-client": true,
"confidential-port": 0
}
I go to my index.html and I add these two lines for calling the script.
<script src="keycloak.js"></script>
<script>
function initKeycloak() {
const keycloak = new Keycloak();
keycloak.init().then(function(authenticated) {
alert(authenticated ? 'authenticated' : 'not authenticated');
}).catch(function() {
alert('failed to initialize');
});
}
</script>
this is what i have in myLogical.js
var keycloak = new Keycloak();
function initKeycloak() {
keycloak.init({onLoad: 'login-required'}).then(function() {
constructTableRows(keycloak.idTokenParsed);
pasteToken(keycloak.token);
}).catch(function() {
alert('failed to initialize');
});
}
function constructTableRows(keycloakToken) {
document.getElementById('row-username').innerHTML = keycloakToken.preferred_username;
document.getElementById('row-firstName').innerHTML = keycloakToken.given_name;
document.getElementById('row-lastName').innerHTML = keycloakToken.family_name;
document.getElementById('row-name').innerHTML = keycloakToken.name;
document.getElementById('row-email').innerHTML = keycloakToken.email;
}
function pasteToken(token){
document.getElementById('ta-token').value = token;
document.getElementById('ta-refreshToken').value = keycloak.refreshToken;
}
var refreshToken = function() {
keycloak.updateToken(-1)
I tried to download the file keycloak.js and put it directly on my root folder but it happen the same problem.
These is the message I got when I try to open the page
I'm confused about point 1, does keycloak automatically load configuration from json file in Apache folder? Let's assume that no, and I think that where your problem lies, you're not passing config param to keycloak constructor.
How to initialize keycloak:
const initKeycloak = async () => {
//you can hardcode these values for now just to see if everything works
const config = { url: 'http://localhost:8080/auth', realm: 'master', clientId: 'test'};
const keycloak = new Keycloak(config);
await keycloak
.init({ onLoad: 'login-required' })
.then(isAuthenticated => {
//user is authenticated
})
.catch(error => { console.log('keycloak error', error); });
}
Another important thing is that keycloak-js library version (in package.json) must match keycloak server version. Sometimes different versions work with each other but it's always best practice that keycloak-js version matches keycloak server version.
You can also look here: https://github.com/m-s7/react-core/blob/devel/src/services/keycloak-service.ts this is my repo with working keycloak-js implementation.
I have some queries from an API-Server that returns a json object that will be static over a user session, but not static forever.
It's a one-pager with Vue router.
How can I achieve that I:
can access this.myGlobals (or similar eg window.myGlobals) in all components, where my prefetched json-data from API-Server is stored.
My approach that is already working is to embed help.js via a mixin.
Oddly enough, I get hundreds of calls to this query. At first I thought that it only happened in the frontend and is chached, but the requests are actually sent hundreds of times to the server. I think it is a mistake of my thinking, or a systematic mistake.
i think the problem is, that the helper.js is not static living on the vue instance
main.js:
import helpers from './helpers'
Vue.mixin(helpers)
helpers.js:
export default {
data: function () {
return {
globals: {},
}
}, methods: {
//some global helper funktions
},
}, mounted() {
let url1 = window.datahost + "/myDataToStore"
this.$http.get(url1).then(response => {
console.log("call")
this.globals.myData = response.data
});
}
}
log in console:
call
SomeOtherStuff
(31) call
SomeOtherStuff
(2) call
....
log on server:
call
call
call (pew pew)
My next idea would be to learn vuex, but since its a easy problem, im not sure if i really need that bomb ?
You can use plugin to achieve this.
// my-plugin.js
export default {
install (Vue, options) {
// start fetching data right after install
let url1 = window.datahost + "/myDataToStore"
let myData
Vue.$http.get(url1).then(response => {
console.log("call")
myData = response.data
})
// inject via global mixin
Vue.mixin({
computed: {
myData () {
return myData
}
}
})
// or inject via instance property
Vue.prototype.$myData = myData
// or if you want to wait until myData is available
Vue.prototype.$myData = Vue.$http.get(url1)
.then(response => {
console.log("call")
myData = response.data
})
}
}
and use it:
Vue.use(VueResource)
Vue.use(myPlugin)
How to return a JavaScript result (js file) to Angular 7 and use it In Javascript all the steps are performed simultaneously but I want the result to come back and then run Angular. I used the following code, but it's not true:
new Promise(function(resolve, reject) {
sign(); // this is in JS file
setTimeout(() => resolve(opSignature), 7000); // opSignature is result of sign()
}).then(
result => {
this.saveChallenge(result); // call angular method
}
);
I need to return the value in my js file and then use this result in Angular In JavaScript all steps are executed sequentially and do not allow the previous steps to be completed and go to the next step My problem is that the top line result still does not return to the next line that is Angular and now the top line result is needed. sign() is method in javascript file and this.saveChallenge(result); is angular 7
angular :
this._loginService.saveSignature(opSign)
.pipe(map(response =>
this.goToProcess(opSign)
))
.subscribe( );
js file:
function sign() {
// my code ...
}
i resolve this problem .
i call angular method in js file .
in js file:
window.angularComponentReference.zone.run(() => { window.angularComponentReference.loadAngularFunction(); });
in angular :
constructor(private ngZone: NgZone) {
}
ngOnInit() {
window['angularComponentReference'] = { component: this, zone: this.ngZone, loadAngularFunction: () => this.angularFunctionCalled(), };
private angularFunctionCalled() {
....
}
I figured out how to pass parameters. By removing the arrow (which is not recognized by IE, by the way), it becomes clear:
In index.html:
<script type="text/javascript">
function callAngularFunction(param) {
window.angularComponentReference.zone.run(function() { return window.angularComponentReference.loadAngularFunction(param); });
}
</script>
Then, in the .ts file:
ngOnInit() {
window['angularComponentReference'] = { component: this, zone: this.ngZone, loadAngularFunction: (param) => this.angularFunctionCalled(param) };
...
}
angularFunctionCalled(param) {
...
}
ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'title' of undefined
I used the same code from 'heroes' example to load a detail object in a single route. I kept getting this error because, I think, the data is not loaded before the view already started to render and that's why I am getting this error.
I had this problem to display "currentUser.name" which I solved by using currentUser?.name but in this case, it doesn't make sense to add to all the places of the object properties with '?'.
I have to spend more time in OnInit than what heroes example did. Because I need to fetch more stuff. So I know that the view just kicks in much earlier than the binding object journal is loaded.
ngOnInit() {
this.userService.getUser().then( (user) => {
this.currentUser = user;
this.accountsService.getAccounts().then( (accounts) => {
this.accounts = accounts;
this.userAccounts = this.currentUser.accounts.map(
accountId => this.accounts.find(
elem => elem.id == new String(accountId)
)
);
this.route.params
.switchMap((params: Params) => this.journalService.getJournal(+params['id']))
.subscribe( (journal) => {
console.log("journal", journal);
this.journal = journal;
});
});
});
}
How can I instruct the view to wait until the data is loaded before it starts to render itself?
Or is there something wrong with the code?
You could wrap your template with a condition.
Steps:
1 - Create a variable and initializes it with a falsy value:
loaded: boolean = false;
2 - Set it to true when your request is finished:
this.route.params
.switchMap((params: Params) => this.journalService.getJournal(+params['id']))
.subscribe((journal) => {
console.log("journal", journal);
this.journal = journal;
this.loaded = true; // -> here
});
3 - In your template use *ngIf to prevent errors:
<ng-container *ngIf="loaded">
... content
</ng-container>
I've just started to have a play with meteor and I'm slowly moving a
NodeJS/Express app over to meteor. However on my blog section, I can't seem to get a single post _id into the helper. With NodeJS alone, I could use req.params._id.
Using the Differential boilerplate as a starting poing, (still using insecure and autopublish in dev) I have this:
//both/controllers/post.js
PostController = AppController.extend({
waitOn: function() {
return this.subscribe('posts');
},
data: function () {
return Posts.findOne({_id:this.params._id});
}
});
And in the router file
//both/router/router.js
Router.route('/posts/:_id', {
name: 'post'
});
The links on the main blog listing page, to the single posts are formated as follows.
//client/templates/blog/blog.html
<h3>{{ title }}</h3>
Then the helper file which seems to be the issue?
// /client/templates/post/post.js
Template.post.rendered = function() {
};
Template.post.helpers({
data: function () {
return Posts.findOne({_id:this.params._id});
}
});
Template.post.helpers({
createdAtFormatted: function () {
return moment(this.createdAt).fromNow();
}
});
I've tried this.params._id, request.params.id, req.params._id,
And other than setting an on click event and setting the _id in session (I don't like this idea, if it will even work efficiently/effectively). I'm stuck on ideas.