How to delete an object in django-channels -socket? - django-channels

I'm making a real-time chat app using django channels.
I want to delete an object from database using django channels. How can it be?
I tried to delete some objects by using ajax but it didn't work.
consumers.py:
import json
from django.contrib.auth.models import User
from channels.generic.websocket import AsyncWebsocketConsumer
from asgiref.sync import sync_to_async
from .models import Room, Message
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'chat_%s' % self.room_name
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self):
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
# Receive message from WebSocket
async def receive(self, text_data):
data = json.loads(text_data)
print(data)
message = data['message']
username = data['username']
room = data['room']
await self.save_message(username, room, message)
# Send message to room group
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message',
'message': message,
'username': username
}
)
# Receive message from room group
async def chat_message(self, event):
message = event['message']
username = event['username']
# Send message to WebSocket
await self.send(text_data=json.dumps({
'message': message,
'username': username
}))
#sync_to_async
def save_message(self, username, room, message):
user = User.objects.get(username=username)
room = Room.objects.get(slug=room)
Message.objects.create(user=user, room=room, content=message)
room.html:
{% extends 'core/base.html' %}
{% block title %}{{ room.name }} | {% endblock %}
{% block content %}
<div class="p-10 lg:p-20 text-center">
<h1 class="text-3xl lg:text-6xl text-white">{{ room.name }}</h1>
</div>
<button onclick="lok()" type="button" class="like-button liked" id="lok"> salam
</button>
<script src="https://code.jquery.com/jquery-3.1.1.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script>
<script type="text/javascript">
function lok() {
$.ajax({
type:'GET',
url:'/chat/uta/like/',
});
}
</script>
<div class="lg:w-2/4 mx-4 lg:mx-auto p-4 bg-white rounded-xl">
<div class="chat-messages space-y-3" id="chat-messages">
<!--from views.py :-->
{% for m in messages %}<b>{{ m.user.username }}</b>: {{ m.content }}<br>{% endfor %}
</div>
</div>
<div class="lg:w-2/4 mt-6 mx-4 lg:mx-auto p-4 bg-white rounded-xl">
<form method="post" action="." class="flex">
<input type="text" name="content" class="flex-1 mr-3" placeholder="Your message..." id="chat-message-input">
<button
class="px-5 py-3 rounded-xl text-white bg-teal-600 hover:bg-teal-700"
id="chat-message-submit"
>Submit</button>
</form>
</div>
{% endblock %}
{% block scripts %}
{{ room.slug|json_script:"json-roomname" }}
{{ request.user.username|json_script:"json-username" }}
<script>
const roomName = JSON.parse(document.getElementById('json-roomname').textContent);
const userName = JSON.parse(document.getElementById('json-username').textContent);
const chatSocket = new WebSocket(
'ws://'
+ window.location.host
+ '/ws/'
+ roomName
+ '/'
);
chatSocket.onclose = function(e) {
console.log('onclose')
}
chatSocket.onmessage = function(e) {
const data = JSON.parse(e.data);
//from consumer.py:
if (data.message) {
document.querySelector('#chat-messages').innerHTML += ('<b>' + data.username + '</b>: ' + data.message + '<br>');
}
scrollToBottom();
};
document.querySelector('#chat-message-input').focus();
document.querySelector('#chat-message-input').onkeyup = function(e) {
if (e.keyCode === 13) {
document.querySelector('#chat-message-submit').click();
}
};
document.querySelector('#chat-message-submit').onclick = function(e) {
e.preventDefault()
const messageInputDom = document.querySelector('#chat-message-input');
const message = messageInputDom.value;
console.log({
'message': message,
'username': userName,
'room': roomName
})
if (message) {
chatSocket.send(JSON.stringify({
'message': message,
'username': userName,
'room': roomName
}));
}
messageInputDom.value = '';
return false
};
/**
* A function for finding the messages element, and scroll to the bottom of it.
*/
function scrollToBottom() {
let objDiv = document.getElementById("chat-messages");
objDiv.scrollTop = objDiv.scrollHeight;
}
// Add this below the function to trigger the scroll on load.
scrollToBottom();
</script>
{% endblock %}
Should i use javascript?
I tried to delete some objects by using ajax but it didn't work.

Related

EXCEPTION in Angular 8: Can't resolve all parameters Angular 8

