map is not a function - web-services

I am trying to make an website that connets with a weather api and gets some info about current weather in given city. When i looked at network flow in my application i do recive a json that contais that information but i cannot map it and display results. Error i revice is :
TypeError: response.json(...).map is not a function WeatherSearchComponent.ts:138
at MapSubscriber.project (WeatherSearchComponent.ts:84)
at MapSubscriber._next (map.js:77)
at MapSubscriber.Subscriber.next (Subscriber.js:89)
at XMLHttpRequest.onLoad (http.umd.js:1083)
at ZoneDelegate.invokeTask (zone.js:225)
at Object.onInvokeTask (core.umd.js:6004)
at ZoneDelegate.invokeTask (zone.js:224)
at Zone.runTask (zone.js:125)
at XMLHttpRequest.ZoneTask.invoke (zone.js:293)
Heres WeatherSearchComponent:
import {
Component,
Injectable,
OnInit,
ElementRef,
EventEmitter,
Inject
} from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs';
import 'rxjs/Rx';
import 'rxjs/add/operator/map';
export var WEATHER_API_KEY: string = 'api_key';
export var WEATHER_API_URL: string ='http://api.openweathermap.org/data/2.5/weather';
export var GDANSK_ID: string = '3099434';
/*let loadingGif: string = ((<any>window).__karma__) ? '' :
require('images/loading.gif');*/
class SearchResult {
content: string;
constructor(obj?: any) {
this.content= obj && obj.content ||
WEATHER_API_URL + 'id=' + obj.city + 'appid=' + WEATHER_API_KEY;
}
}
// http://api.openweathermap.org/data/2.5/weather?
// id=3099434&appid=api_key
#Injectable()
export class WeatherService {
constructor(private http: Http,
#Inject(WEATHER_API_KEY) private apiKey: string,
#Inject(WEATHER_API_URL) private apiUrl: string) {
}
search(city: string): Observable<SearchResult[]> {
let params: string = [
`q=${city}`,
`appid=${this.apiKey}`
].join('&');
let queryUrl: string = `${this.apiUrl}?${params}`;
console.log("query url" , queryUrl);
var getRequest = this.http.get(queryUrl);
console.log("getRequest", getRequest);
return getRequest
.map((response: Response) => {
return (<any>response.json()).map(item => {
console.log("raw item", item); // uncomment if you want to debug
return new SearchResult({
content: item.main.temp
});
});
});
}
}
export var weatherServiceInjectables: Array<any> = [
{provide: WeatherService, useClass: WeatherService},
{provide: WEATHER_API_KEY, useValue: WEATHER_API_KEY},
{provide: WEATHER_API_URL, useValue: WEATHER_API_URL}
];
/**
* SearchBox displays the search box and emits events based on the results
*/
#Component({
outputs: ['loading', 'results'],
selector: 'search-box',
template: `
<input type="text" class="form-control" placeholder="Search" autofocus>
`
})
export class SearchBox implements OnInit {
loading: EventEmitter<boolean> = new EventEmitter<boolean>();
results: EventEmitter<SearchResult[]> = new EventEmitter<SearchResult[]> ();
constructor(private weather: WeatherService,
private el: ElementRef) {
}
ngOnInit(): void {
// convert the `keyup` event into an observable stream
var observable =
Observable.fromEvent(this.el.nativeElement, 'keyup');
observable
.map((e: any) => e.target.value) // extract the value of the input
.filter((text: string) => text.length > 1) // filter out if empty
.debounceTime(250) // only once every 250ms
.do(() => this.loading.next(true)) // enable loading
// search, discarding old events if new input comes in
.map((query: string) => this.weather.search(query))
.switch()
// act on the return of the search
.subscribe(
(results: SearchResult[]) => { // on sucesss
this.loading.next(false);
this.results.next(results);
},
(err: any) => { // on error
console.log(err);
this.loading.next(false);
},
() => { // on completion
this.loading.next(false);
}
);
}
}
#Component({
inputs: ['result'],
selector: 'search-result',
template: `
<div class="col-sm-6 col-md-3">
<div class="thumbnail">
<img src="{{result.thumbnailUrl}}">
<div class="caption">
<h3>{{result}}</h3>
<p>{{result}}</p>
<p><a href="{{result.videoUrl}}"
class="btn btn-default" role="button">Watch</a></p>
</div>
</div>
</div>
`
})
export class SearchResultComponent {
result: SearchResult;
}
#Component({
selector: 'weather-search',
template: `
<div class='container'>
<div class="page-header">
<h1>Weather Search</h1>
</div>
<div class="row">
<div class="input-group input-group-lg col-md-12">
<search-box
(loading)="loading = $event"
(results)="updateResults($event)"
></search-box>
</div>
</div>
<div class="row">
<search-result
*ngFor="let result of results"
[result]="result">
</search-result>
</div>
</div>
`
})
export class WeatherSearchComponent {
results: SearchResult[];
updateResults(results: SearchResult[]): void {
this.results = results;
// console.log("results:", this.results); // uncomment to take a look
}
}
This is what i do recive from api call :
/*
{"coord":{"lon":18.65,"lat":54.35},
"weather":[{"id":801,"main":"Clouds",
"description":"few clouds","icon":"02n"}],"base":"stations",
"main":{"temp":270.15,"pressure":1035,"humidity":92,
"temp_min":270.15,"temp_max":270.15},"visibility":10000,
"wind":{"speed":2.1,"deg":290},"clouds":{"all":20},
"dt":1484665200,
"sys":{"type":1,"id":5349,"message":0.0029,
"country":"PL","sunrise":1484636080,"sunset":1484665040},"
id":3099434,"name":"Gdansk","cod":200}
*/

