Im trying to load the data from my API to custom component using Angular2 ng Smart table plugin.
AS per their documentation (https://github.com/akveo/ng2-smart-table/blob/master/src/app/pages/examples/server/basic-example-load.component.ts)
i have my component like:
import { LocalDataSource } from 'ng2-smart-table';
import { ProductService } from '../../../services/product.service';
export class CategoryItemsComponent implements OnInit {
...
source: LocalDataSource;
constructor(private productService: ProductService,
private flashMessage: FlashMessagesService,
private router: Router,
http: Http) {
this.source = new LocalDataSource();
this.productService.getProductsOncategory(this.categoryid).subscribe((data) => {
this.source.load(data);
});
}
ProductService .ts
getProductsOncategory(category_id) {
let catUrl = "http://localhost:5000/products/getProductsOncategory"
let headers = new Headers();
headers.append('Content-Type', 'application/json');
let catIdObj = JSON.stringify({ category_id: category_id })
return this.http.post(catUrl, catIdObj, { headers: headers })
.map((response: Response) => response.json())
.do(data => console.log(JSON.stringify(data)))
.catch(this.handleError);
}
The above API used in the service function works perfect in my postman.
Now i need to load the dame data from that API into my custom component.
I am getting this error:
ERROR TypeError: this.data.slice is not a function
at LocalDataSource.webpackJsonp.../../../../ng2-smart-table/lib/data-source/local/local.data-source.js.LocalDataSource.getElements (http://localhost:4200/1.chunk.js:22280:30)
at LocalDataSource.webpackJsonp.../../../../ng2-smart-table/lib/data-source/data-source.js.DataSource.emitOnChanged (http://localhost:4200/1.chunk.js:22185:14)
at LocalDataSource.webpackJsonp.../../../../ng2-smart-table/lib/data-source/data-source.js.DataSource.load (http://localhost:4200/1.chunk.js:22105:14)
at LocalDataSource.webpackJsonp.../../../../ng2-smart-table/lib/data-source/local/local.data-source.js.LocalDataSource.load (http://localhost:4200/1.chunk.js:22243:38)
Ok i got it by using like:
source: LocalDataSource;
constructor(private productService: ProductService,
private flashMessage: FlashMessagesService,
private router: Router,
http: Http)
{
this.source = new LocalDataSource();
}
onChange(categoryid) {
this.productService.getProductsOncategory(categoryid).subscribe(data => {
if (data.success) {
this.source.load(data.products);
console.log('Products obtained');
} else {
console.log('Not obtained!');
}
});
}
Had the same problem. It solved when i check all match columns and add the missing in table data. For example i delared a variable
Settings = {
....
columns: {
id: {
title: 'id',
show: false,
type: 'string',
},
name: {
title: 'name',
type: 'string',
},
//compare columns in json response in same variable declaration
//for your data table [source]
}
}
And in second case your table try get data from dataTableSource before full data loading, to avoid this use setTimeout(); method and set more time.
For Example:
getChildData(): Promise<any> {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(this.childsList);
}, 4000);//<= increase this value
});
}
excuse me for my english))
Related
I’m having a problem.
i have list of project i want when i click in one project he take me to another page(components) this is the component ProjectDetail when i can find the detail of that project Inside of it, I have this block of code:
this is the route who take me to another components:
{path: '/detail/:id', name: detail , component: detail },
I want to get the project with the id in the url detail from DB w but nothing Happen this is ProjectDetail.vue:
export default {
data(){
return{
id: this.$route.params.id,
projets:[],
projet:{
id:'',
name:'',
durre:'',
description:'',
budget:'',
owner:'',
}
}
},
methods:{
afficherProjets(){
axios.get('api/getProjects/'+this.id)
.then(data => {
this.projets = data.data;
});
}
},
mounted() {
console.log('Component mounted.')
this.afficherProjets();
}
}
and this is my controller:
public function getProjects($id)
{
return Projet::findOrFail($id);
}
in your vue.
export default {
data(){
return{
id: this.$route.params.id,
projet:{}
}
},
methods:{
//GETID
afficherProjet(){
axios.get('api/getProject/'+this.id)
.then(data => {
this.projet = data.data;
});
}
},
mounted() {
console.log('Component mounted.')
this.afficherProjet();
}
File in routes api.php
//ALL
Route::get('getProjects', ['uses' =>'API\ProjetController#getProjects']);
//SPECIFIQ ID
Route::get('getProject/{id}', ['uses' =>'API\ProjetController#getProject']);
In your controller, app/Http/API/ProjetController
public function getProjects()
{
return Projet::latest()->paginate(15);
}
public function getProject($id)
{
return Projet::findOrFail($id);
}
Your model projet
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Projet extends Model
{
public $timestamps = false;
protected $table = 'projets';
protected $fillable = [
'name',
'durre',
'description',
'budget',
'owner'
];
}
Do you have your model?
Your error comes from the 'data.data' i thing it's empty, you should just call 'data'.
Personally i prefer Mounted.
I thing now it's working!
I am new to Angular. I have a Node and Express backend pulling data from an MS SQL database. If I go to the endpoint URL it displays my data as JSON. I am running on localhost so I set a proxy for CORS. I have a class that defines the data, a service that pulls the data from the endpoint and a component that tries to set an array equal to the data pulled from the service. The HTML has an *ngFor that is supposed to loop through the values and display them in a grid.
If I call my data in my component through my service, so this.userService.getUsers(), and do a console.log I can see the recordset in the browser console. I try to set the array equal to the userService.getUsers() and then call the array and I get "undefined". Being that I am new, I have tried to follow the Heroes tutorial and that did not work. I spent a day searching Google and trying different solutions that I have come across but they all come up as undefined. I will attach the code here. If someone can guide me a bit, it would be much appreciated.
User class defining User:
export class User{
id: number;
ccn: string;
firstName: string;
lastName: string;
email: string;
}
User Service doing Http request:
import { Injectable } from '#angular/core';
import { User } from './user';
import { USERS } from './mock-users';
import { MessageService } from './message.service';
import { Observable, of } from 'rxjs';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { catchError, map, tap } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class UserService {
private userURL = 'api/users'
//private userURL = 'localhost:5000'
httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
constructor(
private http: HttpClient,
private messageService: MessageService) { }
//getUsers(): Observable<User[]> {
// this.messageService.add('UserService: fetched users');
// return of(USERS);
//}
/** GET users from the server */
getUsers(): Observable<User[]> {
//console.log('getting users');
return this.http.get<User[]>("http://localhost:5000/api/user")
.pipe(
tap(_ => this.log('Fetched users')),
catchError(this.handleError<User[]>('getUsers', []))
);
//return this.http.get<User[]>("http://localhost:5000/api/user");
//console.log('got users');
}
/* GET heroes whose name contains search term */
searchUsers(term: string): Observable<User[]> {
if (!term.trim()) {
// if not search term, return empty hero array.
return of([]);
}
return this.http.get<User[]>(`${this.userURL}/?ccn=${term}`).pipe(
tap(_ => this.log(`found users matching "${term}"`)),
catchError(this.handleError<User[]>('searchUsers', []))
);
}
addUser (user: User): Observable<User> {
return this.http.post<User>(this.userURL, user, this.httpOptions).pipe(
tap((newUser: User) => this.log(`added user w/ id=${newUser.id}`)),
catchError(this.handleError<User>('addUser'))
);
}
private handleError<T> (operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
console.error(error);
this.log(`${operation} failed: ${error.message}`);
return of(result as T);
};
}
private log(message: string) {
this.messageService.add(`User service: ${message}`);
}
}
Display Users Component TS file:
import { Component, OnInit } from '#angular/core';
//import { USERS } from '../mock-users';
import { UserService } from '../user.service';
import { User } from '../user';
import { Observable, of } from 'rxjs';
import { catchError, map, tap } from 'rxjs/operators';
import { element } from 'protractor';
#Component({
selector: 'app-display-users',
templateUrl: './display-users.component.html',
styleUrls: ['./display-users.component.css']
})
export class DisplayUsersComponent implements OnInit {
users: User[] = [];
constructor(private userService: UserService) { }
//users$ = this.getUsers();
ngOnInit() {
this.getUsers();
console.log(this.userService.getUsers());
this.userService.getUsers().forEach(element => {
console.log(element);
});
}
getUsers(): void {
/*this.userService.getUsers()
.subscribe(users => this.users = users);*/
const userObservable = this.userService.getUsers();
userObservable.subscribe((userData: User[]) => {
this.users = userData;
});
}
}
Display Users Component HTML:
<div class="clr-row">
<div class="clr-col-lg-11 clr-col-md-11 clr-col-11 main-div">
<div class="card card-style" style="box-shadow: 0 0 0 0;">
<div class="card-header">
<h1><img src="../assets/images/BSOLOGO_gray.png" class="title-img"><span class="title"> Users</span></h1>
</div>
<div class="card-block">
<div class="card-title">
<clr-datagrid>
<clr-dg-column>CCN</clr-dg-column>
<clr-dg-column>Last Name</clr-dg-column>
<clr-dg-column>First Name</clr-dg-column>
<clr-dg-column>Email</clr-dg-column>
<clr-dg-row *ngFor="let user of users">
<clr-dg-cell>{{user.ccn}}</clr-dg-cell>
<clr-dg-cell>{{user.lastName}}</clr-dg-cell>
<clr-dg-cell>{{user.firstName}}</clr-dg-cell>
<clr-dg-cell>{{user.email}}</clr-dg-cell>
</clr-dg-row>
<clr-dg-footer>{{users.length}} users</clr-dg-footer>
</clr-datagrid>
</div>
</div>
</div>
</div>
</div>
Any help would be greatly appreciated!
UPDATED
Ypu can replace getUsers on both classes by these. HTML looks fine to me. I converted users to public too.
//userService
getUsers(callback: Function) {
return this.http.get<User[]>("http://localhost:5000/api/user")
.subscribe(
response => callback(response)
);
}
//Component
public users: User[] = [];
getUsers(): void {
this.userService.getUsers((result) => {this.users = result;})
}
If you do not need it to be Observable you can use toPromise() and using async/await makes it waaay easier
Service
async getUsers(): Promise<User[]> {
return await this.http.get<User[]>('http://localhost:5000/api/user').toPromise();
}
Component.ts
users: User[] = [];
async ngOnInit() {
this.users = await this.userService.getUsers();
}
Component.html
<clr-datagrid *ngIf="users">
<clr-dg-column>CCN</clr-dg-column>
<clr-dg-column>Last Name</clr-dg-column>
<clr-dg-column>First Name</clr-dg-column>
<clr-dg-column>Email</clr-dg-column>
<clr-dg-row *ngFor="let user of users">
<clr-dg-cell>{{user.ccn}}</clr-dg-cell>
<clr-dg-cell>{{user.lastName}}</clr-dg-cell>
<clr-dg-cell>{{user.firstName}}</clr-dg-cell>
<clr-dg-cell>{{user.email}}</clr-dg-cell>
</clr-dg-row>
<clr-dg-footer>{{users.length}} users</clr-dg-footer>
</clr-datagrid>
My issue has been resolved. In my SQL statement I was calling SELECT * FROM table FOR JSON PATH which was creating a weird object being pulled from the server. Removing the FOR JSON PATH provided JSON data. Then the second part of my issue was mapping my DB fields with my user class.
This was done like this:
request.query('SELECT * FROM Table ORDER BY myField', function (err, recordset) {
if (err) console.log(err);
const records = recordset.recordset;
const result = records.map(r => { return { id: r.tableID, field1: r.dbField1, field2: r.dbField2, field3: r.dbField3, field4: r.dbField4}});
res.send(result);
});
I hope this helps someone! Thanks to everyone that posted to help me.
I just have two components, and the app is connected to firebase (firestore). The data called in ngOnInit disappears when I navigate and only appears again if I insert a Post or a User (forms).Why? Can someone help me out?
The routes:
const routes: Routes = [
{ path: "", component: HomeComponent, pathMatch: "full" },
{
path: "admin",
component: AdminComponent
},
{
path: "**",
redirectTo: "/",
pathMatch: "full"
}
];
The Service
itemsCollection: AngularFirestoreCollection<Post>;
items: Observable<Post[]>;
itemDoc: AngularFirestoreDocument<Post>;
constructor(public afs: AngularFirestore) {
this.itemsCollection = this.afs.collection('posts', ref =>
ref.orderBy('date', 'desc')
);
this.items = this.itemsCollection.snapshotChanges().map(changes => {
return changes.map(a => {
const data = a.payload.doc.data() as Post;
data.id = a.payload.doc.id;
return data;
});
});
}
getItems() {
return this.items;
}
Its Component and the subscription
private postService: PostsService,
) {}
ngOnInit() {
this.postService.getItems().subscribe(data => {
this.postInput = data;
});
}
Create posts property in your service.
Then subscribe to your observable in your service, set posts to what u get from firebase.
Also dont write your code inside constructor but wrap in in a function and use it in OnInit in apropriate component. And lastly create a Subject send with it the current posts and subscribe to them in onInit inside desired components
posts = Post[]
postsChanged = new Subject<Post[]>()
getItems(){
this.itemsCollection.snapshotChanges().map(changes => {
return changes.map(a => {
return {
id: a.payload.doc.id,
...a.payload.doc.data()
}
}).subscribe(posts => {
this.posts = posts
this.postsChanged.next([...this.posts])
})
}
Based on Kase44 hints i managed to find a solution: wrapped the capture of the ID in a function, called it in the service constructor and in the ngOnInit():
The Service
export class UsersService {
usersCollection: AngularFirestoreCollection<Users>;
userDoc: AngularFirestoreDocument<Users>;
users: Observable<Users[]>;
user: Observable<Users>;
constructor(public afs: AngularFirestore) {
this.usersCollection = this.afs.collection('employees', ref =>
ref.orderBy('name', 'asc')
);
this.getUserID();
}
getUserID(){
this.users = this.usersCollection.snapshotChanges().map(changes => {
return changes.map(a => {
const data = a.payload.doc.data() as Users;
data.id = a.payload.doc.id;
return data;
});
});
}
And the component:
ngOnInit() {
this.userService.getUsers().subscribe(data => {
this.emp = data;
});
this.userService.getUserID();
}
I find interesting example how transfer data from one component to another component using Subject/Observable in Service:
http://plnkr.co/edit/yMBoVkxohwhPig5COgkU?p=preview
I find it similar to broadcast event from Angular 1.X. But I notice that subscribe to data changes can only component that inishiated data change. I need somehow modify this example to get data in one component after another component changed it. Console.log dos't show any error, but data-transfer not working. Can anyone help?
Service:
#Injectable()
export class ClientsService {
private _client$: Subject<Client>;
private _clients$: Subject<Client[]>;
constructor(private http: Http) {
this._client$ = <Subject<Client>>new Subject();
this._clients$ = <Subject<Client[]>>new Subject();
}
getClients(): Promise<Client[]> {
return this.http.get('api/Client')
.toPromise()
.then(res => {
this._clients$.next(res.json());
return res.json();
})
.catch(this.handleError);
}
get client$() {
return this._client$.asObservable();
}
get clients$() {
return this._clients$.asObservable();
}
clientChangeBroadcast(objClient: Client) {
this._client$.next(objClient);
}
clientsChangeBroadcast(arrClients: Client[]) {
this._clients$.next(arrClients);
}
private handleError(error: any): Promise<any> {
return Promise.reject(error.message || error);
}
First component:
#Component({
selector: 'app',
templateUrl: './app/app.component.html'
})
export class AppComponent implements OnInit {
clients: Client[];
constructor(public clientsService: ClientsService) {
this.clients = [];
}
ngOnInit() {
}
loadClients() {
this.clientsService.getClients().then(clients => {
this.clients = clients;
this.clientsService.clientsChangeBroadcast(clients);
});
}
selectClient(objClient: Client) {
this.clientsService.getClients();
this.clientsService.clientChangeBroadcast(objClient);
}
}
Second component:
export class TestComponent implements OnInit {
client: Observable<Client>;
clients: Observable<Client[]>;
constructor(private clinetsService: ClientsService) {
}
ngOnInit() {
//this.clinetsService.getClients();
this.client = this.clinetsService.client$;
this.clients = this.clinetsService.clients$;
this.clinetsService.client$.subscribe(value => {
// try to get changes here
});
this.clinetsService.clients$.subscribe(value => {
// try to get changes here
});
}
reLoadClients() {
this.clinetsService.getClients();
}
}
I try to get data changinf in 'Second component' after data was changed by 'First component'. When I click on button for 'selectClient' method in 'First component' I expect data transfer wia service and I catch changes ob 'Second component' -> it not working. When from 'Second component' I press button for 'reLoadClients' data transfer wia service and I catch changes in all subscribe functions on 'Second component'.
I can't quite tell if this is a scoping issue or an issue with how I've set up my observables, but maybe you can help.
Firstly, my logic. My overall aim is to be able to check if some data is held locally before sending off a http.get request for it. What I'd like to do is return the locally held data back if it exists, and I thought that I could make this return an observable so that I could subscribe to the output of this function regardless of if it returned local data or remote data. Any thoughts on this logic would be appreciated!
I am following this blog post by Cory Rylan to implement my own observable. I am running into a problem where I cannot get my Observer to be defined in my getData function.
Here is Cory's working demo
Here is my not-working demo - You can see in the log that this._dataObserver in the service is undefined, whereas in Cory's it is. This is what is causing me issues.
Here are snippets from the code:
App.ts
#Component({
selector: 'my-app',
template: `
<landing-page></landing-page>
`,
providers: [DataService],
directives: [LandingPageComponent]
})
export class App {
constructor() { }
}
bootstrap(App, [HTTP_BINDINGS])
.catch(err => console.error(err));
LandingPageComponent.ts
#Component({
selector: 'landing-page',
template : `
hello
`,
})
export class LandingPageComponent implements OnInit{
data : Observable<any[]>;
constructor(
private _dataService : DataService
) { }
ngOnInit() {
this.data = this._dataService.data$;
this._dataService.getData();
}
}
DataService
#Injectable()
export class DataService {
data$ : Observable<any[]>; // data stream
private _baseUrl: string;
private _dataObserver : Observer<any[]>;
private _dataStore : {
data : any[]
};
constructor (
private _http: Http
) {
this._baseUrl = 'http://56e05c3213da80110013eba3.mockapi.io/api';
this.data$ = new Observable(observer => this._todosObserver = observer).share();
this._dataStore = { data: [] };
}
/** **************
* Public functions
*****************/
getData () {
this._http.get(`${this._baseUrl}/todos`)
.map(data => data.json())
.subscribe( data => {
this._dataStore.data = data;
console.log(this._dataObserver); //<------ This is undefined for me!
this._dataObserver.next(this.data);
},
error => {
console.log('couldn\'t load data! ' + error );
});
}
}
Thank you for any thoughts
_dataObserver is null because you don't subscribe on the associated observable. Don't forget that observables are lazy...
You could try the following:
ngOnInit() {
this.data = this._dataService.data$.subscribe(data => { // <-----
console.log(data);
});
this._dataService.getData();
}
Edit
In addition, there is a typo in your code:
this.data$ = new Observable(observer => this._todosObserver = observer).share();
You should use _dataObserver instead of _todosObserver:
this.data$ = new Observable(observer => this._dataObserver = observer).share();