Content not being displayed : Angular 2 - javascript

I'm working on a feature for my website to provide news feed. To get news feed, I have generated an API key from here : News API. The problem I'm facing is that, I'm not able to display the content in my browser. Let me share my code :
results.component.html
<ul type="none" id="search-options">
<li [class.active_view]="Display('all')" (click)="docClick()">All</li>
<li [class.active_view]="Display('news')" (click)="newsContent()">News</li>
<li [class.active_view]="Display('images')" (click)="imageClick()">Images</li>
<li [class.active_view]="Display('videos')" (click)="videoClick()">Videos</li>
</ul>
In results.component.html, it has 4 tabs. In tabs with name : All, Image, Video - I'm getting data from the server from which the desired results are fetched based on query. But in News tab, I'm trying to integrate it with the API which I have mentioned above. Clicking on that tab, should show news feed (just keeping it simple). But, it does not displays anything when news tab is clicked. (No console errors) However, if I use html repeater, how I'm going to use it here ?
news.component.ts
newsFeed: {};
resultDisplay: string;
constructor(private _newsService: NewsService) {}
Display(S) {
return (this.resultDisplay === S);
}
newsContent() {
this.resultDisplay = 'news';
this._newsService.getNews().subscribe(data => {
this.newsFeed = Object.assign({}, data);
});
}
news.service.ts
import { Injectable } from '#angular/core';
import { Http, Jsonp } from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class NewsService {
public generalNews: string = 'https://newsapi.org/v1/articles?source=bbc-news&sortBy=top&apiKey=abc123';
constructor(
private http: Http,
private jsonp: Jsonp
) { }
getNews() {
return this.http.get(this.generalNews).map(response => {
response.json()
});
}
}
It would be great if anybody can point out what mistake I'm doing and provide me the solution to improve it. Thanks in advance. :)

Based on what I got from your question. You should do the following.
Have a repeater on your html first. Something like:
<div *ngFor = "let news of newsList">
<p> {{news}} </p>
</div>
This way you can iterate the news array on your html one news at a time.
Next thing, is getting the response and passing it to the view. I am assuming you get the news on click of <li>, as that is all your view presently contains. So in your component you should have
private newsList: any[] = [] //initializing a blank list of type any.
newsContent() {
this.resultDisplay = 'news'; //not sure why you need this, but let it be for now
this._newsService.getNews().subscribe(data => {
this.newsList = data; //assuming data is the actual news array, if the data is entire response you might have to go for data.data
});
}
This way your newsList variable is populated and will be iterated on the html. May be you might have to make few adjustment but it should help you start.
Let me know if it helps or any further issue faced.
Few more changes would be required based on your response:
First : return data from your service method like:
getNews() {
return this.http.get(this.generalNews).map(response:any => {
return response.json()
});
}
Second, your data contains news in article array. So use that instead:
newsContent() {
this.resultDisplay = 'news'; //not sure why you need this, but let it be for now
this._newsService.getNews().subscribe(data => {
this.newsList = data.article; //assuming data is the actual news array, if the data is entire response you might have to go for data.data
});
}
Next edit your html to bind a particular field of your news. I am binding title you can bind one or more that you like:
<div *ngFor = "let news of newsList">
<p> {{news.title}} </p>
</div>
Try this. should work now.

You have missed return statement return response.json().
news.service.ts
import { Injectable } from '#angular/core';
import { Http, Jsonp } from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class NewsService {
public generalNews: string = 'https://newsapi.org/v1/articles?source=bbc-news&sortBy=top&apiKey=abc123';
constructor(
private http: Http,
private jsonp: Jsonp
) { }
getNews() {
return this.http.get(this.generalNews).map(response => {
return response.json();
});
}
}

Related

Why shared data of Angular Service is not being updated in the html?

