I need to show the data I'm fetching, but none is showing, I tried ngIf and ngFor
I'm using angular 6, and trying to display data from movies in the Html, but data seems to be empty, I tried a lot of solutions but none seems to work.
<app-logo></app-logo>
<app-options></app-options>
<div style="display:none;" id="root"></div>
<div class="col s6">
<button routerLink="/halfhour" (click)=searchData() class="options" id="menos-30-min" value="25 min">Menos de 30 min</button>
</div>
<div *ngIf="data?.data">
<ng-container *ngFor="let data of data">
<div class="row">
<div class="col s5">
<img class="backgrounds" src="{{data?.Poster}}">
<div class="background" style="background-image: url({{data?.Poster}}); background-size: cover; background-position: center center; background-repeat: no-repeat; background-attachment: fixed; height:300px; width: 100%;"></div>
</div>
<div class="col s7">
<div class="information">
<p class="title">Title: {{data?.Title}}</p>
<p class="runtime">Runtime: {{data?.Runtime}}</p>
<p class="other-data">Year: {{data?.Year}}</p>
<p class="other-data"> Genre: {{data?.Genre}}</p>
<p class="resumen"> Plot: {{data?.Plot}}</p>
<p class="other-data"> Tipo: {{data?.Type}}</p>
</div>
</div>
</div>
</ng-container>
</div>
<h1>{{id}}</h1>
#Component({
selector: 'app-half-hour',
templateUrl: './half-hour.component.html',
styleUrls: ['./half-hour.component.css']
})
export class HalfHourComponent implements OnInit {
constructor(private http: Http) {
}
data: any [];
id;
getRandom() {
const omdbData = ['0386676', '1865718', '0098904', '4508902', '0460649', '2861424', '0108778', '1305826', '0096697', '0149460'];
const randomItem = omdbData[Math.floor(Math.random() * omdbData.length - 1) + 1];
return this.id = randomItem;
}
searchData() {
this.getRandom();
this.http
.get('https://cors-anywhere.herokuapp.com/http://www.omdbapi.com/?i=tt' + this.id + '&apikey=')
.pipe(map((res: Response) => res.json()))
.subscribe(data => {
this.data = Array.of(this.data);
console.log(data);
console.log(this.id);
});
}
ngOnInit() {
}
}
The properties of data show as empty.
HTTP request call should be added inside ngOninit(). Here is the updated code
ngOnInit(){
this.data = [];
this.getRandom();
this.http
.get('https://cors-anywhere.herokuapp.com/http://www.omdbapi.com/?i=tt' + this.id + '&apikey=')
.pipe(map((res: Response) => res.json()))
.subscribe(data => {
this.data = (data) ? data.slice() : [];
console.log(data);
console.log(this.id);
});
}
Related
I am using RestApi and trying to use fetch to jsonplaceholder but it shows undefined in the final result.
enter image description here
This is the code:
function searchUser(event) {
event.preventDefault();
const searched_random_users = random_users.filter((user) => user.email.includes(event.target.value));
products_container.innerHTML = '';
searched_random_users.forEach((random_user) => {
products_container.innerHTML += `
<div class="card col-4">
<h3 class="card-text">${random_user.name}</h3>
<h5 class="card-text">${random_user.username}</h5>
<h5 class="card-text">${random_user.email}</h5>
<h5 class="card-text">${random_user.phone}</h5>
</div>
`;
});
}
random_user_add_button.addEventListener('click', addRandomUser);
function addRandomUser(event) {
event.preventDefault();
fetch('https://jsonplaceholder.typicode.com/users')
.then((response) => response.json())
.then((user) => {
random_users.push(user);
products_container.innerHTML += `
<div class="card col-4">
<h3 class="card-text">${user.name}</h3>
<h5 class="card-text">${user.username}</h5>
<h5 class="card-text">${user.email}</h5>
<h5 class="card-text">${user.phone}</h5>
</div>
`;
});
}
I need to add bootstrap effect fadeInDown to a newly added comment in my comments section.
It works but unfortunately it affects all of the comments when I reload the page and the effect is also added when I edit a comment.
Is it possible to add this specific class only to newly created comment? not affecting the rest of comments nor the other application functionalities?
I will be glad for any advice
This is my comment section:
Comment section
This is the structure of my files: Files structure
These are the components I'm working on:
Comment section HTML
<h6>Comments: ({{commentsCount}})</h6>
<app-new-comment class="d-block mt-3" [requestId]="requestId" (update)="updateComments()">
</app-new-comment>
<app-comment *ngFor="let comment of comments; let i = first"
[ngClass]="{'fadeInDown': i}"
class="d-block mt-3 animated"
[comment]="comment"
(update)="updateComments()"></app-comment>
Comment section TS
export class CommentSectionComponent implements OnInit, OnDestroy {
#Input() requestId: string
comments: CommentDetailsDto[]
commentsCount = 0
private subscription: Subscription
constructor(
private commentsService: CommentsService,
private alert: AlertService
) {
}
ngOnInit(): void {
this.updateComments()
}
ngOnDestroy(): void {
this.unsubscribe()
}
updateComments(): void {
this.unsubscribe()
this.subscription = this.commentsService.getComments(this.requestId)
.subscribe({
next: (comments: CommentDetailsDto[]) => {
this.comments = comments.reverse()
this.commentsCount = this.getCommentsCount(comments)
},
error: (error: HttpErrorResponse) => {
this.alert.handleHttpError(error)
}
})
}
private getCommentsCount(comments: CommentDetailsDto[]): number {
return comments.reduce(
(cnt, comment) => cnt + 1 + (comment.replies ? this.getCommentsCount(comment.replies) : 0),
0
)
}
private unsubscribe(): void {
if (this.subscription instanceof Subscription) {
this.subscription.unsubscribe()
}
}
}
Comment section SCSS
#keyframes fadeInDown {
from {
opacity: 0;
transform: translate3d(0, -100%, 0);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
.fadeInDown {
animation-name: fadeInDown;
}
New comment component HTML
<div class="d-flex">
<app-user-image class="mr-3"></app-user-image>
<textarea #textArea class="d-block" placeholder="Add comment" [(ngModel)]="newComment">
</textarea>
</div>
<div class="text-right mt-3">
<button class="btn btn-primary font-weight-bold px-5" type="button"
[disabled]="!newComment" (click)="submit()">
Post
</button>
</div>
New comment TS
export class NewCommentComponent implements OnInit {
#ViewChild('textArea') textArea: ElementRef<HTMLTextAreaElement>
#Input() requestId?: string
#Input() comment?: CommentDetailsDto
#Output() update = new EventEmitter()
newComment: string
private readonly commentResponseObserver = {
error: (error: HttpErrorResponse) => {
this.alert.handleHttpError(error)
},
complete: () => {
delete this.newComment
this.update.emit()
this.alert.showSuccess('Comment submitted successfully')
}
}
constructor(
private commentsService: CommentsService,
private alert: AlertService,
private route: ActivatedRoute
) {
}
ngOnInit(): void {
if (this.comment) {
this.textArea.nativeElement.focus()
}
}
submit(): void {
if (this.requestId) {
this.addComment()
} else if (this.comment) {
this.addReply()
}
}
addComment(): void {
if (this.newComment) {
this.commentsService.addComment(this.requestId, this.newComment)
.subscribe(this.commentResponseObserver)
}
}
addReply(): void {
if (this.newComment) {
this.commentsService.addReply(this.route.snapshot.queryParamMap.get('requestId'), this.comment, this.newComment)
.subscribe(this.commentResponseObserver)
}
}
}
Comment component HTML
<div class="comment">
<app-user-image></app-user-image>
<div class="position-relative d-inline-block flex-fill">
<div class="d-flex justify-content-between align-items-center">
<div><strong>{{comment.author.name}} / {{comment.author.id}}</strong><span
class="date">{{comment.created | krakenDateTime}}</span></div>
<div class="actions">
<button *ngIf="this.hateoas.supports(comment, 'update') && !edit"
type="button" class="bg-transparent border-0" title="Edit"
(click)="toggleEdit()"><i class="icon-kraken icon-kraken-edit"></i></button>
<button *ngIf="this.hateoas.supports(comment, 'delete')"
type="button" class="bg-transparent border-0" title="Delete"
(click)="displayDeletionConfirmation()"><i class="icon-kraken icon-kraken-trash"></i></button>
</div>
</div>
<textarea *ngIf="edit; else readonlyComment"
#textarea
class="d-block w-100"
style="min-height: 7rem;"
[rows]="rows()"
[(ngModel)]="commentContent"></textarea>
<ng-template #readonlyComment>
<div [innerHTML]="commentContentHtml()"></div>
</ng-template>
<strong *ngIf="showReplyButton"
class="reply-button"
(click)="toggleReplyComponent()">Reply</strong>
</div>
</div>
<div *ngIf="edit" class="animated fadeIn text-right mt-3">
<button class="btn btn-sm discard" (click)="cancelEdit()">Discard</button>
<button class="btn btn-sm ml-2 update" (click)="updateComment()">Update</button>
</div>
<div class="replies">
<app-new-comment *ngIf="showNewReplyWindow"
class="d-block mt-3"
[comment]="comment"
(update)="this.update.emit()"></app-new-comment>
<app-comment *ngIf="firstReply"
class="d-block my-3"
[comment]="firstReply"
(update)="update.emit()"></app-comment>
<div *ngIf="moreReplies.length" class="replies-toggle" (click)="showMoreReplies =
!showMoreReplies">
<div class="horizontal-bar"></div>
<span class="mx-3">{{showMoreReplies ? 'Hide' : 'See'}} {{moreReplies.length}} more
comments</span>
<div class="horizontal-bar"></div>
</div>
<div *ngIf="showMoreReplies">
<app-comment *ngFor="let reply of moreReplies"
class="d-block my-3"
[comment]="reply"
(update)="update.emit()"></app-comment>
</div>
<span id="temporaryLastRow"></span>
Comment TS
export class CommentComponent implements OnInit {
#ViewChild('textarea', {static: true}) textarea: ElementRef<HTMLTextAreaElement>
#Input() comment: CommentDetailsDto
#Output() update = new EventEmitter()
edit = false
showReplyButton = false
showNewReplyWindow = false
showMoreReplies = false
commentContent: string
firstReply: CommentDetailsDto
moreReplies: CommentDetailsDto[]
constructor(
private commentsService: CommentsService,
private modalFactoryService: ModalFactoryService,
public hateoas: HateoasService,
private alert: AlertService
) {
}
ngOnInit(): void {
this.commentContent = this.comment.content
this.showReplyButton = this.hateoas.supports(this.comment, 'reply')
this.moreReplies = this.comment.replies.reverse().slice(1)
this.firstReply = this.comment.replies[0]
}
toggleEdit(): void {
this.edit = !this.edit
}
updateComment(): void {
this.commentsService.updateComment(this.comment, this.commentContent)
.subscribe({
error: (error: HttpErrorResponse) => {
this.alert.handleHttpError(error)
},
complete: () => {
this.alert.showSuccess('Comment updated successfully')
this.update.emit()
}
})
}
cancelEdit(): void {
this.edit = false
this.commentContent = this.comment.content
}
displayDeletionConfirmation(): void {
this.modalFactoryService.openConfirmationModal(
'Delete Comment',
{
message: 'Are you sure you want to delete this comment?',
yesOptionButton: 'Yes',
noOptionButton: 'No'
},
() => {
this.hateoas.execute(this.comment, 'delete')
.subscribe({
error: (error: HttpErrorResponse) => {
this.alert.handleHttpError(error)
},
complete: () => {
this.alert.showSuccess('Comment deleted successfully')
this.update.emit()
}
})
}
)
}
toggleReplyComponent(): void {
this.showNewReplyWindow = !this.showNewReplyWindow
}
commentContentHtml(): string {
return this.commentContent.replace(/\n/g, '<br>')
}
rows(): number {
const newLines = this.commentContent.match(/\n/g) || []
return newLines.length + 1
}
}
export interface Author {
id: string
name: string
}
export interface CommentDetailsDto extends Resource {
content: string
author: Author
replies: CommentDetailsDto[]
created: string
}
I'm trying to display a preview of some images when a user selects them before he uploads them, but I'm new to vue and I can't figure out how to apply the background image url style to each div (it must be with background image), here is my UploadComponent so far:
<template>
<div class="content">
<h1>{{ title }}</h1>
<div class="btn-container">
<label class="button" for="images">Select Images</label>
<input type="file" id="images" name="images[]" multiple="multiple" #change="selectFiles"/>
</div>
<br><br>
<div class="block">
<h3>Selected images</h3>
<div v-for="image in images" v-bind:key="image" class="image-result" v-bind:style="{ backgroundImage: 'url(' + image + ')' }">
</div>
</div>
<div class="btn-container">
<button type="button" class="submit-btn" v-on:click="uploadImages">Submit Images</button>
</div>
<br><br>
<div class="block">
<h3>Uploaded images</h3>
<div class="files-preview">
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'UploadComponent',
data: () => ({
title: 'Select your images first and then, submit them.',
images: []
}),
methods:{
selectFiles(event){
var selectedFiles = event.target.files;
var i = 0;
for (i = 0; i < selectedFiles.length; i++){
this.images.push(selectedFiles[i]);
}
for (i = 0; i < this.images.length; i++){
let reader = new FileReader(); //instantiate a new file reader
reader.addEventListener('load', function(){
console.log(this);
}.bind(this), false); //add event listener
reader.readAsDataURL(this.images[i]);
}
},
uploadImages(){
const data = new FormData();
data.append('photos', this.images);
axios.post('url')
.then(res => {
console.log(res);
});
}
}
}
</script>
The divs are being created but I'm missing the part where I assign the background image, how can I solve this?
You can't just put a File directly as the parameter for background-image. You started using FileReader but didn't do anything with the result.
Here's what I would do below, I converted your images array into a list of objects that have a file property and a preview property, just to keep the file and preview linked together.
Vue.config.productionTip = false;
Vue.config.devtools = false;
new Vue({
el: "#app",
data: () => {
return {
title: 'Select your images first and then, submit them.',
images: []
}
},
methods: {
selectFiles(event) {
var selectedFiles = event.target.files;
for (var i = 0; i < selectedFiles.length; i++) {
let img = {
file: selectedFiles[i],
preview: null
};
let reader = new FileReader();
reader.addEventListener('load', () => {
img.preview = reader.result;
this.images.push(img);
});
reader.readAsDataURL(selectedFiles[i]);
}
},
uploadImages() {
const data = new FormData();
data.append('photos', this.images.map(image => image.file));
axios.post('url')
.then(res => {
console.log(res);
});
}
}
});
.image-result {
width: 100px;
height: 100px;
background-size: contain;
background-repeat: no-repeat;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app" class="content">
<h1>{{ title }}</h1>
<div class="btn-container">
<label class="button" for="images">Select Images</label>
<input type="file" id="images" name="images[]" multiple="multiple" #change="selectFiles" />
</div>
<br><br>
<div class="block">
<h3>Selected images</h3>
<div id="target-photos">
</div>
<div v-for="image in images" class="image-result" v-bind:style="{ backgroundImage: 'url(' + image.preview + ')' }">
</div>
</div>
<div class="btn-container">
<button type="button" class="submit-btn" v-on:click="uploadImages">Submit Images</button>
</div>
<br><br>
<div class="block">
<h3>Uploaded images</h3>
<div class="files-preview">
</div>
</div>
</div>
I'm new to angular and I tried to make an accordion component, and it' didn't work like I wanted it to, here's my html code.
<div class="faq-item-container">
<h1 class="mt-1 mb-5"><strong>Frequently Aksed Questions</strong></h1>
<div class="row" (click)="toggleDetail(); toggleIcon();" *ngFor= "let faq of faqs">
<div class="col my-2">
<h3> {{faq.title}} <a><fa-icon [icon]="faChevronDown" class="float-right"></fa-icon></a></h3>
</div>
<div class="col-12" *ngIf="showDetail">
<div class="faq-detail-container mt-1">
<div class="col-12">
<p><small>
{{faq.content}}
</small></p>
</div>
</div>
</div>
</div>
</div>
and here's the ts code
import { Component, OnInit } from '#angular/core';
import {faChevronUp, faChevronDown, IconDefinition, faSquare} from '#fortawesome/free-solid-svg-icons';
#Component({
selector: 'app-jobs-faq',
templateUrl: './jobs-faq.component.html',
styleUrls: ['./jobs-faq.component.scss']
})
export class JobsFaqComponent implements OnInit {
faChevronUp: IconDefinition = faChevronUp;
faChevronDown: IconDefinition = faChevronDown;
showDetail: boolean;
faqs = [
{
id: 1,
title: 'faq1',
content: 'content1'
},
{
id: 2,
title: 'faq2',
content: 'content2'
},
{
id: 3,
title: 'faq3',
content: 'content3'
}
];
constructor() {
this.showDetail = false;
}
toggleDetail(): void {
this.showDetail = !this.showDetail;
}
toggleIcon() {
if (this.faChevronDown === faChevronDown) {
this.faChevronDown = faChevronUp;
} else {
this.faChevronDown = faChevronDown;
}
}
ngOnInit() {
}
}
The problem is when I click faq1, the others also collpasing, yes I know it's because I called the same function, and that is what I want to ask about, how to call the function separately to make this accordion working like it's supposed to be? thanks.
It depends on whether you want to close all other sections when you click one or not, but a solution could look somewhat like this:
<div class="faq-item-container">
<h1 class="mt-1 mb-5"><strong>Frequently Aksed Questions</strong></h1>
<div class="row" (click)="toggleDetail(faq.id); toggleIcon();" *ngFor= "let faq of faqs">
<div class="col my-2">
<h3> {{faq.title}} <a><fa-icon [icon]="faChevronDown" class="float-right"></fa-icon></a></h3>
</div>
<div class="col-12" *ngIf="faq.showDetail">
<div class="faq-detail-container mt-1">
<div class="col-12">
<p><small>
{{faq.content}}
</small></p>
</div>
</div>
</div>
</div>
</div>
import { Component, OnInit } from '#angular/core';
import {faChevronUp, faChevronDown, IconDefinition, faSquare} from '#fortawesome/free-solid-svg-icons';
#Component({
selector: 'app-jobs-faq',
templateUrl: './jobs-faq.component.html',
styleUrls: ['./jobs-faq.component.scss']
})
export class JobsFaqComponent implements OnInit {
faChevronUp: IconDefinition = faChevronUp;
faChevronDown: IconDefinition = faChevronDown;
faqs = [
{
id: 1,
title: 'faq1',
content: 'content1',
showDetail: false
},
{
id: 2,
title: 'faq2',
content: 'content2',
showDetail: false
},
{
id: 3,
title: 'faq3',
content: 'content3',
showDetail: false
}
];
toggleDetail(faqId: number): void {
this.faqs = this.faqs.map(faq => {
faq.showDetail = (faq.id == faqId) ? !faq.showDetail : false;
return faq;
});
}
toggleIcon() {
if (this.faChevronDown === faChevronDown) {
this.faChevronDown = faChevronUp;
} else {
this.faChevronDown = faChevronDown;
}
}
ngOnInit() {
}
}
Note that your [icon]="faChevronDown" should be based on the faq in the thes scope of your *ngFor. I'll leave it to you as practice to find a solution for that. (Hint: you could use a ternary operation based on faq.showDetail)
Im getting the above error message but dont understand why as everything is defined as far as i can see.
Heres my html:
Apparently, the error occurs on line 1
<div *ngIf="pager.index < quiz.questions.length">
<h5 class="flow-text"><span [innerHTML]="quiz.questions[pager.index].text"></span></h5>
<br>
<div class="row text-left">
<div class="col s12 m12" *ngFor="let answer of quiz.questions[pager.index].answers">
<div class="answer">
<input type="checkbox" [checked]="answer.selected" (change)="onSelect(answer);"/><label class="black-text flow-text"> {{answer.text}} </label>
</div>
</div>
</div>
<footer class="row">
<div class="col s6 m6"></div>
<div class="col s6 m6">
<div *ngIf="pager.index < quiz.questions.length - 1">
<button id="nextButton" class="btn-flat black-text right flow-text" (click)="goTo(pager.index + 1);">Next</button>
</div>
<div *ngIf="pager.index == quiz.questions.length - 1">
<button id="nextButton" class="btn-flat black-text right flow-text" (click)="goTo(pager.index + 1);">Finish</button>
</div>
</div>
</footer>
</div>
<div *ngIf="pager.index == quiz.questions.length">
{{ selections }}
</div>
here is the component.ts
import { Component, OnInit, EventEmitter } from '#angular/core';
import { QuizService } from '../services/quiz.service';
import { Answer, Question, Quiz } from '../models/index';
import {MaterializeAction} from "angular2-materialize";
#Component({
selector: 'app-quiz',
templateUrl: './quiz.component.html',
styleUrls: ['./quiz.component.sass'],
providers: [QuizService]
})
export class QuizComponent implements OnInit {
quiz: Quiz;
pager = {
index: 0,
size: 1,
count: 1
};
selections: [string]
constructor(private quizService: QuizService) { }
ngOnInit() {
this.loadQuiz()
}
loadQuiz() {
this.quizService.get().subscribe(res => {
this.quiz = new Quiz(res);
this.pager.count = this.quiz.questions.length;
});
}
goTo(index: number) {
if (index >= 0 ) {
this.pager.index = index;
}
}
onSelect(answer: Answer) {
if (answer.selected = true) {
this.selections.splice(this.selections.indexOf(answer.text), 1);
} else {
this.selections.push(answer.text);
}
answer.selected = !answer.selected
}
}
i dont understand why its happening as despite the error compiling the code works and runs as it should, apart from the check boxes which i have asked in another question.
You should be using safe navigation operator in this case
*ngIf="pager.index < quiz?.questions?.length"
And
*ngIf="pager.index == quiz?.questions?.length"
It seems quiz variable is undefined, define it like this quiz: Quiz={}