I've built a app in Angular 8, but I have encountered a strange issue where I cannot inject a service into one of my components. Basically i want to show all my login/register means under one page without routing by just managing fields. But this error is coming up again and again.
Uncaught Error: Can't resolve all parameters for AuthTestComponent: ([object Object], [object Object], ?).
My auth.service.ts file:-
import { Injectable } from '#angular/core';
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';
import { Router } from '#angular/router'
#Injectable({
providedIn: 'root'
})
export class AuthService {
authUrl = "API"
constructor(private http: HttpClient, private router: Router) { }
registerUser(email: string, password: string) {
return this.http.post<any>(this.authUrl + "user-register", {
email: email,
password: password,
// c_password: password,
token: true
}).pipe(catchError(this.handleError))
}
LoginUser(email: string, password: string) {
return this.http.post<any>(this.authUrl + "user-login", {
email: email,
password: password,
access_token: true
}).pipe(catchError(this.handleError))
}
loggedIn() {
return !!localStorage.getItem('access_token')
}
getToken() {
return localStorage.getItem('access_token')
}
logoutUser() {
localStorage.removeItem('access_token');
this.router.navigate(['login'])
}
private handleError(errorRes: HttpErrorResponse) {
let errorMessage = "An Unkown error occured!";
if (!errorRes.error.error.errors || !errorRes.error.error.email[0]) {
return throwError(errorMessage)
}
switch (errorRes.error.error.email[0]) {
case 'The email has already been taken.':
errorMessage = 'The email already exists!'
break;
}
// switch (errorRes.error.error[0]) {
// case 'Password or Email not matched':
// errorMessage = 'Please Sign Up to continue!'
// case 'INVALID_PASSWORD':
// errorMessage = 'This password is not correct.'
// }
return throwError(errorMessage);
}
sendPasswordResetLink(email: string) {
return this.http.post<any>(this.authUrl + "updatepassword", {
email: email
}).pipe(catchError(this.handleError))
}
}
My component file:-
import { Component, Inject } from '#angular/core';
import { FormGroup, FormControl, Validators } from '#angular/forms';
// import { MatDialogRef, MAT_DIALOG_DATA } from '#angular/material/dialog';
import { $animations } from './login-animations';
import { $providers } from './login-providers';
import { $pages } from './login-pages';
import { from } from 'rxjs';
import { AuthService } from '../services/auth/auth.service';
import { Router } from '#angular/router';
export type loginAction = 'register' | 'signIn' | 'forgotPassword' | 'signOut';
#Component({
selector: 'app-auth-test',
templateUrl: './auth-test.component.html',
styleUrls: ['./auth-test.component.css'],
animations: $animations
})
export class AuthTestComponent {
readonly providers = $providers;
private pages = $pages;
private page: loginAction;
// private code: string;
readonly form: FormGroup;
// private password2: FormControl;
private email: FormControl;
private password: FormControl;
public error = null;
// private newPassword: FormControl;
isLoading = false;
public progress = false;
constructor(private auth: AuthService, private router: Router, private action: loginAction) {
// Form controls
// this.password2 = new FormControl(null, Validators.required);
this.email = new FormControl(null, [Validators.required, Validators.email]);
this.password = new FormControl(null, Validators.required);
// this.newPassword = new FormControl(null, Validators.required);
// Empty form group
this.form = new FormGroup({});
// Populates the form according to the page
this.switchPage(this.page = action);
}
get currentPage() { return this.pages[this.page || 'signIn']; }
private switchPage(page: loginAction) {
// Removes all the controls from the form group
Object.keys(this.form.controls).forEach(control => {
this.form.removeControl(control);
});
// Add the relevant controls to the form according to selected page
switch (this.page = page) {
case 'register':
this.form.addControl('email', this.email);
this.form.addControl('password', this.password);
// this.form.addControl('password2', this.password2);
break;
default:
// case 'signIn':
// this.form.addControl('email', this.email);
// this.form.addControl('password', this.password);
// break;
// case 'forgotPassword':
// this.form.addControl('email', this.email);
// break;
/*
case 'resetPassword':
this.form.addControl('newPassword', this.newPassword);
break;
*/
}
}
// private showError(error: string) {
// this.error = error;
// this.progress = false;
// setTimeout(() => this.error = null, 5000);
// }
public activate(action: loginAction) {
this.progress = true;
switch (action) {
default:
// case 'signIn':
// this.signIn(this.email.value, this.password.value);
// break;
case 'register':
this.registerNew(this.email.value, this.password.value);
break;
// case 'forgotPassword':
// this.forgotPassword(this.email.value);
// break;
/*
case 'resetPassword':
this.resetPassword( this.code, this.newPassword.value );
break;
*/
}
}
private registerNew(email: string, password: string) {
// Registering a new user with a email/password
this.auth.registerUser(email, password).subscribe(
res => {
console.log(res);
localStorage.setItem('token', res.token)
this.isLoading = false;
this.router.navigate(['login']);
},
errorMessage => {
console.log(errorMessage);
this.error = errorMessage;
this.isLoading = false;
}
);
}
}
HTML form file -:
<div [#vanish]="page">
<h1 class="mat-title">{{ currentPage.title }}</h1>
<p class="mat-small">{{ currentPage.message }}</p>
</div>
<form [formGroup]="form" fxLayout="column" fxLayoutAlign="space-around stretch" fxLayoutGap="10px"
(ngSubmit)="activate(page)" *ngIf="page !== 'promptEmail' && page !== 'verifyEmail' && page !== 'recoverEmail'">
<!-- ERROR MESSAGE -->
<mat-error *ngIf="error" #inflate>{{ error }}</mat-error>
<!-- NAME
<mat-form-field appearance="legacy" *ngIf="form.contains('confirm')" #inflate>
<mat-label>Full name</mat-label>
<input matInput formControlName="confirm">
<mat-error *ngIf="form.controls.confirm.errors?.required">
Please specify your name here
</mat-error>
</mat-form-field> -->
<!-- EMAIL -->
<mat-form-field appearance="legacy" *ngIf="form.contains('email')" #inflate>
<mat-label>Email</mat-label>
<input matInput formControlName="email">
<mat-error *ngIf="form.controls.email.errors?.required">
Please specify an email address
</mat-error>
<mat-error *ngIf="form.controls.email.errors?.email">
Ooops! it looks like this is not a valid email
</mat-error>
</mat-form-field>
<!-- PASSWORD -->
<mat-form-field appearance="legacy" *ngIf="form.contains('password')" #inflate>
<mat-label>Password</mat-label>
<input matInput [type]="hidePassword ? 'password' : 'text'" formControlName="password">
<mat-icon matSuffix (click)="hidePassword = !hidePassword">
{{ hidePassword ? 'visibility_off' : 'visibility'}}
</mat-icon>
<mat-error *ngIf="form.controls.password.errors?.required">
A password is required
</mat-error>
</mat-form-field>
<!-- confirm PASSWORD -->
<mat-form-field appearance="legacy" *ngIf="form.contains('password')" #inflate>
<mat-label>Password</mat-label>
<input matInput [type]="hidePassword ? 'password' : 'text'" formControlName="password2">
<mat-icon matSuffix (click)="hidePassword = !hidePassword">
{{ hidePassword ? 'visibility_off' : 'visibility'}}
</mat-icon>
<mat-error *ngIf="form.controls.password2.errors?.required">
A password is required
</mat-error>
<mat-hint class="mat-link" align="end" (click)="switchPage('forgotPassword')" *ngIf="page == 'signIn'"
#inflate>Forgot password?</mat-hint>
</mat-form-field>
<!-- NEW EMAIL -->
<!-- <mat-form-field appearance="legacy" *ngIf="form.contains('newEmail')" #inflate>
<mat-label>New email</mat-label>
<input matInput formControlName="newEmail">
<mat-error *ngIf="form.controls.newEmail.errors?.required">
A new email is required
</mat-error>
<mat-error *ngIf="form.controls.newEmail.errors?.email">
This email looks wrong
</mat-error>
</mat-form-field> -->
<!-- NEW PASSWORD -->
<!-- <mat-form-field appearance="legacy" *ngIf="form.contains('newPassword')" #inflate>
<mat-label>New password</mat-label>
<input matInput formControlName="newPassword">
<mat-error *ngIf="form.controls.newPassword.errors?.required">
A new password is required
</mat-error>
</mat-form-field> -->
<!-- ACTION BUTTON -->
<button mat-stroked-button color="primary" type="submit" [disabled]="!form.valid" class="btn">
{{ currentPage.caption }}
</button>
<mat-progress-bar *ngIf="progress" mode="indeterminate" #inflate></mat-progress-bar>
</form>
<p class="mat-small" *ngIf="page == 'signIn'">
Are you a new user? <span class="mat-link" (click)="switchPage('register')">Register</span>
</p>
<p class="mat-small" *ngIf="page === 'register' || page === 'forgotPassword'">
Already have login and password? <span class="mat-link" (click)="switchPage('signIn')">Sign-in</span>
</p>
<!-- SIGN-IN PROVIDERS -->
<div fxLayout="column" fxLayoutAlign="center center" *ngIf="page == 'signIn'" #inflate>
<p class="mat-small">or sign-in with:</p>
<div fxLayout="row wrap" fxLayoutAlign="center center" fxLayoutGap="10px">
<button mat-icon-button *ngFor="let p of providers" (click)="signInWith(p.name)">
<mat-icon class="providers" [fontSet]="p.icon.split(':')[0]" [fontIcon]="p.icon.split(':')[1]"
[ngStyle]="{ color: p.color }">
</mat-icon>
</button>
</div>
</div>
The error trace provides a clue about what's going on: ([object Object], [object Object], ?). In Angular, it's objects all the way down (even a module is just some syntatical sugar around an object).
Since the first items in the error messages are objects, we can assume that dependency injection was successful for first two services referenced in the constructor. The ? in the error message (the third position) indicates there may be an issue with injecting the final dependency, i.e., private action: loginAction.
The Angular guide on dependency injection (DI) provides some context for how it operates by calling out that "dependencies are services or objects". The error you're seeing might be caused by trying to inject a non-injectable entity, i.e., a string.
It might be helpful to see how the component is implemented in your code, e.g., the corresponding HTML. Aside from the InjectionToken example provided in one of the other responses, you might also investigate whether an Input property (see Angular Input on the component allows you to pass the necessary page action).
Seems like the problem is here:
private action: loginAction
loginAction is a type and can not be injected.
For example, you can use InjectionToken
const LOGIN_ACTION = new InjectionToken<loginAction>('Login action token');
...
#Inject(LOGIN_ACTION) private action: loginAction

Need to make deviceorientation recognized on iphone safari through localhost

I'm building a mobile web that needs to read device orientation value from iphone, and the other webpage that gets the value.
This project is going to be displayed in the exhibition, so I'm planning to use USB to connect mobile and the macbook through localhost for realtime response.
But when I see it on iphone by connecting it to my localhost, device orientation value cannot be read.
https://medium.com/#wiekatz/testing-web-applications-running-on-localhost-with-an-iphone-7db6258b8f2
I followed this instruction to connect the device to localhost, and the connection works fine so that I can see it on my phone. I checked that other features like recognizing which button has been clicked are working fine in realtime.
I could see device orientation working when I deploy this to other domain and check on the phone, but I need to make this working through localhost.
Let me know if there's any clue what is causing this!
//mobile.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<title>Mobile PtoR</title>
<link rel="stylesheet" href="mobile_style.css" />
<script src="https://www.gstatic.com/firebasejs/6.3.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/6.3.1/firebase-firestore.js"></script>
<style></style>
</head>
<body>
<div id="closewrapper"><button id="closebutton">x</button></div>
<div class="iconwrapper">
<div class="icons" id="AppBank"><p>Bank</p></div>
<div class="icons" id="AppWeather"><p>Weather</p></div>
<div class="icons" id="AppDict"><p>Dictionary</p></div>
<div class="icons" id="AppFb"><p>Facebook</p></div>
<div class="icons" id="AppCal"><p>Calendar</p></div>
</div>
<div class="sensorcheck">
<h1>Device Orientation API</h1>
<span id="do-unsupported" class="hidden"
>deviceorientation event not supported</span
>
<span id="dm-unsupported" class="hidden"
>devicemotion event not supported</span
>
<span id="cnc-unsupported" class="hidden"
>compassneedscalibration event not supported</span
>
<div id="do-results">
<div id="cube" class="cube">
<div class="face one">1</div>
<div class="face two">2</div>
<div class="face three">3</div>
<div class="face four">4</div>
<div class="face five">5</div>
<div class="face six">6</div>
</div>
<div id="do-info" class="hidden">
<p>
Coordinates: (<span id="beta" class="value">null</span>,
<span id="gamma" class="value">null</span>,
<span id="alpha" class="value">null</span>)
<br />
Position absolute?
<span id="is-absolute" class="value">unavailable</span>
</p>
</div>
<div id="dm-info" class="hidden">
<p>
Acceleration: (<span id="acceleration-x" class="value">null</span>,
<span id="acceleration-y" class="value">null</span>,
<span id="acceleration-z" class="value">null</span>) m/s<sup>2</sup>
</p>
<p>
Acceleration including gravity: (<span
id="acceleration-including-gravity-x"
class="value"
>null</span
>,
<span id="acceleration-including-gravity-y" class="value">null</span>,
<span id="acceleration-including-gravity-z" class="value">null</span>)
m/s<sup>2</sup>
</p>
<p>
Rotation rate: (<span id="rotation-rate-beta" class="value">null</span
>, <span id="rotation-rate-gamma" class="value">null</span>,
<span id="rotation-rate-alpha" class="value">null</span>)
</p>
<p>Interval: <span id="interval" class="value">0</span> milliseconds</p>
</div>
</div>
</div>
<script>
if (!window.DeviceOrientationEvent) {
document.getElementById("do-unsupported").classList.remove("hidden")
} else {
document.getElementById("do-info").classList.remove("hidden")
window.addEventListener("deviceorientation", function(event) {
document.getElementById(
"cube"
).style.webkitTransform = document.getElementById(
"cube"
).style.transform =
"rotateX(" +
event.beta +
"deg) " +
"rotateY(" +
event.gamma +
"deg) " +
"rotateZ(" +
event.alpha +
"deg)"
document.getElementById("beta").innerHTML = Math.round(event.beta)
document.getElementById("gamma").innerHTML = Math.round(event.gamma)
document.getElementById("alpha").innerHTML = Math.round(event.alpha)
document.getElementById("is-absolute").innerHTML = event.absolute
? "true"
: "false"
})
}
</script>
<!-- Custom fuctions of firebase-->
<script src="mobileFirebase.js"></script>
</body>
</html>
//mobileFirebase.js
//the value is passed to firebase
var firebaseConfig = {
--------
}
firebase.initializeApp(firebaseConfig)
var firestore = firebase.firestore()
/* define things to be used*/
const AppBank = document.querySelector("#AppBank")
const AppWeather = document.querySelector("#AppWeather")
const AppDict = document.querySelector("#AppDict")
const AppFb = document.querySelector("#AppFb")
const AppCal = document.querySelector("#AppCal")
const closewrapper = document.querySelector("#closewrapper")
const closebutton = document.querySelector("#closebutton")
//const CurrentStatus = "landing"
const DOsensorRef = firestore.doc("status/DOsensor")
const docRef = firestore.doc("status/ClickStatus")
/* device orientation */
if (!window.DeviceOrientationEvent) {
document.getElementById("do-unsupported").classList.remove("hidden")
} else {
document.getElementById("do-info").classList.remove("hidden")
window.addEventListener("deviceorientation", function(event) {
const val_beta = Math.round(event.beta)
const val_gamma = Math.round(event.gamma)
const val_alpha = Math.round(event.alpha)
console.log(
"beta : ",
val_beta,
"gamma : ",
val_gamma,
"alpha : ",
val_alpha
)
DOsensorRef.set({
fire_beta: val_beta,
fire_alpha: val_alpha,
fire_gamma: val_gamma
})
.then(function() {
console.log("in sync")
})
.catch(function(error) {
console.log(error)
})
})
}
/* status recognize */
closebutton.addEventListener("click", function(){
closewrapper.style.display="none"
const CurrentStatus = "Landing"
docRef.set({
AppStatus: CurrentStatus
})
.then(function() {
console.log("Status changed!")
})
.catch(function(error) {
console.log("got an error: ", error)
})
})
AppBank.addEventListener("click", function() {
const CurrentStatus = "Bank"
docRef.set({
AppStatus: CurrentStatus
})
.then(function() {
console.log("Status changed!")
})
.catch(function(error) {
console.log("got an error: ", error)
})
closewrapper.style.display="block"
})
AppWeather.addEventListener("click", function() {
const CurrentStatus = "Weather"
docRef.set({
AppStatus: CurrentStatus
})
.then(function() {
console.log("Status changed!")
})
.catch(function(error) {
console.log("got an error: ", error)
})
closewrapper.style.display="block"
})
AppDict.addEventListener("click", function() {
const CurrentStatus = "Dictionary"
docRef.set({
AppStatus: CurrentStatus
})
.then(function() {
console.log("Status changed!")
})
.catch(function(error) {
console.log("got an error: ", error)
})
closewrapper.style.display="block"
})
AppFb.addEventListener("click", function() {
const CurrentStatus = "Facebook"
docRef.set({
AppStatus: CurrentStatus
})
.then(function() {
console.log("Status changed!")
})
.catch(function(error) {
console.log("got an error: ", error)
})
closewrapper.style.display="block"
})
AppCal.addEventListener("click", function() {
const CurrentStatus = "Calendar"
docRef.set({
AppStatus: CurrentStatus
})
.then(function() {
console.log("Status changed!")
})
.catch(function(error) {
console.log("got an error: ", error)
})
closewrapper.style.display="block"
})
I had the same Problem yesterday. Your localhost probably does not run on https but on http. So certain features as device orientation gets blocked. You are only able to use them in a secure context.
Easy solution is to tunnel your local server to the outside e.g. with https://localtunnel.github.io/www/
or make your own secure certificate for your local webserver. Maybe you can find some infos here.

Remove item from cart React

I have 2 problems regarding the below code. I want to remove specific items from my cart in React. ( backend Rails). I know that the splice approach is one of the most used but in my case, it does not delete the specific clicked one, but it deletes the last item all the time regardless of the one that I click one.
My second problem is that my Total does not get updated even if the items do get deleted in the wrong way but still get deleted.
This is the code:
import React, { Component } from 'react';
import BasketPic from './images/cart.png';
import StripePayment from './stripePayment';
class MainBasket extends React.Component {
constructor(props) {
super(props);
this.state = {
toggle: true,
showBasket: false,
items: this.props.items
}
this.toggleBasket = this.toggleBasket.bind(this)
this.removeItemFromBasket = this.removeItemFromBasket.bind(this)
console.log(this.state.items)
}
toggleBasket(){
this.setState({toggle: !this.state.toggle});
}
showCheckout(){
this.setState({showBasket: !this.state.showBasket})
}
addItemToBasket(itemId){
fetch(`http://localhost:3000/books/${itemId}`)
.then( item => item.json())
.then( item => {
this.state.items.push(item);
this.state.total += item.price;
this.setState(this.state);
})
}
removeItemFromBasket(itemId){
var itemToBeDeleted = this.state.items.indexOf(itemId)
var deleted = this.state.items.splice(itemToBeDeleted, 1)
this.setState(this.state)
}
render(){
var count_items = this.props.items.length
var total = this.props.total
return(
<div className="basketInfo">
<div className="mainBasketDiv">
<img src={BasketPic} onClick={this.toggleBasket} />
<strong><p className="itemsCart">{count_items}</p></strong>
<strong><p className="checkOutConf">CheckOut</p></strong>
<div className={"toggleDiv-" + this.state.toggle}>
<h3>{"Total: " + this.props.total}</h3>
<span><h4>{"Items: " + count_items }</h4></span>
<hr/>
{this.props.items.map( item =>
<div className="animated fadeInRight" key={"item-" + item.id}>
<h2>{item.title}</h2>
<h6 onClick={this.removeItemFromBasket} className="remvProd">{"Remove " + item.title}</h6>
</div>
)}
<button onClick={function(){this.showCheckout()}.bind(this)}> Check out</button>
</div>
</div>
<div className="container">
<div className={"checkOutStripe-" + this.state.showBasket}>
<div className="totalBar">
<p className="totalBarTypography"> Total {this.props.items.length} {this.props.items.length < 2 ? "item" : "items"}</p>
<p className="totalBarTypography"> Total {this.props.total} GBP</p>
</div>
<div className="row">
{this.props.items.map(eachItem =>
<div className="col-sm" key={"eachItem-" + eachItem.id}>
<img src={eachItem.image.url} className="checkOutImg"/>
<div className="prodDetails">
<h3>{"Product: " + eachItem.title }</h3>
<h3>{"Price: " + eachItem.price }</h3>
</div>
</div>
)}
</div>
<div>
<StripePayment
description={'BooksMania'}
amount={total}
/>
</div>
</div>
</div>
</div>
)
}
}
// module.exports = MainBasket;
export default MainBasket;
and this is the code where i set my total and my items initial state:
import React, { Component } from 'react';
import BuyButton from './images/buyButton.jpg';
import MainBasket from './mainBasket';
import CheckOutBasket from './checkOutBasket';
import Reviews from './reviews';
class EachBook extends React.Component {
constructor(props){
super(props);
this.state = {
newReview: [],
items: [],
total: 0
}
}
seeAllReviews(bookId){
fetch(`http://localhost:3000/books/${bookId}/reviews`)
.then( reviews => reviews.json())
.then( reviews => {
this.setState({
bookReviews: reviews
})
})
}
addReview(bookId){
fetch(`http://localhost:3000/books/${bookId}/reviews`,{
method: 'POST',
mode: 'cors',
body: JSON.stringify({comment: this.refs.comment.value}),
headers: new Headers({
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-CSRF-Token' : this.props.parent.props.parent.props.csrf
}),
credentials: 'same-origin'
}).then(response => response.json())
.catch(error => alert("There is something wrong with this request"))
.then( response => {
this.setState({newReview: response})
})
}
addItemToBasket(itemId){
fetch(`http://localhost:3000/books/${itemId}`)
.then( item => item.json())
.then( item => {
this.state.items.push(item);
this.state.total += item.price;
this.setState(this.state);
})
}
render(){
return(
<div>
<MainBasket items={this.state.items} total={this.state.total} parent={this}/>
<div className="container">
{this.props.singleBook.map(indBook =>
<div className="indBook" key={indBook.id}>
<h1>{indBook.title}</h1> <br />
<h2>{"Author: " + indBook.author}</h2> <br />
<h4>{"Genre: " + indBook.genre}</h4>
<h4>{"Price: " + indBook.price}£</h4>
<img src={indBook.image.url} />
<div className="button"><img src={BuyButton} onClick={function(){this.addItemToBasket(indBook.id)}.bind(this)}/></div>
<div className="description">{indBook.description}</div>
<h3>{this.state.newReview.comment}</h3>
<div>
<h4>Leave a new review</h4>
<textarea ref="comment" type="text" placeholder='Tell us your thoughts '></textarea>
<button onClick={ function(){this.addReview(indBook.id)}.bind(this) } >Submit</button>
</div>
</div>
)}
</div>
</div>
)
}
}
export default EachBook;
Many thanks for your help!
You’re biggest issue is that you’re mutating items in state, so state doesn’t cause a re-render. I would consider using filter instead of splice and just setting items from that like so:
removeItemFromBasket(itemId) {
const items = this.stat.items.filter(item => item.id !== itemId)
this.setState({ items })
}
The addItemToBasket should also have that issue. You should not mutate state as JavaScript passes objects by reference. You should be using this.state.total instead of this.props.total in the render method.
Hope that helps.

faye messages are not displayed

I made a simple faye tutorial application using this page:
http://code.tutsplus.com/tutorials/how-to-use-faye-as-a-real-time-push-server-in-rails--net-22600
And got a common problem with message displaying — server gets a parameter and renders it, but nothing happens on the page. I have alredy fixed faye.ru file, as was reccomended in comments. What should I do?
The most of the code is stored in a view:
<script>
$(function() {
// Subscribe to receive messages!
var client = new Faye.Client('http://localhost:9292/faye');
// Our public subscription
var public_subscription = client.subscribe('/messages/public', function(data) {
$('<p></p>').html(data.username + ": " + data.msg).appendTo('#chat_room');
});
// Our own private channel
var private_subscription = client.subscribe('/messages/private/<%= session[:username] %>', function(data) {
$('<p></p>').addClass('private').html(data.username + ": " + data.msg).appendTo('#chat_room');
});
// Handle form submission to publish messages.
$('#new_message_form').submit(function(){
// Is it a private message?
if (matches = $('#message').val().match(/#(.+) (.+)/)) {
client.publish('/messages/private/' + matches[1], {
username: '<%= session[:username] %>',
msg: matches[2]
});
}
else {
// It's a public message
client.publish('/messages/public', {
username: '<%= session[:username] %>',
msg: $('#message').val()
});
}
// Clear the message box
$('#message').val('');
return false;
});
});
<div class="chat_container">
<div id="chat_room">
<p class="alert"> Welcome to the chat room <%= session[:username] %>! </p>
</div>
<form id="new_message_form">
<input type="text" id="message" name="message">
<input type="submit" value="Send">
</form>
</div>
Okay, I replaced "rails s thin" by "rails server thin" and, by some sort of magic, it has started working since

Jquery Mobile val() returns undefined after changePage

I have 2 pages that I'm working with: first being the page where the values are being fetched from php server and populating the selects/inputs and the second page being a dialog box that fetches the value from the hidden inputs in the first page. The first transition opens the dialog box and fetches the values properly. After which I save the values in php session and reload the first page. After this process when I open the dialog box again the jquery is not able to fetch val() and shows undefined. I'm not sure if this is due to some reloading of the page issue or something else. If I refresh the page then it will work fine again.
<div data-role="page" id="page1">
<div data-theme="a" data-role="header">
.....
<div data-role="navbar" data-iconpos="top">
.....
</div>
<div data-theme="c" id="cashtab" data-role="content">
<div style="display:none" id="proddata" data=""></div>
<div style="display:none" id="prodstock" data=""></div>
<form id="mainsubmit" action="form.php" method="post" data-ajax="false">
<input id="formproduct" type="hidden" name="product" value=""/>
<div id="productsearch" style="width:48%; float:left; margin-right:2%;">
<label for="search">Search Product:</label><br/><br/>
<ul id="productautocomplete" data-role="listview" data-inset="true" data-filter="true" data-filter-placeholder="Select a product... (type at least 3 letters)" data-filter-theme="d"></ul>
</div>
<div id="packingselect" style=" width:23%; float:left; margin-right:2%;">
<label for="packing">Select Packing:</label>
<select name="packing" id="packing" data-iconpos="left">
</select>
</div>
<div id="qtyenter" style=" width:23%; float:left; margin-right:2%;">
<label for="quantity">Select Qty:</label>
<input type="number" data-clear-btn="true" name="quantity" id="qty" value=""/>
</div><br/><br/><br/><br/><br/><br/><br/><br/>
<div style="display:inline-block; width:33%; margin-left:33%; margin-right:33%;">
<a href="#page3" data-rel="dialog" data-role="button" >ADD</a>
</div>
</form>
</div>
</div>
<div data-role="page" id="page3" data-url="dialog.html" data-close-btn="right">
<div data-role="header">
<h1>Batch Selection</h1>
</div>
<div data-role="content">
<div style="overflow:auto;">
<table id="batchsel" style="border:1px;">
<thead>
<tr>
<th></th>
<th>Batch No</th>
<th>Exp Date</th>
<th>Brate</th>
<th>Srate</th>
<th>Packing</th>
<th>Stock</th>
<th>Supplier</th>
<th>ST%</th>
<th>Bill Date</th>
<th>Bill No</th>
<th>btax</th>
</tr>
</thead>
<!--data populated from server once the values from first page is read properly.
<!-- currently not loading the second time as unable to fetch val() -- >
<tbody>
</tbody>
</table>
</div>
<div id="remainingdata">
<p1 id="changeable_requirements"></p1>
<!-- function the send the checked checkboxes relavent info to store in session -->
<button id="saveprod" onclick="addProduct(); return false;">Add Product</button>
</div>
</div>
</div>
<script>
$( document ).on( "pageinit", "#page1", function() {
//for product select autopopulate -- working //
$("#productautocomplete").live( "listviewbeforefilter", function ( e, data ) {
var $ul = $( this ),$input = $( data.input ),value = $input.val(),html = "";
$ul.html( "" );
if ( value && value.length > 2 ) {
$ul.html( "<li><div class='ui-loader'><span class='ui-icon ui-icon-loading'></span></div></li>" );
$ul.listview( "refresh" );
$.getJSON('ajax/getProductList.php', {term:$input.val()}, function(data) {
var items = [];
var str = "";
for (var key in data) {
if (data.hasOwnProperty(key)) {
var value = data[key].value;
var label = data[key].label;
var stock = data[key].stock;
var proddata = data[key].data;
str += '<li code="'+value+'" name="'+label+'" stock="'+stock+'" data="'+proddata+'">';
str += '<a data-ajax="false" rel="external">'+label+' [ '+stock+' ]</a>';
str += '</li>';
}
}
$ul.html( str );
$ul.listview( "refresh" );
$ul.trigger( "updatelayout" );
});
}
});
//end search
//on click set hidden input fields to be used in dialog box. -- working
$('#productautocomplete li').live('click', function(e) {
//--------------------fetch data ------------------------
var id = $(this).attr('code');
var name = $(this).attr('name');
var data = $(this).attr('data');
var stock = $(this).attr('stock');
//add packaging type and unit info to div data
$('#proddata').attr('data',data);
//add currstock info to div
$('#prodstock').attr('data',stock);
//----------------------hide list
$('#productautocomplete li').hide();
//----------------------place name in visible input box
$('#productsearch input').attr('value',name);
//----------------------place id in hidden input box for the actual form.
$('#formproduct').val(id);
//----------------------fill options for package + show select package div
var filteroptions = data.split(",");
$('#packing option').remove();
for (var x=0; x<3 ; x++) {
var eachoption = filteroptions[x].split(":");
//if unit wise option is less than that of stock show as option.
if (eachoption[0]!="0" && eachoption[0] <= stock.valueOf()) {
$('#packing').append($('<option>', {
value: eachoption[0]+':'+eachoption[1],
text : eachoption[1]+' [ '+eachoption[0]+' ] '
}));
}
}
});
});
//this is where the problem lies ..
//have tried with pageinit.. but that only calls it once.
$( document ).on( "pageshow", "#page3", function() {
$('#batchsel tbody').empty();
// !!!!!!!!!!!!!!!!!!!!!!! // !!!!!!!!!!!!!!!!!!!!!!! //
//doesnt fetch any of 4 following values after pageChange back to page1.
//not sure if this is due to how i'm reloading the page1.
//see function addProduct below.
var prodcode = $('#formproduct').val(); //
var prodstock = $('#prodstock').attr('data');
var prodqty = $('#qty').val();
var packing = $('#packing').find(":selected").val();
//returns undefined
alert(prodcode); alert(packing); alert(prodqty);
//always ends here when dialog opens second time.
if (!prodcode || !packing || !prodqty) {
alert("Please give all required information");
//does not close also when opens the second time.
$('#page3').dialog('close');
}
var packinginfo = packing.split(":");
var totalrequired = prodqty * packinginfo[0];
//alert(packinginfo[1]);alert(totalrequired);
if (totalrequired > prodstock ) {
alert("Not enough Stock");
$('#page3').dialog('close');
} else {
//------------------------------ Getting Batch Info ---------------------------------------------------
var rows = '';
$.getJSON('ajax/getBatchDetails.php', {code:prodcode,pack:packinginfo[1],qty:totalrequired}, function(data) {
for (var key in data) {
if (data.hasOwnProperty(key)) {
//alert (data[key].Batch);
rows += '<tr><td><input type="checkbox" class="batchcheckbox" id="batchcheckbox_'+data[key].BatchId+'" value="'+data[key].BatchId+':'+data[key].Stock+'" onchange="resetRemainingQty(this.value);""/></td><td>' + data[key].Batch + '</td><td>' + data[key].ExDt +'</td><td>' + data[key].BRate + '</td><td>' + data[key].SRate + '</td><td>' + data[key].Pack + '</td><td>' + data[key].Stock + '</td><td>' + data[key].Supname + '</td><td>' + data[key].Stax + '</td><td>' + data[key].BillDt + '</td><td>' + data[key].BillNo + '</td><td>' + data[key].btax + '</td><tr>';
}
}
$('#batchsel tbody').append(rows);
//add remaining amount in the data field of p1.
$('#remainingdata p1').attr('data',totalrequired);
$('#remainingdata p2').attr('data',totalrequired);
$('#remainingdata p1').html("<h4>Remaining Amount : "+totalrequired+"</h4>");
});
//---------------------------------------------end batch info display: -----------------------------------
}
});
function addProduct() {
//--------code info---------
var prodcode = $("#formproduct").val(); // to send
//--------packing info---------------
var packing = $('#packing').find(":selected").val();
var packinginfo = packing.split(":");
//-----------qty req ---------------------
var prodqty = $('#qty').val();
var totalrequired = prodqty * packinginfo[0]; // to send
//-------------batch info -----------
var allbatchids = "";
$('.batchcheckbox').each(function() {
if($(this).is(':checked')){
var data = $(this).val();
var datasplit = data.split(":");
var batchid = datasplit[0];
allbatchids += batchid+":";
}
});
allbatchids = allbatchids.substring(0, allbatchids.length - 1); // to send
alert(prodcode+",,"+packinginfo[1]+",,"+totalrequired+",,"+allbatchids);
//-------------- send to server to save to session ---------
$.getJSON('ajax/saveProductSession.php', {code:prodcode,pack:packinginfo[1],qty:totalrequired,batch:allbatchids}, function(data) {
if (data.error == "1") {
alert(data.message);
} else {
/// !!!!!!!! !!!!!!!!!!!!!!! !!!!!!!!!!!!!!!
///
/// the loads the page1. but jquery doesnt take val() after this.
///tried multiple variations of this but to no effect.
///removed all options.. redirect to main.php.. reloadpage:false.. etc.
///Any other way to reload the page so that the dialog once open again can
///get the values from the page1 again.
$.mobile.changePage("#page1", { reloadPage: true , dataUrl : "page1", reverse : true, changeHash: true } );
}
});
//
// $.ajax({
// type: "POST",
// url: "ajax/saveProductSession.php",
// data: { code:prodcode,pack:packinginfo[1],qty:totalrequired,batch:allbatchids }
// }).done(function() {});
}
</script>
Ok ! I got it to work ! thanks anyway #Gajotres. Steps :
1a. Send out the variables from main.php through changePage :
var prodcode = $('#formproduct').val();
var prodstock = $('#prodstock').attr('data');
var prodqty = $('#qty').val();
var packing = $('#packing').find(":selected").val();
$.mobile.changePage('batch.php', {
role: 'dialog',
data: {'prodcode': prodcode,'prodstock': prodstock, 'prodqty' : prodqty , 'packing' : packing},
type: 'get'
});
2a. Moved the entire div id 'page3' to a new php page named 'batch.php' where I get the variables from php and set it to the html divs.
<?php
extract($_GET);
if (!$prodcode && !$prodstock && !$packing && !$prodqty) {
header('Location: '.DEF_SITEURL."main.php");
exit;
}
?>
<div data-role="page" id="batchpage" data-url="batch.php" data-close-btn="right">
<div data-role="header">
<h1>Batch Selection</h1>
</div>
<div data-role="content">
<div style="display:none;" id="batchprodcode" data="<?php echo $prodcode; ?>"></div>
<div style="display:none;" id="batchprodstock" data="<?php echo $prodstock; ?>"></div>
<div style="display:none;" id="batchpacking" data="<?php echo $packing; ?>"></div>
<div style="display:none;" id="batchqty" data="<?php echo $prodqty; ?>"></div>
<div style="overflow:auto;">
<table id="batchsel" style="border:1px;">
<thead>
<tr>
<th></th>
<th>Batch No</th>
<th>Exp Date</th>
<th>Brate</th>
<th>Srate</th>
<th>Packing</th>
<th>Stock</th>
<th>Supplier</th>
<th>ST%</th>
<th>Bill Date</th>
<th>Bill No</th>
<th>btax</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div id="remainingdata">
<p1 id="changeable_requirements"></p1>
<button id="saveprod" onclick="addProduct(); return false;">Add Product</button>
</div>
</div>
</div>
3a. Then I just change the pageshow that i was using for page3 to the new div that is created on batch.php. The script still runs on main.php.
$( document ).on( "pageshow", "#batchpage", function() {
$('#batchsel tbody').empty();
var prodcode = $('#batchprodcode').attr('data');
var prodstock = $('#batchprodstock').attr('data');
var prodqty = $('#batchqty').attr('data');
var packing = $('#batchpacking').attr('data');
...
});

Resources