infinity scroll for data in Observable [duplicate] - javascript

How do i loop through my data, that i subscribed to as an Observable, push it to an array, and displaying the whole data of the array?
My present code only displays data from each "page", and not all the pages.
The reason why i want to do this, is because i want to make an infinity scroll.
Thank you!
Component:
this.storiesService.getData(this.page, this.hits, this.feed)
.subscribe(
(data) => {
console.log(data);
if (!data || data.hits === 0) {
this.finished = true;
console.log("NO MORE HITS")
} else {
this.finished = false;
for (let story of data.hits) {
this.hitsArray.push(story);
console.log("Hit me!")
console.log(this.hitsArray);
}
}
})
HTML:
<div class="col-4 col-md-4 col-sm-12" *ngFor="let story of hitsArray">
<div class="thumbnail">
<img *ngIf="story.storyMediaType === 'image'" class="img-fluid" src="{{story.storyThumbnailImage}}" />
<img *ngIf="story.storyMediaType === 'video'" class="img-fluid" src="{{story.storyThumbnailImage}}" width="150" height="94" />
<div class="caption">
<p>{{story.storyCity}}, {{story.storyCountry}}</p>
<h3>{{story.storyHeadline}}</h3>
<p>Uploadet {{story.uploadDate}}</p>
<p>Bruger: {{story.userDisplayName}}</p>
<p>Like Button</p>
</div>
</div>
</div>

