How to show only one item in Angular2, Ionic2 - node.js

Please bear with my question as I'm new to Angular and I don't know exactly where the problem is, I've tried to google for a solution but with no good luck, basically I'm running a simple NodeJS server with socket.io:
SERVER
var socket = require('socket.io'),
http = require('http'),
server = http.createServer(),
socket = socket.listen(server);
var msgProva = 2;
socket.on('connection', function(connection) {
console.log('User Connected');
socket.emit('prova', msgProva);
connection.on('message', function(msg){
socket.emit('message', msg);
});
connection.on('sValue', function(value){
console.log('sValue', value );
socket.emit('sValue', value);
});
});
server.listen(3000, function(){
console.log('Server started');
});
and my client TS code:
HOME.TS
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import * as io from 'socket.io-client';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
socket:any
chat_input:string;
chats = [];
prova = [];
slideValueBadge = [];
constructor(public navCtrl: NavController) {
var a;
this.socket = io('http://localhost:3000');
this.socket.on('message', (msg) => {
console.log("message", msg);
this.chats.push(msg);
});
this.socket.on('prova', (msgProva) => {
console.log("msgProva", msgProva);
this.prova.push(msgProva);
});
this.socket.on('sValue', (value) => {
let index: number = this.slideValueBadge.indexOf(value);
if (index != -1) {
a = this.slideValueBadge.splice(index, 1);
}
console.log("sliderValue:", value);
});
}
send(msg) {
if(msg != ''){
this.socket.emit('message', msg);
}
this.chat_input = '';
}
cValue(event, nome) {
console.log("SliderValue", event._valA);
this.socket.emit('sValue', event._valA);
}
}
HOME.HTML
<ion-content padding>
<ion-item>
<ion-range [(ngModel)]="brightness" (ionChange)="cValue($event, 'slider1')">
<ion-icon range-left small name="water"></ion-icon>
<ion-icon range-right name="water"></ion-icon>
</ion-range>
</ion-item>
Flusso d'acqua
<ion-badge color="secondary">{{slideValueBadge}}</ion-badge>
<ion-list>
<ion-item *ngFor="let message of chats">{{message}}</ion-item>
</ion-list>
<ion-item>
<ion-input type="text" [(ngModel)]="chat_input" placeholder="Enter message"></ion-input>
</ion-item>
<button ion-button block (click)="send(chat_input)">Send</button>
</ion-content>
And that is what I see as result:
How can I display only one value? (the last one)?

Basically you are binding your badge to an array (slideValueBadge = []).
Simply change the binded value to a number and then update it in your socket.on method.
Hope this could help you

Apparently this was the solution, dunno if is good or bad coding:
<ion-badge color="secondary">{{slideValueBadge[slideValueBadge.length -1]}}</ion-badge>
without the *ng-if

Related

Implementing a collaborative text editor using nodejs/react/socket but encountering problems because of slow heroku servers