I'm using a service to share an array between 02 unrelated components, "home" and "results". In my interaction.service.ts I have this code:
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class InteractionService {
private _teacherMessageSource = new Subject<any>();
teacherMessage$ = this._teacherMessageSource.asObservable();
constructor() { }
sendMessage(message: any){
this._teacherMessageSource.next(message)
}
}
"message" is an array of floats but I just set it to any. When i get a response from the backend in the "home" component, I just pass it to the sendMessage() function of the Servic:
this.http.post<any>("http://127.0.0.1:5000/", formData).subscribe(
(response)=> {
console.log(response) // array of floats
this._interationService.sendMessage(response);
},
(error)=> console.log(error)
)
Then in the result component I get the message and I can print it on the console, but when I affect it to another array "selectedMessage" in order to print it in the HTML, it always shows null:
public selectedMessage=[];
constructor(public _interactionService: InteractionService, private http: HttpClient) { }
ngOnInit(): void {
this.getSpecialities();
this._interactionService.teacherMessage$
.subscribe(
message => {
alert(message); // message is an array of floats and it shows normally with alert(message)
this.selectedMessage = message;
//console.log(this.selectedMessage);
}
);
}
My html is:
<ul>
<li><strong> SSI </strong>: {{selectedMessage[0]}}</li>
<li> <strong>RSD</strong>:{{selectedMessage[1]}} </li>
<li><strong>IL</strong>: {{selectedMessage[2]}}</li>
<li><strong>MIV</strong>: {{selectedMessage[3]}}</li>
<li><strong>SSI</strong>: {{selectedMessage[4]}}</li>
<li><strong>BIGDATAA</strong>: {{selectedMessage[5]}}</li>
<li><strong>BIOINFO</strong>: {{selectedMessage[6]}} </li>
</ul>
but nothing shows up in the page:
What could be the problem please?
The problem is your html is rendering sync data, while in component you provide async data (even in ngOnInit).
Easy workaround would be put *ngIf in your list and better declare selectedMessage as proper array, but not assigning it as empty array:
selectedMessage: selectedMessage[];
<ul *ngIf=selectedMessage>
<li><strong> SSI </strong>: {{selectedMessage[0]}}</li>
<li> <strong>RSD</strong>:{{selectedMessage[1]}} </li>
<li><strong>IL</strong>: {{selectedMessage[2]}}</li>
<li><strong>MIV</strong>: {{selectedMessage[3]}}</li>
<li><strong>SSI</strong>: {{selectedMessage[4]}}</li>
<li><strong>BIGDATAA</strong>: {{selectedMessage[5]}}</li>
<li><strong>BIOINFO</strong>: {{selectedMessage[6]}} </li>
</ul>
You're trying to access an async data that may not be there. The straight forward solution would be the async pipe.
<ul>
<li *ngFor="let msg of (interactionService.teacherMessage$ | async)">
<strong> Something </strong>: {{msg}}
</li>
</ul>
This avoids access to empty array
Link to a sample code on stackblitz: https://stackblitz.com/edit/angular-12ziph?file=src/app/app.component.html

Refresh List View items when an item added to the Database in Angular+Nativescript

