Why is React not displaying any data? - django

import React, { Component } from 'react';
import axios from 'axios';
I would like to display this on the page:
class ProjectDeatil extends Component {
constructor(props) {
super(props);
this.state = { user: { name: '' } };
}
.
componentDidMount() {
const { match: { params } } = this.props;
axios.get(`http://localhost:8000/api/project/${params.pk}`)
.then(({ data: user }) => {
console.log( user);
this.setState({"User:": user });
});
}
I added her const
render() {
const{ user } = this.state;
return (
Then I tried displaying it again and it still didn't work
<div className="col-md-4 text-white animated fadeInUp delay-2s if " >
<h1>{user.title}</h1>
<h1> Hello Dear</h1>
</div>
I also tried using django rest api and that also didn't work.
</div>
);
}
}
export default ProjectDeatil

You have to fix this line:
this.setState({"User:": user });
to
this.setState({"user": user });

Related

How to create and update products to a REST API from Angular

I am new to and currently working on a Django and Angular webapp and I cant seem to be able to create and update my products from the angular web app and send them to the REST API to update the server. I have written a delete function and service request that works and I can retrieve the products from the API but I don't know how to write the functions for create products and update products. Would anyone be able to help me here?
Here is what I have so far:
api.service.ts
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http'
import { Product } from './models/Product';
import { CookieService } from 'ngx-cookie-service';
import { Category } from './models/Category';
import { Shop } from './models/Shop';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class ApiService {
baseUrl = 'http://127.0.0.1:8000/';
baseProductUrl = `${this.baseUrl}app/products/`
baseCategoryUrl = `${this.baseUrl}app/categories/`
baseShopUrl = `${this.baseUrl}app/shops/`
headers = new HttpHeaders({
'Content-Type': 'application/json',
});
constructor(
private httpClient: HttpClient,
private cookieService: CookieService,
) { }
/* Product CRUD */
/* ADD: add a product to the server */
addProduct(): Observable<Product> {
const productUrl = `${this.baseProductUrl}`
return this.httpClient.post<Product>(productUrl, {headers: this.getAuthHeaders()})
}
// DELETE: delete the product from the server
deleteProduct(product_code: number): Observable<Product> {
const productUrl = `${this.baseProductUrl}${product_code}/`
return this.httpClient.delete<Product>(productUrl, {headers: this.getAuthHeaders()})
}
// PUT: update the product on the server
updateProduct(product_code: Product): Observable<any> {
const productUrl = `${this.baseProductUrl}${product_code}/`
return this.httpClient.put(productUrl, {headers: this.getAuthHeaders()})
}
// GET: get all products from the server
getProducts() {
return this.httpClient.get<Product[]>(this.baseProductUrl, {headers: this.getAuthHeaders()});
}
// GET: get one product from the server
getProduct(product_code: number): Observable<Product> {
const productUrl = `${this.baseProductUrl}${product_code}/`
return this.httpClient.get<Product>(productUrl, {headers: this.getAuthHeaders()});
}
// GET: get all categories from the server
getCategories() {
return this.httpClient.get<Category[]>(this.baseCategoryUrl, {headers: this.getAuthHeaders()});
}
// GET: get one category from the server
getCategory(id: number): Observable<Category> {
const url = `${this.baseCategoryUrl}${id}/`;
return this.httpClient.get<Category>(url, {headers: this.getAuthHeaders()});
}
// GET: get all shops from the server
getShops() {
return this.httpClient.get<Shop[]>(this.baseShopUrl, {headers: this.getAuthHeaders()});
}
// GET: get one shop from the server
getShop(business_reg: string): Observable<Shop> {
const url = `${this.baseShopUrl}${business_reg}/`;
return this.httpClient.get<Shop>(url, {headers: this.getAuthHeaders()});
}
loginUser(authData: any) {
const body = JSON.stringify(authData);
return this.httpClient.post(`${this.baseUrl}auth/`, body, {headers: this.headers});
}
registerUser(authData: any) {
const body = JSON.stringify(authData);
return this.httpClient.post(`${this.baseUrl}user/users/`, body, {headers: this.headers});
}
getAuthHeaders() {
const token = this.cookieService.get('ur-token')
return new HttpHeaders({
'Content-Type': 'application/json',
Authorization: `Token ${token}`
});
}
}
dashboard.component.ts
import { Component, OnInit } from '#angular/core';
import { Product } from 'src/app/models/Product';
import { CookieService } from 'ngx-cookie-service';
import { Router } from '#angular/router';
import { ApiService } from '../../api.service';
import { Category } from '../../models/Category';
#Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.css']
})
export class DashboardComponent implements OnInit {
products: Product[] =[]
constructor(
private cookieService: CookieService,
private router: Router,
private apiService: ApiService
) { }
ngOnInit(): void {
this.getProducts()
}
getProducts(): void {
const urToken = this.cookieService.get('ur-token')
if (!urToken) {
this.router.navigate(['/auth']);
} else {
this.apiService.getProducts().subscribe(
data => {
this.products = data;
console.log('selectedProduct', this.products);
},
error => console.log(error)
);
}
}
delete(product: Product): void {
this.products = this.products.filter(h => h !== product);
this.apiService.deleteProduct(product.product_code).subscribe();
}
}
dashboard.component.html
<section>
<div class="container">
<h1>My Dashboard <button class="btn" routerLink="add/">Add Product</button></h1>
<div class="card-wrapper">
<div class="card" *ngFor="let product of products">
<!-- card left -->
<div class="product-content">
<img class="img-fluid image" src="{{product.product_image}}">
<div class="product-item">
<h2 class="product-title">{{ product.name }}</h2>
<a class="edit"><button class="btn" routerLink="edit/{{ product.product_code }}">Edit</button></a>
<a class="delete"><button class="btn" (click)="delete(product)">Delete</button></a>
</div>
</div>
</div>
</div>
</div>
</section>
product-form.component.ts
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { ApiService } from 'src/app/api.service';
import { Product } from 'src/app/models/Product';
import { Location } from '#angular/common';
import { FormGroup, FormControl } from '#angular/forms';
#Component({
selector: 'app-product-form',
templateUrl: './product-form.component.html',
styleUrls: ['./product-form.component.css']
})
export class ProductFormComponent implements OnInit {
product!: Product;
constructor(
private route: ActivatedRoute,
private apiService: ApiService,
private location: Location
) { }
ngOnInit(): void {
this.getProduct()
}
getProduct(): void {
const product_code = Number(this.route.snapshot.paramMap.get('product_code'));
this.apiService.getProduct(product_code)
.subscribe(product => this.product = product);
}
goBack(): void {
this.location.back();
}
save(): void {
this.apiService.updateProduct(this.product)
.subscribe(() => this.goBack());
}
}
product-form.component.html
<div *ngIf="product">
<input id="product-code" [(ngModel)]="product.product_code" placeholder="product_code">
<input id="product-name" [(ngModel)]="product.name" placeholder="name">
<input id="product-price" [(ngModel)]="product.price" placeholder="price">
</div>
Looking at your API service the HTTP POST and PUT calls are missing payload data.
Can you try passing the data for your product as a parameter?
The product code for the PUT should be in a class with populated data (from your component) so that your backend API can pick up the code (and other product data fields) and update your data. Your backend API methods should then read the request data from the body (the payload).
For your Angular API service methods you should pass product data parameters from your component as follows:
/* ADD: add a product to the server */
addProduct(product: Product): Observable {
const productUrl = ${this.baseProductUrl}
return this.httpClient.post(productUrl, product, {headers: this.getAuthHeaders()})
}
// PUT: update the product on the server
updateProduct(product: Product): Observable {
const productUrl = ${this.baseProductUrl}${product_code}/
return this.httpClient.put(productUrl, product {headers: this.getAuthHeaders()})
}
There is a post (https://andrewhalil.com/2020/12/01/calling-web-apis-from-your-angular-ui/) on my site that shows how this is done in Angular.

Pass data from Django template to react App

I'm building an app using Django - DRF - React through template component invocation. Question is: Whats the right way to pass data from django view-template to react app-component, for example, if the api called in app fetch method used a dynamic parameter:
fetch("/api/endpoint") to fetch("/api/endpoint/modelPrimaryKey")
Sources used:
views.py
def index(request):
return render(request, 'frontend/index.html')
index.html
...
</body>
{% load static %}
<script src="{% static "frontend/main.js" %}"></script>
</html>
main.js (compiled by webpack from this source)
class App extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
loaded: false,
placeholder: "Loading"
};
}
componentDidMount() {
fetch("/api/endpoint")
.then(response => {
if (response.status > 400) {
return this.setState(() => {
return { placeholder: "Something went wrong!" };
});
}
return response.json();
})
.then(data => {
this.setState(() => {
return {
data,
loaded: true
};
});
});
}
render() {
return (
... some jsx
);
}
}
export default App;
const container = document.getElementById("app");
render(<App />, container);