It's not clear from your question whether you're trying to .map() on a variable or on an observable.
Scenario #1: variable.map()
variable must be an array. Looks from your code that your variable contains an object.
const jsonData = {
"coord":{"lon":18.65,"lat":54.35},
// Abridged for brevity...
id":3099434,"name":"Gdansk","cod":200
};
jsonData.map(...); // WON'T WORK - `jsonData` must be an array
Scenario #2: observable.map()
You must import the map operator in your code before mapping:
import 'rxjs/add/operator/map';
// Then, later
Observable.map(...);

Related

Unit Test using Jest. TypeError: Cannot read property 'getters' of undefined

I have a component that I want to test that uses Vuex.
components/Main/Header.vue
<template>
<v-container fluid grid-list-xl>
<v-card flat class="header" color="transparent">
<v-layout align-center justify-start row fill-height class="content">
<v-flex xs5>
<v-img :src="avatar" class="avatar" aspect-ratio="1" contain></v-img>
</v-flex>
<v-flex xs7>
<div class="main-text font-weight-black">
WELCOME
</div>
<div class="sub-text">
{{currentLocation.description}}
</div>
<div #click="showStory" class="show-more font-weight-bold">
Explore More
</div>
</v-flex>
</v-layout>
</v-card>
</v-container>
</template>
<script>
import avatar from '../../assets/images/home/avatar.png';
export default {
name: 'Header',
computed: {
currentLocation() {
return this.$store.getters.getCurrentLocation;
},
avatar() {
return avatar;
}
},
methods: {
showStory() {
this.$router.push( { name: 'Stories', params: { name: 'Our Story' } });
}
}
}
</script>
and from /test/unit/components/Main/Header.spec.js
import Vuex from 'vuex'
import { shallowMount, createLocalVue } from "#vue/test-utils"
import Header from "#/components/Main/Header.vue"
const localVue = createLocalVue()
localVue.use(Vuex)
const store = new Vuex.Store({
state: {
currentLocation: {
name: 'this is a name',
description: "lorem ipsum",
latitude: '1.123123',
longitude: '103.123123',
radius: '5000'
}
},
getters: {
getCurrentLocation: (state) => state.currentLocation
}
})
describe('Header', () => {
const wrapper = shallowMount(Header, {
store,
localVue
})
it('should render a computed property currentLocation', () => {
expect(Header.computed.currentLocation()).toBe(store.getters.getCurrentLocation)
});
});
The error I'm getting is from the computed property currentLocation
TypeError: Cannot read property 'getters' of undefined

I am try to login with django using Angular4

i create Rest API for login in Django and i want to call it in angular4
heare is my angular4 code
login.components.ts
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { Http, Response, Headers} from '#angular/http';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
constructor(private router:Router, private http:Http) { }
getData = [];
with this function i get data from my APIand i am trying to compare this data with form data
fatchData = function(){
this.http.get("http://127.0.0.1:8000/loginAdmin/").subscribe((res:Response) => {
this.getData = res.json();
})
}
loginUser(e){
e.preventDefault();
var username = e.target.elements[0].value;
var password = e.target.elements[1].value;
if(username == 'this.getData.username' && password == 'this.getData.password')
{
this.router.navigate(['/deshbord'])
}
else{
console.log('Error Find');
}
}
ngOnInit() {
this.fatchData();
}
}
this is my login.comonents.login i want that when user give user name and password its compare with my API data and it's true then navigate to other page..
<form (submit)="loginUser($event)">
<div class="input">
<label>UserName</label>
<input type="text">
</div>
<div class="input">
<label>password</label>
<input type="password">
</div>
<div class="input">
<input type="submit" value="Login">
</div>
</form>

