Not able to display image from API's URL in Angular dynamically - javascript

as i said in title, i'm using free api for displaying crypto currency news for my practice project. Seems like everything is fine except displaying the images in card view.
I will post here my code, so if you have any idea how to fix, please help me.
From the app service:
getNews(): Observable<any> {
return this._http.get("https://min-api.cryptocompare.com/data/v2/news/?lang=EN")
.map(result => this.result = result)
.pipe(catchError(this.handleError('getPriceSingle', [])));
}
Controller:
this._data.getNews()
.subscribe(res => {
this.receiveData = res.Data
let newsObj: any = Object.keys(this.receiveData);
let newsValues: any = Object.values(this.receiveData);
for (let i = 0; i < newsValues.length; i++) {
this.newsX[newsValues[i]] = {
title: newsValues[i].title,
url: newsValues[i].url,
body: newsValues[i].body,
image: newsValues[i].imageurl,
date: newsValues[i].published_on,
tags: newsValues[i].tags
};
}
this.newsData = JSON.parse(JSON.stringify(Object.values(newsValues)));
console.log(this.newsData)
});
And view:
<nz-layout>
<nz-content>
<header>
<h1>Latest Crypto Currency News</h1>
</header>
<div class="band">
<div class="item-7" *ngFor="let news of newsData">
<a href="{{news.url}}" class="card">
<div class="thumb">
<img [src]='news.image' />
</div>
<article>
<h1>{{news.title}}</h1>
<span>Release Date: {{today | date:'dd/MM/yyyy'}}</span>
</article>
</a>
</div>
</div>
</nz-content>

Seeing what you're trying to accomplish, I'd say the back and forth conversions using Object.keys and Object.values aren't required here. Try the following
Avoid the subscription in the controller. Use Angular async pipe instead. This also avoids potential memory leaks due to open subscriptions.
Use RxJS map operator along with JS Array#map function to transform the data as per your requirement.
This is more of a subjective semantic opinion. While binding variables in the template, using the same quotes across all the bindings is more elegant compared to using double for few and single for others like you're doing.
Controller
import { Component } from "#angular/core";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";
#Component({ ... })
export class NzDemoLayoutFixedComponent {
newsData$: Observable<any>;
constructor(private http: HttpClient) {}
ngOnInit() {
this.newsData$ = this._data.getNews().pipe(
map((res: any) =>
res["Data"].map(item => ({
title: item["title"],
url: item["url"],
body: item["body"],
image: item["imageurl"],
date: new Date(item["published_on"] * 1000), // credit: https://stackoverflow.com/a/847196/6513921
tags: item["tags"]
}))
)
);
}
}
Template
<ng-container *ngIf="(newsData$ | async) as newsData">
<nz-layout class="layout">
<nz-content style="padding:0 50px;margin-top:64px;">
<header>
<h1>Latest Crypto Currency News</h1>
</header>
<div class="band">
<div class="item-7" *ngFor="let news of newsData">
<a [href]="news.url" class="card">
<div class="thumb">
<img [src]="news.image" />
</div>
<article>
<h1>{{ news.title }}</h1>
<span>Release Date: {{ news.date | date: "dd/MM/yyyy hh:mm" }}</span>
</article>
</a>
</div>
</div>
</nz-content>
</nz-layout>
</ng-container>
Working example: Stackblitz

Related

problem in looping and passing data in vue component