Angular View does't refresh on array push

I am very new to ionic and angular.
Anyway, I am trying to following an tutorial to create a notes app using ionic4 https://www.joshmorony.com/building-a-notepad-application-from-scratch-with-ionic/.
So, I follow the instruction. Everything is ok except that the view doesn't updated when I add new note. The code is as follow:
Note services:
import { Injectable } from '#angular/core';
import { Storage } from '#ionic/storage';
import { Note } from '../interfaces/note';
#Injectable({
providedIn: 'root'
})
export class NotesService {
public notes: Note[] = [];
public loaded: boolean = false;
constructor(private storage: Storage) {
}
load(): Promise<boolean> {
// Return a promise so that we know when this operation has completed
return new Promise((resolve) => {
// Get the notes that were saved into storage
this.storage.get('notes').then((notes) => {
// Only set this.notes to the returned value if there were values stored
if (notes != null) {
this.notes = notes;
}
// This allows us to check if the data has been loaded in or not
this.loaded = true;
resolve(true);
});
});
}
save(): void {
// Save the current array of notes to storage
this.storage.set('notes', this.notes);
}
getNote(id): Note {
// Return the note that has an id matching the id passed in
return this.notes.find(note => note.id === id);
}
createNote(title): Promise<boolean> {
return new Promise((resolve) => {
// Create a unique id that is one larger than the current largest id
let id = Math.max(...this.notes.map(note => parseInt(note.id)), 0) + 1;
this.notes.push({
id: id.toString(),
title: title,
content: ''
});
this.save();
console.log('Service Log ' + this.notes);
resolve(true);
});
}
}
The HTML code:
<ion-header>
<ion-toolbar color="primary">
<ion-title>Notes</ion-title>
<ion-buttons slot="end">
<ion-button (click)="addNote()">
<ion-icon slot="icon-only" name="clipboard"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item button detail *ngFor="let note of notesService.notes" [href]="'/notes/' + note.id" routerDirection="forward">
<ion-label>{{ note.title }}</ion-label>
</ion-item>
</ion-list>
</ion-content>
I've followed the same tutorial and got the same issue. The issue is because of something very interesting and powerful called Zones.
The idea is that you'd need to let Angular know that the array with the notes has changed, by doing something like this:
// Angular
import { Component, NgZone } from '#angular/core';
// Ionic
import { NavController, AlertController } from '#ionic/angular';
// Services
import { NotesService } from '../services/notes.service';
import { AlertOptions } from '#ionic/core';
#Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage {
constructor(
private ngZone: NgZone, // Add this in the constructor
private navCtrl: NavController,
private alertCtrl: AlertController,
private notesService: NotesService,
) { }
ngOnInit() {
this.notesService.load();
}
addNote() {
const alertOptions: AlertOptions = {
header: 'New Note',
message: 'What should the title of this note be?',
inputs: [
{
type: 'text',
name: 'title'
}
],
buttons: [
{
text: 'Cancel'
},
{
text: 'Save',
handler: (data) => {
// Create the note inside a Zone so that Angular knows
// that something has changed and the view should be updated
this.ngZone.run(() => {
this.notesService.createNote(data.title);
});
}
}
]
};
this.alertCtrl
.create(alertOptions)
.then((alert) => {
alert.present();
});
}
}