I've tried making a collaborative editor using socket.io with reactjs frontend and node backend. Here's the piece of logic which I think is causing problems....
When a client starts typing on the editor, I've used onInput event to emit a socket response say "typing" which carries the complete text on the editor inside data object at the moment client presses a key. Now server catches this typing event and in response to that, emits another socket response called "typed" which contains the same data but the server sends this response to all the clients connected to the server.... Now all clients receive this event inside componentDidMount and then update the state variable "codeValue" which updates the editor content for all the clients.
There are two problems, first one that on one single typing event, server is emitting numerous typed events ( it happens only in heroku server and not on local host ) and the other problem is that heroku servers are slow and before the server sends response to update the state of clients, clients had already entered more text on the editor which simply vanishes when the state is updated.....
FRONTEND CODE:
import React from "react";
import { Dropdown } from "semantic-ui-react";
import languages from "../utils/languages";
//Styles
import "../styles/app.css";
//Editor
import * as ace from "ace-builds";
// import SocketIOClient from "socket.io-client";
import "ace-builds/src-noconflict/mode-c_cpp";
import "ace-builds/src-noconflict/theme-github";
import "ace-builds/src-noconflict/ext-language_tools";
import AceEditor from "react-ace";
let check = true;
let ld;
// const endpoint = "http://localhost:4676";
// const socket = SocketIOClient(endpoint, { transports: ["websocket"] });
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
codeValue: languages[0].template,
currentLang: languages[0].key,
};
this.codeEditor = React.createRef();
this.fireTyping = this.fireTyping.bind(this);
this.onDDChange = this.onDDChange.bind(this);
this.runCode = this.runCode.bind(this);
this.handleOutput = this.handleOutput.bind(this);
}
componentDidMount() {
this.props.socket.on("typed", (data) => {
console.log(35, data.text)
this.setState({
codeValue: data.text,
});
check = true;
console.log(check)
});
this.props.socket.on('ans',(data) => {
console.log(data.output)
//handleOutput(data.output)
})
}
fireTyping = () => {
ld = this.codeEditor.current.editor.getValue()
//console.log(ld)
if(check) {
console.log(48, this.codeEditor.current.editor.getValue(), check);
this.props.socket.emit("typing", {
text: ld,
});
check = false;
}
console.log(check)
};
onDDChange = (e, data) => {
const selectedVal = languages.filter((v) => v.key == data.value)
this.setState({currentLang : data.value, codeValue: selectedVal[0].template})
}
runCode = () => {
this.props.socket.emit('run', {
code: this.codeEditor.current.editor.getValue(),
lang: this.state.currentLang,
input: ''
})
}
handleOutput = () => {
}
render() {
return (
<div>
<Dropdown
placeholder="Languages"
onChange = {this.onDDChange}
selection
value = {this.state.currentLang}
options={languages}
/>
<AceEditor
style={{
margin: "3rem auto",
width: "80vw",
height: "70vh",
}}
fontSize={18}
ref={this.codeEditor}
mode="c_cpp"
theme="github"
value={this.state.codeValue}
onInput={this.fireTyping}
showPrintMargin={false}
name="UNIQUE_ID_OF_DIV"
editorProps={{ $blockScrolling: true }}
setOptions={{
enableBasicAutocompletion: true,
enableLiveAutocompletion: true,
enableSnippets: true,
}}
/>
<div className="container">
<button
onClick={this.runCode}
style={{
marginLeft: "40rem",
}}
className="large ui teal button"
>
Run
</button>
</div>
</div>
);
}
}
export default App;
BACKEND CODE:
const express = require("express");
const request = require("request");
const app = express();
const http = require("http");
const server = http.createServer(app);
const path = require('path')
const socket = require("socket.io");
const io = socket(server);
const port = process.env.PORT || 4676
app.use(express.static(path.join(__dirname, 'client/build')))
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname + '/client/build/index.html'))
})
io.on("connection", (socket) => {
let previousCode, currentCode;
console.log(socket.id);
socket.on("typing", (data) => {
currentCode = data.text
console.log('typing')
console.log(previousCode === currentCode)
if(previousCode !== currentCode){
console.log(1)
io.emit("typed", data);
previousCode = currentCode;
currentCode = ''
}
});
});
server.listen(port, () => {
console.log("server started at http://localhost:4676");
});
I've spent hours trying to fix this but I couldn't.... Any help would be appreciated ☺️
Let me know if you need code reference, I'll share the repository

Next.js: document is not defined

