Assign retrieved data from request to template - ajax

I'm trying to get a list of news and then render them in the template of my component.
I was learning from this tutorial, which is a little bit out of date, http://chariotsolutions.com/blog/post/angular2-observables-http-separating-services-components/
The news item class looks like this
export class NewsItemComponent {
text: string;
date: Date;
author: string;
link: string
constructor(text: string,
date: Date,
author: string,
link: string) {
this.text = text;
this.date = date;
this.author = author;
this.link = link;
}
}
The service retrieving the data:
import { Injectable } from '#angular/core';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import { NewsItemComponent } from './news-item/news-item.component';
#Injectable()
export class NewsService {
constructor(public http: Http) {
console.log('Creating NewsService');
}
getNews() {
let url = 'http://localhost/test-api/';
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
this.http.post(url, '', options)
// initial transform - result to json
.subscribe( (res:Response) => {
let articles: Array<any> = [];
res.json().map((articles: Array<any>) => {
articles.push(new NewsItemComponent('asd', new Date(), 'asdf', 'asdf'));
});
console.log('articles', articles);
return articles
});
}
}
The main component:
import { Component, OnInit, AfterViewInit, AfterViewChecked } from '#angular/core';
import { Http, Response, RequestOptions, Headers, Request, RequestMethod } from '#angular/http';
import { TranslateService } from 'ng2-translate/ng2-translate';
import { NewsItemComponent } from './news-item/news-item.component';
import { NewsService } from './news-service.service';
#Component({
selector: 'news-section',
templateUrl: './news-section.component.html',
styleUrls: ['./news-section.component.scss'],
providers: [ NewsService ]
})
export class NewsSectionComponent implements AfterViewInit {
articles: NewsItemComponent[];
constructor(translate: TranslateService, private http: Http, public newsService: NewsService) {
translate.setDefaultLang('en');
translate.use('en');
this.articles = [];
newsService.getNews().subscribe(res => this.articles = res);
}
}
And the view:
<section id="news" sectionVisible>
<div>
<h1>{{ 'Nav.News' | translate }}</h1>
</div>
<div class="news-wrapper clear-fix">
<div class="column carousel-cell" ngFor="#article of articles">
<article>
<a href="#" class="hovered">
<div class="bg-hover">
<p>
Visit
</p>
</div>
</a>
<h2>News title</h2>
<time>21.10.2015 16:30</time>
<p>
Etiam faucibus sodales leo, rutrum molestie nisi luctus in. Duis quis viverra diam, vitae hendrerit augue. Donec ultricies nisi vel placerat sodales. Donec varius convallis mauris egestas vulputate. Integer fermentum tincidunt hendrerit. Donec eget nisl
eros. Pellentesque a fringilla lorem.
</p>
</article>
</div>
</div>
</section>
Of course in the view the lorem ipsum parts will be changed by the data retrieved. But there is something wrong with the service:
newsService.getNews().subscribe(res => this.articles = res);
this line raises an error:
Property subscribe does not exist on type void
And I have no idea why it is caused.
Any hints?

You need to remove the subscribe method from your service. Services should only return the Observables, subscribing should be done in the actual component.
Subscribing to an Observable is equal to calling a function. You want to retrieve the result in your component, so that's where you need to subscribe. The service should only hold the logic to prepare the data after the HTTP call (calling for example res.json()).
Service
getNews() {
let url = 'http://localhost/test-api/';
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.post(url, '', options)
.map(res => res.json());
}
Component:
newsService.getNews()
.subscribe( news => {
let articles: Array<any> = [];
news.map((articles: Array<any>) => {
articles.push(new NewsItemComponent('asd', new Date(), 'asdf', 'asdf'));
});
this.articles = articles
});

Related

GraphQL returning no errors but undefined

Have been searching for a while. I am using Gatsby and I have no errors in the console or terminal but values are undefined. Could it be because of the second "data" under "articles"?
GraphiQL Query:
Query/Component Code:
import React, { Component } from 'react'
import { graphql } from 'gatsby'
// This query is executed at build time by Gatsby.
export const GatsbyQuery = graphql`
{
umdHub {
articles {
data {
title
}
}
}
}
`
class ClientFetchingExample extends Component {
render() {
const {
umdHub: { articles },
} = this.props.data
console.log(articles.title)
return (
<div style={{ textAlign: 'center', width: '600px', margin: '50px auto' }}>
<h1>{articles.title} - Is the title of the article</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</div>
)
}
}
export default ClientFetchingExample
You already halfway there just small changes
const {
umdHub: { articles },
} = this.props.data
Now you have this articles object like this
articles: {
data: [
{
title: 'Learning to Listen'
},
{
title: 'Second Title'
}
...
]
}
Now you need to loop over data
import _ from 'lodash'
const data = _.get(articles, 'data', [])
_.map(data, title => {
console.log({ title })
})
If you want to only first title then
const title = _.get(articles, 'data[0].title', 'default value or blank string')
Here lodash is A modern JavaScript utility library delivering modularity, performance & extras.