React Apollo: apollo-cache-persist seems to not be working

Maybe I misunderstood what this package does, but I assumed that it would read cached responses and help with offline application functionality.
import React from 'react'
import { graphql } from 'react-apollo'
import gql from 'graphql-tag'
export const DATA_QUERY = gql`
query Data {
me {
name
bestFriend {
name
}
}
}
`
const options = () => ({
fetchPolicy: 'cache-only'
})
const withData = graphql(DATA_QUERY, { options })
export const Start = ({ data }) =>
data.loading ? (
'loading!'
) : data.me ? (
<div>
{console.log('data', data)}
<h3>Me: {data.me.name}</h3>
<p>Best friend: {data.me.bestFriend.name}</p>
</div>
) : (
'no data'
)
export default withData(Start)
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { ApolloProvider } from 'react-apollo'
import { ApolloClient } from 'apollo-client'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { HttpLink } from 'apollo-link-http'
import { persistCache } from 'apollo-cache-persist'
const cache = new InMemoryCache()
persistCache({
cache,
storage: window.localStorage,
debug: true
})
export const client = new ApolloClient({
link: new HttpLink({ uri: 'https://v9zqq45l3.lp.gql.zone/graphql' }),
cache
})
ReactDOM.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById('root'));
registerServiceWorker();
I do have the cache in my localStorage
apollo-cache-persist: "{"$ROOT_QUERY.me":{"name":"Bob","bestFriend":{"type":"id","id`enter code here`":"$ROOT_QUERY.me.bestFriend","generated":true}"
When running the above example with fetchPolicy: 'cache-only' the component renders 'no data'. If I do the default fetchPolicy, cache-first, then I get the expected result but I can see the network request is being made.
EDIT: Now works with Daniels answer and this workaround waits for cache to be restored before running the query.
import Start from './Start'
class App extends Component {
state = {
show: false
}
toggle = () =>
this.setState({ show: !this.state.show })
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<br/><br/>
<button onClick={this.toggle}>Show it</button>
<br/><br/>
{this.state.show && <Start />}
</div>
);
}
}
In order to correctly cache and later retrieve the data from the cache, Apollo needs an id (or _id) to work with. If you want to use a different property as the id (like name), you can pass a dataIdFromObject function to your configuration for the in-memory cache:
const cache = new InMemoryCache({
dataIdFromObject: object => {
switch (object.__typename) {
//User is whatever type "me" query resolves to
case 'User': return object.name;
default: return object.id || object._id;
}
}
});
Something like this works, though I wonder if there should be a more elegant solution. Maybe the Retry Link.
https://github.com/apollographql/apollo-cache-persist/issues?utf8=%E2%9C%93&q=is%3Aissue+
export class Index extends Component {
state = {
client: null
}
async componentWillMount() {
const httpLink = new HttpLink({ uri: 'https://v9zqq45l3.lp.gql.zone/graphql' })
const link = ApolloLink.from([ httpLink ])
const cache = new InMemoryCache({
dataIdFromObject: (object) => {
switch (object.__typename) {
// User is whatever type "me" query resolves to
case 'User':
return object.name
default:
return object.id || object._id
}
}
})
await persistCache({
cache,
storage: window.localStorage,
debug: true
})
const client = new ApolloClient({
link,
cache
})
this.setState({ client })
}
render() {
return !this.state.client ? (
null
) : (
<ApolloProvider client={this.state.client}>
<App />
</ApolloProvider>
)
}
}
ReactDOM.render(<Index />, document.getElementById('root'))