I am trying to create a payment form where people can pay but I keep getting this error.
document is not defined
I'm using Next.js. Please see my code below:
import React from "react";
import {Elements, StripeProvider} from 'react-stripe-elements';
import CheckoutForm from '../../components/Payment/CheckoutForm';
import { useRouter } from 'next/router';
var stripe_load = () => {
var aScript = document.createElement('script');
aScript.type = 'text/javascript';
aScript.src = " https://js.stripe.com/v3/";
document.head.appendChild(aScript);
aScript.onload = () => {
};
};
function Payment({host}) {
const key = host.includes('localhost') ? 'test' : 't';
stripe_load();
const router = useRouter();
return (
<div className="Payment Main">
<StripeProvider apiKey={key}>
<Elements>
<CheckoutForm planid={router.query.id}/>
</Elements>
</StripeProvider>
<br/>
<br/>
<p>Powered by Stripe</p>
</div>
);
};
Payment.getInitialProps = async ctx => {
return { host: ctx.req.headers.host }
};
export default Payment
I think, in server rendering mode, the document is undefined.
You should be able to use it inside class lifecycle methods or useEffect
import React, {useEffect} from "react";
import {Elements, StripeProvider} from 'react-stripe-elements';
import CheckoutForm from '../../components/Payment/CheckoutForm';
import { useRouter } from 'next/router';
var stripe_load = () => {
var aScript = document.createElement('script');
aScript.type = 'text/javascript';
aScript.src = " https://js.stripe.com/v3/";
document.head.appendChild(aScript);
aScript.onload = () => {
};
};
function Payment({host}) {
const key = host.includes('localhost') ? 'test' : 't';
useEffect(() => {
var aScript = document.createElement('script');
aScript.type = 'text/javascript';
aScript.src = " https://js.stripe.com/v3/";
document.head.appendChild(aScript);
aScript.onload = () => {
};
}, [])
//stripe_load();
const router = useRouter();
return (
<div className="Payment Main">
<StripeProvider apiKey={key}>
<Elements>
<CheckoutForm planid={router.query.id}/>
</Elements>
</StripeProvider>
<br/>
<br/>
<p>Powered by Stripe</p>
</div>
);
};
Payment.getInitialProps = async ctx => {
return { host: ctx.req.headers.host }
};
export default Payment
You need to wrap your document using validator process.browser, because this document is belong to client side, and the error occured when nextjs render in server side.
var stripe_load = () => {
if (process.browser) {
var aScript = document.createElement('script');
aScript.type = 'text/javascript';
aScript.src = " https://js.stripe.com/v3/";
document.head.appendChild(aScript);
aScript.onload = () => {
};
}
};
For example, when the module includes a library that only works in the browser. Some times dynamic import can solve this issue.
Take a look at the following example:
import dynamic from 'next/dynamic'
const DynamicComponentWithNoSSR = dynamic(
() => import('../components/hello3'),
{ ssr: false }
)
function Home() {
return (
<div>
<Header />
<DynamicComponentWithNoSSR />
<p>HOME PAGE is here!</p>
</div>
)
}
export default Home
for me, this error occurs from lots of mistakes:
first, you have to use
if (typeof window !== "undefined") {
for every window. and document. and especially for localStorage. functions (my self forgot to use this if clause for localStorage.getItem and the error didn't fix)
another problem was the import of the line below which was totally wrong!:
import {router} from "next/client";
To access the document in Next.js you need await the page render first then get it in a variable, like so:
const [_document, set_document] = React.useState(null)
React.useEffect(() => {
set_document(document)
}, [])
You should read the difference between JS in node.js and JS in Browser. In node.js, you don't have DOM APIs (window, document, document.getElementById,...), the thing can be only have when your HTML is rendered in a thing called windows of Browsers. So next.js use node.js to run JS code and take result to render HTML file. But in node.js, nothing is windows of Browser.
My English is quite bad. But I hope this answer will be helpful for you.
you can use a new feature called dynamic imports with ssr off. this is a workaround for rerendering the component.
You have to make sure you have two things setup.
DOM render is completed.
Then load the function.
Step 1: create hook isMounted this will make sure that your DOM is rendered.
import React, {useEffect, useState} from 'react';
function RenderCompleted() {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true)
return () => {
setMounted(false)
}
});
return mounted;
}
export default RenderCompleted;
Inside function Payment load the hook :
import React, {useEffect} from "react";
import {Elements, StripeProvider} from 'react-stripe-elements';
import CheckoutForm from '../../components/Payment/CheckoutForm';
import { useRouter } from 'next/router';
import RenderCompleted from '../hooks/isMounted';
var stripe_load = () => {
var aScript = document.createElement('script');
aScript.type = 'text/javascript';
aScript.src = " https://js.stripe.com/v3/";
document.head.appendChild(aScript);
aScript.onload = () => {
};
};
function Payment({host}) {
const[key,setKey] = useEffect('');
//will help you on re-render or host changes
useEffect(()=>{
const key = host.includes('localhost') ? 'test' : 't';
setKey(key);
},[host])
useEffect(() => {
var aScript = document.createElement('script');
aScript.type = 'text/javascript';
aScript.src = " https://js.stripe.com/v3/";
document.head.appendChild(aScript);
aScript.onload = () => {
};
}, [isMounted])
const router = useRouter();
const isMounted = RenderCompleted();
return (
<div className="Payment Main">
<StripeProvider apiKey={key}>
<Elements>
<CheckoutForm planid={router.query.id}/>
</Elements>
</StripeProvider>
<br/>
<br/>
<p>Powered by Stripe</p>
</div>
);
};
Payment.getInitialProps = async ctx => {
return { host: ctx.req.headers.host }
};
export default Payment
Another way to do this is, to use head component of next.js: https://nextjs.org/docs/api-reference/next/head
<Head>
<title>My page title</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
</Head>
This might be helpful to some else, I got this error when Material UI anchorEl is true
const [anchoeEl, setAnchorEl] = useState(null)
<Menu
anchorEl={anchorEl}
anchorOrigin={{
vertical: "top"
horizontal: "right"
}}
/>