Filter items with search field

I'm trying to filter categories when links.items contains a specific string.
const links = [
{
id: 1,
category: 'Category 1',
items: [
{
id: 1,
name: 'Link1',
url: 'https://#',
info: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
},
{
id: 2,
name: 'Link2',
url: 'https://#',
info: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
}
]
},
{
id: 2,
category: 'Category2',
items: [{
id: 1,
name: 'Link1',
url: 'https://#',
info: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
}]
},
{
id: 3,
category: 'Category3',
items: [{
id: 1,
name: 'Link1',
url: 'https://#',
info: ''
}]
}
]
Vue.component('link-component', {
template: `
<div>
<h4>Links</h4>
<!-- Search -->
<input type="text" v-model="search" append-icon="search" placeholder="Search"></input>
<!-- Level 1 -->
<div v-for="value in filteredLinks" :key="value.id">
<pre>{{ value.category }}</pre>
</div>
<!-- Level 2 -->
<div class="card-content" v-for="item in value.items" :key="item.id">
<p>
<a :href="item.url" target="_blank">{{ item.name }}</a>
</p>
<p> {{ item.info }} </p>
</div>
</div>
`,
data: {
links,
search: ''
},
computed: {
filteredLinks () {
return this.links.filter(links => {
return links.items.toLowerCase().includes(this.search.toLowerCase())
})
}
}
});
new Vue({
el: '#app'
});
I think this is really simple but I don't get it running... Can someone help? Thanks!
I import the links from a json file and figured out to filter them like this:
filteredLinks () {
return this.links.filter((link) => {
return link.items.some((item) => {
return item.name.toLowerCase().includes(this.searchQuery.toLowerCase())
})
})
}

How to Implement React Redux Search and Checkbox Filter

