Angular Get Data From JSON to Model And List It - node.js

I'm trying to get data from a JSON using Angular and map it to a model, then show it on a webpage.
I did it buy I'm not getting any results, like the data from JSON cannot be taken or something.
Here's my try:
The JSON:
{
"location": [
{
"_id": "5f3567a8d8e66b41d4bdfe5f",
"lat": "44.4363228",
"lng": "25.9912305",
"token": "edb153fb9d8d5628",
"__v": 0
}
]
The model:
export class Post {
public _id: string;
public token: string;
public lat: string;
public lng: string;
}
Service class:
#Injectable({ providedIn: 'root' })
export class PostsService {
public posts: Post[] = [];
private postsUpdated = new Subject<Post[]>();
Post: Promise<any>;
constructor(private http: HttpClient) {}
private url: string = 'http://localhost:8000/location';
getPosts() {
this.http
.get<{ posts: any[] }>(this.url)
.pipe(
map((postData) => {
return postData.posts.map((post) => {
console.log(this.posts);
return {
_id: post._id,
token: post.token,
lat: post.lat,
lng: post.lng,
};
});
})
)
.subscribe((transformedPosts) => {
this.posts = transformedPosts;
this.postsUpdated.next([...this.posts]);
});
}
getPostUpdateListener() {
return this.postsUpdated.asObservable();
}
}
post-list.component.ts:
#Component({
selector: 'app-post-list',
templateUrl: './post-list.component.html',
styleUrls: ['./post-list.component.css'],
})
export class PostListComponent implements OnInit, OnDestroy {
posts: Post[] = [];
private postsSub: Subscription;
result: any;
constructor(public postsService: PostsService) {
//dependency-injection
}
ngOnInit() {
this.postsService.getPosts();
this.postsSub = this.postsService
.getPostUpdateListener()
.subscribe((posts: Post[]) => {
this.posts = posts;
});
}
onShow() {
console.log('TODO');
}
ngOnDestroy() {
this.postsSub.unsubscribe();
}
}
post-list.component.html:
<mat-accordion multi="true" *ngIf="posts.length > 0">
<mat-expansion-panel *ngFor="let post of posts">
<mat-expansion-panel-header>
{{ post.token }}
</mat-expansion-panel-header>
<p>{{ post.lat }}</p>
<p>{{ post.lng }}</p>
<mat-action-row>
<button mat-raised-button color="accent" (click)="onShow(post._id)">
SHOW
</button>
</mat-action-row>
</mat-expansion-panel>
</mat-accordion>
<p class="info-text mat-body-1" *ngIf="posts.length <= 0">No posts added yet</p>
app.component.html:
<app-header></app-header> <br /><br />
<agm-map [latitude]="lat" [longitude]="lng" [zoom]="zoom">
<agm-marker [latitude]="lat" [longitude]="lng"></agm-marker>
</agm-map>
<br /><br />
<app-post-list></app-post-list>
And here's my result (photo):
I also tried to do it in different ways, always getting no result.
Any help or ideas would be much appreciated!