Same event emitting multiple times in socket.io-client reactjs

I am creating a chat app in react, expressjs and socket.io. When I click on Send Button, I am emitting an event and listening that event on server side and again emitting another event from server side and listening that event on client side. And I have written the event listening code on componentDidMount. But don't know why my client side calling same event multiple times. Below is my both side code:
Client side
var socketIOClient = require('socket.io-client')('http://localhost:4001');
sendMessageClicked(e) {
e.preventDefault();
let message = this.state.originalMessage;
var data = {
message: message,
time: Date.now()
}
socketIOClient.emit('send_message',data);
}
componentDidMount() {
socketIOClient.on('msg',function(result){
let messageHtml = 'messages working!';
let messageBox = document.getElementById('messageBox');
if (messageBox ) {
messageBox.appendChild(messageHtml);
}
})
}
render(){
return(
<div>
<form onSubmit={this.sendMessageClicked}>
<textarea onChange={this.handleMessageChange} name="originalMessage" value={this.state.originalMessage}></textarea>
<input type="submit" value="" />
</form>
</div>
);
}
Server side
const app = require('./app');
var server = require('http').Server(app);
var io = require('socket.io')(server);
server.listen(4001);
io.on('connection',function(socket){
socket.on('send_message',function(data){
io.emit('msg',data);
})
})
Can anyone please help with the same?
I had the same issue, and I solved it with useEffect hook.
in your case it would be (on client side):
useEffect(()=>{
socket.on('msg', (result) => {
let messageHtml = 'messages working!';
let messageBox = document.getElementById('messageBox');
if (messageBox ) {
messageBox.appendChild(messageHtml);
})
return function cleanup() {socket.off('msg')}
},[])
I'm sure you could do this also with ComponentDidUpdate or ComponentDidUnmount, but useEffect is eassier.
In the useEffect() hook, checking if socket connection already exists using socket.current helped me get rid of this problem -
useEffect(() => {
(async () => {
if (!socket.current) {
socket.current = io(`http://localhost:8080/`)
.on("connect", () => {
//do something
});
socket.current.on("message", (instance) => {
//receive message from server
});
}
})();
});

How to render the React component with dynamic data realtime from socket.io high efficiency

My front-end page is made by React + Flux, which sends the script data to back-end nodejs server.
The script data is an Array which contains the linux shell arguments (more than 100000). When to back-end received, it will execute the linux shell command.
Just an example:
cat ~/testfile1
cat ~/testfile2
.
.
.
(100000 times ...etc)
When the backend finished one of the linux shell commands, I can save the readed content to result data. Therefore, socket.io will emit the result data to the front-end.
I want to get the result data from my webpage in real time, so I have done some stuff in my project below.
My React component code:
import React from 'react';
import AppActions from '../../../actions/app-actions';
import SocketStore from '../../../stores/socket-store';
import ResultStore from '../../../stores/result-store';
function getSocket () {
return SocketStore.getSocket();
}
function getResult () {
return ResultStore.getResultItem();
}
class ListResultItem extends React.Component {
constructor () {
super();
}
render () {
return <li>
{this.props.result.get('name')} {this.props.result.get('txt')}
</li>;
}
}
class ShowResult extends React.Component {
constructor () {
super();
this.state = {
socket: getSocket(),
result: getResult()
};
}
componentWillMount () {
ResultStore.addChangeListener(this._onChange.bind(this));
}
_onChange () {
this.setState({
result: getResult()
});
}
render () {
return <div>
<ol>
{this.state.result.map(function(item, index) {
return <ListResultItem key={index} result={item} />;
})}
</ol>
</div>;
}
componentDidMount () {
this.state.socket.on('result', function (data) {
AppActions.addResult(data);
});
}
}
My Flux store (ResultStore) code:
import AppConstants from '../constants/app-constants.js';
import { dispatch, register } from '../dispatchers/app-dispatcher.js';
import { EventEmitter } from 'events';
import Immutable from 'immutable';
const CHANGE_EVENT = 'changeResult';
let _resultItem = Immutable.List();
const _addResult = (result) => {
let immObj = Immutable.fromJS(result);
_resultItem = _resultItem.push(immObj);
}
const _clearResult = () => {
_resultItem = _resultItem.clear();
}
const ResultStore = Object.assign(EventEmitter.prototype, {
emitChange (){
this.emit( CHANGE_EVENT );
},
addChangeListener (callback) {
this.on(CHANGE_EVENT, callback);
},
removeChangeListener (callback) {
this.removeListener(CHANGE_EVENT, callback);
},
getResultItem () {
return _resultItem;
},
dispatcherIndex: register(function (action) {
switch (action.actionType) {
case AppConstants.ADD_RESULT:
_addResult(action.result);
break;
case AppConstants.CLEAR_RESULT:
_clearResult();
break;
}
ResultStore.emitChange();
})
});
However, the page will become very slow after rendering more than 1000 datas. How to enhance the performance for rendering? I need to execute the linux script persistently more than 3 days. Any solutions? Thanks~
Is there any need to render all the data on screen? If not then there are a few ways to deal with cutting down the amount of visible data.
Filter / Search
You can provide a search/filter component that complements the list and creates a predicate function that can be used to determine whether each item should or should not be rendered.
<PredicateList>
<Search />
<Filter />
{this.state.result
.filter(predicate)
.map(function(item, index) {
return <ListResultItem key={index} result={item} />;
})
}
</PredicateList>
Lazy Load
Load the items only when they are asked for. You can work out whether item is needed by keeping track of whether it would be onscreen, or whether the mouse was over it.
var Lazy = React.createClass({
getInitialState: function() {
return { loaded: false };
},
load: function() {
this.setState({ loaded: true });
},
render: function() {
var loaded = this.state.loaded,
component = this.props.children,
lazyContainer = <div onMouseEnter={this.load} />;
return loaded ?
component
lazyContainer;
}
});
Then simply wrap your data items inside these Lazy wrappers to have them render when they are requested.
<Lazy>
<ListResultItem key={index} result={item} />
</Lazy>
This ensures that only data needed by the user is seen. You could also improve the load trigger to work for more complex scenarios, such as when the component has been onscreen for more then 2 seconds.
Pagination
Finally, the last and most tried and tested approach is pagination. Choose a limit for a number of data items that can be shown in one go, then allow users to navigate through the data set in chunks.
var Paginate = React.createClass({
getDefaultProps: function() {
return { items: [], perPage: 100 };
},
getInitialState: function() {
return { page: 0 };
},
next: function() {
this.setState({ page: this.state.page + 1});
},
prev: function() {
this.setState({ page: this.state.page - 1});
},
render: function() {
var perPage = this.props.perPage,
currentPage = this.state.page,
itemCount = this.props.items.length;
var start = currentPage * perPage,
end = Math.min(itemCount, start + perPage);
var selectedItems = this.props.items.slice(start, end);
return (
<div className='pagination'>
{selectedItems.map(function(item, index) {
<ListResultItem key={index} result={item} />
})}
<a onClick={this.prev}>Previous {{this.state.perPage}} items</a>
<a onClick={this.next}>Next {{this.state.perPage}} items</a>
</div>
);
}
});
These are just very rough examples of implementations for managing the rendering of large amounts of data in efficient ways, but hopefully they will make enough sense for you to implement your own solution.

React and Socket.io

I'm trying to create a simple app using ReactJS and Socket.io
In my component I want to be able to communicate with the server, but the problem is that I don't know how to do io.connect()
1.Do I need to explicitly specify the IP address like io.connect("http://myHost:7000") or is it enough to say : io.connect() ? As we can see in this piece of code :
https://github.com/DanialK/ReactJS-Realtime-Chat/blob/master/client/app.jsx
2.I do more or less the same as this code , but I receive error when I do npm start as io is undefined. I think , io is provided globally by including the socket.io script. How can I solve this problem ?
'use strict';
var React = require('react');
var socket = io.connect();
var chatWindow = React.createClass({
displayName: 'chatWindow',
propTypes: {},
getDefaultProps: function() {
return ({
messages: 0
});
},
componentDidMount: function() {
socket = this.props.io.connect();
socket.on('value', this._messageRecieve);
},
_messageRecieve: function(messages) {
this.setState({
messages: messages
});
},
getInitialState: function() {
return ({
messages: 0
});
},
_handleSend: function(){
var newValue = parseInt(this.refs.messageBox.value) + this.props.messages;
this.setState({
messages: newValue
});
socket.emit('clientMessage', { message: newValue});
},
render: function() {
var window =
<div>
<div>{this.props.messages}</div>
<input type="text" id="messageBox" refs="messageBox"></input>
<input type="button" onClick={this._handleSend} value="send" id="send"/>
</div>;
return (window);
}
});
module.exports = chatWindow;
This is the code :
https://github.com/arian-hosseinzadeh/simple-user-list
Answers:
1) No, you don't need to specify the IP, you can even use / and it will go through the default HTTP 80 port, anyway, you can find more examples on the socket.io site.
2) Require io too, remember to add socket.io-client to your package:
var React = require('react'),
io = require('socket.io-client');
Anyway, if you want to include the client script that socket.io server provides as a static file, then remember to add it into your HTML using a <script/> tag, that way you'll have io on the global scope avoiding the require part, but well, I prefer to require it.
NOW, WHAT ABOUT...
Trying my lib: https://www.npmjs.com/package/react-socket
It will handle the socket connection on mount and disconnection on unmount (the same goes for socket event listeners), give it a try and let me know.
Here you have an example:
http://coma.github.io/react-socket/
var App = React.createClass({
getInitialState: function() {
return {
tweets: []
};
},
onTweet: function(tweet) {
var tweets = this
.state
.tweets
.slice();
tweet.url = 'https://twitter.com/' + tweet.user + '/status/' + tweet.id;
tweet.at = new Date(tweet.at);
tweet.avatar = {
backgroundImage: 'url(' + tweet.img + ')'
};
tweets.unshift(tweet);
this.setState({
tweets: tweets
});
},
renderTweet: function (tweet) {
return (
<li key={tweet.id}>
<a href={tweet.url} target="_blank">
<div className="user">
<div className="avatar" style={ tweet.avatar }/>
<div className="name">{ tweet.user }</div>
</div>
<div className="text">{ tweet.text }</div>
</a>
</li>
);
},
render: function () {
return (
<div>
<ReactSocket.Socket url="http://tweets.socket.io"/>
<ReactSocket.Event name="tweet" callback={ this.onTweet }/>
<ul className="tweets">{ this.state.tweets.map(this.renderTweet) }</ul>
</div>
);
}
});
React.render(<App/>, document.body);

Resources