Based on your component's template, you are using data which is updated for every newly fetched data from observable here in your subscription
for (let story in data) {
this.data = data; --> here you update regularly
from the above you are saying for every array key update our this.data which seems wrong, because the keys in an array is 0,1,2 which are the index.
Both for..of and for..in statements iterate over lists; the values iterated on are different though, for..in returns a list of keys on the object being iterated, whereas for..of returns a list of values of the numeric properties of the object being iterated. readmore
So instead of the long trip your subscription code that handles the processing of the retrieved data should look like the one below
for (let story of data) { // now of
// this.data = data; not needed else your data is always overriden
// for (let i = 0; i < this.data.length; i++) { no need for this again since the above for..of is handling it
this.hitsArray.push(story);
this.data.hits.push(story);// add the new stories(as last element) to the main data array using this.data here now
console.log(this.hitsArray);
// } for loop of this.data.length
}
So with the above code following the comments should explain what the code is doing and how the irrelevant parts are causing data override.
P.S. your this.data.hits must be an array initialized as [] when the program is loading the component. And your data from your observable must be an array or if object with hits as array then use this code instead for (let story in data.hits) {.
Hope this helps.

So this is the final fix (note: the hitsArray is being loaded as an empty array, every time the params changes):
Component:
this.storiesService.getData(this.page, this.hits, this.feed)
.subscribe(
(data) => {
console.log(data);
if (!data || data.hits == 0) {
this.finished = true;
console.log("No more hits :-(")
} else {
this.finished = false;
for (let story of data.hits) {
this.hitsArray.push(story);
console.log("Hit me!")
console.log(this.hitsArray);
}
}
})
HTML:
<div class="col-4 col-md-4 col-sm-12" *ngFor="let story of hitsArray">
<div class="thumbnail">
<img *ngIf="story.storyMediaType === 'image'" class="img-fluid" src="{{story.storyThumbnailImage}}" />
<img *ngIf="story.storyMediaType === 'video'" class="img-fluid" src="{{story.storyThumbnailImage}}" width="150" height="94" />
<div class="caption">
<p>{{story.storyCity}}, {{story.storyCountry}}</p>
<h3>{{story.storyHeadline}}</h3>
<p>Uploadet {{story.uploadDate}}</p>
<p>Bruger: {{story.userDisplayName}}</p>
<p>Like Button</p>
</div>
</div>
</div>
Many thanks to #Theophilus for the suggestion! His example may work in most situations other than mine!

Related

Dynamically changing the Image source for HTML,JSON

I have been trying to make some CSS cards using JSON data from Firebase database. Main idea is using the data from Firebase, I am trying to build CSS cards for each submission from a form.
During form submission, User chooses which category it belongs to and specific Image should be displayed in CSS card.
As the CSS cards are dynamically created, the entire HTML code is built using JS.
First this is the code for fetching data from Firebase and calling the function to create the CSS card.
let reference = firebase.database().ref("data-block");
reference.on("value",gotData);
Now check the function for creating the CSS card.
function gotData(data) {
let x = data.val();
let keys = Object.keys(x);
console.log(keys.length);
var q = 0;
for (let i = 0; i < keys.length; i++) {
let a = keys[i];
let title_a = expens[a].Entry;
let category_a = expens[a].Category;
console.log(title_a, category_a);
let expense = document.querySelector(".card-list");
expense.innerHTML += `
<article class="card">
<header class="card-header">
<h2>${title_a} </h2>
</header>
<div class="card-author">
<a class="author-avatar" href="#">
<img id="x" src=""/>
</a>
<svg class="half-circle" viewBox="0 0 106 57">
<path d="M102 4c0 27.1-21.9 49-49 49S4 31.1 4 4"></path>
</svg>
<div class="author-name">
<div class="author-name-prefix">Author</div>
${category_a}
</div>
</article>
`;
}
}
As you can see src of the image is not declared because I couldn't figure out how to assign different images based on Category fetched from Database
I have 3 images like
shopping.png
food.png
learn.png
Help me out on how to assign src for the image tag dynamically based on data from Firebase.
function getImageByCategory(category) {
if (category == 'shopping') {
return '<img src="shopping.png" />';
} else if (category == 'food') {
return '<img src="food.png" />'
} else if (category == 'learn') {
return '<img src="learn.png" />'
}
}
`
<a class="author-avatar" href="#">
${getImageCategory(category_a)}
</a>
Assuming you know the values of the categories you could create an object that will translate those category values into the image names.
Try this (changing categoryA, categoryB, and categoryC into the values being returned as category_a):
...
let category_a=expens[a].Category;
const IMAGE {
categoryA: 'shopping.png',
categoryB: 'food.png'.
categoryC: 'learn.png'
}
console.log(title_a,category_a);
let expense=document.querySelector(".card-list");
expense.innerHTML+= `
<article class="card">
<header class="card-header">
<h2>${title_a} </h2>
</header>
<div class="card-author">
<a class="author-avatar" href="#">
<img id="x" src=${IMAGE[category_a]}/>
</a>
...

Angular - Execute function after http request

hope you're okey.
I have a blog component which obtains posts from a DB via http request.
So, what i'm trying is, when the posts are in my component i want to filter them with a function.
I'm calling this function after get the posts in the subscribe method, but it doesn't works.
Function to filter the posts:
filtrarArticles(tipus)
{
var articles = document.getElementsByClassName("article") as HTMLCollectionOf<HTMLElement>;
for (var i = 0; i < articles.length; i++)
{
var tipusArticle = this.articles[i].categoria;
if (tipus == tipusArticle)
{
articles[i].style.display = "";
}
else
{
articles[i].style.display = "none";
}
}
}
HTML code that filters my function:
<div class="articles row w-100 align-self-end d-flex justify-content-center">
<div class="article col-xl-3 mb-md-0 mb-3" *ngFor="let article of articles; index as i" (click)="obrirModalArticle(article)">
<img class="imatgeAllargada d-block w-100" src="https://drive.google.com/uc?id={{article.img}}">
<br>
<h4>{{article.title_article}}</h4>
<p [innerHTML]="article.descripcio1"></p>
</div>
</div>
The articles array have one property which name is "categoria" an it's the key to filter it.
I call the filter function here:
this.articleService.obtenirArticles(this.idioma).subscribe(
res=>
{
this.articles = res;
this.filtrarArticles('generals');
},
error =>
{
alert("No s'han pogut obtenir els articles, intenta-ho més tard.");
});
this.translate.onLangChange.subscribe((event: LangChangeEvent) =>
{
this.idioma = event.lang;
this.articleService.obtenirArticles(this.idioma).subscribe(
res=>
{
this.articles = res;
},
error =>
{
alert("No s'han pogut obtenir els articles, intenta-ho més tard.");
}
)
});
This code is on init method. And I don't know why doesn't works?
It is considered a bad practice to access the DOM directly, one of the reasons being that you should not manipulate the template directly but rather let Angular do it for you. So what you should be doing instead is, create a model and let Angular know about it, bind it to the template and once it changes Angular will update the template for you.
Component
export class YourComponent {
allArticles; // Property to hold the result of the response
filteredArticles; // Property to hold what is going to be rendered on the template
...
ngOnInit() {
this.articleService.obtenirArticles(this.idioma).subscribe(res => {
this.allArticles = res; // Save the original response
this.filteredArticles = this.filtrarArticles('generals'); // Get the articles filtered and show this property on the template
},
error => {
alert("No s'han pogut obtenir els articles, intenta-ho més tard.");
});
this.translate.onLangChange.subscribe((event: LangChangeEvent) => {
this.idioma = event.lang;
this.articleService.obtenirArticles(this.idioma).subscribe(res => {
this.allArticles = res; // Save the original response
this.filteredArticles = [...this.allArticles]; // Clone the response array unfiltered
},
error => {
alert("No s'han pogut obtenir els articles, intenta-ho més tard.");
})
});
}
}
Filter function
filtrarArticles(tipus) {
// The filter method will return a copy of the array once it has been iterated over each item.
// If the condition is met, the item is going to be included in the resulting array, otherwise it won't.
return this.allArticles.filter(article => article.categoria === tipus);
}
Then in your template, you iterate over the filterArticles array, once that array change, Angular will update the template.
Template
<div class="articles row w-100 align-self-end d-flex justify-content-center">
<div class="article col-xl-3 mb-md-0 mb-3" *ngFor="let article of filteredArticles; index as i" (click)="obrirModalArticle(article)">
<img class="imatgeAllargada d-block w-100" src="https://drive.google.com/uc?id={{article.img}}">
<br>
<h4>{{article.title_article}}</h4>
<p [innerHTML]="article.descripcio1"></p>
</div>

I wanna attach a tooltip to ngx-newsticker events array "for each item"

I have a news bar and I used ngx-newsticker, I need to attach a tooltip for each item in the news bar. However, the plugin takes an array of string as shown below.
I tried to loop throw the array but it doesn't work:
<div class="M_announcementDiv" *ngIf="announcementsArrInTicker && announcementsArrInTicker.length>0">
<div class="container">
<div placement="bottom" [ngbTooltip]="announcementsArrInTicker">
<ngx-newsticker title="{{'ANNOUNCEMENTS'|translate}}" [events]="announcementsArrInTicker" [interval]="interval"></ngx-newsticker>
</div>
</div>
</div>
I need to push the current item to the tooltip in this line instead of pushing
the whole array.
<div placement="bottom" [ngbTooltip]="announcementsArrInTicker">
and this is the ts function that retrieves the data:
getGeneralAnnouncements() {
// this.isAuthenticated = this.authService.isAuthenticated();
this.isAuthenticated =false;
this.newsService.searchGeneralAnnouncements(this.searchModel, this.isAuthenticated).subscribe(res => {
this.announcementArr = res;
res.announcements.map((announcement) => {
if(this.translate.currentLang=="ar"){
this.announcementsArrInTicker.push(announcement.bodyAr);
}else{
this.announcementsArrInTicker.push(announcement.bodyEn);
}
});
}, error => {
// this.businessException = errorsUtility.getBusinessException(error); //this.notificationService.showNotification(this.businessException.message,
NotificationType.Error);
})
}

How to pass my updated array into my .handelbars file

been stuck with this for a few days now.
In my application, when my listview.handelbars loads I am pulling my data from firebase. A number of objects (with data about rooms) that after this pull are being pushed in een array called allRooms[].
After that, the update compile passes this array on to my listview.handelbars file where everything gets put properly inside the html and handlebars tags. This is al working very fine and gives me the proper outcome in browser.
But then I need to be able to sort this objects by there properties -distance.
So I made a function to do that and i store the sorted objects in the array sortedRooms[];
So far so good. My question now is how do I pass this new sorted array on to my .handlebars file?
I tried to execute an update compile again inside my function sortRooms() and this worked but then i'm getting other problems (not able to go to detailview anymore for one).
Also important is that this sorted objects must only be shown by a button press. So at first i want to output the objects in the order they where added to firebase.
Here is my code
// JAVASCRIPT
ref.on("value", function (data) {
let rooms = data.val();
if (rooms === undefined || rooms === null) {
console.log('geen data');
} else {
let keys = Object.keys(rooms);
roomKeys.push(keys);
for (let i = 0; i < keys.length; i++) {
let k = keys[i];
Room = {
rentalPrice: rooms[k].rentalPrice,
warrant: rooms[k].warrant,
type: rooms[k].type,
surface: rooms[k].surface,
floors: rooms[k].floors,
numberOfPersons: rooms[k].numberOfPersons,
toilet: rooms[k].toilet,
douche: rooms[k].douche,
bath: rooms[k].bath,
kitchen: rooms[k].kitchen,
furnished: rooms[k].furnished,
address: rooms[k].address,
ownerKey: rooms[k].ownerKey,
lat: rooms[k].lat,
lon: rooms[k].lon,
image: rooms[k].image,
roomKey: keys[i],
adminName: rooms[k].adminName
}
allRooms.push(Room);
}
}
})
update(compile(studentListViewTemplate)({
allRooms,
}));
let sortedRooms = allRooms.sort((a, b) => a.distance - b.distance);
console.log(sortedRooms);
// Question ?? -> How to pass this sorted array on to my handelbars
// HANDLEBARS
{{#each allRooms }}
<div class="info-list" id="regularInfoList">
<h5 class="card-room-type-lv">{{ this.type }}</h5>
<div class="info-list-img">
<img src="{{ this.image }}" class="room-picture-list" alt="">
</div>
<div class="info-list-text">
<div class="sub-div">
<p class="card-titles-lv width"></p>
<p class="card-values-lv card-surface">{{ this.surface }}
m²</p>
</div>
<div class="sub-div">
<p class="card-titles-lv distance"></p>
<p class="card-values-lv card-distance">{{ this.distance }}
km</p>
</div>
<div class="sub-div">
<p class="card-titles-lv price"></p>
<p class="card-values-lv card-price">€ {{ this.rentalPrice
}}</p>
</div>
</div>
</div>
{{else}}
<p style="text-align:center">Nog geen koten om weer te geven</p>
{{/each}}
Recompile the template with allRooms inside the template being the sorted array. The sorting and updating should happen when the value arrives, so inside the callback:
ref.on("value", function (data) {
// no need for a for loop if you use Object.values
const allRooms = Object.values(data.val());
// sort mutates the array, it doesnt return a new one
allRooms.sort((a, b) => a.distance - b.distance);
// update afterwards.
update(compile(studentListViewTemplate)({
allRooms,
}));
});

How to use localStorage value filter in ng-repeat

I've set a value when the user click vote choice. Its working.
.then(function(response) {
localStorage.setItem('isClicked', 'Yes');
var i = +localStorage.key("nid");
var nidId = 'nid' + localStorage.length;
localStorage.setItem(nidId, vm.nid);
vm.clickedBefore = true;
})
This is my HTML scope:
<div class="card myfunc" ng-repeat="myfunc in myfuncPage.myfuncs" id="{{myfunc.nid}}" >
<div class="item item-text-wrap">
<h2 class="item-icon-left"><i class="icon ion-ios-analytics"></i>
{{myfunc.title}}</h2>
</div>
<div class="item item-text-wrap" ng-show="localstorage">
<img ng-src="{{myfunc.field_image.und[0].imgPath}}"/>
<p class="custom-class" ng-bind-html='myfunc.body.und[0].value'>
</p>
<ul ng-repeat="vote in myfunc.advmyfunc_choice">
<li ng-repeat="voteselect in vote">
<div class="row">
<button class="col button button-outline button-dark" ng-click="myfuncPage.sendNid(myfunc);myfuncPage.sendVote(voteselect);showVoted = ! showVoted" >{{voteselect.choice}}</button>
<div class="voted-screen" ng-show="showVoted">
<h3>Thanks.</h3>
</div>
</div>
</li>
</ul>
</div>
</div>
In basically, I need remember the div via localStorage and when the page refresh, hide choice divs.
ng-show="showVoted" working on click but I need on refresh.
What is the best way to do it? Thanks.
I am not sure what local storage module you are using, so I can't be specific, but I would write factory that handles the retrieval and storage of the values you need
(function () {
var voteFactory = function (localStorage) {
return {
getVote: function (key) {
var isVoted = false;
// get local storage item, set voted etc
var voted = localStorage.getItem(key);
if(voted) {
isVoted = voted;
}
return isVoted;
},
setVote: function(key, value) {
localStorage.setItem(key,value);
}
};
};
app.factory('voteFactory', ['localStorage', voteFactory]);
})();
Then within the scope controller/directive you are using to show or hide you would have a function:
$scope.showVoted = function(key) {
return voteFactory.getVote(key);
}
then ng-show="showVoted(theidentifieryouwantouse)"
It is also worthwhile to mention I would use ng-if instead of ng-show. To be more efficient you could store your votes as an array instead of individual values and do a check to see if you have retrieved all values, get from local storage if not. Then your functions would interrogate retrieved array instead of repeated calls to local storage. I would also advice maybe using a promise in the factory because retrieval from local storage could be delayed causing some ui oddities.
Hopefully this is along the lines of what you are looking for. I can elaborate if needed.

Categories

Resources