I have the Tour of Heroes app running, but I want to extend it to make ajax calls.
I have a WebAPI service that serves up the data (CORS enabled) and have proven it w/ a silly little non Angular client using JQuery $.post and $GetJson ... All was going well...
Here is my hero-details.component.ts file
(happy to include any others that may help...)
import {Component , Input, OnInit } from '#angular/core';
import { ActivatedRoute, Params } from '#angular/router';
import { Location } from '#angular/common';
import { HttpModule } from '#angular/http';
import 'rxjs/add/operator/switchMap';
import { Hero } from './hero';
import { HeroService } from './hero.service';
#Component({
selector: 'hero-detail',
templateUrl: './hero-detail.component.html',
styleUrls : ['./hero-detail.component.css']
})
export class HeroDetailComponent { // implements OnInit {
#Input() hero: Hero;
powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];
submitted = false;
constructor(
private heroService: HeroService,
private route: ActivatedRoute,
private location: Location,
$http //**--LINE OF INTEREST #2**
) { }
ngOnInit(): void {
this.route.params
.switchMap((params: Params) => this.heroService.getHero(+params['id']))
}
save(): void {
this.heroService.update(this.hero)
.then(() => this.goBack());
}
goBack(): void {
this.location.back();
}
onSubmit() { this.submitted = true; }
callService( ) {
var uri = 'http://localhost:61212/api/heros';
//**// LINE OF INTEREST #1**
$http({
method: 'GET',
url: uri
}).then(function () { alert('success'); }, function () { alert('fail');});
};
}
If I try to compile I get
TS2304: Cannot find '$http'
I can comment the $HTTP call (Line of Interest #1 ) and it compiles, it runs and i do enter the function and hit a breakpoint where i declare and assign the variable "uri". So I am reasonably sure I have the problem isolated.
So I believe, based on hours of googling, that I need to DI the $http object into this component
But when I pass $http into the constructor (LINE OF INTEREST #2) I get the following error when I try to compile
TS7006 Parameter '$http' implicitly has an 'any' type
I have googled this so much Larry and Sergy have asked me to knock it off.
What I have found is $http being passed into controllers, maybe Im missing something, but I can not seem to translate those articles into something that works for this.
1) Am I right that injecting the $http object is what needs to be done
2) What is the syntax?
when I was googling , i was just googling angular and most the articles were for angular1. Thats why I was seeing answers that involved controllers, etc.
angular2 is much different. If you are trying to get off the ground, try searching angular2 instead. at least the articles you run across will be relevant.
if you are using visual studio.. here is a nice link to get you started...
https://jonhilton.net/2016/12/01/fast-track-your-angular-2-and-net-core-web-app-development/
Related
I am new to Angular and following this tutorial to create a MailChimp submission form. I have replaced the list information & id and the tutorial with my own. On submission of the form, I want to redirect to a Thank You page, which was not shown in the tutorial.
When I submit user email to the list, I get a 200 response back from the server on my POST request.
However, I have two problems.
#1 The redirect does not navigate to the '/thanks' route. I'm not sure if this is the actual way this function should be used for navigation. I thought it would work similar to React's this.history.push. I got the basic idea for this function from this Stack Overflow question
subscribe-form-component.ts
export class SubscribeFormComponent implements OnInit {
subscribeData: any = <any>{};
constructor(
private subscribeService: SubscribeService,
private router: Router
) {}
ngOnInit() {}
onSuccess() {
this.router.navigate(['/thanks']);
}
subscribe(subscribeForm: NgForm) {
if (subscribeForm.invalid) {
return;
}
this.subscribeService.subscribeToList(this.subscribeData).subscribe({
complete: () => {this.subscribeData},
next: () => {this.onSuccess},
error: (err) => {
console.log('err', err);
},
});
}
}
However, in the console log console.log('err', err), though the submit form returns a 200 response from the sever, I did notice a JSONP error:
Error: JSONP injected script did not invoke callback.
message: "Http failure response for https://xxxxxxx.us11.list-manage.com/subscribe/post?u=afd1f3490xxxxxxxx7883fb&id=035xxxx952&f_id=009fa6e0f0&EMAIL=xxxxxx#icloud.com&c_afd1f34907923e052b17883fb_009fa6e0f0=&c=ng_jsonp_callback_0: 0 JSONP Error"
name: "HttpErrorResponse"
ok: false
status: 0
statusText: "JSONP Error"
url: "https://xxxxxx.us11.list-manage.com/subscribe/post?u=afd1f349xxxxxxx7883fb&id=035b97f952&f_id=009xxxxf0&EMAIL=xxxxx#icloud.com&c_afd1f34907923e052b17883fb_009fa6e0f0=&c=ng_jsonp_call
If my onSuccess navigation route function/syntax is correct, I'm assuming that the reason it is not redirecting is because of this error in the console.
subscribe.service.ts
import { Injectable } from '#angular/core';
import { HttpClient, HttpParams } from '#angular/common/http';
import { Router } from '#angular/router';
#Injectable({
providedIn: 'root'
})
export class SubscribeService {
mailChimpEndpoint = 'https://xxxxxx.us11.list-manage.com/subscribe/post?u=afd1f3490xxxxxxxxxb&id=035b9xxxx52&f_id=009faxxxf0';
constructor(
private http: HttpClient,
private router: Router
) { }
subscribeToList(data: any) {
const params = new HttpParams()
.set('EMAIL', data.email)
.set('afd1f3490xxxxxxxxxxb_009fa6e0f0', '');
const mailChimpUrl = `${this.mailChimpEndpoint}&${params.toString()}`;
return this.http.jsonp(mailChimpUrl, 'c')
}
}
How do I fix this JSON P error and correctly redirect after submission?
By default, JSONP will cause the error that you are seeing when using the Angular HttpClient.
There is a HttpClientJsonpModule that can be used instead of the HttpClientModule, and it does support JSONP.
Documentation is at https://angular.io/api/common/http/HttpClientJsonpModule
I couldn't seem to find any related question to my problem on here, so i decided to ask my first question, please be gentle :>
I want to provide an endpoint for frontend logging in several of our backends that use nestjs.
To solve this, i want to use a dynamic module that exposes said endpoint and package it as an npm package to be used in other applications.
I inject a logger into the controller via an injection token and the logging service via nestjs standard dependency injection.
The problem now is, after publishing the module, installing it in another application, the service i injected via nestjs standard DI is undefined.
I tried to follow the nestjs documentation as closly as possible when it comes to dynamic modules.
I also tried to retrieve the service via ModuleRef, but still the same problem. In this case, moduleRef is undefined.
Here is basically all of the involved code:
frontend-log.module.ts:
import { DynamicModule, Module } from "#nestjs/common";
import { FrontendLogService } from "./frontend-log.service";
import { Logger } from "./interfaces/logger";
import { FrontendLogController } from "./frontend-log.controller";
#Module({})
export class FrontendLogModule {
static forRoot(logger: Logger): DynamicModule {
return {
module: FrontendLogModule,
controllers: [FrontendLogController],
providers: [FrontendLogService, { provide: "LOGGER", useValue: logger }],
};
}
}
frontend-log.controller.ts:
import { Body, Controller, Inject, Post, Req } from "#nestjs/common";
import { FrontendLogMessage } from "../model/frontend-log-message";
import { Request } from "express";
import { RequestWithToken } from "../model/request-with-token";
import { Logger } from "./interfaces/logger";
import { ArrayMaxSize, ArrayMinSize, ValidateNested } from "class-validator";
import { Type } from "class-transformer";
import { FrontendLogService } from "./frontend-log.service";
class FrontendLogData {
#Type(() => FrontendLogMessage)
#ArrayMinSize(1)
#ArrayMaxSize(20)
#ValidateNested({ each: true })
messages: FrontendLogMessage[];
}
#Controller()
export class FrontendLogController {
constructor(
#Inject("LOGGER") private logger: Logger,
private logService: FrontendLogService
) {}
#Post("log")
public log(#Req() req: Request, #Body() data: FrontendLogData) {
this.logService.logFrontendMessages( // logService is undefined here
this.logger,
data.messages,
(req as RequestWithToken).kauth.grant.access_token.content
);
}
}
frontend-log.service.ts:
import { Injectable } from "#nestjs/common";
import { FrontendLogMessage } from "../model/frontend-log-message";
import { AccessTokenContent } from "../model/request-with-token";
import { Logger } from "./interfaces/logger";
#Injectable()
export class FrontendLogService {
public logFrontendMessages(
logger: Logger,
data: FrontendLogMessage[],
accessToken?: AccessTokenContent
) {
const user = accessToken
? `${accessToken.name} (${accessToken.sub})`
: "anonymous";
data.forEach((logEntry) => {
logger.log(logEntry.level, `[FRONTEND] [${user}] ${logEntry.message}`);
});
}
}
I then build the package and publish it.
After installing it in the consuming project I import the frontend-log.module into the AppModule as follows:
#Module({
imports: [
// ...
FrontendLogModule.forRoot(getLogger('debug')),
],
controllers: [
// ...
],
providers: [
// ...
],
})
export class AppModule implements NestModule {
// ...
}
When i then try to post to the /log endpoint via Postman, the following error occurs:
[Nest] 15280 - 26.10.2022, 09:03:50 ERROR [ExceptionsHandler] Cannot read properties of undefined (reading 'logFrontendMessages')
TypeError: Cannot read properties of undefined (reading 'logFrontendMessages')
at FrontendLogController.log
This tells me, the frontend-log.controller works properly, since the log endpoint is exposed as expected.
So my question: What am I doing wrong here, is there something i am not considering, when it comes to publishing nestjs Modules over npm? Or am I doing some rookie mistake here?
I want to read a response from a get http request, my server is in Javascript and the part of the where I send a response is:
app.get('/getReport',function(req,res) {
try {
const data=fs.readFileSync('./report.txt', 'utf8')
res.end(data)
}
catch (err) {
res.end("File not found")
}
})
If if I use Postman I can see the content of the report file.
I want to read this text on my Angular front-end so I create this function in my home.service.ts:
public getReport(){
return this.http.get<any>(this.url+'/getReport').subscribe(data => {
this.value = data;})
}
It doesn't work. what can I do to read my content correctly?
Check your setup. You have to import some modules before get http requests:
#NgModule({
imports: [
BrowserModule,
// import HttpClientModule after BrowserModule.
HttpClientModule,
],...
In order to recive any response, you have to start a communication with your API/server subscribing to the 'getReport' method.
HOW CAN YOU DO THAT?
You have that method in your home.service.ts.
Inject that service in the component where you want to load your report (i.e 'home-component'):
// NOTE: Fit all the paths to your specific system
...
import { HomeService } from '../services/home.service';
...
#Component({
selector: 'app-home-component',
templateUrl: './home-component.html',
styleUrls: ['./home-component.scss'],
})
export class HomeComponent {
public report;
constructor(
...
private homeService: HomeService,
...
) {}
Subscribe to the observable 'getReport' method of homeService, and get the desired data.
For instance, change the constructor like this:
constructor(
...
private homeService: HomeService,
...
) {
this.homeService.getReport
.subscribe((_response) => {
console.log(_response);
this.report = _response;
alert(this.report);
});
}
I have a difficult case which was organized from an old angularjs and angular8, besides it's was also limited in the extensive refactoring to resolve in code of micronization.And the code of angularjs cannot be changed.
In the end I choose the library ngx-planet that is closest to my situation, but i got this error
Issue:
When I then local run it, I get this error message.
I have tested this way of writing under angularjs and angular8 code without planet library, it can work, but after using planet, the aforementioned error occurred.
After searching, it was found that the reasons for this error message are known as the following 6 types:
Barrel index
Circularity dependency
Forgot to enable polyfills import'core-js/es6/reflect'
Injectable decorator incorrect usage (EX: missing # or capital & lower case error etc...)
Tsconfig does not configure emitDecoratorMetadata
Decorate parameter type use any in your constructor
The first 5 have been excluded, I suspect it is the last, because of this Configuring Dependency Injection in Angular
But I am confused, whether a certain configuration of planet causes parameter type to fail?
Code Structure:
1. There is a common service exported from angularjs
(File name: angular1-root-module.js)
(function () {
angular.module('angular1', [
'angular1.export-service'
]);
angular.module('angular1.export-service', []);
angular.module('angular1.export-service').factory('Angular1ExportService', Angular1ExportService);
Angular1ExportService.$inject = [];
function Angular1ExportService() {
function outPutString() {
return 'I from Angular1 export service string';
}
return {
outPutAngular1String: outPutString,
};
}
})();
2. Inject into the class named Angular1InjectorService through the factory provider and depend on angularjs's $injector
export function Angular1InjectorServiceFactory(injector: any) {
return new Angular1InjectorService(injector);
}
export const Angular1InjectorServiceProvider = {
provide: Angular1InjectorService,
useFactory: Angular1InjectorServiceFactory,
deps: ['$injector']
};
#Injectable()
export class Angular1InjectorService {
// I think probably this injector of type is any cause
constructor(private angular1Injector: any) {
}
getService(serviceName: String) {
return this.angular1Injector.get(serviceName);
}
}
3. Then inject into the common AppBaseService
#Injectable()
export class AppBaseService {
readonly angular1InjectorService: Angular1InjectorService;
readonly testService: any;
constructor(readonly injector: Injector) {
this.angular1InjectorService = this.injector.get(Angular1InjectorService);
this.testService = this.angular1InjectorService.getService('Angular1ExportService');
}
testGetAngular1String() {
console.log('app base service is work!');
return this.testService.outPutAngular1String();
}
}
4. Then the service of the sub app inherits AppBaseService, and obtains the method that exists in angularjs
export class App1RootService extends AppBaseService {
constructor(readonly injector: Injector) {
super(injector);
}
GetLogAngular1String() {
console.log('app1 root service is work!');
return this.testGetAngular1String();
}
}
5. Portal root planet config
this.planet.registerApps([
{
name: 'app1',
hostParent: '#app-host-container',
routerPathPrefix: '/app1',
selector: 'app1-root-container',
resourcePathPrefix: '/static/app1',
preload: settings.app1.preload,
switchMode: settings.app1.switchMode,
loadSerial: true,
manifest: '/static/app1/manifest.json',
scripts: [
'main.js'
],
styles: [
],
}
]);
6. Sub app1 main config
defineApplication('app1', (portalApp: PlanetPortalApplication) => {
return platformBrowserDynamic([
{
provide: PlanetPortalApplication,
useValue: portalApp
},
{
provide: AppRootContext,
useValue: portalApp.data.appRootContext
}
])
.bootstrapModule(App1RootModule)
.then(appModule => {
return appModule;
})
.catch(error => {
console.error(error);
return null;
});
});
Issue related resources:
EXCEPTION: Can't resolve all parameters
Uncaught Error: Can't resolve all parameters for
After upgrade Angular to v8: Can't resolve all parameters for Component:
Intact issue code:
Stackblitz
Github
I have not found an answer, and already have the official github open issue, but hope to get more help
Thanks.
I have 2 pages Page1 and Page2. I have used this.nav.pop() in Page2 and it will pop the Page2 and Page1 will enable but i want to refresh the Page1.
Thank you in advance.
you could pass the parent page along with the nav push. that way you could accces the parent page as a navParamter.
in parent page:
goToChildPage() {
this.navCtrl.push(ChildPage, { "parentPage": this });
}
and in the child page before pop you could call functions on parent page
this.navParams.get("parentPage").someFnToUpdateParent();
//or
this.navParams.get("parentPage").someFnToRefreshParent();
Ignore the direct angular implementations suggested here, especially since you are using Ionic 2 and the suggestions are assuming Ionic 1. Don't start mixing too much of direct angular in your ionic app unless there is no ionic implementation for what you need. Import "Events" from ionic/angular2 in both Page1 and Page2, then in Page2 do something like
this.events.publish('reloadPage1');
this.nav.pop();
And in Page1 put
this.events.subscribe('reloadPage1',() => {
this.nav.pop();
this.nav.push(Page1);
});
You may want to implement one of these in your page:
ionViewWillEnter
ionViewDidEnter
Please review the navController and page lifecycle documentation:
http://ionicframework.com/docs/v2/api/components/nav/NavController/
Simple solution that worked for me was calling the get service method again in ionViewDidEnter
ionViewDidEnter() {
this.loadGetService();
}
On PAGE 1:
import { Events } from 'ionic-angular'
constructor(public events:Events){
this.listenEvents();
}
... ...
listenEvents(){
this.events.subscribe('reloadDetails',() => {
//call methods to refresh content
});
}
On PAGE 2:
import { Events } from 'ionic-angular'
constructor(public events:Events, public navCtrl:NavController){
}
function(){
this.events.publish('reloadDetails');
this.navCtrl.pop();
}
You may consider send an event before call this.nav.pop to let page 1 reload itself.
Like Jonathan said, you can import Events from ionic-angular, but you don't need push and pop again, call your methods to reload only the content.
In page2:
this.events.publish('reloadDetails');
this.navCtrl.pop();
In page1:
this.events.subscribe('reloadDetails',() => {
//call methods to refresh content
});
That works for me.
I simply load the details in page 1 in an ionViewWillEnter function (using Ionic 2). This handles both the initial load and any refresh when popping back to page 1.
Documentation is here.
ionViewWillEnter
"Runs when the page is about to enter and become the active page."
I found this technique to reload a page:
this.navCtrl.insert(1, MyPage);
this.navCtrl.pop();
I had the same problem and spend many hours searching and trying the solution.
If I understand, your problem is:
Page 1 have some bindings that you get from an API / Webservice.
Page 2 have some inputs and when pushing the back button (pop) you want to SAVE data + refresh the Page 1 bindings.
The way I solved it has been reading a post on StackOverflow that now I can't find :( !!
The solution is using an Injectable Service.
PAGE 1:
/* IMPORTS */
import { App, Nav, NavParams } from 'ionic-angular';
import { Oportunidades } from '../../services/oportunidades.service';
/* SOME BINDINGS HERE */
{{oportunidades.mesActual.num_testdrive}}
/* CONSTRUCTOR */
constructor(
private oportunidades: Oportunidades, // my injectable service!
public app: App,
public nav: Nav,
public params: NavParams
) {
// Call to the Injectable Service (named oportunidades):
this.oportunidades.getOportunidades();
}
INJECTABLE SERVICE:
/* IMPORTS */
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Injectable()
export class Oportunidades {
public url = 'http://your-API.URL';
public data: Observable<Object>;
public mesActual: Object = [];
constructor(private http: Http){
//GET API DATA
this.data = http.get(this.url).map(res => res.json());
}
getOportunidades() {
this.data.subscribe(data => {
this.mesActual = new MesActual(
data["mes_actual_slide"].num_testdrive,
...
//Here I get the API data and set it on my injectable object
);
});
}
}
PAGE 2:
/* SOME IMPORTS */
import { NavController } from 'ionic-angular';
import { UserData } from '../../services/data.service';
import { Oportunidades } from '../../services/oportunidades.service';
import { Http, Headers, URLSearchParams } from '#angular/http';
/* SOME example BINDINGS and INPUTS: */
#Component({
template: `
{{ day[selectedDay].dia }}
Input data:
<ion-input type="number" clearOnEdit="true"
#ventas id="ventas" value={{day[selectedDay].ventas}}
(keyup)="setVal(ventas.value, $event)">
</ion-input>
`,
providers: [
]
})
export class PageInsert {
constructor(
public navCtrl: NavController,
private http: Http,
private userData: UserData,
public oportunidades: Oportunidades // my injectable service!
) {
send(selectedDay){
var url = 'http://your.api.url/senddata';
// I SAVE data TO API / Webservice
this.http.post(url, params, { headers: headers })
.map(res => res.json())
.subscribe(
data => {
console.log(data);
// Here i'll call to the Injectable service so It refresh's the new data on Page1
// in my case, this send function is called when "pop" or "back" button of ionic2 is pressed
// This means: On click on back button -> Save and refresh data of the Injectable that is binded with the Page1
this.oportunidades.getOportunidades();
return true; },
error => {
console.error("Error saving!");
}
);
}
}
I hope it can help you!! Ask for any similar problems :)
I spent a day and a half on a similar issue. The solution is anti-climatic really.
I'm passing a FormGroup from Page-1 to Page-2. I update the FormGroup values in Page-2. When I pop Page-2, Page-1's form is not updated with the new values. It hasn't been watching for changes.
To fix this, I patch the FormGroup with itself after Page-2 has been popped but still in the Page-2 component.
This is more responsive, but requires a direct call to close().
// Page-2 close method
public close(): void {
this.navCtrl.pop();
this.formGroup.patchValue(this.formGroup.value);
}
This is all encompassing, but I do see the refresh on Page-1.
// Page-2 nav controller page event method
ionViewWillUnload() {
this.formGroup.patchValue(this.formGroup.value);
}
In some situations instead of pop() you can use the push() function. When you enter the page with the push() function it is reloaded.
Also you can remove page2 from the navigation.
this.navCtrl.push(TabsPage).then(() => {
const index = this.viewCtrl.index;
this.navCtrl.remove(index);
});
Or if you have more than one page for example page1->page2->pag3:
this.navCtrl.push(TabsPage).then(() => {
const index = this.viewCtrl.index;
this.navCtrl.remove(index, 2); //this will remove page3 and page2
});
ionViewWillEnter() {
this.refresh();
}
ionViewWillEnter will be called just before any time you (re)visualize the page
Please checkout my solution, which I've posted here:
https://forum.ionicframework.com/t/ionviewdidenter-is-not-invoked-on-leaving-a-pushed-page/131144/19?u=unkn0wn0x
Maybe you can adapt it to your needs.
The main point of my intention was to prevent, passing a whole module with this as a navCtrlParam into the push()'ed page, like it was mentioned in some comments before.
Hope it helps!
Cheers
Unkn0wn0x