I have an app that saves items in an sqlite database and shows them in a list view.
It loads the listview with the correct data at the beginning of the app, but it doesn't refreshes the listview when I add a new item to the database.
This is the component.ts where I load the items to the observable
export class HomeComponent implements OnInit {
items: ObservableArray<IDataItem>;
constructor(public _itemService: DataService) {
}
ngOnInit(): void {
this.items = this._itemService.selectItems();
}
}
This is the DataService:
export class DataService {
private items = new ObservableArray<IDataItem>();
private database = new DatabaseService();
private db: any;
selectItems(): ObservableArray<IDataItem> {
this.database.getdbConnection()
.then(db => {
db.all("SELECT * FROM items").then(rows => {
for (let row in rows) {
this.items.push({ id: rows[row][0], sitioWeb: rows[row][1], usuario: rows[row][2], password: rows[row][3] });
}
this.db = db;
}, error => {
console.log("SELECT ERROR", error);
});
});
for(let i = 0; i < this.items.length; i++){
Toast.makeText(""+this.items[i].id+" "+this.items[i].sitioWeb+" "+this.items[i].usuario+" "+this.items[i].password,"10")
}
return this.items;
}
getItems(): ObservableArray<IDataItem> {
return this.items;
}
getItem(id: number): IDataItem {
return this.items.filter((item) => item.id === id)[0];
}
}
This is the view where the listview is located at:
<ActionBar class="action-bar">
<Label class="action-bar-title" text="Home"></Label>
</ActionBar>
<GridLayout class="page page-content" >
<ListView [items]="items | async" class="list-group" >
<ng-template let-item="item">
<Label [nsRouterLink]="['../item', item.id]" [text]="item.sitioWeb" class="list-group-item"></Label>
</ng-template>
</ListView>
</GridLayout>
It only loads items to the list view on the init of the app, but I want to refresh it when an item is added.
A simple solution would be to either fire a new request to get the list after you add a new item, or just add it in memory on the front end if you get a success response from the save request. However, while this would work, it's not a great solution. It wouldn't update the list when another user added an item.
I think you should look into using websockets for this. You can have a socket open listening for messages on the front end. The back end would emit a message every time something was added, even if it was added by another user. The front end listener would add that item to the list.
Here is a good tutorial using Sock.js and STOMP to implement websockets in angular.
https://g00glen00b.be/websockets-angular/
You can create sharer service with list of your item like this:
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs';
#Injectable()
export class DataService {
private messageSource = new BehaviorSubject('default message');
currentMessage = this.messageSource.asObservable();
constructor() { }
changeMessage(message: string) {
this.messageSource.next(message)
}
}
Where currentMessage is your list of items. Bind that list to you html and in callback function on create new item call method changeMessage witch will be add new created item in your array.
See this example
And not forget subscribe on changes in your list component.
Once you add a new item to the database you can call selectItems() service which will update the item list whenever new item is added or you can call selectItems() in ngAfterViewChecked() hook as it is executed every time the view of the given component has been checked by the change detection algorithm of Angular. This method executes after every subsequent execution of the ngAfterContentChecked()

Angular 4 update view after deleting from http server

This is probably asked, but I am looking for a most efficient way to update view in a component which fetches data from server, taking that I delete an item from the server in other component
service
getAllMessages(): Observable<any> {
return this.http.get('/api/messages/);
}
applyTagToMessage(messageId): Observable<any> {
return this.http.delete('/api/messages/' + messageId)
}
componentA
this.messageService.getAllMessages().subscribe(data = > {
this.messages = data;
})
<div *ngFor="let message of messages">{{message.tags}}</div>
componentB
addTagToMessage(messsageId)
this.messageService.applyTagToMessage(messageId).subscribe(data = > {
alert("success added tag to message")
})
}
<button (click)="addTagToMessage(messageId)"></button>
So the question how to update the componentA which fetches the data from server when I delete the item from server? Should I use async pipe?
You can use an EventEmitter in your service whenever you change/reload your data. Then subscribe to that event in component A.
Handle the deletion/adding of the items in component B through your service.
https://angular.io/api/core/EventEmitter
In my template I do use an async pipe wherever I'm expecting data to change.
<div *ngFor="let exp of model$ | async">
Where model$ is defined as an observable:
private model$: Observable<Experiment[]>;

Refreshing data through constructor in Angular 2 / Ionic 2

