I was using JQuery for http requests to .NET controllers (I'm using .NET MVC 4.5.2), but am now starting to use Angular 2, so I want to handle those JQuery AJAX calls with Angular 2 instead. Here is the JQuery I was using before, which worked just like I wanted:
$.get('/Plan/Variety/ListVarietiesInMenuForSelling')
.done(function(data){
console.log(data)
});
How do I set up my Angular 2 service to accomplish the same thing? I've tried the code below:
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class SellingMenuVarietiesService {
private url = '/Plan/Variety/ListVarietiesInMenuForSelling'; // URL to web api
constructor(private http: Http) { }
getVarieties() {
return this.http.get(this.url);
}
}
Unfortunately this doesn't work. Instead, I get this error in the console:
EXCEPTION: Uncaught (in promise): TypeError: Cannot read property '0' of undefined core.umd.js:3462
The only examples I've been able to find handle JSON data and then use Angular to parse the data and format it into HTML. My controller already returns the HTML I need, so I don't need Angular to parse JSON. How do I get the http request to work like it did when I was using JQuery?
Just to be clear, for testing purposes I changed return this.http.get(this.url); to return '<h1>test data</h1>'; and was able to get that to display correctly, so I know that the only issue is with the http request.
EDIT:
Here is the code where I call getVarieties():
export class SellingMenuVarietiesComponent implements OnInit {
varietyListSelling: any;
constructor(
private router: Router,
private sellingMenuVarietiesService: SellingMenuVarietiesService) { }
ngOnInit(): void {
this.varietyListSelling = this.sellingMenuVarietiesService.getVarieties();
console.log('initializing SellingMenuVarietiesComponent');
}
}
And here is how I bind it to the HTML:
<div [innerHtml]="varietyListSelling"></div>
I used that instead of {{varietyListSelling}} because I need the string to be converted to HTML.
UPDATE
I upgraded TypeScript and removed my InMemoryWebApiModule and InMemoryDataService imports from app.module.ts, which is resulting in a new error. The error now says: EXCEPTION: Unexpected token < in JSON at position 4
Is this because we are turning my HTML data into JSON? How can I just return the HTML like my JQuery was doing?
You could do something like this:
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/toPromise';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class SellingMenuVarietiesService {
private url = '/Plan/Variety/ListVarietiesInMenuForSelling'; // URL to web api
constructor(private http: Http) { }
public getVarieties(): Observable<any> {
return this.http.get(this.url).map(response=>{return response});
}
}
then to consume the service:
this.sellingMenuVarietiesService.getVarieties().subscribe(res => {
console.log(res);
});
Update:
The html you are trying to render is not rendering because the request has not completed when the template is being rendered. A possible solution for this is to add an ngIf to the div tag.
div *ngIf="varietyListSelling" [innerHtml]="varietyListSelling"></div>
You must subscribe to really send the request and read the response.
getVarieties() {
return this.http.get(this.url).map((response: Response) => {
return response.json();
},(error) => {
console.log(error);
//your want to implement your own error handling here.
});
}
And your component would look like
ngOnInit(): void {
this.sellingMenuVarietiesService.getVarieties().subscribe((res) => {
this.varietyListSelling = res;
});
console.log('initializing SellingMenuVarietiesComponent');
}
"Cannot find name 'Response'" and "Property 'map' does not exist on type 'Observable' I have same issue but it's not a problem , It show error .map but it's work.
Related
I'm trying to practice using ActionCable in Angular. I created a quick Rails application that I put up on Heroku and then created an Angular application with the actioncable npm module as a dependency.
I configured my Rails appplication to allow http://localhost:4200 as an origin while I play around with my Angular app in development. I also didn't make this an API application because I wanted to have a working UI from the get-go. So I can log into the Rails application, send a message, and my separate Angular application is subscribed to that channel as well. I'm successfully receiving those notifications/messages.
Now I'd like to render something in Angular based on that message. I think I'm missing something pretty silly here, but I cannot refer to methods in the component that instantiates the subscription to that channel in the receive callback of the subscription.
import {
ComponentFactoryResolver,
ComponentRef,
OnInit,
ViewContainerRef,
Component,
ViewChild,
Output
} from '#angular/core';
import * as ActionCable from 'actioncable';
import { MessageComponent } from 'app/message/message.component';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
messageRef: ComponentRef<MessageComponent>;
#ViewChild('message', { read: ViewContainerRef }) message: ViewContainerRef;
title = 'app works!';
private cable: ActionCable.Cable;
private subscription: ActionCable.Channel;
constructor(
private componentFactoryResolver: ComponentFactoryResolver,
private viewContainerRef: ViewContainerRef) {
}
public ngOnInit(): void {
this.cable = ActionCable.createConsumer('wss://<my-heroku-app>.herokuapp.com/cable');
this.subscription = this.cable.subscriptions.create(
'RoomChannel',
{
connected: this.connected,
disconnected: this.disconnected,
received: this.received,
});
}
private showMessage(messageString) {
if (!this.messageRef) {
const messageComponent = this.componentFactoryResolver.resolveComponentFactory(MessageComponent);
this.messageRef = this.message.createComponent(messageComponent);
}
this.messageRef.instance.message = messageString;
this.messageRef.changeDetectorRef.detectChanges();
}
private connected() {
console.log('connected!');
}
private disconnected() {
console.log('disconnected!');
}
private received(data: any) {
console.log('received');
// What do I put here? `this` is of type Subscription,
// and thus, I can't call `this.showMessage(data.message)`
}
}
I want to use some sort of predicate or inject something into that context (sorry if I'm not using the right terminology), but I am just not sure how to do this. I plan on cleaning things up instead of having this all in the AppComponent class, but for now I'm just trying to learn.
Any ideas? Thanks!
The answer seems to be this:
this.cable = ActionCable.createConsumer(this.url)
this.subscription = this.cable.subscriptions.create(
'RoomChannel',
{
connected: this.connected,
disconnected: this.disconnected,
// The key here, apparently, is to use a fat-arrow
// function so that the `this` I care about is
// lexicographically scoped. I still need to better
// understand what exactly that means, but I have a
// general idea.
received: (data) => this.received(data)
});
I am having a component that has canactivate
import {isLoggedIn} from '../login/isLoginedIn';
#CanActivate((next, previous) => {
isLoggedIn()
})
My "isLoggedIn" is as below
import {Http, Headers} from 'angular2/http';
class Auth {
constructor( #Inject(Http) private _http: Api) { }
check() {
this._http.get('/Users/LoggedInUser')
}
}
export const isLoggedIn = () => {
let injector = Injector.resolveAndCreate([Auth, Http]);
let auth = injector.get(Auth);
return auth.check();
};
I can't inject a service which has http as dependancy. Can this be done like this or is there a better way to do it?
Since the CanActivate is a decorator instead of a method as with OnActivate or CanDeactivate then you are correct in assuming that constructor dependency injection of the component that you are attempting to authorize is not an option.
The method which you are using will work, but there is a missed #Injectable() on your Auth class.
import {Injectable} from 'angular2/core';
import {Http, Headers} from 'angular2/http';
#Injectable()
class Auth {
constructor( #Inject(Http) private _http: Api) { }
check() {
this._http.get('/Users/LoggedInUser')
}
}
This approach is sound and I don't think that besides some syntactic sugar or minor refactoring that there would be much to improve this and still achieve the same amount of readability / maintainability for this approach.
One other addition that could be made to improve the flow and prevent a potential bug would be to return the observable in CanActivate so that the navigation will wait for the Http request to complete before deciding to continue or cancel.
#CanActivate((next, previous) => {
return isLoggedIn()
})
or for short
#CanActivate(() => isLoggedIn())
(Single statement arrow functions are auto-returning)
This question already has answers here:
Angular2 Beta dependency injection
(3 answers)
Closed 7 years ago.
I am using angular2 Beta. and getting error when using the #Inject annotation to DI my one service to another, not able to figure out where I am wrong. Everything seem to be as per Angular2 documentation.
I am using a cloud based data-services - CloudDB - for my application's data needs.
CloudDB gives me a javascript based client library that I can include in my js app and use to do CRUD operations in my cloudDB database or call other custom API I have stored in my CloudDB account, like UserAuth API (API to authenticate user's credentials).
Before using cloudDB js client lib API , I need to supply my cloudDB account's URL and authKey by calling CloudDB js object's getClient method.
In my angualar2 app, I created a injectable service class - CloudDBProvider - the would store my CloudDB account URL and authKey and call CloudDB.getClient to set the provider's js client object for my CloudDB account.
import {Injectable} from 'angular2/angular2';
///<reference path="../typeDefs/CloudDB.d.ts" /> //typedef of CloudDB js library
#Injectable()
export class CloudDBProvider {
private cloudDBClient: CloudDB.JSClient;
public get cloudDBClient(): CloudDB.JSClient {
return this.cloudDBClient;
}
constructor() {
this.cloudDBClient = new CloudDB.getClient(
"https://myaccount.CloudDB.com/",
"AcfdsfmyDdCMHeadfsdsdfHdsf68" // account authKey
);
}
}
Now, I want to create a UserUtils service in this angular2 app, to which I want to inject above class to get cloudDBClient object. I coded UserUtils service class like below, as learnt from your tutorial
import {Injectable, Inject} from 'angular2/angular2';
import {CloudDBProvider} from './CloudDBProvider';
#Injectable()
export class UserUtils {
private _userDetails: Object = {};
private _cloudDBProvider: CloudDBProvider;
private _cloudDBClient: Microsoft.WindowsAzure.MobileServiceClient;;
constructor( #Inject(CloudDBProvider) cloudDBPrvdr: CloudDBProvider) {
this._cloudDBProvider = cloudDBPrvdr;
this._cloudDBClient = this._cloudDBProvider.cloudDBClient; //the public getter property in the class CloudDBProvider
}
public authenicateUser(p_strUserName: string, p_strUserPassword: string) {
var p: Promise<any> = new Promise(
(resolve: (result: any) => void, reject: (err: any) => void) =>
this._cloudDBClient.userlogin(p_strUserName, p_strUserPassword).done( //using API 'userlogin' of cloudDB to authenticate user against my cloudDB's users table.
(loginResult) => {
alert("from Userutils - You are now logged in as: " + loginResult.user.basicProfile.firstName);
resolve(loginResult);
},
(loginErr: any) => {
alert("Error: " + loginErr.request.responseText);
reject(loginErr);
}
)
);
return p;
}
}
then I am trying to use UserUtils in my LoginPage component like below:
import {Component} from 'angular2/core';
import {WelcomePage} from "../views/welcome/welcome";
import {UserUtils} from "../services/UserUtils";
#Component({
templateUrl: 'app/login/login.html',
providers: [UserUtils]
})
export class LoginPage {
private _userUtils: UserUtils;
constructor( userUtils: UserUtils) {
this._userUtils = userUtils;
}
public loginButtonClicked(event, userName, password) { //called when Login Button is clicked by user
//...
//... to-do field value verification
//...
this._userUtils.authenicateUser(userName, password).then(
(result) => {
//navigate to WelcomePage
},
(err) => { alert(err); }
);
}
}
the component LoginPage doesn't work when I use UserUtils. The browser console throws error - No provider for CloudDBProvider! (LoginPage -> UserUtils -> CloudDBProvider)
Note that, if I move the 'authenicateUser' method from UserUtils to CloudDBProvider directly and use CloudDBProvider in LoginPage component for user authentication, then everything works just fine, user gets authenticated and navigated to welcome page after login. Also, no error is thrown and app working if I remove #Inject(CloudDBProvider) cloudDBPrvdr from UserUtils's constructor obviously I cannot use CloudDBProvider then in UserUtils, but point is app doesn't throw any error, which means something is wrong with #Inject.
any clue where I am going wrong?
Upto my Understanding your mistake is in the imports change the import of Injectablewith this
import {Component, Inject, Injectable} from 'angular2/core';
also accoriding to me when we have used #injectable annotation no need to use #inject in the constructor you simply put your service with the public identifier and can use that service into any another method of the same class.
Perhaps you could add the CloudDBProvider provider in the list of providers of your component:
#Component({
templateUrl: 'app/login/login.html',
providers: [UserUtils, CloudDBProvider]
})
export class LoginPage {
(...)
}
Or at application level within the second parameter of the bootstrap function:
bootstrap(MainComponent, [CloudDBProvider]);
This answer could give you some additional hints: Angular2 Beta dependency injection.
Hope it helps you,
Thierry
How can I configure the Http service adding headers to the call.
I try the following
class GlobalHttpHeaders {
static setup(Injector inj){
HttpDefaultHeaders http = inj.get(HttpDefaultHeaders);
http.setHeaders([{"X-Requested-With":"XMLHttpRequest"}], "COMMON");
}
}
And in the app the last line is:
Injector inj = ngBootstrap(module: new SiteIceiModule());
GlobalHttpHeaders.setup(inj);
But that don't work.
(I think) I got it working with:
#Injectable()
class MyDefaultHeaders extends HttpDefaultHeaders {
#override
setHeaders(Map<String, String> headers, String method) {
super.setHeaders(headers, method);
//if(method.toUpperCase() == 'POST') {
headers['X-Requested-With'] = 'XMLHttpRequest';
//}
}
}
#NgComponent(selector: 'my-comp', publishAs: 'ctrl', template:
'<div>My component</div>')
class MyComponent {
Http http;
MyComponent(this.http) {
//http.request('http://www.google.com/');
var h = http;
// debugger didn't show http so I filed https://code.google.com/p/dart/issues/detail?id=17486
Map m = {};
http.defaults.headers.setHeaders(m, 'GET');
print(m);
// prints:
// {Accept: application/json, text/plain, */*, X-Requested-With: XMLHttpRequest}
}
}
class MyAppModule extends Module {
MyAppModule() {
type(MyComponent);
type(HttpDefaultHeaders, implementedBy: MyDefaultHeaders);
init.createParser(this);
}
}
I couldn't examine http to verify the headers because the debugger didn't show me the field but as stated in the comment
when I apply headers.setHeaders do a map inside MyComponent I get my custom header (this is what Http does with headers)
I used DI 0.0.33, Angular 0.9.9
I'm a little late to the discussion, but the answer provided was not usable for me, as my http requests are made by a 3rd party library. But I figured out a way to change the default headers.
You can access and modify the HttpDefaultHeaders object like a map.
headers['Common'].addAll({'Authorization': 'Basic $auth', 'X-Testing': 'Testing'});
This also works with 3rd Party libraries like hammock.
Note: I used angular 1.1.1 I don't know in which version this was added.
Have you tried something like this:
class SiteIceiModule extends Module {
SiteIceiModule() {
// ...
factory(HttpDefaultHeaders, (inj) => inj.get(HttpDefaultHeaders)..setHeader({...}));
// ...
}
}
I'm using AngularDart for a new application of mine. I have a component set up like so:
#NgComponent(
selector: 'game-timeline',
templateUrl: '/static/dart/game/web/views/timelineview.html',
cssUrl: '/static/dart/game/web/views/timelineview.css',
publishAs: 'ctrl'
)
But my problem is, the template and css locations aren't necessarily known at build-time. They'll be on a CDN with a unique deploy-identifier on them. Something like...
http://cdn.domain.com/static/30294832098/dart/game/web/views/timelineview.html
And that number varies every deploy.
The path seems to be relative to the html page it's hosted on, which is not on the CDN, so I can't just use a relative path (../blah/blah)
I can inject a JS variable into the page telling me where the static root it, if that helps.
The main .dart/.js file is loaded from the CDN, but I can't seem to make it be relative to that.
Any ideas?
Update, here's my full solution adapted from pavelgj's great answer that reads in a js variable called STATIC_URL on the page.
import 'package:angular/angular.dart';
import 'package:js/js.dart' as js;
class CDNRewriter implements UrlRewriter {
String staticUrl;
CDNRewriter() {
var context = js.context;
staticUrl = js.context.STATIC_URL;
}
String call(String url) {
if (url.startsWith('/static/')) {
return _rewriteCdnUrl(url);
}
return url;
}
String _rewriteCdnUrl(String url) {
return url.replaceFirst(new RegExp(r'/static/'), staticUrl);
}
}
You can implement a custom UrlRewriter. UrlRewriter is called for all resources fetched via angular Http service, including templates and css.
class MyUrlRewriter implements UrlRewriter {
String call(String url) {
if (url.startsWith('/static/')) {
return _rewriteCdnUrl(url);
}
return url;
}
}
myModule.type(UrlRewriter, implementedBy: MyUrlRewriter);