The error in the image says posts attribute does not exists on postData object which is gotten by get request. Absence of posts attribute is also clear in the JSON you provided.
{
"location": [
{
"_id": "5f3567a8d8e66b41d4bdfe5f",
"lat": "44.4363228",
"lng": "25.9912305",
"token": "edb153fb9d8d5628",
"__v": 0
}
]
You should completely remove the pipe and it should be fine.

Related

Algolia autocomplete: How do I highlight items?

I use Algolia autocomplete in "standalone" mode, meaning without the commercial Algolia service to return the search results; I have my own server respond with an array of search results.
How do I highlight matches in my returned items/strings (search results)?
The backend must return results that are already highlighted using a tag such as: <mark>this-is-to-be-highlighted</mark>. Here's an example result array for an search for "pie":
const items = [
{ some_attribute: 'Apple <mark>pie</mark>' },
{ some_attribute: 'American <mark>pie</mark>' },
{ some_attribute: 'Chocolate <mark>pie</mark>' }
]
The complete javascript code would then be something like this:
import { autocomplete } from "#algolia/autocomplete-js"
autocomplete({
container: '#search_input',
// ...
getSources({ query }) {
// This would be an example response from your server
const items = [
{ some_attribute: 'Apple <mark>pie</mark>' },
{ some_attribute: 'American <mark>pie</mark>' },
{ some_attribute: 'Chocolate <mark>pie</mark>' }
]
return [
{
sourceId: 'pies',
getItems({ query }) {
const HIGHLIGHT_PRE_TAG = '__aa-highlight__'
const HIGHLIGHT_POST_TAG = '__/aa-highlight__'
return items.map((item) => ({
item,
_highlightResult: {
some_attribute: {
value: item.some_attribute
.replace(/<mark>/g, HIGHLIGHT_PRE_TAG)
.replace(/<\/mark>/g, HIGHLIGHT_POST_TAG)
}
}
}))
},
templates: {
// ...
item({ item, components, html }) {
return html`<div className="aa-ItemWrapper">
<div className="aa-ItemContent">
<div className="aa-ItemContentBody">
<div className="aa-ItemContentTitle">
${components.Highlight({ hit: item, attribute: 'some_attribute' })}
</div>
</div>
</div>
</div>`
},
// ...
}
},
// ...
]
},
// ...
})

Strange behavior on when I press button many times very quick in NODE.JS

I have got 2 cards with 2 candidates, every candidate has a button where user can have a unique vote, the card have a box with total votes also, if the user make a vote for the first candidate, then the value of total in both cards is updated (in a card is substracted in another is added), . But when I press the button many times and very fast, the data returned by the backend is incorrect. Someone can explain what is happening please.
TEMPLATE OF COMPONENT IN ANGULAR
<mat-card class="example-card" *ngFor="let candidate of candidates; let i = index">
<mat-card-actions>
<button mat-raised-button class="button-vote" (click)="saveVote(candidate.userVotes[0], candidate)">
<mat-icon color="accent" aria-hidden="false" aria-label="Example home icon">pan_tool</mat-icon>
</button>
<div class="result" mat-fab>{{candidate.totalVotes}} votos</div>
</mat-card-actions>
</mat-card>
COMPONENENT.TS
export class CandidatesContainerComponent implements OnInit {
candidates!: any[]
constructor(
private candidatesService: CandidatesService,
) { }
ngOnInit(): void {
this.obtainCandidates()
}
obtainCandidates() {
this.candidatesService.getCandidates().subscribe((response: any) => {
this.candidates = response
})
}
saveVote(userVote: any, candidate: object | any) {
this.candidatesService.updateVote(candidate._id, userVote.thisChoosed).subscribe(response => {
if (response) {
this.obtainCandidates()
}
})
}
}
CONTROLLER IN NODE.JS
const updateVote = async ( req = request, res = response ) => {
const { candidateId, thisChoosed } = req.params
const query = thisChoosed === 'true' ? candidateId: { $ne:candidateId }
const previousVote = await Candidate.findOneAndUpdate(
{ _id:query },
{
'$set': { 'userVotes.$[el].thisChoose': false },
'$inc': { 'totalVotes': -1 }
},
{ arrayFilters: [
{"el.user": req.user._id }]
}
)
const newVote = await Candidate.findOneAndUpdate(
{ _id:candidateId },
{
'$set': { 'userVotes.$[el].thisChoose': true },
'$inc': { 'totalVotes': 1 }
},
{
arrayFilters: [ {"el.user": req.user._id }, ], new:true
}
)
res.json( newVote )
}
DATABASE
const VoteSchema = Schema({
user:{
type:Schema.ObjectId,
ref:'User',
required:[true, 'User is required - vote user']
},
thisChoosed:{
type:Boolean,
default:false
},
firstVote:{
type:Boolean,
default:true
}
})
const CandidateSchema = Schema({
names:{
type:String,
required:[true]
},
surnames:{
type:String,
required:[true]
},
userVotes:[VoteSchema],
totalVotes:{
type:Number,
default:0
},
})

How to access ReactQuill Ref when using dynamic import in NextJS?

I'm running into a funny problem. I'm using NextJS for its server-side rendering capabilities and am using ReactQuill as my rich-text editor. To get around ReactQuill's tie to the DOM, I'm dynamically importing it. However, that presents another problem which is that when I try to attach a ref to the ReactQuill component, it's treated as a loadable component instead of the ReactQuill component. I need the ref in order to customize how images are handled when uploaded into the rich-text editor. Right now, the ref returns current:null instead of the function I can use .getEditor() on to customize image handling.
Anybody have any thoughts on how I can address this? I tried ref-forwarding, but it's still applying refs to a loadable component, instead of the React-Quill one. Here's a snapshot of my code.
const ReactQuill = dynamic(import('react-quill'), { ssr: false, loading: () => <p>Loading ...</p> }
);
const ForwardedRefComponent = React.forwardRef((props, ref) => {return (
<ReactQuill {...props} forwardedRef={(el) => {ref = el;}} />
)})
class Create extends Component {
constructor() {
super();
this.reactQuillRef = React.createRef();
}
imageHandler = () => {
console.log(this.reactQuillRef); //this returns current:null, can't use getEditor() on it.
}
render() {
const modules = {
toolbar: {
container: [[{ 'header': [ 2, 3, false] }],
['bold', 'italic', 'underline', 'strike'],
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
[{ 'script': 'sub'}, { 'script': 'super' }],
['link', 'image'],
[{ 'indent': '-1'}, { 'indent': '+1' }],
[{ 'align': [] }],
['blockquote', 'code-block'],],
handlers: {
'image': this.imageHandler
}
}
};
return(
<ForwardedRefComponent
value={this.state.text}
onChange={this.handleChange}
modules={modules}
ref={this.reactQuillRef}/> //this.reactQuillRef is returning current:null instead of the ReactQuill function for me to use .getEditor() on
)
}
}
const mapStateToProps = state => ({
tutorial: state.tutorial,
});
export default connect(
mapStateToProps, {createTutorial}
)(Create);
I share my solution with hope that it helps you too.
Helped from https://github.com/zenoamaro/react-quill/issues/642#issuecomment-717661518
const ReactQuill = dynamic(
async () => {
const { default: RQ } = await import("react-quill");
return ({ forwardedRef, ...props }) => <RQ ref={forwardedRef} {...props} />;
},
{
ssr: false
}
);
export default function QuillWrapper() {
const quillRef = React.useRef(false)
return <>
<ReactQuill forwardedRef={quillRef} />
</>
}
for example you can use the ref to upload image with custom hanlder
import React, { useMemo } from "react";
import dynamic from "next/dynamic";
const ReactQuill = dynamic(
async () => {
const { default: RQ } = await import("react-quill");
return ({ forwardedRef, ...props }) => <RQ ref={forwardedRef} {...props} />;
},
{
ssr: false,
}
);
export default function QuillWrapper({ value, onChange, ...props }) {
const quillRef = React.useRef(false);
// Custom image upload handler
function imgHandler() {
// from https://github.com/quilljs/quill/issues/1089#issuecomment-318066471
const quill = quillRef.current.getEditor();
let fileInput = quill.root.querySelector("input.ql-image[type=file]");
// to prevent duplicate initialization I guess
if (fileInput === null) {
fileInput = document.createElement("input");
fileInput.setAttribute("type", "file");
fileInput.setAttribute(
"accept",
"image/png, image/gif, image/jpeg, image/bmp, image/x-icon"
);
fileInput.classList.add("ql-image");
fileInput.addEventListener("change", () => {
const files = fileInput.files;
const range = quill.getSelection(true);
if (!files || !files.length) {
console.log("No files selected");
return;
}
const formData = new FormData();
formData.append("file", files[0]);
formData.append("uid", uid);
formData.append("img_type", "detail");
quill.enable(false);
console.log(files[0]);
axios
.post("the/url/for/handle/uploading", formData)
.then((response) => {
// after uploading succeed add img tag in the editor.
// for detail visit https://quilljs.com/docs/api/#editor
quill.enable(true);
quill.insertEmbed(range.index, "image", response.data.url);
quill.setSelection(range.index + 1);
fileInput.value = "";
})
.catch((error) => {
console.log("quill image upload failed");
console.log(error);
quill.enable(true);
});
});
quill.root.appendChild(fileInput);
}
fileInput.click();
}
I don't know much about useMemo
but if i don't use the hook,
the editor keeps rerendered resulting in losing focus and I guess perfomance trouble too.
const modules = useMemo(
() => ({
toolbar: {
container: [
[{ font: [] }],
[{ size: ["small", false, "large", "huge"] }], // custom dropdown
["bold", "italic", "underline", "strike"], // toggled buttons
[{ color: [] }, { background: [] }], // dropdown with defaults from theme
[{ script: "sub" }, { script: "super" }], // superscript/subscript
[{ header: 1 }, { header: 2 }], // custom button values
["blockquote", "code-block"],
[{ list: "ordered" }, { list: "bullet" }],
[{ indent: "-1" }, { indent: "+1" }], // outdent/indent
[{ direction: "rtl" }], // text direction
[{ align: [] }],
["link", "image"],
["clean"], // remove formatting button
],
handlers: { image: imgHandler }, // Custom image handler
},
}),
[]
);
return (
<ReactQuill
forwardedRef={quillRef}
modules={modules}
value={value}
onChange={onChange}
{...props}
/>
);
}
In NextJS, React.useRef or React.createRef do not work with dynamic import.
You should Replace
const ReactQuill = dynamic(import('react-quill'), { ssr: false, loading: () => <p>Loading ...</p> }
);
with
import ReactQuill from 'react-quill';
and render after when window is loaded.
import ReactQuill from 'react-quill';
class Create extends Component {
constructor() {
super();
this.reactQuillRef = React.createRef();
this.state = {isWindowLoaded: false};
}
componentDidMount() {
this.setState({...this.state, isWindowLoaded: true});
}
.........
.........
render(){
return (
<div>
{this.isWindowLoaded && <ReactQuil {...this.props}/>}
</div>
)
}
}
Use onChange and pass all the arguments, here one example to use the editor.getHTML()
import React, { Component } from 'react'
import dynamic from 'next/dynamic'
import { render } from 'react-dom'
const QuillNoSSRWrapper = dynamic(import('react-quill'), {
ssr: false,
loading: () => <p>Loading ...</p>,
})
const modules = {
toolbar: [
[{ header: '1' }, { header: '2' }, { font: [] }],
[{ size: [] }],
['bold', 'italic', 'underline', 'strike', 'blockquote'],
[
{ list: 'ordered' },
{ list: 'bullet' },
{ indent: '-1' },
{ indent: '+1' },
],
['link', 'image', 'video'],
['clean'],
],
clipboard: {
// toggle to add extra line breaks when pasting HTML:
matchVisual: false,
},
}
/*
* Quill editor formats
* See https://quilljs.com/docs/formats/
*/
const formats = [
'header',
'font',
'size',
'bold',
'italic',
'underline',
'strike',
'blockquote',
'list',
'bullet',
'indent',
'link',
'image',
'video',
]
class BlogEditor extends Component {
constructor(props) {
super(props)
this.state = { value: null } // You can also pass a Quill Delta here
this.handleChange = this.handleChange.bind(this)
this.editor = React.createRef()
}
handleChange = (content, delta, source, editor) => {
this.setState({ value: editor.getHTML() })
}
render() {
return (
<>
<div dangerouslySetInnerHTML={{ __html: this.state.value }} />
<QuillNoSSRWrapper ref={this.editor} onChange={this.handleChange} modules={modules} formats={formats} theme="snow" />
<QuillNoSSRWrapper value={this.state.value} modules={modules} formats={formats} theme="snow" />
</>
)
}
}
export default BlogEditor
If you want to use ref in Next.js with dynamic import
you can use React.forwardRef API
more info

Get child component reference in parent component

I am new to Angular and stuck on an issue. Anyone can help me to figure out that what I am doing wrong in parent component. I am not able to get the child component reference in parent. I already followed following reference but not succeeded.
Angular 2 #ViewChild annotation returns undefined
https://expertcodeblog.wordpress.com/2018/01/12/angular-resolve-error-viewchild-annotation-returns-undefined/
Parent:
import { Component, OnInit, OnDestroy, ViewChild, HostListener, AfterViewInit, ViewChildren, QueryList } from '#angular/core';
import { Router, NavigationEnd, NavigationStart } from '#angular/router';
import { NavItem, NavItemType } from '../../md/md.module';
import { Subscription } from 'rxjs/Subscription';
import { Location, LocationStrategy, PathLocationStrategy, PopStateEvent } from '#angular/common';
import 'rxjs/add/operator/filter';
import { NavbarComponent } from '../../shared/navbar/navbar.component';
import PerfectScrollbar from 'perfect-scrollbar';
import { ChatService } from 'app/services/chat.service';
import swal from 'sweetalert2';
import { JitsiService } from 'app/services/jitsi.service';
import { UserService } from 'app/services/user.service';
import { ConferenceStudentComponent } from 'app/conference-student/conference-student.component';
declare const $: any;
#Component({
selector: 'app-layout',
templateUrl: './admin-layout.component.html'
})
export class AdminLayoutComponent implements OnInit, AfterViewInit {
public navItems: NavItem[];
private _router: Subscription;
private lastPoppedUrl: string;
private yScrollStack: number[] = [];
url: string;
location: Location;
#ViewChild('sidebar') sidebar: any;
#ViewChild(NavbarComponent) navbar: NavbarComponent;
#ViewChildren(ConferenceStudentComponent) stuConf: QueryList<ConferenceStudentComponent>;
constructor( private router: Router, location: Location,
private chatService: ChatService,
private jitsiService: JitsiService,
private userService: UserService
) {
this.location = location;
this.chatService.callVisibilityChange
.subscribe(callFrom => {
console.log('admin layout call from', callFrom);
if (callFrom) {
this.userService.getLoggedUserDetail()
.subscribe(loggedUser => {
if (!loggedUser) {
console.log(`Invalid token, logged user data not fetched`);
return false;
}
this.userService.getUser(callFrom['fromUser'])
.subscribe(otherUser => {
swal({
title: `${otherUser['fullName']} is calling`,
text: `Click on accept to join session`,
type: `info`,
showCancelButton: true,
cancelButtonColor: `#d33`,
cancelButtonText: `reject`,
confirmButtonColor: `#3085d6`,
confirmButtonText: `accept`
}).then((result) => {
if (result.value) {
const jitsiSessionData = {
loggedUser,
otherUser,
roomName: callFrom['roomName']
}
this.router.navigateByUrl(`/conference-student/${otherUser['_id']}`);
window.setTimeout(() => this.jitsiService.joinSession(jitsiSessionData), 10000);
} else {
console.log('user select rejected');
this.chatService.jitsiCallReject(otherUser._id, loggedUser._id, callFrom['roomName']);
}
})
});
});
}
});
}
ngOnInit() {
const elemMainPanel = <HTMLElement>document.querySelector('.main-panel');
const elemSidebar = <HTMLElement>document.querySelector('.sidebar .sidebar-wrapper');
this.location.subscribe((ev:PopStateEvent) => {
this.lastPoppedUrl = ev.url;
});
this.router.events.subscribe((event:any) => {
if (event instanceof NavigationStart) {
if (event.url != this.lastPoppedUrl)
this.yScrollStack.push(window.scrollY);
} else if (event instanceof NavigationEnd) {
if (event.url == this.lastPoppedUrl) {
this.lastPoppedUrl = undefined;
window.scrollTo(0, this.yScrollStack.pop());
}
else
window.scrollTo(0, 0);
}
});
this._router = this.router.events.filter(event => event instanceof NavigationEnd).subscribe((event: NavigationEnd) => {
elemMainPanel.scrollTop = 0;
elemSidebar.scrollTop = 0;
});
const html = document.getElementsByTagName('html')[0];
if (window.matchMedia(`(min-width: 960px)`).matches && !this.isMac()) {
let ps = new PerfectScrollbar(elemMainPanel);
ps = new PerfectScrollbar(elemSidebar);
html.classList.add('perfect-scrollbar-on');
}
else {
html.classList.add('perfect-scrollbar-off');
}
this._router = this.router.events.filter(event => event instanceof NavigationEnd).subscribe((event: NavigationEnd) => {
this.navbar.sidebarClose();
});
this.navItems = [
{ type: NavItemType.NavbarLeft, title: 'Dashboard', iconClass: 'fa fa-dashboard' },
{
type: NavItemType.NavbarRight,
title: '',
iconClass: 'fa fa-bell-o',
numNotifications: 5,
dropdownItems: [
{ title: 'Notification 1' },
{ title: 'Notification 2' },
{ title: 'Notification 3' },
{ title: 'Notification 4' },
{ title: 'Another Notification' }
]
},
{
type: NavItemType.NavbarRight,
title: '',
iconClass: 'fa fa-list',
dropdownItems: [
{ iconClass: 'pe-7s-mail', title: 'Messages' },
{ iconClass: 'pe-7s-help1', title: 'Help Center' },
{ iconClass: 'pe-7s-tools', title: 'Settings' },
'separator',
{ iconClass: 'pe-7s-lock', title: 'Lock Screen' },
{ iconClass: 'pe-7s-close-circle', title: 'Log Out' }
]
},
{ type: NavItemType.NavbarLeft, title: 'Search', iconClass: 'fa fa-search' },
{ type: NavItemType.NavbarLeft, title: 'Account' },
{
type: NavItemType.NavbarLeft,
title: 'Dropdown',
dropdownItems: [
{ title: 'Action' },
{ title: 'Another action' },
{ title: 'Something' },
{ title: 'Another action' },
{ title: 'Something' },
'separator',
{ title: 'Separated link' },
]
},
{ type: NavItemType.NavbarLeft, title: 'Log out' }
];
}
ngAfterViewInit() {
this.runOnRouteChange();
this.stuConf.changes.subscribe((comp: QueryList<ConferenceStudentComponent>) => {
console.log(`student component`, comp);
})
}
public isMap() {
if (this.location.prepareExternalUrl(this.location.path()) === '/maps/fullscreen') {
return true;
} else {
return false;
}
}
runOnRouteChange(): void {
if (window.matchMedia(`(min-width: 960px)`).matches && !this.isMac()) {
const elemSidebar = <HTMLElement>document.querySelector('.sidebar .sidebar-wrapper');
const elemMainPanel = <HTMLElement>document.querySelector('.main-panel');
let ps = new PerfectScrollbar(elemMainPanel);
ps = new PerfectScrollbar(elemSidebar);
ps.update();
}
}
isMac(): boolean {
let bool = false;
if (navigator.platform.toUpperCase().indexOf('MAC') >= 0 || navigator.platform.toUpperCase().indexOf('IPAD') >= 0) {
bool = true;
}
return bool;
}
}
Child:
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute, ParamMap } from '#angular/router';
import '../../vendor/jitsi/external_api.js';
import { JitsiService } from 'app/services/jitsi.service.js';
import { UserService, UserSchema } from 'app/services/user.service.js';
import swal from 'sweetalert2';
declare var JitsiMeetExternalAPI: any;
declare const $: any;
#Component({
selector: "app-conference-student-cmp",
templateUrl: "conference-student.component.html"
})
export class ConferenceStudentComponent implements OnInit {
roomName: string;
tutor: UserSchema
student: UserSchema;
domain: string;
options: any;
api: any;
hasActiveRoom: boolean;
tutorId: string;
conferenceJoined: boolean;
constructor(
private route: ActivatedRoute,
private jitsiService: JitsiService,
private userService: UserService
) { }
ngOnInit() {
this.conferenceJoined = false;
// this.domain = "jitsi.liquidclouds.in";
this.domain = 'meet.jit.si';
this.route.paramMap.subscribe((params: ParamMap) => this.tutorId = params.get('id'));
this.userService
.getLoggedUserDetail()
.subscribe(student => {
// store student
this.student = student;
this.userService.getUser(this.tutorId)
.subscribe((tutor: UserSchema) => {
// store tutor
this.tutor = tutor;
const obj = { tutorId: this.tutor['_id'], studentId: this.student['_id'] };
this.jitsiService.getActiveRoomForStudent(obj).subscribe(resp => {
if (resp && resp['result'] && resp['result']['roomName']) {
this.hasActiveRoom = true;
this.roomName = resp['result']['roomName'];
}
});
});
});
}
joinSession() {
this.options = {
roomName: this.roomName,
width: 800,
height: 500,
parentNode: document.querySelector('#jitsiVideo'),
configOverwrite: {},
interfaceConfigOverwrite: {
// filmStripOnly: true,
TOOLBAR_BUTTONS: [
'microphone', 'camera', 'closedcaptions', 'desktop', 'fullscreen',
'hangup', 'profile', 'chat', 'recording'
],
}
};
this.api = new JitsiMeetExternalAPI(this.domain, this.options);
this.api.executeCommand('displayName', this.student['fullName']);
this.api.addEventListeners({
readyToClose: this.unload,
participantLeft: this.handleParticipantLeft,
participantJoined: this.handleParticipantJoined,
videoConferenceJoined: this.handleVideoConferenceJoined,
videoConferenceLeft: this.handleVideoConferenceLeft
});
this.conferenceJoined = true;
}
unload = () => {
if (this.api instanceof JitsiMeetExternalAPI) {
this.api.dispose();
}
$('#jitsiVideo').html('');
this.conferenceJoined = false;
this.hasActiveRoom = false;
}
handleParticipantLeft = (arg) => {
this.jitsiService.getJitsiDetailByParticipantId(arg.id)
.subscribe(async roomDetail => {
if (!roomDetail) {
console.log(`No room is joined by the participant id: ${arg.id}`);
return false;
} else {
const participantDetail = roomDetail['participants'].filter(el => el.jitsiParticipantId === arg.id)[0];
if (participantDetail) {
switch (participantDetail.type) {
case 'manager':
console.log('Left participant is manager');
break;
case 'supervisor':
console.log('Left participant is supervisor');
break;
case 'student':
console.log('Left participant is student');
break;
case 'tutor':
console.log('Left participant is tutor');
this.api.dispose();
this.conferenceJoined = false;
this.hasActiveRoom = false;
alert('Tutor left the session.');
break;
default:
console.log('Left participant is not a valid type');
break;
}
}
}
});
}
handleParticipantJoined = (arg) => {
console.log('participant joined: ', arg, this.api);
}
handleVideoConferenceJoined = (arg) => {
const obj = {
participantId: arg.id,
roomName: arg.roomName,
tutorId: this.tutor['_id'],
studentId: this.student['_id'],
studentJoined: 'yes',
joineeType: 'student',
}
// save new room
this.jitsiService.updateJitsiRoomForStudent(obj);
}
handleVideoConferenceLeft = (arg) => {
}
}
admin-layout.componet.html
<div class="wrapper">
<div class="sidebar" data-color="rose" data-background-color="white" data-image="./assets/img/sidebar-1.jpg">
<app-sidebar-cmp></app-sidebar-cmp>
<div class="sidebar-background" style="background-image: url(assets/img/sidebar-1.jpg)"></div>
</div>
<div class="main-panel">
<router-outlet></router-outlet>
<div *ngIf="!isMap()">
<app-footer-cmp></app-footer-cmp>
</div>
</div>
<app-fixedplugin></app-fixedplugin>
</div>