I have Ionic 2 app with one view for 3 different data sets. Data are loaded in constructor and based on variable in page params, it's decided which data set to show.
At every successful data call by observable, event handler logs success when data are loaded. But this only works when I click/load view for a first time. If I click for 2nd or any other time, data are not re-loaded (no log). Also, when I just console log anything, it won't show at 2nd+ click.
So I wonder what should I change to load data everytime and how constructor works in this manner.
This is how my code looks like. Jsons are called from namesListProvider.
#Component({
templateUrl: '...',
})
export class ListOfNames {
...
private dataListAll: Array<any> = [];
private dataListFavourites: Array<any> = [];
private dataListDisliked: Array<any> = [];
constructor(private nav: NavController, ...) {
...
this.loadJsons();
console.log('whatever');
}
loadJsons(){
this.namesListProvider.getJsons()
.subscribe(
(data:any) => {
this.dataListFavourites = data[0],
this.dataListDisliked = data[1],
this.dataListAll = data[2]
if (this.actualList === 'mainList') {
this.listOfNames = this.dataListAll;
this.swipeLeftList = this.dataListDisliked;
this.swipeRightList = this.dataListFavourites;
}
else if (...) {
...
}
this.listSearchResults = this.listOfNames;
}, err => console.log('hey, error when loading names list - ' + err),
() => console.info('loading Jsons complete')
)
}
What you're looking for are the Lifecycle events from Ionic2 pages. So instead of using ngOnInit you can use some of the events that Ionic2 exposes:
Page Event Description
---------- -----------
ionViewLoaded Runs when the page has loaded. This event only happens once per page being created and added to the DOM. If a page leaves but is cached, then this event will not fire again on a subsequent viewing. The ionViewLoaded event is good place to put your setup code for the page.
ionViewWillEnter Runs when the page is about to enter and become the active page.
ionViewDidEnter Runs when the page has fully entered and is now the active page. This event will fire, whether it was the first load or a cached page.
ionViewWillLeave Runs when the page is about to leave and no longer be the active page.
ionViewDidLeave Runs when the page has finished leaving and is no longer the active page.
ionViewWillUnload Runs when the page is about to be destroyed and have its elements removed.
ionViewDidUnload Runs after the page has been destroyed and its elements have been removed.
In your case, you can use the ionViewWillEnter page event like this:
ionViewWillEnter {
// This will be executed every time the page is shown ...
this.loadJsons();
// ...
}
EDIT
If you're going to obtain the data to show in that page asynchronously, since you don't know how long would it take until the data is ready, I'd recommend you to use a loading popup so the user can we aware of something happening in the background (instead of showing a blank page for a few seconds until the data is loaded). You can easily add that behaviour to your code like this:
// Import the LoadingController
import { LoadingController, ...} from 'ionic/angular';
#Component({
templateUrl: '...',
})
export class ListOfNames {
...
private dataListAll: Array<any> = [];
private dataListFavourites: Array<any> = [];
private dataListDisliked: Array<any> = [];
// Create a property to be able to create it and dismiss it from different methods of the class
private loading: any;
constructor(private loadingCtrl: LoadingController, private nav: NavController, ...) {
...
this.loadJsons();
console.log('whatever');
}
ionViewWillEnter {
// This will be executed every time the page is shown ...
// Create the loading popup
this.loading = this.loadingCtrl.create({
content: 'Loading...'
});
// Show the popup
this.loading.present();
// Get the data
this.loadJsons();
// ...
}
loadJsons(){
this.namesListProvider.getJsons()
.subscribe(
(data:any) => {
this.dataListFavourites = data[0],
this.dataListDisliked = data[1],
this.dataListAll = data[2]
if (this.actualList === 'mainList') {
this.listOfNames = this.dataListAll;
this.swipeLeftList = this.dataListDisliked;
this.swipeRightList = this.dataListFavourites;
}
else if (...) {
...
}
this.listSearchResults = this.listOfNames;
}, err => console.log('hey, error when loading names list - ' + err),
() => {
// Dismiss the popup because data is ready
this.loading.dismiss();
console.info('loading Jsons complete')}
)
}
The solution is don't do this in the constructor, use ngOnInit() instead. Components are created only once, therefore the constructor will only be called when first created.
Your component class must implement the OnInit interface:
import { Component, OnInit } from '#angular/core';
#Component({
templateUrl: '...',
})
export class ListOfNames implements OnInit {
constructor(...)
ngOnInit() {
this.loadJsons();
}
private loadJsons() {
...
}
}
i'm coming from Angular 2 world, not ionic, but angular 2 has the option to register callbacks on init/destory (ngInit/ngDestory).
try to move initialization to ngInit, save subscription handler, and don't forget to unsubscribe it on destory.
i think your issue related to that you are not unsubscribing.. :\

