<script lang="ts">
import { Vue, Component } from 'vue-property-decorator'
#Component
export default class Index extends Vue {
form = {
name: '',
adress: '',
age: '',
items: [],
}
async fetch() {
this.form.items = await fetch('https://api.nuxtjs.dev/posts').then(c => c.json())
console.log(this.form.items)
}
fetchOnServer = true;
changeLanguage(language: string) {
this.$i18n.setLocale(language);
}
}
</script>
async fetch dont work in class component
If I use a standard component it works fine. But if I use a class component, the function call stops working.
What is the problem and how can it be fixed
As an annotation argument it works
#Component({
components: {ValidationProvider, ValidationObserver},
async fetch() {
console.log('** fetch at ');
console.log(this.$route);
await new Promise((resolve) => {
setTimeout(resolve, 1000);
});
}
})
export default class xrayClient extends Vue {
As class methods created() works:
async created() {
console.log('** created ');
console.log(this.$route);
await new Promise((resolve) => {
setTimeout(resolve, 1000);
});
Mounted() works:
async mounted() {
console.log('** mounted ' + this.data);
await new Promise((resolve) => {
setTimeout(resolve, 1000);
});
}
Calling
async asyncData()
and
fetch() {
Don't run.
"nuxt": "^2.15.7",
Instead of vue-property-decorator, you should use nuxt-property-decorator.
Then you can specify your asyncData or fetch methods in the component options, like this:
<script lang="ts">
import { Vue, Component } from 'nuxt-property-decorator'
#Component({
async fetch() {
this.form.items = await fetch('https://api.nuxtjs.dev/posts').then(c => c.json())
console.log(this.form.items)
}
})
export default class Index extends Vue {
form = {
name: '',
adress: '',
age: '',
items: [],
}
changeLanguage(language: string) {
this.$i18n.setLocale(language);
}
}
</script>
See this GitHub issue comment for details: https://github.com/nuxt/nuxt.js/issues/5330#issuecomment-475595112
I'm getting this error in the following code. I'm using ionic3:
Property 'catch' does not exist on type 'PromiseLike
This is the link to the tutorial that I am following.
This error is showing in the VS Code.
This probably some updated syntax I'm not aware of
storetoken(t) {
this.afd.list(this.firestore).push({
uid: firebase.auth().currentUser.uid,
devtoken: t
}).then(() => {
alert('Token stored');
})
.catch(() => {
alert('Token not stored');
})
this.afd.list(this.firemsg).push({
sendername: 'vivek',
message: 'hello'
}).then(() => {
alert('Message stored');
})
.catch(() => {
alert('Message not stored');
})
}
**This is the entire code for the home.ts file which sends token onto firebase database:Please refer this as well, as i'm also getting another error:Error: Uncaught (in promise): Error: StaticInjectorError[AngularFireDatabase] when I remove the catch block. **
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { AngularFireDatabase } from 'angularfire2/database';
import firebase from 'firebase';
import { HttpClientModule } from '#angular/common/http';
import { HttpModule } from '#angular/http';
declare var FCMPlugin;
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
firestore = firebase.database().ref('/pushtokens');
firemsg = firebase.database().ref('/messages');
constructor(public navCtrl: NavController, public afd:
AngularFireDatabase) {
this.tokensetup().then((token) => {
this.storetoken(token);
})
}
ionViewDidLoad() {
FCMPlugin.onNotification(function(data){
if(data.wasTapped){
//Notification was received on device tray and tapped by the user.
alert( JSON.stringify(data) );
}else{
//Notification was received in foreground. Maybe the user needs to be
notified.
alert( JSON.stringify(data) );
}
});
FCMPlugin.onTokenRefresh(function(token){
alert( token );
});
}
tokensetup() {
var promise = new Promise((resolve, reject) => {
FCMPlugin.getToken(function(token){
resolve(token);
}, (err) => {
reject(err);
});
})
return promise;
}
storetoken(t) {
this.afd.list(this.firestore).push({
uid: firebase.auth().currentUser.uid,
devtoken: t
}).then(() => {
alert('Token stored');
}).catch(() => {
alert('Token not stored');
})
this.afd.list(this.firemsg).push({
sendername: 'vivek',
message: 'hello'
}).then(() => {
alert('Message stored');
}).catch(() => {
alert('Message not stored');
})
}
}
push returns a ThenableReference and not a Promise.
Combined Promise and Reference; resolves when write is complete, but can be used immediately as the Reference to the child location.
It means you can use it as a future reference to the written data.
See also in codebase.
ThenableReference Def here.
export interface ThenableReference extends Reference, PromiseLike<Reference> {}
Or you can do the recommended way i.e:
You cannot use catch currently.
this.afd.list(this.firestore).push({ uid: firebase.auth().currentUser.uid, devtoken: t });
Note: There is an open issue here if you want to follow it.
I am using Redux Saga to handle async requests. I have tried to go onto to other posts but there were not of help for me
The issue
I have created a route in my file called IndexRoute, my issue is that when it loads the page, it automatically loads my saga function, when it should only be called when the user clicks on a button calling an action creator. Also, when I call an action inside of my index.js, an action is called, however, the saga function is not called. I am very confused, any help will be appreciated thank you
route.js
import { getAsyncInjectors } from 'utils/asyncInjectors';
import globalSagas from 'containers/App/sagas';
import App from 'containers/App';
const errorLoading = (err) => {
console.error('Dynamic page loading failed', err); // eslint-disable-line no-console
};
const loadModule = (cb) => (componentModule) => {
cb(null, componentModule.default);
};
export function createRoutes(store, auth) {
// create reusable async injectors using getAsyncInjectors factory
const { injectReducer, injectSagas } = getAsyncInjectors(store);
// injectReducer('global', globalReducer);
injectSagas(globalSagas);
const routes = [
{
path: '/',
name: 'main',
getComponent(nextState, cb) {
const importModules = Promise.all([
System.import('containers/Main/actions'),
System.import('containers/Main/reducer'),
System.import('containers/Main/sagas'),
System.import('containers/Main'),
]);
const renderRoute = loadModule(cb);
importModules.then(([actions, reducer, sagas, component]) => {
injectReducer('main', reducer.default);
injectSagas(sagas.default);
renderRoute(component);
});
importModules.catch(errorLoading);
},
indexRoute: {
path:'/',
name:'posts',
getComponent(nextState, cb) {
const importModules = Promise.all([
System.import('containers/Dashboard/actions'),
System.import('containers/Dashboard/reducer'),
System.import('containers/Dashboard/sagas'),
System.import('containers/Dashboard'),
]);
const renderRoute = loadModule(cb);
importModules.then(([actions, reducer, sagas, component]) => {
injectReducer('posts', reducer.default);
injectSagas(sagas.default);
renderRoute(component);
});
importModules.catch(errorLoading);
},
},
childRoutes: [
{
path: '/reports',
name: 'Reports List',
getComponent(nextState, cb) {
const importModules = Promise.all([
System.import('containers/Reports/reducer'),
System.import('containers/Reports/sagas'),
System.import('containers/Reports'),
]);
const renderRoute = loadModule(cb);
importModules.then(([reducer, sagas, component]) => {
injectReducer('reportsList', reducer.default);
injectSagas(sagas.default);
renderRoute(component);
});
importModules.catch(errorLoading);
},
childRoutes: [
{
path: '/reports/new',
name: 'Create a Report',
getComponent(nextState, cb) {
const importModules = Promise.all([
System.import('containers/Reports/CreateReport'),
]);
const renderRoute = loadModule(cb);
importModules.then(([component]) => {
renderRoute(component);
});
importModules.catch(errorLoading);
},
},
],
},
{
path: 'matrixView(/:reportId)',
name: 'Matrix View',
getComponent(nextState, cb) {
const importModules = Promise.all([
System.import('containers/MatrixView/reducer'),
System.import('containers/MatrixView/sagas'),
System.import('containers/MatrixView'),
]);
const renderRoute = loadModule(cb);
importModules.then(([reducer, sagas, component]) => {
injectReducer('matrixView', reducer.default);
injectSagas(sagas.default);
renderRoute(component);
});
importModules.catch(errorLoading);
},
},
{
path: '/forbidden',
name: 'No Access',
getComponent(nextState, cb) {
const importModules = Promise.all([
System.import('containers/NoAccess'),
]);
const renderRoute = loadModule(cb);
importModules.then(([component]) => {
renderRoute(component);
});
importModules.catch(errorLoading);
},
},
],
},
{
path: '/login',
name: 'login',
getComponent(nextState, cb) {
const importModules = Promise.all([
System.import('containers/Login'),
]);
const renderRoute = loadModule(cb);
importModules.then(([component]) => {
renderRoute(component);
});
importModules.catch(errorLoading);
},
},
{
path: '/signup',
name: 'signup',
getComponent(nextState, cb) {
const importModules = Promise.all([
System.import('containers/Signup'),
]);
const renderRoute = loadModule(cb);
importModules.then(([component]) => {
// injectReducer('signup', reducer.default);
// injectSagas(sagas.default);
renderRoute(component);
});
importModules.catch(errorLoading);
},
},
];
return {
component: App,
// path: '/',
// indexRoute: { onEnter: (nextState, replace) => replace('/account/me') },
childRoutes: routes,
};
}
actions.js
import {
CREATE_MATRIX_REQUEST,
CREATE_MATRIX_SUCCESS,
CREATE_MATRIX_ERROR
} from './constants';
export function createMatrixRequest() {
return { type: CREATE_MATRIX_REQUEST};
}
export function createMatrixSuccess(data) {
return { type: CREATE_MATRIX_SUCCESS, data };
}
export function createMatrixError(error) {
return { type: CREATE_MATRIX_ERROR , error };
}
reducer.js
/*
* The reducer takes care of state changes in our app through actions
*/
import { fromJS } from 'immutable';
import {
CREATE_MATRIX_REQUEST,
CREATE_MATRIX_SUCCESS,
CREATE_MATRIX_ERROR
} from './constants';
// The initial application state
const initialState = fromJS({
success: '',
error: ''
});
// Takes care of changing the application state
function createMatrixReducer(state = initialState, action) {
switch (action.type) {
case CREATE_MATRIX_REQUEST:
console.log('hello')
return state;
case CREATE_MATRIX_SUCCESS:
console.log('second hello')
return state.set('success', action.payload);
case CREATE_MATRIX_ERROR:
return state.set('error', action.payload);
default:
return state;
}
}
export default createMatrixReducer;
sagas.js
import { call, put } from 'redux-saga/effects';
import { createMatrix } from './utils';
import { CREATE_MATRIX_REQUEST, CREATE_MATRIX_SUCCESS, CREATE_MATRIX_ERROR } from './constants';
export function* createMatrixSaga(action) {
console.log('Generator called')
yield put({ type:CREATE_MATRIX_REQUEST});
try {
const data = yield call(createMatrix);
yield put({type: CREATE_MATRIX_SUCCESS, success: data})
} catch (error) {
yield put({type: CREATE_MATRIX_ERROR, error: error })
}
}
export default [
createMatrixSaga,
];
index.js
/*
* Dashboard
*
**/
import React, { Component } from 'react';
import { connect } from 'react-redux'
import { Input, Button } from 'muicss/react';
import { Link } from 'react-router';
import { createMatrixRequest } from './actions';
import { UserIsAuthenticated } from 'config.routes/UserIsAuthenticated';
import styles from './styles.scss';
class Dashboard extends Component {
constructor(props) {
super(props);
this.state = {
domain: '',
};
this.inputChange = this.inputChange.bind(this);
this.clearInput = this.clearInput.bind(this);
this.createMatrix = this.createMatrix.bind(this);
}
inputChange(event) {
const name = event.target.name;
const value = event.target.value;
this.setState({
[name]: value,
});
}
clearInput(){
this.setState({
domain: ''
})
}
createMatrix(){
this.props.createMatrixRequest();
}
render() {
console.log(this.props, 'This are the props')
return (
<div className={styles.dashboardContainer}>
<div className={styles.dashboardBody}>
<h1>Let's Get Started</h1>
<h5>Begin by entering a domain</h5>
<Input
className={styles.domainInput}
label="Domain Name"
type="text"
name="domain"
value={this.state.domain}
floatingLabel="true"
onChange={this.inputChange}
required
/>
<Button
variant="raised"
type="button"
onClick={this.createMatrix}
disabled={this.state.domain.length === 0}
>
</Button>
<h5 onClick={this.clearInput}><Link>Clear</Link> input</h5>
</div>
</div>
);
}
}
export function mapDispatchToProps(dispatch) {
return {
createMatrixRequest: () => dispatch(createMatrixRequest()),
};
}
function mapStateToProps(state){
return { matrix: state };
}
export default UserIsAuthenticated(connect(mapStateToProps, mapDispatchToProps)(Dashboard));
I also tried adding it to the global sagas file that I import routes.js, but it didn't help
This filed is named the same as my above file, but it stays in another folder called App
sagas.js
// This file contains the sagas used for async actions in our app. It's divided into
// "effects" that the sagas call (`authorize` and `logout`) and the actual sagas themselves,
// which listen for actions.
// Sagas help us gather all our side effects (network requests in this case) in one place
import { take, call, put, race } from 'redux-saga/effects';
import { browserHistory } from 'react-router';
import auth from 'utils/auth';
import { toastr } from 'lib/react-redux-toastr';
import {
SENDING_REQUEST,
LOGIN_REQUEST,
REGISTER_REQUEST,
SET_AUTH,
LOGOUT,
FETCH_USER,
CHANGE_FORM,
REQUEST_ERROR,
SET_USER,
CLEAR_USER,
CREATE_MATRIX_REQUEST,
CREATE_MATRIX_ERROR,
CREATE_MATRIX_SUCCESS
} from './constants';
/**
* Effect to handle authorization
* #param {string} email The email of the user
* #param {string} password The password of the user
* #param {object} options Options
* #param {boolean} options.isRegistering Is this a register request?
*/
export function* authorize({ name, email, password, accountType, isRegistering }) {
// We send an action that tells Redux we're sending a request
yield put({ type: SENDING_REQUEST, sending: true });
// We then try to register or log in the user, depending on the request
try {
// let salt = genSalt(email);
// let hash = hashSync(password, salt);
let response;
// For either log in or registering, we call the proper function in the `auth`
// module, which is asynchronous. Because we're using generators, we can work
// as if it's synchronous because we pause execution until the call is done
// with `yield`!
if (isRegistering) {
response = yield call(auth.register, name, email, password, accountType);
} else {
response = yield call(auth.login, email, password);
}
return response;
} catch (error) {
console.log('hi');
// If we get an error we send Redux the appropiate action and return
yield put({ type: REQUEST_ERROR, error: error.message });
return false;
} finally {
// When done, we tell Redux we're not in the middle of a request any more
yield put({ type: SENDING_REQUEST, sending: false });
}
}
/**
* Effect to handle logging out
*/
export function* logout() {
// We tell Redux we're in the middle of a request
yield put({ type: SENDING_REQUEST, sending: true });
// Similar to above, we try to log out by calling the `logout` function in the
// `auth` module. If we get an error, we send an appropiate action. If we don't,
// we return the response.
try {
const response = yield call(auth.logout);
yield put({ type: SENDING_REQUEST, sending: false });
return response;
} catch (error) {
yield put({ type: REQUEST_ERROR, error: error.message });
return error.message;
}
}
/**
* Log in saga
*/
export function* loginFlow() {
// Because sagas are generators, doing `while (true)` doesn't block our program
// Basically here we say "this saga is always listening for actions"
while (true) {
// And we're listening for `LOGIN_REQUEST` actions and destructuring its payload
const request = yield take(LOGIN_REQUEST);
const { email, password } = request.data;
// A `LOGOUT` action may happen while the `authorize` effect is going on, which may
// lead to a race condition. This is unlikely, but just in case, we call `race` which
// returns the "winner", i.e. the one that finished first
const winner = yield race({
auth: call(authorize, { email, password, isRegistering: false }),
logout: take(LOGOUT),
});
// If `authorize` was the winner...
if (winner.auth) {
// ...we send Redux appropiate actions
yield put({ type: SET_AUTH, newAuthState: true }); // User is logged in (authorized)
yield put({ type: SET_USER, user: winner.auth });
yield put({ type: CHANGE_FORM, newFormState: { email: '', password: '' } }); // Clear form
yield call(forwardTo, '/'); // Go to dashboard page
// If `logout` won...
} else if (winner.logout) {
// ...we send Redux appropiate action
yield put({ type: SET_AUTH, newAuthState: false }); // User is not logged in (not authorized)
yield call(logout); // Call `logout` effect
yield call(forwardTo, '/login'); // Go to root page
}
}
}
/**
* Log out saga
* This is basically the same as the `if (winner.logout)` of above, just written
* as a saga that is always listening to `LOGOUT` actions
*/
export function* logoutFlow() {
while (true) {
yield take(LOGOUT);
yield put({ type: SET_AUTH, newAuthState: false });
yield call(logout);
yield put({ type: CLEAR_USER });
yield call(forwardTo, '/login');
toastr.success('Success!', 'You are now logged out.');
}
}
/**
* Get user information saga
*/
export function* getUserFlow() {
while (true) {
yield take(FETCH_USER);
try {
const response = yield call(auth.getUserInfo);
yield put({ type: SET_USER, user: response });
} catch (error) {
yield put({ type: REQUEST_ERROR, error: error.message });
return error.message;
}
}
}
/**
* Register saga
* Very similar to log in saga!
*/
export function* registerFlow() {
while (true) {
// We always listen to `REGISTER_REQUEST` actions
const request = yield take(REGISTER_REQUEST);
const { name, email, password, accountType } = request.data;
// We call the `authorize` task with the data, telling it that we are registering a user
// This returns `true` if the registering was successful, `false` if not
const wasSuccessful = yield call(authorize, {name, email, password, accountType, isRegistering: true });
// If we could register a user, we send the appropiate actions
if (wasSuccessful) {
yield put({ type: SET_AUTH, newAuthState: true }); // User is logged in (authorized) after being registered
yield put({ type: CHANGE_FORM, newFormState: { name: '', password: '' } }); // Clear form
yield put({ type: LOGIN_REQUEST, data: { email, password } });
forwardTo('/dashboard'); // Go to dashboard page
}
}
}
// The root saga is what we actually send to Redux's middleware. In here we fork
// each saga so that they are all "active" and listening.
// Sagas are fired once at the start of an app and can be thought of as processes running
// in the background, watching actions dispatched to the store.
export default [
loginFlow,
logoutFlow,
registerFlow,
getUserFlow,
];
// Little helper function to abstract going to different pages
export function* forwardTo(location) {
yield call(browserHistory.push, location);
}
Found an answer, basically I forgot to add a watcher to my saga file, now it works perfectly!
import { call, put } from 'redux-saga/effects';
import { takeEvery } from 'redux-saga/effects'
import { createMatrix } from './utils';
import { CREATE_MATRIX_REQUEST, CREATE_MATRIX_SUCCESS, CREATE_MATRIX_ERROR } from './constants';
export function* createMatrixSaga(action) {
try {
const data = yield call(createMatrix);
yield put({type: CREATE_MATRIX_SUCCESS, success: data})
} catch (error) {
yield put({type: CREATE_MATRIX_ERROR, error: error })
}
}
function* watchFetchData() {
yield takeEvery(CREATE_MATRIX_REQUEST, createMatrixSaga)
}
export default [
watchFetchData,
];
I new in Angular 2 and i'm trying create an App with JWT. So, to do this I follow the post http://www.adonespitogo.com/articles/angular-2-extending-http-provider/.
But i'm a issue, the request method is never call, after login i have to refresh the page to send the token.
Here my classes
http.service.ts
import { Injectable } from '#angular/core';
import { Http, XHRBackend, RequestOptions, Request, RequestOptionsArgs, Response, Headers } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
#Injectable()
export class HttpService extends Http {
constructor (backend: XHRBackend, options: RequestOptions) {
let token = localStorage.getItem('auth_token'); // your custom token getter function here
options.headers.set('Authorization', `Bearer ${token}`);
options.headers.append('Content-Type', 'application/json');
super(backend, options);
}
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
let token = localStorage.getItem('auth_token');
if (typeof url === 'string') { // meaning we have to add the token to the options, not in url
if (!options) {
// let's make option object
options = {headers: new Headers()};
}
options.headers.set('Authorization', `Bearer ${token}`);
options.headers.append('Content-Type', 'application/json');
} else {
// we have to add the token to the url object
url.headers.set('Authorization', `Bearer ${token}`);
url.headers.append('Content-Type', 'application/json');
}
return super.request(url, options)
.catch(this.catchAuthError(this));
}
private catchAuthError (self: HttpService) {
// we have to pass HttpService's own instance here as `self`
return (res: Response) => {
console.log(res);
if (res.status === 401 || res.status === 403) {
// if not authenticated
console.log(res);
}
return Observable.throw(res);
};
}
}
app.module.ts
providers: [{
provide: HttpService,
useFactory: (backend: XHRBackend, options: RequestOptions) => {
return new HttpService(backend, options);
},
deps: [XHRBackend, RequestOptions]
}, LoggedInGuard, UserService],
picture.service.ts
#Injectable()
export class PictureService {
url: string = 'v1/pictures';
constructor(private http: HttpService) { }
list(): Observable<PictureComponent[]> {
return this.http
.get(this.url)
.map(res => res.json());
}
}
Component to consume picture.service.ts
#Component({
moduleId: module.id,
selector: 'picture-list',
templateUrl: './pictureList.component.html'
})
export class ListagemComponent {
pictures: PictureComponent[] = [];
service: PictureService;
msg: String = '';
constructor(service: PictureService){
this.service = service;
this.service
.list()
.subscribe(pictures => {
this.pictures = pictures;
}, err => console.log(err));
}
}
thanks for help
Did you provide picture.service in your module? If not, you should provide it in your #component or in your module if you want it globally.
I extend XHRBackend
import { Injectable } from '#angular/core';
import { Request, XHRBackend, BrowserXhr, ResponseOptions, XSRFStrategy, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
#Injectable()
export class ExtendedXHRBackend extends XHRBackend {
constructor(browserXhr: BrowserXhr, baseResponseOptions: ResponseOptions, xsrfStrategy: XSRFStrategy) {
super(browserXhr, baseResponseOptions, xsrfStrategy);
}
createConnection(request: Request) {
let token = localStorage.getItem('token');
request.headers.set('x-access-token', `${token}`);
request.headers.set('Content-Type', 'application/json');
let xhrConnection = super.createConnection(request);
xhrConnection.response = xhrConnection.response.catch((error: Response) => {
if (error.status === 401 || error.status === 403) {
console.log('access not alowed');
localStorage.removeItem('token');
}
return Observable.throw(error);
});
return xhrConnection;
}
}
and use on app module
providers: [{ provide: XHRBackend, useClass: ExtendedXHRBackend }]
After this I resolve the issue and works preety well
First: I'm aware that Angular2 is in alpha and changing frequently.
I'm working with Angular2. There is an injectable service with http dependency that I'd like to test using a mock backend. The service works when the app starts but I'm having no luck writing the test and getting the mock backend to respond. Any insight, is there something obvious in the test setup or implementation that I'm missing?
service/core.ts:
import { Injectable } from 'angular2/angular2';
import { Http } from 'angular2/http';
#Injectable()
export class CoreService {
constructor(public http:Http) {}
getStatus() {
return this.http.get('/api/status')
.toRx()
.map(res => res.json());
}
}
service/core_spec.ts:
import {
AsyncTestCompleter,
TestComponentBuilder,
By,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit
} from 'angular2/test';
import { MockBackend, MockConnection, BaseRequestOptions, Http, Response } from 'angular2/http';
import { Injector, bind } from 'angular2/angular2';
import { ObservableWrapper } from 'angular2/src/core/facade/async'
import { CoreService } from 'public/services/core'
export function main() {
describe('public/services/core', () => {
let backend: MockBackend;
let response: Response;
let coreService: CoreService;
let injector: Injector;
afterEach(() => backend.verifyNoPendingRequests());
it('should get status', inject([AsyncTestCompleter], (async) => {
injector = Injector.resolveAndCreate([
BaseRequestOptions,
MockBackend,
bind(Http).toFactory((backend, options) => {
return new Http(backend, options)
}, [MockBackend, BaseRequestOptions]),
bind(CoreService).toFactory((http) => {
return new CoreService(http);
}, [Http])
]);
backend = injector.get(MockBackend);
coreService = injector.get(CoreService);
response = new Response('foo');
ObservableWrapper.subscribe<MockConnection>(backend.connections, c => {
expect(c.request.url).toBe('/api/status');
c.mockRespond(response);
});
// attempt #1: fails because res argument is undefined
coreService.getStatus().subscribe(res => {
expect(res).toBe('');
async.done();
});
// attempt #2: fails because emitter.observer is not a function
ObservableWrapper.subscribe(coreService.getStatus(), res => {
expect(res).toBe('');
async.done();
});
}));
});
}
Related:
https://github.com/angular/angular/issues/3502
https://github.com/angular/angular/issues/3530
I just found this topic while looking for testing tips but I can't see a direct answer to that so...
This one is based on Angular RC.1
Testing service with Mock Backend
Let's say your service is:
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
#Injectable()
export class CoreService {
constructor(private http: Http) {}
getStatus() {
return this.http.get('/api/status');
}
}
Test to the service above will look like this:
import {
beforeEach,
beforeEachProviders,
describe,
expect,
inject,
it,
} from '#angular/core/testing';
import { provide } from '#angular/core';
import { BaseRequestOptions, Response, ResponseOptions } from '#angular/http';
import { MockBackend, MockConnection } from '#angular/http/testing';
describe('Http', () => {
beforeEachProviders(() => [
CoreService,
BaseRequestOptions,
MockBackend,
provide(Http, {
useFactory: (backend: MockBackend, defaultOptions: BaseRequestOptions) => {
return new Http(backend, defaultOptions);
},
deps: [MockBackend, BaseRequestOptions]
})
]);
beforeEach(inject([MockBackend], (backend: MockBackend) => {
const baseResponse = new Response(new ResponseOptions({ body: 'status' }));
backend.connections.subscribe((c: MockConnection) => c.mockRespond(baseResponse));
}));
it('should return response when subscribed to getStatus',
inject([CoreService], (coreService: CoreService) => {
coreService.getStatus().subscribe((res: Response) => {
expect(res.text()).toBe('status');
});
})
);
})
What you really have to look at there is to have proper mocking in beforeEachProviders. Test itself is quite simple and ends up with subscribing to the service method.
Note: Don't forget to set base providers first:
import { setBaseTestProviders } from '#angular/core/testing';
import {
TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS,
TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
} from '#angular/platform-browser-dynamic/testing';
setBaseTestProviders(TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS, TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS);
Since asking this question we did upgrade to Angular2 RC 1. Our imports look like Wojciech Kwiatek's (thank you for your answer!) but our testing strategy is slightly different. We wanted to assert on the request as well as the response. Instead of using beforeEachProviders(), we used beforeEach() where we create our own injector and save a reference to the service-under-test and mock backend. This allows us to assert on the request and manage the response inside the test, and it lets us use the verifyNoPendingRequests() method after each test.
describe('core-service', () => {
let service: CoreService;
let backend: MockBackend;
beforeEach(() => {
injector = ReflectiveInjector.resolveAndCreate(<any> [
CoreService,
BaseRequestOptions,
MockBackend,
provide(Http, {
useFactory: (mockBackend, defaultOptions) => new Http(mockBackend, defaultOptions),
deps: [MockBackend, BaseRequestOptions]
})
]);
service = <CoreService> injector.get(CoreService);
backend = <MockBackend> injector.get(MockBackend);
});
afterEach(() => backend.verifyNoPendingRequests());
it('should get status', () => {
backend.connections.subscribe((c: MockConnection) => {
expect(c.request.url).toEqual('api/status');
c.mockRespond(new Response(new ResponseOptions({ body: 'all is well' })));
});
service.getStatus().subscribe((status) => {
expect(status).toEqual('all is well');
});
}));
});
Edit: Plunker updated to RC2.
https://plnkr.co/edit/nlvUZVhKEr8d2mz8KQah?p=preview