Vue.js unit test w avoriaz , how to test submit event

I am trying to test a form submit.. it seems that trigger is not appropriate
1) calls store action login when the form is submitted
LoginPage.vue
TypeError: wrapper.find(...).trigger is not a function
at Context.<anonymous> (webpack:///test/unit/specs/pages/LoginPage.spec.js:35:28 <- index.js:50826:29)
my vue component to be tested
LoginPage.vue
<template>
<div class="container">
<div class="login-page">
<h1 class="title">Login to existing account</h1>
<form #submit.prevent="submit()" class="form form--login grid">
<div class="row">
<label for="login__email">Email</label>
<input type="text" id="login__email" class="input--block" v-model="email" v-on:keyup="clearErrorMessage" />
</div>
<div class="row">
<label for="login__password">Password</label>
<input type="password" id="login__password" class="input--block" v-model="password" v-on:keyup="clearErrorMessage" />
</div><!-- /.row -->
<div class="row">
<label></label>
<button id="submit" type="submit">Login</button>
</div><!-- /.row -->
<div v-show='hasError' class="row">
<label></label>
<p class="error">Invalid credentials</p>
</div>
</form>
</div><!-- /.login-page -->
</div>
</template>
<script>
import store from '#/vuex/store'
import { mapActions } from 'vuex'
import _ from 'underscore'
export default {
name: 'loginPage',
data () {
return {
email: 'john.doe#domain.com',
password: 'john123',
hasError: false
}
},
methods: _.extend({}, mapActions(['login']), {
clearErrorMessage () {
this.hasError = false
},
submit () {
return this.login({user: { email: this.email, password: this.password }})
.then((logged) => {
if (logged) {
this.$router.push('shoppinglists')
} else {
this.hasError = true
}
})
}
}),
store
}
</script>
LoginPage.spec.js
import LoginPage from '#/pages/LoginPage'
import Vue from 'vue'
import Vuex from 'vuex'
import sinon from 'sinon'
import { mount } from 'avoriaz'
Vue.use(Vuex)
describe('LoginPage.vue', () => {
let actions
let getters
let store
beforeEach(() => {
actions = {
login: sinon.stub()
}
getters = {
isAuthenticated: () => {
state => state.isAuthenticated
}
}
store = new Vuex.Store({
actions,
getters,
state: {
isAuthenticated: false,
currentUserId: ''
}
})
})
it('calls store action login when the form is submitted', (done) => {
const wrapper = mount(LoginPage, { store })
wrapper.find('#submit').trigger('submit')
wrapper.vm.$nextTick(() => {
expect(actions.login.calledOnce).to.equal(true)
done()
})
})
})
Should be trigger 'click' on the form tag !
it('calls store action login when the form is submitted', (done) => {
const wrapper = mount(LoginPage, { store })
const form = wrapper.find('form')[0]
form.trigger('submit')
wrapper.vm.$nextTick(() => {
expect(actions.login.calledOnce).to.equal(true)
done()
})
})

Vue.js Vuex unit test failing , [vuex] unknown getter:

I am testing my App.vue , and I am stuck with a Vuex error on getters...
I guess it's related to a badly define getters property , but I don't see how to solve it ..
feeedback welcome
Console.log
ERROR LOG: '[vuex] unknown getter: getLists'
App.vue
✗ calls store action addShoppingList when a click event is fired from the plus-sign icon
AssertionError: expected false to equal true
at Context.<anonymous> (webpack:///test/unit/specs/App.spec.js:33:50 <- index.js:24490:51)
App.spec.js
import App from '#/App'
import Vue from 'vue'
import Vuex from 'vuex'
import sinon from 'sinon'
import { mount } from 'avoriaz'
Vue.use(Vuex)
describe('App.vue', () => {
let actions
let getters
let store
beforeEach(() => {
actions = {
addShoppingList: sinon.stub(),
populateShoppingLists: sinon.stub()
}
getters = {
shoppinglists: () => 'getLists'
}
store = new Vuex.Store({
state: {},
actions,
getters
})
})
it('calls store action addShoppingList when a click event is fired from the plus-sign icon', (done) => {
const wrapper = mount(App, { store })
wrapper.find('a')[0].trigger('click')
wrapper.vm.$nextTick(() => {
expect(actions.createShoppingList.calledOnce).to.equal(true)
done()
})
})
App.vue
<template>
<div id="app" class="container">
<ul class="nav nav-tabs" role="tablist">
<li :class="index===shoppinglists.length-1 ? 'active' : ''" v-for="(list, index) in shoppinglists" :key="list.id" role="presentation">
<shopping-list-title-component :id="list.id" :title="list.title"></shopping-list-title-component>
</li>
<li>
<a href="#" #click="addShoppingList">
<i class="glyphicon glyphicon-plus-sign"></i>
</a>
</li>
</ul>
<div class="tab-content">
<div :class="index===shoppinglists.length-1 ? 'active' : ''" v-for="(list, index) in shoppinglists" :key="list.id" class="tab-pane" role="tabpanel" :id="list.id">
<shopping-list-component :id="list.id" :title="list.title" :items="list.items"></shopping-list-component>
</div>
</div>
</div>
</template>
<script>
import ShoppingListComponent from './components/ShoppingListComponent'
import ShoppingListTitleComponent from './components/ShoppingListTitleComponent'
import store from './vuex/store'
import { mapGetters, mapActions } from 'vuex'
import _ from 'underscore'
export default {
components: {
ShoppingListComponent,
ShoppingListTitleComponent
},
computed: {
...mapGetters({ shoppinglists: 'getLists' })
},
methods: _.extend({}, mapActions(['populateShoppingLists', 'createShoppingList']), {
addShoppingList () {
let list = { title: 'New Shopping List', items: [] }
this.createShoppingList(list)
}
}),
store,
mounted: function () {
this.$nextTick(function () {
this.populateShoppingLists()
})
}
}
</script>
UPDATE
here are my getters.js anf store.js files
store.js is imported in App.vue
store.js
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters' // import getters !
import actions from './actions'
import mutations from './mutations'
Vue.use(Vuex)
const state = {
shoppinglists: []
}
export default new Vuex.Store({
state,
mutations,
getters,
actions
})
getters.js
import _ from 'underscore'
export default {
getLists: state => state.shoppinglists,
getListById: (state, id) => {
return _.findWhere(state.shoppinglists, { id: id })
}
}
You don't define a getLists getter in your store. You're defining a shoppinglists getter that returns 'getLists'.
You need to either change your mapGetters line to be:
...mapGetters(['shoppinglists'])
Or change the name of the getter to getLists:
getters = {
getLists: () => 'getLists'
}
(Although I'm not sure if you are really meaning to return a string value in that getter or not)
#thanksd put me on tracks... see my comment
so I need to define the getters in my Vur.spec.js as following
getters = {
getLists: () => {
// console.log('WE ARE S TEST')
state => state.shoppinglists
}
}

Vue component testing using Karma: 'undefined is not an object'

I am working on an app which was created with the Vue loader's webpack template.
I included testing with Karma as an option when creating the project, so it was all set up and I haven't changed any of the config.
The app is a Github user lookup which currently consists of three components; App.vue, Stats.vue and UserForm.vue. The stats and form components are children of the containing app component.
Here is App.vue:
<template>
<div id="app">
<user-form
v-model="inputValue"
#go="submit"
:input-value="inputValue"
></user-form>
<stats
:username="username"
:avatar="avatar"
:fave-lang="faveLang"
:followers="followers"
></stats>
</div>
</template>
<script>
import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
import _ from 'lodash'
import UserForm from './components/UserForm'
import Stats from './components/Stats'
Vue.use(VueAxios, axios)
export default {
name: 'app',
components: {
UserForm,
Stats
},
data () {
return {
inputValue: '',
username: '',
avatar: '',
followers: [],
faveLang: '',
urlBase: 'https://api.github.com/users'
}
},
methods: {
submit () {
if (this.inputValue) {
const api = `${this.urlBase}/${this.inputValue}`
this.fetchUser(api)
}
},
fetchUser (api) {
Vue.axios.get(api).then((response) => {
const { data } = response
this.inputValue = ''
this.username = data.login
this.avatar = data.avatar_url
this.fetchFollowers()
this.fetchFaveLang()
}).catch(error => {
console.warn('ERROR:', error)
})
},
fetchFollowers () {
Vue.axios.get(`${this.urlBase}/${this.username}/followers`).then(followersResponse => {
this.followers = followersResponse.data.map(follower => {
return follower.login
})
})
},
fetchFaveLang () {
Vue.axios.get(`${this.urlBase}/${this.username}/repos`).then(reposResponse => {
const langs = reposResponse.data.map(repo => {
return repo.language
})
// Get most commonly occurring string from array
const faveLang = _.chain(langs).countBy().toPairs().maxBy(_.last).head().value()
if (faveLang !== 'null') {
this.faveLang = faveLang
} else {
this.faveLang = ''
}
})
}
}
}
</script>
<style lang="stylus">
body
background-color goldenrod
</style>
Here is Stats.vue:
<template>
<div class="container">
<h1 class="username" v-if="username">{{username}}</h1>
<img v-if="avatar" :src="avatar" class="avatar">
<h2 v-if="faveLang">Favourite Language: {{faveLang}}</h2>
<h3 v-if="followers.length > 0">Followers ({{followers.length}}):</h3>
<ul v-if="followers.length > 0">
<li v-for="follower in followers">
{{follower}}
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'stats',
props: [
'username',
'avatar',
'faveLang',
'followers'
]
}
</script>
<style lang="stylus" scoped>
h1
font-size 44px
.avatar
height 200px
width 200px
border-radius 10%
.container
display flex
align-items center
flex-flow column
font-family Comic Sans MS
</style>
And here is UserForm.vue:
<template>
<form #submit.prevent="handleSubmit">
<input
class="input"
:value="inputValue"
#input="updateValue($event.target.value)"
type="text"
placeholder="Enter a GitHub username..."
>
<button class="button">Go!</button>
</form>
</template>
<script>
export default {
props: ['inputValue'],
name: 'user-form',
methods: {
updateValue (value) {
this.$emit('input', value)
},
handleSubmit () {
this.$emit('go')
}
}
}
</script>
<style lang="stylus" scoped>
input
width 320px
input,
button
font-size 25px
form
display flex
justify-content center
</style>
I wrote a trivial test for UserForm.vue which test's the outerHTML of the <button>:
import Vue from 'vue'
import UserForm from 'src/components/UserForm'
describe('UserForm.vue', () => {
it('should have a data-attribute in the button outerHTML', () => {
const vm = new Vue({
el: document.createElement('div'),
render: (h) => h(UserForm)
})
expect(vm.$el.querySelector('.button').outerHTML)
.to.include('data-v')
})
})
This works fine; the output when running npm run unit is:
UserForm.vue
✓ should have a data-attribute in the button outerHTML
However, when I tried to write a similarly simple test for Stats.vue based on the documentation, I ran into a problem.
Here is the test:
import Vue from 'vue'
import Stats from 'src/components/Stats'
// Inspect the generated HTML after a state update
it('updates the rendered message when vm.message updates', done => {
const vm = new Vue(Stats).$mount()
vm.username = 'foo'
// wait a "tick" after state change before asserting DOM updates
Vue.nextTick(() => {
expect(vm.$el.querySelector('.username').textContent).toBe('foo')
done()
})
})
and here is the respective error when running npm run unit:
ERROR LOG: '[Vue warn]: Error when rendering root instance: '
✗ updates the rendered message when vm.message updates
undefined is not an object (evaluating '_vm.followers.length')
I have tried the following in an attempt to get the test working:
Change how the vm is created in the Stats test to be the same as the UserForm test - same error is returned
Test individual parts of the component, for example the textContent of a div in the component - same error is returned
Why is the error referring to _vm.followers.length? What is _vm with an underscore in front? How can I get around this issue to be able to successfully test my component?
(Repo with all code: https://github.com/alanbuchanan/vue-github-lookup-2)
Why is the error referring to _vm.followers.length? What is _vm with an underscore in front?
This piece of code is from the render function that Vue compiled your template into. _vm is a placeholder that gets inserted automatically into all Javascript expressions when vue-loader converts the template into a render function during build - it does that to provide access to the component.
When you do this in your template:
{{followers.length}}
The compiled result in the render function for this piece of code will be:
_vm.followers.length
Now, why does the error happen in the first place? Because you have defined a prop followers on your component, but don't provide any data for it - therefore, the prop's value is undefined
Solution: either you provide a default value for the prop:
// Stats.vue
props: {
followers: { default: () => [] }, // function required to return fresh object
// ... other props
}
Or you propvide acual values for the prop:
// in the test:
const vm = new Vue({
...Stats,
propsData: {
followers: [/* ... actual data*/]
}
}).$mount()