How to reload the ion-page after pop() in ionic2

I have 2 pages Page1 and Page2. I have used this.nav.pop() in Page2 and it will pop the Page2 and Page1 will enable but i want to refresh the Page1.
Thank you in advance.
you could pass the parent page along with the nav push. that way you could accces the parent page as a navParamter.
in parent page:
goToChildPage() {
this.navCtrl.push(ChildPage, { "parentPage": this });
}
and in the child page before pop you could call functions on parent page
this.navParams.get("parentPage").someFnToUpdateParent();
//or
this.navParams.get("parentPage").someFnToRefreshParent();
Ignore the direct angular implementations suggested here, especially since you are using Ionic 2 and the suggestions are assuming Ionic 1. Don't start mixing too much of direct angular in your ionic app unless there is no ionic implementation for what you need. Import "Events" from ionic/angular2 in both Page1 and Page2, then in Page2 do something like
this.events.publish('reloadPage1');
this.nav.pop();
And in Page1 put
this.events.subscribe('reloadPage1',() => {
this.nav.pop();
this.nav.push(Page1);
});
You may want to implement one of these in your page:
ionViewWillEnter
ionViewDidEnter
Please review the navController and page lifecycle documentation:
http://ionicframework.com/docs/v2/api/components/nav/NavController/
Simple solution that worked for me was calling the get service method again in ionViewDidEnter
ionViewDidEnter() {
this.loadGetService();
}
On PAGE 1:
import { Events } from 'ionic-angular'
constructor(public events:Events){
this.listenEvents();
}
... ...
listenEvents(){
this.events.subscribe('reloadDetails',() => {
//call methods to refresh content
});
}
On PAGE 2:
import { Events } from 'ionic-angular'
constructor(public events:Events, public navCtrl:NavController){
}
function(){
this.events.publish('reloadDetails');
this.navCtrl.pop();
}
You may consider send an event before call this.nav.pop to let page 1 reload itself.
Like Jonathan said, you can import Events from ionic-angular, but you don't need push and pop again, call your methods to reload only the content.
In page2:
this.events.publish('reloadDetails');
this.navCtrl.pop();
In page1:
this.events.subscribe('reloadDetails',() => {
//call methods to refresh content
});
That works for me.
I simply load the details in page 1 in an ionViewWillEnter function (using Ionic 2). This handles both the initial load and any refresh when popping back to page 1.
Documentation is here.
ionViewWillEnter
"Runs when the page is about to enter and become the active page."
I found this technique to reload a page:
this.navCtrl.insert(1, MyPage);
this.navCtrl.pop();
I had the same problem and spend many hours searching and trying the solution.
If I understand, your problem is:
Page 1 have some bindings that you get from an API / Webservice.
Page 2 have some inputs and when pushing the back button (pop) you want to SAVE data + refresh the Page 1 bindings.
The way I solved it has been reading a post on StackOverflow that now I can't find :( !!
The solution is using an Injectable Service.
PAGE 1:
/* IMPORTS */
import { App, Nav, NavParams } from 'ionic-angular';
import { Oportunidades } from '../../services/oportunidades.service';
/* SOME BINDINGS HERE */
{{oportunidades.mesActual.num_testdrive}}
/* CONSTRUCTOR */
constructor(
private oportunidades: Oportunidades, // my injectable service!
public app: App,
public nav: Nav,
public params: NavParams
) {
// Call to the Injectable Service (named oportunidades):
this.oportunidades.getOportunidades();
}
INJECTABLE SERVICE:
/* IMPORTS */
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Injectable()
export class Oportunidades {
public url = 'http://your-API.URL';
public data: Observable<Object>;
public mesActual: Object = [];
constructor(private http: Http){
//GET API DATA
this.data = http.get(this.url).map(res => res.json());
}
getOportunidades() {
this.data.subscribe(data => {
this.mesActual = new MesActual(
data["mes_actual_slide"].num_testdrive,
...
//Here I get the API data and set it on my injectable object
);
});
}
}
PAGE 2:
/* SOME IMPORTS */
import { NavController } from 'ionic-angular';
import { UserData } from '../../services/data.service';
import { Oportunidades } from '../../services/oportunidades.service';
import { Http, Headers, URLSearchParams } from '#angular/http';
/* SOME example BINDINGS and INPUTS: */
#Component({
template: `
{{ day[selectedDay].dia }}
Input data:
<ion-input type="number" clearOnEdit="true"
#ventas id="ventas" value={{day[selectedDay].ventas}}
(keyup)="setVal(ventas.value, $event)">
</ion-input>
`,
providers: [
]
})
export class PageInsert {
constructor(
public navCtrl: NavController,
private http: Http,
private userData: UserData,
public oportunidades: Oportunidades // my injectable service!
) {
send(selectedDay){
var url = 'http://your.api.url/senddata';
// I SAVE data TO API / Webservice
this.http.post(url, params, { headers: headers })
.map(res => res.json())
.subscribe(
data => {
console.log(data);
// Here i'll call to the Injectable service so It refresh's the new data on Page1
// in my case, this send function is called when "pop" or "back" button of ionic2 is pressed
// This means: On click on back button -> Save and refresh data of the Injectable that is binded with the Page1
this.oportunidades.getOportunidades();
return true; },
error => {
console.error("Error saving!");
}
);
}
}
I hope it can help you!! Ask for any similar problems :)
I spent a day and a half on a similar issue. The solution is anti-climatic really.
I'm passing a FormGroup from Page-1 to Page-2. I update the FormGroup values in Page-2. When I pop Page-2, Page-1's form is not updated with the new values. It hasn't been watching for changes.
To fix this, I patch the FormGroup with itself after Page-2 has been popped but still in the Page-2 component.
This is more responsive, but requires a direct call to close().
// Page-2 close method
public close(): void {
this.navCtrl.pop();
this.formGroup.patchValue(this.formGroup.value);
}
This is all encompassing, but I do see the refresh on Page-1.
// Page-2 nav controller page event method
ionViewWillUnload() {
this.formGroup.patchValue(this.formGroup.value);
}
In some situations instead of pop() you can use the push() function. When you enter the page with the push() function it is reloaded.
Also you can remove page2 from the navigation.
this.navCtrl.push(TabsPage).then(() => {
const index = this.viewCtrl.index;
this.navCtrl.remove(index);
});
Or if you have more than one page for example page1->page2->pag3:
this.navCtrl.push(TabsPage).then(() => {
const index = this.viewCtrl.index;
this.navCtrl.remove(index, 2); //this will remove page3 and page2
});
ionViewWillEnter() {
this.refresh();
}
ionViewWillEnter will be called just before any time you (re)visualize the page
Please checkout my solution, which I've posted here:
https://forum.ionicframework.com/t/ionviewdidenter-is-not-invoked-on-leaving-a-pushed-page/131144/19?u=unkn0wn0x
Maybe you can adapt it to your needs.
The main point of my intention was to prevent, passing a whole module with this as a navCtrlParam into the push()'ed page, like it was mentioned in some comments before.
Hope it helps!
Cheers
Unkn0wn0x

Categories

Resources