working on an angular4 app that has 2 components/pages.
the first component is related to the object id:1 and it is one page and the second component is related to id:2 and it is another page. both of these pages share the same template 'page.component.html'
how do get the first component to only render the object with id:1? and the same for the second component. I understand that right now as it is set up, each component is going to both objects in the array.
is there a way i can do this in the service or each component?
data.json
[
{
"id": 1,
"array":
[
{
"name": "name1.a",
"title": "title1.a",
},
{
"name": "name1.b",
"title": "title1.b",
},
{
"name": "name1.c",
"title": "title1.c",
}
],
}
{
"id": 2,
"array":
[
{
"name": "name2",
"title": "title2",
}
]
}
]
page.component.html
<div *ngFor="let set of sets">
<p>{{set.name}}</p>
<p>{{set.title}}</p>
</div>
page.component.ts
// Imports
import { Injectable } from '#angular/core';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import { page } from '../page';
import { Observable } from 'rxjs/Rx';
// Import RxJs required methods
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
#Injectable()
export class GenreService {
// Resolve HTTP using the constructor
constructor (private http: Http) {}
private pagesUrl = '../assets/json/data.json';
// Fetch all existing comments
getPages() : Observable<Page[]>{
// ...using get request
return this.http.get(this.pagesUrl)
// ...and calling .json() on the response to return data
.map((res:Response) => res.json())
//...errors if any
.catch((error:any) => Observable.throw(error.json().error || 'Server error'));
}
}
page.ts
export class Page {
constructor(
public id: number,
public array: array,
public name: string,
public title: string
){}
}
page.component.ts
import { Component, OnInit } from '#angular/core';
import { Page } from './page';
import { PageService } from '../../services/page.service';
#Component({
selector: 'page1',
templateUrl: './page.component.html',
providers: [ PageService ],
})
export class Page1Component implements OnInit {
pages: Page[];
errorMessage: string;
ngOnInit() {
this.getPages();
}
getPages() {
this.genreService.getPages()
.subscribe(
pages => this.pages = pages,
error => this.errorMessage = <any>error);
}
}
This will work good with you
<div *ngFor="let set of sets.array">
<p>{{set.name}}</p>
<p>{{set.title}}</p>
</div>
Maybe you can change the method getPages() for getPage(id: number) and filter by id. It would be like so:
getPage(id: number) : Observable<Page>{
// ...using get request
return this.http.get(this.pagesUrl)
// ...and calling .json() on the response to return data
.map((res:Response) => res.json())
// ... do antoher map an return the correct object
.map((data: Array<any>) => {
return data.find(x => x.id === id)
})
//...errors if any
.catch((error:any) => Observable.throw(error.json().error || 'Server error'));
}
With that fucntion it will only return the Page that you want,
Hope that helps.
Related
*I'm making a angular project (version 6+).
I want to transfer array parameter, using routing.
*
const routes: Routes = [
{
path: 'books',
component: BookComponent
}
,
{
path: 'books/:id',
component: BookDetailComponent
},
...
There are two ways you can use,
Method 1:
HTML
<a routerLinkActive="active" [routerLink]="['/home', userIDs.join()]">Home</a>
TS:
userIDs: Array<number> = [1, 3, 4];
Home component typescript:
export class HomeViewComponent implements OnInit {
userIDs: Array<number> = [];
constructor(private router: ActivatedRoute) { }
ngOnInit() {
this.router.params.subscribe(params => {
this.userIDs = params['ids'].split(',');
});
}
}
Router Module:
path: 'home/:ids', component: HomeViewComponent }
Method 2 (Using queryParams):
You don't need to add anything into router module as we are doing in above method.
HTML
<a routerLinkActive="active" [routerLink]="['/home']" [queryParams]="{ids: userIDs}">Home</a>
TS
userIDs: Array<number> = [1, 3, 4];
Home component typescript:
export class HomeViewComponent implements OnInit {
userIDs: Array<number> = [];
constructor(private router: ActivatedRoute) { }
ngOnInit() {
this.router.queryParams.subscribe(p => {
this.userIDs = p.ids;
});
}
}
In html
send data
here first You will import the activated route like following.
import { ActivatedRoute } from '#angular/router';
and also in construction
construction(private _routes:ActivatedRoute) { }
In====== ngOnInit( this._route.params.subscribe(params => {
this.id=param['id'];
)
A newbie in React here. I'm using axios to retrieve this object requested to my Django server rest framework Api:
{
"count": 3,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"url": "http://localhost:8000/blog/api/categories/1/",
"title": "Django",
"slug": "django"
},
{
"id": 2,
"url": "http://localhost:8000/blog/api/categories/2/",
"title": "Git",
"slug": "git"
},
{
"id": 3,
"url": "http://localhost:8000/blog/api/categories/3/",
"title": "Introduction to docker",
"slug": "introduction-to-docker"
}
]
}
So far I've been able to store those variables in separate arrays. This is the implementation of my FetchDemo.js:
import React, {Component} from 'react';
import axios from 'axios';
{/* https://daveceddia.com/ajax-requests-in-react/ */}
class FetchDemo extends Component {
state = {
urls: []
}
state = {
titles: []
}
state = {
slugs: []
}
componentDidMount() {
axios.get(`${this.props.url}${this.props.path}`).then(res => {
const urls = res.data.results.map(num => num.url);
const titles = res.data.results.map(num => num.title);
const slugs = res.data.results.map(num => num.slug);
this.setState( {urls} );
this.setState( {titles} );
this.setState( {slugs} );
});
}
render() {
return (
<div>
<h1>{`${this.props.url}${this.props.path}`}</h1>
<ul>
/// How to generate the JSX objects? ///
</ul>
</div>
);
}
}
export default FetchDemo;
Is it possible to build in the axios request a state array from this JSON object like the next one instead? How do I generate JSX with this new state categories?
this.setState({
categories: [
{url: "http://localhost:8000/blog/api/categories/1/", title: "Django", slug: "django"},
{url: "http://localhost:8000/blog/api/categories/2/", title: "Git", slug: "git"},
{url: "http://localhost:8000/blog/api/categories/3/", title: "Introduction to Docker", slug: "introduction-to-docker"},
]
}
)
I wish I knew more React and JavaScript. Any help is appreciated, thanks in advance.
There are a few things to note here.
state is a single object with multiple keys.
setState() is a single async transaction. When you are updating your state, you should update it in one go.
You can do things in a simpler way if I understand your question right. You don't have to store the values in multiple keys. It can be stored in a single key categories.
import React, { Component } from 'react';
import axios from 'axios';
class FetchDemo extends Component {
constructor(props) {
super(props);
this.state = {
categories = []
}
}
componentDidMount() {
axios.get(`${this.props.url}${this.props.path}`).then((res) => {
const urls = res.data.results.map((num) => num.url);
const titles = res.data.results.map((num) => num.title);
const slugs = res.data.results.map((num) => num.slug);
this.setState({
categories: res.data.results
});
});
}
render() {
const { categories } = this.state;
return (
<div>
<h1>{`${this.props.url}${this.props.path}`}</h1>
<ul>
{categories.length ?
categories.map(cat => (
<li key={cat.id}>
<div>{cat.title}</div>
</li>
))
:
(<li>categories not loaded yet</li>)}
</ul>
</div>
);
}
}
export default FetchDemo;
I tried every possible variation of this code, but I don't really manage to get whatever the API fetched into my data store. I am absolutely stuck and would appreciate some help.
I think I just don't get the essential part of this construct and I would really like to understand how it works properly.
The data looks like this - it's basically a simple JSON (from a django restframework API) with some nested elements:
EDIT 2 (changed JSON to screenshot of axios API/ Redux action)
My Redux action - works perfectly fine. console.log pulls exactly the data from above (with correct inputs) :
// ./action/plan.js
import axios from 'axios';
export function fetchBudgets(){
return function(dispatch){
axios.get("/api/budgets/")
.then((response) => {
console.log(response)
dispatch({ type: "FETCH_BUDGETS", budgets: response.data})
})
.catch((err) => {
dispatch({type: "FETCH_DATA_REJECTED", budgets: err})
})
}
}
So until now, everything seems fine. The problems starts with the reducer - as I am not sure how to model the reducer to use the nested data.
My reducer:
// ./reducer/plan.js
const initialState = {}
export default function budgets(state=initialState, action) {
switch (action.type) {
case 'FETCH_BUDGETS':
console.log(action)
return {
...state,
id: action.budgets.id,
value_jan: action.budgets.value_jan,
value_feb: action.budgets.value_feb,
value_mar: action.budgets.value_mar,
value_apr: action.budgets.value_apr,
value_may: action.budgets.value_may,
value_jun: action.budgets.value_jun,
value_jul: action.budgets.value_jul,
value_aug: action.budgets.value_aug,
value_sep: action.budgets.value_sep,
value_oct: action.budgets.value_oct,
value_nov: action.budgets.value_nov,
value_dec: action.budgets.value_dec,
p_version: action.budgets.p_version,
entry_time: action.budgets.entry_time,
campaign: {
...state.campaign, ...action.budgets.campaign
},
segment: {
...state.segment, ...action.budgets.segment
},
touch_point: {
...state.touch_point, ...action.budgets.touch_point
},
year: {
...state.year, ...action.budgets.year
},
user: {
...state.user, ...action.budgets.user
}
}
default:
return state
}
}
I already cannot display data in here - so this.props.fetchBudgets() doesn't seem to fetch any data.
My .jsx App
//./container/PlanContainer.jsx
import React, { Component } from 'react';
import {connect} from 'react-redux';
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory from 'react-bootstrap-table2-editor';
import 'jquery';
import 'popper.js'
import 'bootstrap';
import 'underscore'
import _ from 'lodash'
import {plan} from "../actions";
const columns = [
{ dataField: 'id', text: 'ID', hidden: true},
{ dataField: 'year', text: 'Year', editable: false},
{ dataField: 'segment', text: 'Segment', editable: false},
{ dataField: 'campaign.name',text: 'Campaign', editable: false},
{ dataField: 'touch_point',text: 'Touchpoint', editable: false},
{ dataField: 'value_jan',text: 'Jan'},
{ dataField: 'value_feb',text: 'Feb'},
{ dataField: 'value_mar',text: 'Mar'},
{ dataField: 'value_apr',text: 'Apr'},
{ dataField: 'value_may',text: 'May'},
{ dataField: 'value_jun',text: 'Jun'},
{ dataField: 'value_jul',text: 'Jul'},
{ dataField: 'value_aug',text: 'Aug'},
{ dataField: 'value_sep',text: 'Sep'},
{ dataField: 'value_oct',text: 'Oct'},
{ dataField: 'value_nov',text: 'Nov'},
{ dataField: 'value_dec',text: 'Dec'},
{ dataField: 'user',text: 'User'},
];
const RemoteCellEdit = (props) => {
const { columns, data, keyField } = props
const cellEdit = {
mode: 'click',
errorMessage: props.errorMessage,
blurToSave: true
};
return (
<div>
<BootstrapTable
remote={ { cellEdit: true } }
keyField = { keyField }
data={ data }
columns={ columns }
/>
</div>
);
};
class PlanContainer extends React.Component {
componentDidMount() {
this.props.fetchBudgets();
console.log(this.props.fetchBudgets())
}
render() {
return (
<div>
<RemoteCellEdit
data={ this.props.budgets }
columns = { columns }
keyField = 'id'
/>
</div>
);
}
}
const mapStateToProps = state => {
return {
budgets: state.budgets,
}
}
const mapDispatchToProps = dispatch => {
return {
fetchBudgets: () => {
dispatch(plan.fetchBudgets());
},
}
}
export default connect(mapStateToProps, mapDispatchToProps)(PlanContainer);
Finally, my store - according to the console.log nothing is beeing passed:
// .Planning.jsx
import React from "react"
import { hot } from 'react-hot-loader'
import { render } from "react-dom"
import {
createStore,
compose,
applyMiddleware,
combineReducers,
} from "redux"
import { Provider } from "react-redux"
import thunk from "redux-thunk"
import PlanContainer from "./containers/PlanContainer"
import reducerApp from "./reducers";
import Sidebar from "./components/Sidebar"
import axios from 'axios';
import axiosMiddleware from 'redux-axios-middleware';
let store = createStore(reducerApp, applyMiddleware(thunk, axiosMiddleware(axios)));
console.log(store)
class Planning extends React.Component {
render() {
return (
<Sidebar>
<Provider store={store}>
<PlanContainer />
</Provider>
</Sidebar>
)
}
}
render(<Planning />, document.getElementById('Planning'))
Again, I would appreciate as I've been stuck on this issue for quite some time and I really want to understand how to do this properly.
Edit:
Here's a screenshot of my browser: 1st element is the store, second in the .jsx app, 3rd of the action (that looks perfectly fine) and 4th of the action in the reducer.
PlanContainer is messed up. Here's how:
componentDidMount() {
this.budgets = this.props.fetchBudgets();
}
this.budgets is pointing to the value returned by this.props.fetchBudgets() which, in this case, is a Promise, and not the actual data.
state = {
data: this.budgets
};
state now holds the promise, not the data.
render() {
return (
<div>
<RemoteCellEdit
data={ this.state.data }
...
}
So data here is not the actual data but the promise.
The confusion is happening because you are mixing redux state with react state. Use one or the other, not both (there are expcetions to this but not in this particular scenario).
There are some more issues with PlanContainer which are not clear as to whether they are real issues, or just a result of code ommission in OP.
See annotations below:
class PlanContainer extends React.Component {
componentDidMount() {
this.props.fetchBudgets();
}
constructor(props) {
... // removed for brevity, use the same code as you have right now
}
render() {
return (
<div>
<RemoteCellEdit
data={ this.props.budgets}
columns = { this.columns }
keyField = 'id'
errorMessage={ /* should come from props.data or similar - it's not in state */ }
/>
<tbody>
{this.props.budgets} /* not sure what this is for - I assumed RemoteCellEdit is the one rendering the data */
</tbody>
</div>
);
}
}
Fixing these should set you on the correct course. Good luck!
I am using observable in angular .Actually my issue when I click button my subscribe function not called why ?
as per documentation subscribe function will call when we call next function
https://plnkr.co/edit/83NaHoVaxiXAeUFoaEmb?p=preview
constructor() {
this.data = new Observable(observer => this.dataObserver = observer);
this.data.subscribe(value => {
console.log('+++')
console.log(value)
})
}
hndle(){
this.name.push({name:"navee"});
this.dataObserver.next(this.name);
}
here is documentation
http://reactivex.io/rxjs/manual/tutorial.html
On basis of Volodymyr Bilyachat suggestion i have modified your code. its working now plz check. Problem was in your way of using dataObserver
//our root app component
import {Component, NgModule} from '#angular/core'
import {BrowserModule} from '#angular/platform-browser'
import 'rxjs/Rx';
import {Observable} from 'rxjs/Observable';
#Component({
selector: 'my-app',
template: `
<div>
<ul>
<li *ngFor ="let n of name">{{n.name}}</li>
</ul>
<button (click)="hndle()">heelo</button>
</div>
`,
})
export class App {
private data:Observable;
private dataObserver:Observer;
name:string;
name[];
constructor() {
this.dataObserver = new Observable(observer => this.dataObserver = observer);
this.dataObserver.subscribe(value => {
console.log('+++')
console.log(value)
});
}
hndle(){
this.name.push({name:"navee"});
this.dataObserver.next(this.name);
}
}
#NgModule({
imports: [ BrowserModule ],
declarations: [ App ],
bootstrap: [ App ]
})
export class AppModule {}
link https://plnkr.co/edit/PO80y2udrOhsVq4QQXc5?p=preview
I believe you are subscribing to the observable 2 times. You should be able to fix it by adding .share()
constructor() {
this.data = new Observable(observer => this.dataObserver = observer).share();
this.data.subscribe(value => {
console.log('+++')
console.log(value)
})
}
hndle(){
this.name.push({name:"navee"});
this.dataObserver.next(this.name);
}
In your case, it's better to use this solution:
constructor() {
this.data = new Subject();
this.data.subscribe(value => {
console.log('+++');
console.log(value);
});
}
hndle() { // TYPO: Probably it was meant to be handle
this.name.push({
name: 'navee'
});
this.data.next(this.name);
}
Don't forget to add:
import { Subject } from 'rxjs/Subject'
Working example:
https://plnkr.co/edit/zB8FHTVEm2QUHiEAYuQB?p=preview
I need to modify my code where loading detail category will first look whether it is not already loaded in the statement, and if not then detail loads. Thanks for help!
Constructor of CategoryProvider:
private _obServers = {
'categoryList': undefined,
'category': undefined,
'idCategory': new Subject<Number>()
};
constructor(){
this.categories = new Observable(observer => this._obServers.categoryList = observer).share();
this._categoryObservable = this.categories
.combineLatest(this._obServers.idCategory, (categories, idCategory) => {
return categories.filter(category => category.id === idCategory)[0];
})
.distinctUntilChanged((oldCategory, newCategory) => {
return oldCategory.id === newCategory.id;
});
}
CategoryList:
loadCategories(search?:string):void{
this._http
.get('/services/category/list?search=' + search)
.map(res => res.json())
.subscribe(data => {
this._obServers.categoryList.next(this.createCategoryEntities(data));
});
}
CategoryDetail:
loadCategory(categoryId:number){
this._obServers.idCategory.next(categoryId);
//If category not loaded I need to load it
}
I have followed this way https://github.com/JonatanSCS/Angular-2-Tutorial/blob/master/node_modules/rxjs/src/add/observable/combineLatest.ts
import { Component, Injectable, Inject, provide } from '#angular/core';
import { HTTP_PROVIDERS } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { combineLatestStatic } from 'rxjs/operator/combineLatest.js';
import { MessageApi } from '../providers/lb-service/lb-services.provider'
import { EstimatesService } from './estimates.service';
#Component({
pipes: [TranslatePipe],
})
#Injectable()
export class InvoiceService {
constructor(private _message:MessageApi,
#Inject(EstimatesService) _estimates:EstimatesService) {
this._message = _message;
this._estimates = _estimates;
Observable.combineLatest = combineLatestStatic;
declare module 'rxjs/Observable' {
namespace Observable {
export let combineLatest: typeof combineLatestStatic;
}
}
Observable.combineLatest(
this._estimates.getEstimates(),
this._message.findOne({
where: {
moduleTag: 'monthlyStat',
'dynamic.date': "2016-07-01" //new Date
},
fields: {
dynamic: true
}
}),this._message.findOne({
where: {
moduleTag: 'areaPriceSE1',
'dynamic.date': ''
},
fields: {
dynamic: true
}
})
).subscribe(res => console.log("observable", res));