guys its my first time to ask a question here in stackoverflow and i really needs an answer
i have a project which i get data from external api from pinia (similar to VueX) then i pass them into a page then i loop through the data and purse them into a component card to be a dynamic component which renders what ever the data i get
i am having a problem in passing the data into the dynamic component.
i fetched the data successflly in pinia , store it into the state in the store . but cant make it into a variable to loop through them
first iam using typescript
for shop interface ShopData.ts
export default interface ShopData {
id: string
name: string
logoPath: string
address: string
}
for types.ts
export type Shop = ShopData
that is my ShopQueries.ts
import { acceptHMRUpdate, defineStore } from 'pinia'
import type { Shop } from '~/types'
import { getShops } from '~/api/ShopsQueries'
export const useShopQueriesStore = defineStore('ShopQueries', {
state: () => ({
shops: [] as Shop[],
}),
actions: {
async getShops(num: number) {
const response = await getShops(num)
this.shops = response.data
return this.shops
},
},
})
if (import.meta.hot)
import.meta.hot.accept(acceptHMRUpdate(useShopQueriesStore, import.meta.hot))
the page file index.vue
<script setup lang="ts">
import { useShopQueriesStore } from '~/stores/ShopQueries'
import type { Shop } from '~/types'
const shopStore = useShopQueriesStore()
const shops = ref<Shop[] | null>()
onMounted(async() => {
shops.value = await shopStore.getShops(6)
})
</script>
<template>
<div class="row">
<div class="col-md-6 col-xxl-4 mt-3 my-3">
<ShopCard
v-for="shop in shopStore.$state.shops"
:key="shop.id"
:address="shop.address"
:name="shop.name"
:image="shop.logoPath"
/>
</div>
</div>
</template>
Which i also want to make it a card and wraps down and i cant :(
that is the card component ShopCard.vue
<script setup lang="ts">
import type { PropType } from '#vue/runtime-core'
import type { Shop } from '~/types'
const props = defineProps({
shop: null as null | PropType<Shop>,
})
console.log(props)
onMounted(() => {
})
const { shop } = toRefs(props)
</script>
<template>
<div class="card">
<div class="card-body d-flex flex-center flex-column pt-12 p-9">
<div class="symbol symbol-65px symbol-circle mb-5">
<img src="{{shop.image}}" alt="image">
</div>
<a class="fs-4 text-gray-800 text-hover-primary fw-bolder mb-0" href="">{{ shop.name }}</a>
</div>
<div class="fw-bold text-gray-400 mb-6">
{{ shop.address }}
</div>
</div>
</template>
i know its hard .. but i really needs some help please !
the whole task depends on it
waiting for help ...

Access nested nodes with GraphQL (Nuxt)

i’m experiencing some issues with Apollo, GraphQL and Nuxt. i don’t know if it’s especially related to Nuxt though, or with vue.
i’m trying to use WordPress as headless CMS via WP-GraphQL plugin. here’s my query
WP-GraphQL interface
i basically created a graphql folder with a posts.js file inside, that contains my query
import gql from 'graphql-tag'
export const myQuery = gql`
query myQuery {
posts {
nodes {
id
title
date
slug
author {
node {
name
}
}
featuredImage {
node {
uri
sourceUrl
srcSet
}
}
}
}
}
`
then, all i need to do is to print my data in the template. here's the script part first.
<script>
import { myQuery } from '~/graphql/posts'
export default {
data() {
return {
posts: [],
}
},
apollo: {
posts: {
prefetch: true,
query: myQuery,
},
},
watch: {
async $route() {
await this.$nuxt.refresh()
window.scrollTo(0, 0)
},
},
transition: 'home',
async mounted() {
this.posts = await this.$apollo.query({ query: myQuery })
this.posts = this.posts.data.posts.nodes
this.loading = false
}
</script>
and then comes the template :
<template>
<section class="featured-projects">
<div class="featured-projects__wrapper">
<article v-for="post in posts" :key="post.id">
<p>{{ post.id }}</p>
<h2>{{ post.title }}</h2>
<span>{{ post.date }}</span>
</article>
</div>
</section>
</section>
</template>
everything just works!
now, i would like to print post author name as well. i first tried this :
<span>{{ post.author }}</span>
and this actually prints this :
{
"node": {
"name": "max max",
"__typename": "User"
},
"__typename": "NodeWithAuthorToUserConnectionEdge"
}
it totally makes sense, as author is an object with nested items in it. so according to what i’m being returned and following GraphQL API structure, to display post author name, think i should do something like this instead :
<span>{{ post.author.node.name }}</span>
and here’s the error i get, and i don’t know what to do to access what i want :
Cannot read property 'node' of undefined.
your problem arises from reading the data before it is loaded.
depending on your js settings you should be able to use one of the following:
<span>{{ post?.author.node.name }}</span>
or <span>{{ post ? post.author.node.name : '' }}</span>
according to the Vue Apollo documentation it could also be a problem with the duplication of the query
<script>
import { myQuery } from '~/graphql/posts'
export default {
data() {
return {
posts: [], // initialization
}
},
apollo: {
posts: {
prefetch: false, // to prevent SSR
query: myQuery,
update: data => {
console.log('overwrite posts with new data', data.posts)
return data.posts
}
},
}
}
</script>
further as there seem to be cases in which the author has more than one entry (perhaps co authors?) I would try to update the author rendering to the following:
<template>
<section class="featured-projects">
<div class="featured-projects__wrapper">
<article v-for="post in posts" :key="post.id">
<p>{{ post.id }}</p>
<div v-if="Array.isArray(post.author)">
first author is: {{ post.author[0].node.name }}
</div>
<div v-else-if="post.author">
author is: {{ post.author.node.name }}
</div>
<div v-else="post.author">
no author
</div>
</article>
</div>
</section>
</section>
</template>

Sorting an array by date in typescript

I want to custom a page in D365 Event Management.
Its written in HTML,CSS,JS,AngularJS and Typescript.
I have a html file with an overview of events:
<div class="spinner-container m-5" *ngIf="isLoading">
<app-spinner></app-spinner>
</div>
<div *ngIf="!isLoading">
<div class="container">
<div class="page-header">
<h3 [appTranslate]="'AvailableEvents'">Available events</h3>
</div>
</div>
<div *ngIf="error">
<app-errormessage
[serverErrorMessage]="error.message"
[errorMessageTranslationKey]="error.localizationKey">
</app-errormessage>
</div>
<div *ngIf="allEvents" class="container mt-5" id="all-events">
<div class="row">
<div class="col-12 col-md-6 col-lg-4" attr.data-href="/event?id={{ event.readableEventId }}" *ngFor="let event of allEvents">
<div class="card mx-auto mb-4" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">
<a [routerLink]="['/event']" [queryParams]="{id: event.readableEventId}" title="{{ event.eventName }}" [attr.aria-label]="getAreaLabel(event)">
{{ event.eventName }}</a>
</h5>
<h6 *ngIf="event.building" class="card-subtitle mb-2 text-dark">at {{ event.building.name }} </h6>
</div>
<div class="card-footer text-dark" >
{{ getDateString(event) }}
</div>
</div>
</div>
</div>
</div>
</div>
First I tried to sort the divs but as the value of the dates are dynamic I thought its not possible.
So I went to the typescript file:
import { EVENT_SERVICE } from './../../providers/service.providers';
import { EventService } from '../../services/event.service';
import { Component, Inject, OnInit } from '#angular/core';
import { Event } from '../../models/Event';
...
export class HomeComponent implements OnInit {
public allEvents: Event[];
public isLoading: boolean;
public error?: LocalizableError;
constructor(#Inject(EVENT_SERVICE) private eventService: EventService) {
}
ngOnInit(): void {
this.sortEvents();
this.loadPublishedEvents();
}
//---> This is my try to sort it
private sortEvents(){
this.allEvents.sort((a, b) => a.startDate.getDate() - b.startDate.getDate());
}
//<---
private loadPublishedEvents() {
this.isLoading = true;
this.eventService.getPublishedEvents().subscribe(
events => {
this.allEvents = events;
this.isLoading = false;
},
(error: LocalizableError) => this.handleErrorResponse(error)
);
}
...
private getDateString(event:Event): string{
var startDate = new Date(event.startDate.toString());
var endDate = new Date(event.endDate.toString());
var locale = undefined;
const dateOptions = { year: 'numeric', month: 'short', day: 'numeric' };
const timeOptions = { hour:'numeric', minute:'numeric' };
var includeTime = startDate.getFullYear() === endDate.getFullYear() && startDate.getMonth() === endDate.getMonth() && startDate.getDate() === endDate.getDate();
if(includeTime)
{
return `${startDate.toLocaleDateString(locale, dateOptions)} ${startDate.toLocaleTimeString(locale, timeOptions)} - ${endDate.toLocaleTimeString(locale, timeOptions)}`;
}
return `${startDate.toLocaleDateString(locale, dateOptions)} - ${endDate.toLocaleDateString(locale, dateOptions)}`;
}
Event.ts:
export interface Event {
...
endDate: Date;
startDate: Date;
...
}
My question is now how to sort this Events by date. In standard its sorted by name.
when I sort it like I did the elements won't appear.
I think the problem is that the date of the events are not created with "newDate" like in getDateString().
I'm not very familiar with typescript. Also couldn't find anything on google except of this what I already tried.
Would be very nice if someone could help me here.
If you have any questions, just ask.
thx in advance :)
It might be the case here, that the actual date values of a.startDate and b.startDate during runtime are undefined.
You could try to see if you get some results again when you use this:
this.allEvents.sort((a, b) => (a.startDate && b.startDate) ? a.startDate.getTime() - b.startDate.getTime() : 0);
If you see (partially unsorted) results again, you know the issue is indeed caused by undefined values of those date fields. In that case, you can customize the sorting function further by correctly handling those undefined values:
this.allEvents.sort((a, b) => {
if (a.startDate && b.startDate) {
return a.startDate.getTime() - b.startDate.getTime();
}
else {
// do something sophisticated regarding the `undefined` values and return a correct sorting value here...
}
});

How to route dynamically between tabs?

I have mat-tab angular app.And I want to get links dynamically and transfer to navLinks object.I did but it doesn't work.Its okay to give string like './1' for id parameter but I made concatanation and it doesnt work(I checked that concatenation is correct).Here's what I tried below
TS File
export class CarsComponent implements OnInit {
navLinks: any[];
public href: string = "";
activeLinkIndex = -1;
mySubject;
ngOnInit(): void {
this.href = this.router.url;
console.log(this.router.url);
this.router.events.subscribe((res) => {
this.activeLinkIndex = this.navLinks.indexOf(this.navLinks.find(tab => tab.link === '.' + this.router.url));
});
this.mySubject=this.carService.carrierSubject.subscribe(value=>
{
this.id=value;
let numid=this.id.toString();
this.newString="./".concat(numid);
console.log(this.newString);
})
}
newString:string='';
id:number;
car:Car;
constructor(private carService:CarService,private route: ActivatedRoute,private router: Router) {
this.navLinks = [
{
label: 'Preview',
link: this.newString,
index: 0
}, {
label: 'Tuning',
link: './tabtest2',
index: 1
}, {
label: 'Payment',
link: './tabtest3',
index: 2
},
];
}
HTML
<div class="row">
<div class="col-md-5">
<app-car-list></app-car-list>
</div>
<div class="col-md-7">
<nav mat-tab-nav-bar>
<a mat-tab-link
*ngFor="let link of navLinks"
[routerLink]="link.link"
routerLinkActive #rla="routerLinkActive"
[active]="rla.isActive">
{{link.label}}
</a>
</nav>
<router-outlet></router-outlet>
</div>
</div>
I copied your implementation and get and error regarding the handling of the "routerLinkActive" (Angular 8.1.2). The following change in the template worked for me:
<a mat-tab-link
*ngFor="let link of navLinks"
[routerLink]="link.link"
routerLinkActive="active">
{{ link.label }}
</a>
Angular adds an '.active' class automatically if a route is active. You can style an active route with your css afterwards.

Pass json data from one component to an other. Vue.js

I'm trying all day to figure out how to pass data from one component to another. I have read a lot of relevant tutorials and instructions, unfortunately with out luck.
I have fetched some data from an api and i present them in the home.vue
and i want to pass the data into a new file to generate a page that will show a random product from the list.
Maybe the approach is totally wrong, but it is the first time that i use vue components, i have experience just with the instance
I'm trying to implement it using props to return the data to the new page.
Here is the randomize.vue file where I would like to pass my data
<template>
<div class="hello">
<p> {{ this.propsdata[0].e }} </p>
<h1>dla;dl;djal;d</h1>
</div>
</template>
<script>
export default {
name: "randomize",
props: ["propsdata"],
data() {
return {
obj: this.propsdata
};
},
mounted(){
console.log(this.props);
},
};
</script>
This is the home.vue file that i fetch the data
<template>
<div>
<div class=" main-conte" >
<randomize :propsData=toBeShown />
<transition-group name="fade" tag="div" id="container" class=" row " >
<figure v-for="(value,index) in toBeShownOrdered" id="page-wrap" :key="index" class="beer-container col-xs-6 col-sm-6 col-lg-4 col-xl-2" >
<a >
<img #click="goTodetail(value.id)" class="logo lazy img-responsive loaded" v-bind:src="getMissingImg(index)"/>
<figcaption>
<div class="beer-title">{{value.name}}</div>
<div class="beer-availability"> {{value.tagline}}</div>
<div class="learn-more">
<h4 class="beer-info-title">Format</h4>
<span class="serving-icons"></span>
<div class="serving">
<i v-if="value.abv >= 0 && value.abv <=6 " class="fas fa-wine-glass-alt"></i>
<i v-if="value.abv >= 6 && value.abv <=7" class="fas fa-glass-cheers"></i>
<i v-if="value.abv >= 7 && value.abv <=100" class="fas fa-wine-bottle"></i>
<span class="measure">{{value.abv}}</span>%</div>
</div>
</figcaption>
</a>
</figure>
</transition-group>
<div class=prev-next>
<button #click="orderByName = !orderByName">Click Me!</button>
<button class="prev" #click="prevPage" :disabled="currentPage==1">
<i class="fas fa-angle-double-left"></i></button>
<button class="next" #click="nextPage" :disabled="currentPage == totalPages">
<i class="fas fa-angle-double-right"></i> </button>
</div>
</div>
<div>
</div>
</div>
</template>
<script>
import { mdbView, mdbMask } from "mdbvue";
import FadeTransition from "./fade-transition.vue";
import randomize from "#/components/randomize";
export default {
name: "home",
components: {
mdbView,
mdbMask,
FadeTransition,
randomize
},
data() {
return {
items: [],
message: '',
currentPage: 1,
orderByName: false,
};
},
computed: {
//show more less products
toBeShown() {
return this.items.slice(0, this.currentPage * 5);
},
totalPages() {
return Math.ceil( this.items.length / 4);
},
toBeShownOrdered() {
return this.orderByName ? _.orderBy(this.toBeShown, 'name', 'asc') : this.toBeShown;
}
},
mounted() {
this.fetchData();
},
methods: {
fetchData: function() {
const myRequest = new Request("https://api.punkapi.com/v2/beers");
fetch(myRequest)
.then(response => {
return response.json();
})
.then(data => {
this.items = data;
console.log(this.items);
})
.catch(error => {
console.log(error);
});
},
getMissingImg(index) {
return this.images[index];
},
nextPage(){
if(this.currentPage < this.totalPages) this.currentPage++;
},
prevPage(){
this.currentPage = this.currentPage - 1 || 1;
},
goTodetail(prodId) {
let proId=prodId
this.$router.push({name:'blog',params:{Pid:proId}})
},
index.js
import Vue from 'vue'
import Router from 'vue-router'
import home from '#/components/home'
import blog from '#/components/blog'
import randomize from '#/components/randomize'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'home',
component: home,
props:true
},
{
path: '/blog/:Pid',
name: 'blog',
component: blog,
props:true
},
{
path: '/randomize/',
name: 'randomize',
component: randomize,
props:true
},
]
})
You would benefit from using vuex as it will keep your state at the application level (as opposed to component data which keeps each component state at the component level).
Setting up vuex requires a bit more work and has a learning curve, but unless you won't grow your app to a medium/large size it will in the long term benefit you by decreasing the overall complexity of your app.
In short, all components from your app can access the state stored in vuex without having to deal with props. And from any component, you can dispatch actions implemented in your vuex store to alter the vuex state. Vuex will help keeping your components focused on presenting data and capturing user actions.
To ease setting up a Vue app with vue-router and vuex, you could choose to build your app with nuxt.js which is a framework that provides you with vue+vue-router+vuex with no effort. Nuxt.js will also help setting up server side rendering which would be beneficial to SEO if your app is to be indexed by search engines.

Categories

Resources