Having issues using axios to handle my JSON data

I am trying to setState from this data,
var axios = require('axios');
import Trails from './trails';
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
trails: []
}
}
componentWillMount() {
axios
.get('https://www.mtbproject.com/data/get-trails-by-id?ids=2081068,830442%208013961&key=(API-KEY)')
.then(response => response.data)
.then(trails => this.setState({trails}));
}
which looks like this:
{
"trails": [
{
"id": 2081068,
"name": "San Dieguito River Park - Bernardo Bay\/ Piedras Pintadas Trail",
"type": "Featured Ride",
"summary": "Sweet little loop of singletrack trails.",
"difficulty": "blue",
"stars": 3.6,
"starVotes": 24,
"location": "Escondido, California",
"url": "https:\/\/www.mtbproject.com\/trail\/2081068\/san-dieguito-river-park-bernardo-bay-piedras-pintadas-trail",
"imgSqSmall": "https:\/\/cdn-files.apstatic.com\/mtb\/2148715_sqsmall_1372258680.jpg",
"imgSmall": "https:\/\/cdn-files.apstatic.com\/mtb\/2148715_small_1372258680.jpg",
"imgSmallMed": "https:\/\/cdn-files.apstatic.com\/mtb\/2148715_smallMed_1372258680.jpg",
"imgMedium": "https:\/\/cdn-files.apstatic.com\/mtb\/2148715_medium_1372258680.jpg",
"length": 8.2,
"ascent": 570,
"descent": -567,
"high": 488,
"low": 317,
"longitude": -117.0766,
"latitude": 33.0512,
"conditionStatus": "All Clear",
"conditionDetails": "Dry",
"conditionDate": "2018-09-11 09:12:17"
}
],
"success": 1
}
Then I am trying to map it like this:
render() {
return (
<div className='App'>
<div className="container">
<div className="jumbotron">
<h4>Mtb</h4>
<p>Trails:</p>
</div>
{this.state.trails.map(trail => (
<Trails key={trail.id}
conditions={trail.conditionDetails}
/>
))
}
</div>
</div>
);
}
}
I then get an error saying that my map method is not a function. Can someone point out what I am doing wrong?
When I console.log my state it appears that it is not being set, might this be the issue and be the explanation for why it is not working?
You are setting trails to be the entire data object you get in response to your request. Use the trails property of the data object instead.
componentWillMount() {
axios
.get('https://www.mtbproject.com/data/get-trails-by-id?ids=2081068,830442%208013961&key=(API-KEY)')
.then(response => this.setState({ trails: response.data.trails }));
}

Resources