I am trying to implement search/filter in my react project but can't seem to get it right.
Screenshot
This is what the user should be able to do:
Filter by text
Filter by location using the checkbox
or filter by job category
What I have right now is that I can filter by both text and checkbox, but I want the user to be able to search by text, e.g., "Dallas", "information technology", "sales" and then filter further by category or by location using the returned data from the text search.
This is my set up:
Searching by text uses a reducer to filter the array by search text
Checking a location or category checkbox stores the value in the category or keyword array
In the combine reducer file, I filter the jobs array in the store and memorize it using reselect.
I have a couple of glitches with the search text and checkbox functionalities. The filter by location checkbox functionality works fine by itself; however, when a checkbox is selected, and I enter search text, checkbox gets deselected. I want the checkbox to be still checked and displayed in the window when text is entered in the input field.
I'm a newbie, any help, suggestions, or a better approach to this functionality will be greatly appreciated.
SEARCHFORM.JS:
import React, {Component} from 'react';
//import {FormGroup, FormControl, InputGroup} from 'react-bootstrap';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { searchJobs, getJobs } from '../actions/jobaction';
import {selectKeywordCity,deselectKeywordCity} from '../actions/selectDeselectCityAction';
import {selectKeywordCategory,deselectKeywordCategory} from '../actions/selectDeselectCategoryAction';
class Search extends Component{
constructor(props){
super();
this.state = {
keyword:'',
checked:[]
};
this.handleChk = this.handleChk.bind(this);
}
handleInput = (event) => {
this.setState({
keyword: event.target.value
});
this.props.searchJobs(this.state.keyword);
}
/* handleSearch = (event) => {
event.preventDefault();
this.props.getJobs(this.state.keyword);
} */
clearForm = () => {
this.inputSearch.value = "";
this.setState({
keyword: this.inputSearch.value
});
this.props.getJobs();
}
handleChk = (e, keyword, type) => {
if(type==='city'){
e.target.checked ? this.props.selectKeywordCity(keyword) : this.props.deselectKeywordCity(keyword);
}else if(type==='category'){
e.target.checked ? this.props.selectKeywordCategory(keyword) : this.props.deselectKeywordCategory(keyword);
}
}
//Render Cities
renderCities(data){
if(data){
const cities = Object.keys(data).map(el => {
return data[el].city
}).sort()
.reduce((city,name) => {
const cityCount = city[name] ? city[name] + 1 : 1;
return{
...city, [name]:cityCount,
};
},{});
return Object.keys(cities).map((keyname, index) => {
return (
<li key={keyname}>
<input type="checkbox"
onChange={e=>this.handleChk(e,keyname,'city')}
className="chkbox"
/>
{keyname}
<span className="pull-right">{cities[keyname]}</span>
</li>)
})
}else{
return("nada")
}
}
//Render Job Categories
renderCategories(data){
if(data){
const categories = Object.keys(data).map(el => {
return data[el].category
}).sort()
.reduce((category,name) => {
const categoryCount = category[name] ? category[name] + 1 : 1;
return{
...category, [name]:categoryCount,
};
},{});
return Object.keys(categories).map((keyname, index) => {
return (
<li key={keyname}>
<input type="checkbox"
onChange={e=>this.handleChk(e,keyname,'category')}
className="chkbox"
/>
{keyname}
<span className="pull-right">{categories[keyname]}</span>
</li>)
})
}else{
return("nada")
}
}
render(){
let closeBtn = null;
if(this.state.keyword){
closeBtn = (<i className="fa fa-times" onClick={this.clearForm}></i>);
}
return(
<div>
<div className="side-search">
<i className="fa fa-search"></i>
<input type="text"
placeholder="Keyword Search"
ref={el => this.inputSearch = el}
onChange={this.handleInput}
value={this.state.keyword}
/>
{closeBtn}
</div>
<div className="chk_boxes">
<h4>Location</h4>
<ul>
{
this.renderCities(this.props.jobs)
}
</ul>
</div>
<div className="chk_boxes">
<h4>Category</h4>
<ul>
{
this.renderCategories(this.props.jobs)
}
</ul>
</div>
</div>
)
}
}
function mapStateToProps(state){
return{
jobs: state.jobs.jobs
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators({searchJobs,
getJobs,
selectKeywordCity,
deselectKeywordCity,
selectKeywordCategory,
deselectKeywordCategory},
dispatch)
}
export default connect(mapStateToProps,mapDispatchToProps)(Search);
JOBACTION:
import axios from 'axios';
const FETCH_URL = 'http://localhost:3006';
/* GET ALL JOB LISTINGS */
export function getJobs(){
return function(dispatch){
axios.get(`${FETCH_URL}/jobs`)
.then(function(response){
dispatch({type:'GET_JOBS',payload:response.data})
}).catch(function(err){
dispatch({type:'GET_JOBS_ERROR', payload:err});
})
}
}
/* GET ALL JOB LISTINGS */
export function searchJobs(keyword){
return function(dispatch){
axios.get(`${FETCH_URL}/jobs?q=${keyword}`)
.then(function(response){
dispatch({type:'SEARCH_JOBS',payload:response.data})
}).catch(function(err){
dispatch({type:'SEARCH_JOBS_ERROR', payload:err});
})
}
}
JOBSLIST REDUCER:
export function Listings (state={}, action){
switch(action.type){
case 'GET_JOBS':
return{...state, jobs:action.payload}
case 'SEARCH_JOBS':
return{...state, jobs:action.payload}
default:
return state;
}
}
SELECTDESELECT(CHECKBOX) REDUCER:
const INITIAL_STATE = {
keywords:[]
}
export function filterCity(state=INITIAL_STATE, action){
switch(action.type){
case 'SELECT_CITY':
return{
keywords:[...state.keywords, action.payload]
}
case 'DESELECT_CITY':
const currentKeywordToDelete = [...state.keywords];
const indexOfKeyword = currentKeywordToDelete.findIndex(function(keyword){
return keyword === action.payload
});
return{
keywords:[...state.keywords.slice(0,indexOfKeyword), ...currentKeywordToDelete.slice(indexOfKeyword+1) ]
}
default:
return state;
}
}
COMBINEREDUCER:
import {combineReducers} from 'redux';
import {createSelector} from 'reselect';
import {Listings} from './listingsReducer';
import {filterCity} from './select-deselect-city';
import {filterCategory} from './select-deselect-category';
const reducer = combineReducers({
jobs: Listings,
keywords: filterCity,
category: filterCategory
});
export const selectJobList = (state) => state.jobs.jobs;
export const selectCities = (state) => state.keywords.keywords;
//export const selectCategories = (state) => state.categories.categories;
export const selectFilteredJobs = createSelector(
selectJobList,selectCities,
(jobs,cities) => {
if(cities && cities.length > 0){
const filteredData = Object.keys(jobs)
.filter(key => cities.includes(jobs[key].city))
.reduce((obj, key) => {
return [...obj, {...jobs[key]}]
}, []);
//console.log(filteredData);
return filteredData;
}else{
return jobs
}
}
)
export default reducer;
JSON DATA:
{
"jobs": [
{
"id": 1,
"title": "Financial Analyst II",
"description": "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. .",
"category": "Sales",
"dept": "Finance",
"date": "07/11/2018",
"experience": ["Bachelors Degree","Five years of sales experience"],
"city": "Dallas",
"state":"Texas",
"salary": "10,000"
},
{
"id": 2,
"title": "Application Support Speacialist",
"description": "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.",
"category": "Information Technology",
"dept": "Accounting",
"date": "07/10/2018",
"experience": ["Bachelors Degree","Two years of sales experience"],
"city": "Dallas",
"state":"Texas",
"salary": "20,000"
}
}

Module not found: Can't resolve 'material-ui/FlatButton

I got the following error:
/src/Components/Home/post.jsx
Module not found: Can't resolve 'material-ui/FlatButton' in /Users/apple/Documents/dev/source/sm-ui/servicemonster-ui
I tried to install material-ui but it failed. How can I fix this?
post.jsx:
import React from 'react';
import {Card, CardActions, CardHeader, CardMedia, CardTitle, CardText} from 'material-ui/Card';
import FlatButton from 'material-ui/FlatButton';
import Toggle from 'material-ui/Toggle';
export default class CardExampleControlled extends React.Component {
constructor(props) {
super(props);
this.state = {
expanded: false,
};
}
handleExpandChange = (expanded) => {
this.setState({expanded: expanded});
};
handleToggle = (event, toggle) => {
this.setState({expanded: toggle});
};
handleExpand = () => {
this.setState({expanded: true});
};
handleReduce = () => {
this.setState({expanded: false});
};
render() {
return (
<Card expanded={this.state.expanded} onExpandChange={this.handleExpandChange}>
<CardHeader
title="URL Avatar"
subtitle="Subtitle"
avatar="images/ok-128.jpg"
actAsExpander={true}
showExpandableButton={true}
/>
<CardText>
<Toggle
toggled={this.state.expanded}
onToggle={this.handleToggle}
labelPosition="right"
label="This toggle controls the expanded state of the component."
/>
</CardText>
<CardMedia
expandable={true}
overlay={<CardTitle title="Overlay title" subtitle="Overlay subtitle" />}
>
<img src="images/nature-600-337.jpg" alt="" />
</CardMedia>
<CardTitle title="Card title" subtitle="Card subtitle" expandable={true} />
<CardText expandable={true}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Donec mattis pretium massa. Aliquam erat volutpat. Nulla facilisi.
Donec vulputate interdum sollicitudin. Nunc lacinia auctor quam sed pellentesque.
Aliquam dui mauris, mattis quis lacus id, pellentesque lobortis odio.
</CardText>
<CardActions>
<FlatButton label="Expand" onClick={this.handleExpand} />
<FlatButton label="Reduce" onClick={this.handleReduce} />
</CardActions>
</Card>
);
}
}
//error:./src/Components/Home/post.jsx Module not found: Can't resolve
'material-ui/FlatButton' in
'/Users/apple/Documents/dev/source/sm-ui/servicemonster-ui/src/Components/Home'
home.jsx:
import React, {Component} from 'react';
import { connect } from 'react-redux';
import EditLocation from 'material-ui-icons/EditLocation';
import {Input, IconButton, } from 'material-ui';
import { Services, Search, } from './';
import {CardExampleControlled} from './post.jsx'
const Icon = require('../../assets/icon.svg');
class home extends Component {
render() {
// const { services, fetched, fetching, error } = this.props;
return (
<div className="home-page">
<div className="home">
<img className="logo" src={Icon} alt="Service Monster" />
<Search />
<div className="flex flex-row flex-center">
<IconButton className="" color="contrast" aria-label="Menu">
<EditLocation />
</IconButton>
<Input
defaultValue="india"
className="location"
inputProps={{
'aria-label': 'location',
}}
/>
</div>
</div>
<Services />
</div>
);
}
}
const mapStateToProps = (store) => {
return {
services: store.services.data,
fetched: store.services.fetched,
fetching: store.services.fetching,
error: store.services.error
}
}
const Home = connect(mapStateToProps)(home);
export { Home };
index.js:
import { Home, } from './Home.jsx';
import { Services, } from './Services.jsx';
import { Menus, } from './Menu.jsx';
import { Search } from './Search';
import { CardExampleControlled } from './post.jsx';
export { Home, Menus, Services, Search, CardExampleControlled };
There is version mismatch of material-ui.
From your code FlatButton component belongs to version v0.20 and probably you have installed v1.0.0-beta.xx which does not have that component.
so you need to install npm install material-ui for currently stable release. instead of npm install material-ui#next
I had the same problem when trying out material-ui.
It felt kind of tricky because it is not explicitly documented and there are differences between v0.20 and v1.0. There is no Button component in v0.20, for example look at http://www.material-ui.com/#/components/raised-button
But I have found solution on https://github.com/mui-org/material-ui/tree/master ( for version v0.20.0), titled Usage
you will need to wrap your 'App' (root react component) with tag <MuiThemeProvider>
I have created test app with react-create-app script, and changed file App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import RaisedButton from 'material-ui/RaisedButton';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
class App extends Component {
render() {
return (
<MuiThemeProvider>
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
<RaisedButton label="Primary" primary={true} />
</div>
</MuiThemeProvider>
);
}
}
export default App;
voila, now it works :-)
If you've installed the non-alpha version of Material-UI you should be able to resolve this warning by changing your import line from:
import FlatButton from 'material-ui/FlatButton';
to
import { FlatButton } from 'material-ui';

Error: JSON.parse: expected ',' or ']' after array element

Am newbie to Json am getting this error while call json my json file expected ',' or ']'
[
{
"modules":
[
{
"title":"name of module1",
"description":"description of module1",
"weeks":[{"id":1, "title":"Week 01"}]
},
{
"title":"name of module2",
"description":"description of module2",
"weeks":[{"id":2, "title":"Week 02"},{"id":3,"title":"Week 03"}]
}
]
},
{
"products":
[
{
"url":"http://dummyimage.com/242x200/f3f3f3/2d2d2d.png",
"description":"Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.",
"viewurl" : "/",
"savebtn" : "save"
},
{
"url":"http://dummyimage.com/242x200/f3f3f3/2d2d2d.png",
"description":"Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.",
"viewurl" : "/",
"savebtn" : "save"
}
]
}
]
hope i did correct i dont know what is happening its getting Error: JSON.parse: expected ',' or ']' after array element am attempting to use in angular js controller
app.controller('settingsController', function($scope, $http){
$http.get('assets/javascripts/datas.json').then(function(result){
$scope.employe = result.data;
$scope.prod = result
})
});
and in view
<div class="col-sm-6 col-md-4" ng-repeat="datas in prod.products">
<div class="thumbnail">
<img ng-src="{{ datas.url }}" alt="product">
<div class="caption">
<h3>{{ datas.caption}} </h3>
<p>{{ datas.description}}</p>
<p><a role="button" class="btn btn-primary" ng-href="{{datas.viewurl}}">Button</a> <a role="button" class="btn btn-default" href="#">{{datas.savebtn}}</a></p>
</div>
</div>
</div>
and the error in console
Error: JSON.parse: expected ',' or ']' after array element at line 76 column 9 of the JSON data cc#/test/assets/javascripts/vendors/angular.js:14:289 Ud/this.defaults.transformResponse<#/test/assets/javascripts/vendors/angular.js:69:58 xc/<#/test/assets/javascripts/vendors/angular.js:68:352 r#/test/assets/javascripts/vendors/angular.js:7:288 xc#/test/assets/javascripts/vendors/angular.js:68:336 b#/test/assets/javascripts/vendors/angular.js:70:65 ye/e/l.promise.then/K#/test/assets/javascripts/vendors/angular.js:100:59 ye/f/<.then/<#/test/assets/javascripts/vendors/angular.js:101:235 Zd/this.$get
This error occurred for me when I parsed JSON from a string literal. When I copied in the JSON, I forgot that the escape sequences would need to be double escaped.
Example of the problem:
var data = JSON.parse('[{"key": "This value has \"a quoted\" string"}]');
While the string value is valid JSON, the escape sequences are lost. Then need to be double escaped:
Corrected version:
var data = JSON.parse('[{"key": "This value has \\\"a quoted\\\" string"}]');
Well for me the problem occurs if I have a :// (e.g. http://) inside my json stirng for this reason I replaced it and it worked.

Resources