How to properly unit test login and local storage

After 3 days researching and not ariving anywhere, I decided to ask here for someone that already have similar experience or can point a better path to follow.
The better SO question I've found was this but left some questions in air: React - how to test form submit?
Since I'm begginer I believe I may getting something wrong, but no sure exactly which. If it's the way I build the components or even test concept itself.
I have the following case:
When a user logins in, it calls API (mock) then save token result (when successful) to localStorage (mock)
When user is already logged in, it gets redirected to homepage
My code until now:
Login Component
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: ''
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleSubmit(e) {
e.preventDefault();
this.props.sendLoginRequest(this.state).then(
({data}) => {
console.log(data);
},
(data) => {
console.error(data);
}
);
}
handleChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
render() {
return (
<div id='auth-container' className='login'>
<Form onSubmit={this.handleSubmit}>
<FormGroup controlId='emailaddress'>
<InputGroup bsSize='large'>
<InputGroup.Addon>
<Icon glyph='icon-fontello-mail' />
</InputGroup.Addon>
<FormControl
autoFocus
className='border-focus-blue'
type='email'
placeholder='email#fixdin.com'
name='email'
onChange={this.handleChange}
value={this.state.email} />
</InputGroup>
</FormGroup>
<FormGroup controlId='password'>
<InputGroup bsSize='large'>
<InputGroup.Addon>
<Icon glyph='icon-fontello-key' />
</InputGroup.Addon>
<FormControl
className='border-focus-blue'
type='password'
placeholder='password'
name='password'
onChange={this.handleChange}
value={this.state.password} />
</InputGroup>
</FormGroup>
</Form>
</div>
)
}
}
Login.propTypes = {
sendLoginRequest: React.PropTypes.func.isRequired
}
authAction.js
import createApi from '../services/api';
import { saveToken } from '../services/session';
export function sendLoginRequest(loginData) {
return dispatch => {
const api = createApi();
const loginPromise = api.post('auth/', loginData);
loginPromise.then(
({ data }) => {
saveToken(data.token);
}
);
return loginPromise;
}
}
API..js
import axios from 'axios';
import { isAuthenticated, getToken } from './session';
export const BASE_URL = 'http://localhost:8000/api/v1/';
export default function createAPI() {
let auth = { }
if (isAuthenticated()) {
auth = {
Token: getToken()
}
}
return axios.create({
baseURL: BASE_URL,
auth: auth
});
};
session.js
const TOKEN_KEY = 'token';
export function saveToken(value)
{
localStorage.setItem(TOKEN_KEY, value);
}
export function getToken()
{
return localStorage.getItem(TOKEN_KEY)
}
export function isAuthenticated() {
return getToken() !== null;
}
My test stack is Mocha/Chai/Enzyme/sinon and it's defined
setup.js
var jsdom = require('jsdom');
class LocalStorageMock {
constructor() {
this.store = {};
}
clear() {
this.store = {};
}
getItem(key) {
return this.store[key];
}
setItem(key, value) {
this.store[key] = value.toString();
}
};
if(!global.document) {
global.document = jsdom.jsdom('<!doctype html><html><body></body></html>');
global.window = document.defaultView;
global.navigator = {userAgent: 'node.js'};
global.localStorage = new LocalStorageMock;
}
login-test.js
import React from 'react';
import sinon from 'sinon';
import { mount, shallow } from 'enzyme';
import { expect } from 'chai';
import { Provider } from 'react-redux';
import axios from 'axios'
import moxios from 'moxios'
import store from './../src/store';
import LoginPage from './../src/auth/components/Login';
describe('Login', () => {
beforeEach(function () {
moxios.install(axios)
})
afterEach(function () {
moxios.uninstall(axios)
})
it('should call action on form submit', () => {
const submitRequest = sinon.stub(LoginPage.prototype, 'handleSubmit').returns(true);
const wrapper = mount(<Provider store={store}><LoginPage /></Provider>);
wrapper.find('form').simulate('submit');
expect(submitRequest.called).to.be.true;
submitRequest.restore();
});
it('should save token on succesfull login', () => {
const wrapper = mount(<Provider store={store}><LoginPage /></Provider>);
const emailInput = wrapper.find('input[type="email"]');
const passInput = wrapper.find('input[type="password"]');
const form = wrapper.find('form');
emailInput.value = "valid#email.com";
passInput.value = '123456789';
form.simulate('submit'); // Should I use submit button instead???
moxios.wait(function () {
let request = moxios.requests.mostRecent()
request.respondWith({
status: 200,
response:
{ Token: 'validToken' }
}).then(function () {
expect(localStorage.getItem('Token')).to.equal('validToken');
});
});
});
});
Above test does not pass, since it returns false for submitRequest.called and second test fails with error "Cannot read property 'respondWith' of undefined". I'm not sure how to fix and more, I'm not sure if I idealized it right!!
When doing a lot of research about it, I've seen examples with tests specific for component method call + isolated action test.
So...
When I think about "click login and save token" I'm overthinking a unit test? There's a better way to test things like that? Maybe separate some concerns?
This is the correctly way to test if a form submit invoke its callback? If so, why sinon is not working there?
This is the correctly way to mock + test api call to login and localStorage? If so, why Moxios is not working properly? It keeps giving me that mostRecent() is undefined.
If no, to question 2 and 3, where can I find a valid and working example of how to properly test cited behavior?
Thanks in advance.