When we add static data in angular datatable all things work fine, but when we load the data table from an API, the pagination, search, and sorting don't work. And it show's "No data available in table", but data is present in the table, also attaching the images.
Click me to view img
Here is the HTML Code
<table datatable [dtOptions]="dtOptions" class="datatable-init nowrap nk-tb-list nk-tb-ulist">
<thead>
<tr class="nk-tb-item nk-tb-head">
<th class="nk-tb-col nk-tb-col-check">
<div class="custom-control custom-control-sm custom-checkbox notext">
<input type="checkbox" class="custom-control-input" id="uid" />
<label class="custom-control-label" for="uid"></label>
</div>
</th>
<th class="nk-tb-col"><span class="sub-text">Customer Name</span></th>
<th class="nk-tb-col tb-col-mb">
<span class="sub-text">Phone Number</span>
</th>
<th class="nk-tb-col tb-col-md">
<span class="sub-text">CNIC</span>
</th>
<th class="nk-tb-col tb-col-lg">
<span class="sub-text">Joining Date</span>
</th>
<th class="nk-tb-col tb-col-lg">
<span class="sub-text">Expiry Date</span>
</th>
<th class="nk-tb-col tb-col-lg">
<span class="sub-text">Status</span>
</th>
<th class="nk-tb-col nk-tb-col-tools text-end"></th>
</tr>
</thead>
<tbody >
<tr class="nk-tb-item" *ngFor="let user of allUsers">
<td class="nk-tb-col nk-tb-col-check">
<div class="custom-control custom-control-sm custom-checkbox notext">
<input type="checkbox" class="custom-control-input" id="uid1" />
<label class="custom-control-label" for="uid1"></label>
</div>
</td>
<td class="nk-tb-col">
<span>{{user.customerName}}</span>
</td>
<td class="nk-tb-col tb-col-md">
<span>{{user.phoneNumber}}</span>
</td>
<td class="nk-tb-col tb-col-lg">
<span>{{user.CNIC_Number}}</span>
</td>
<td class="nk-tb-col tb-col-lg">
<span class="badge bg-secondary">{{user.Joining_Date}}</span>
</td>
<td class="nk-tb-col tb-col-md">
<span class="badge bg-danger">{{user.Expiry_Date}}</span>
</td>
<td class="nk-tb-col tb-col-md">
<span class="badge bg-success">Active</span>
</td>
<td class="nk-tb-col nk-tb-col-tools">
<ul class="nk-tb-actions gx-1">
<li>
<div class="drodown">
<a href="#" class="dropdown-toggle btn btn-icon btn-trigger" data-bs-toggle="dropdown"><em
class="icon ni ni-more-h"></em></a>
<div class="dropdown-menu dropdown-menu-end">
<ul class="link-list-opt no-bdr">
<li>
<a data-bs-toggle="modal" href="#mViewDetails"><em
class="icon ni ni-eye"></em><span>View
Details</span></a>
</li>
<li>
<a data-bs-toggle="modal" href="#mEditDetails"><em
class="icon ni ni-edit"></em><span>Edit
Details</span></a>
</li>
<li>
<a class="cursor" (click)="Toast.sendReminder()"><em
class="icon ni ni-send"></em><span>Send
Reminder</span></a>
</li>
<li>
<a data-bs-toggle="modal" href="#mChangeStatus"><em
class="icon ni ni-exchange"></em><span>Change Status</span></a>
</li>
<li>
<a href=""><em
class="icon ni ni-delete"></em><span>Delete
User</span></a>
</li>
</ul>
</div>
</div>
</li>
</ul>
</td>
</tr>
<!-- .nk-tb-item -->
</tbody>
</table>
Here is the TS Code
import { Component, OnInit } from '#angular/core';
import { Subject } from 'rxjs';
import { ToastService } from 'src/app/services/toast.service';
import { IDropdownSettings } from 'ng-multiselect-dropdown';
import { UserDetailService } from '../../services/user-detail.service';
import { Inter } from '../../inter';
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'app-view-user',
templateUrl: './view-user.component.html',
styleUrls: ['./view-user.component.css']
})
export class ViewUserComponent implements OnInit {
constructor(public Toast: ToastService,
private userDetails: UserDetailService, private http: HttpClient) {
}
dtOptions: DataTables.Settings = {};
dtTrigger: Subject<any> = new Subject<any>();
dropdownList: any = [];
selectedItems = [];
allUsers: Inter[] = [];
dropdownSettings: IDropdownSettings = {
singleSelection: false,
idField: 'item_id',
textField: 'item_text',
selectAllText: 'Select All',
unSelectAllText: 'UnSelect All',
itemsShowLimit: 3,
allowSearchFilter: true
};
dataForTable: any;
fetch() {
this.http.get<any>('http://localhost:3000/user').subscribe(data => {
this.dataForTable = data;
console.log(this.dataForTable);
})
}
ngOnInit(): void {
this.dtOptions = {
pagingType: 'full_numbers',
language: {
searchPlaceholder: "Type Into Search"
}
};
this.fetch();
this.dropdownList = [
{ item_id: 1, item_text: 'Naan Shami' },
{ item_id: 2, item_text: 'Chicken Manchurian' },
{ item_id: 3, item_text: 'Pune' },
{ item_id: 4, item_text: 'Navsari' },
{ item_id: 5, item_text: 'New Delhi' }
];
this.get()
}
get() {
this.userDetails.get().subscribe((data) => {
this.allUsers = data;
});
}
delete(id: number) {
this.userDetails.delete(id).subscribe((data) => {
this.allUsers = this.allUsers.filter((_) => _.id !== id);
});
}
}
Here is the users.module.ts Code
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { UsersRoutingModule } from './users-routing.module';
import { AddUserComponent } from './components/add-user/add-user.component';
import { NgbModule } from '#ng-bootstrap/ng-bootstrap';
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
import { ViewUserComponent } from './components/view-user/view-user.component';
import { DataTablesModule } from 'angular-datatables';
import { NgMultiSelectDropDownModule } from 'ng-multiselect-dropdown';
import { HttpClientModule } from '#angular/common/http';
#NgModule({
declarations: [
AddUserComponent,
ViewUserComponent
],
imports: [
CommonModule,
UsersRoutingModule,
NgbModule,
FormsModule,
ReactiveFormsModule,
DataTablesModule,
HttpClientModule,
NgMultiSelectDropDownModule.forRoot(),
]
})
export class UsersModule { }
Please add the following.
this.dtTrigger.next();
It will update the rows in the table.
your component, declare the following
#ViewChild(DataTableDirective)
dtElement: DataTableDirective;
dtOptions: DataTables.Settings = {};
dtTrigger: Subject<any> = new Subject();
after you pull your data from the service
this.serviceName.getData().subscribe((data) => {
// ADD THIS
this.dtTrigger.next();
}, (err) => {
})
For more please refer to the answer.
Related
I am trying to develop crud using mean stack. When making HTTP requests, angular works fine, but I only want angular to make post request when the submit button for the reactive form is pressed, but it makes both get and post request to the nodejs/express server automatically. is there any option to make the post only when the form submit button is triggered? Thanks a lot.
pdt: I know is making both requests because I am using morgan in nodejs server.
empleados.component.html
<form [formGroup]="nuevoEmpleado" (submit)="addEmpleado()">
<label>
Nombre:
<input type="text" formControlName="nombre">
</label>
<label>
Apellido:
<input type="text" formControlName="apellido">
</label>
<label>
Cargo:
<input type="text" formControlName="cargo">
</label>
<button type="submit" class="btn btn-primary">Agregar Empleado</button>
</form>
<!--tabla lista de empleados -->
<table class="table table-dark">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Nombre</th>
<th scope="col">Apellido</th>
<th scope="col">Cargo</th>
<th scope="col">acciones</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let empleado of listaEmpleados; let i=index">
<th scope="row">{{ i+1 }}</th>
<td>{{ empleado.nombre }}</td>
<td>{{ empleado.apellido }}</td>
<td>{{ empleado.cargo }}</td>
<td>Eliminar</td>
</tr>
</tbody>
</table>
empleados.components.ts
import { Component, OnInit } from '#angular/core';
import { EmpleadosService } from './empleados.service';
import { FormControl , FormGroup } from '#angular/forms';
import { empleado } from '../interfaces/empleado'
#Component({
selector: 'app-empleados',
templateUrl: './empleados.component.html',
styleUrls: ['./empleados.component.css']
})
export class EmpleadosComponent implements OnInit {
nuevoEmpleado = new FormGroup({
nombre: new FormControl(''),
apellido: new FormControl(''),
cargo: new FormControl('')
});
listaEmpleados: empleado[];
constructor(private empleadosService: EmpleadosService) {}
getListaEmpleados():void{
this.empleadosService.getlistaEmpleados().subscribe(empleados => this.listaEmpleados = empleados);
};
addEmpleado(){
//this.empleadosService.addEmpleado(this.nuevoEmpleado.value).subscribe(res => console.log(res));
if(!this.nuevoEmpleado){
return;
}
this.empleadosService.addEmpleado(this.nuevoEmpleado.value).subscribe(res => console.log(res));
}
ngOnInit(): void {
this.getListaEmpleados();
this.addEmpleado();
}
}
empleados.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { empleado } from '../interfaces/empleado';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class EmpleadosService {
url = 'http://localhost:3000';
constructor(private http: HttpClient) {}
getlistaEmpleados():Observable<empleado[]>{
return this.http.get<empleado[]>(this.url);
}
addEmpleado(nuevoEmpleado):Observable<Object>{
return this.http.post<empleado>(this.url , nuevoEmpleado);
}
}
I have this code and want to add the people picker from https://social.technet.microsoft.com/wiki/contents/articles/37728.sharepoint-online-step-by-step-client-side-people-picker-control.aspx#Full_Code
import { Version } from '#microsoft/sp-core-library';
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneTextField
} from '#microsoft/sp-webpart-base';
import { escape } from '#microsoft/sp-lodash-subset';
import styles from './FinanceSysAccWebPart.module.scss';
import * as strings from 'FinanceSysAccWebPartStrings';
import { SPHttpClient, SPHttpClientResponse } from '#microsoft/sp-http';
import { IListItem } from './app/interfaces/IListItem';
export interface IFinanceSysAccWebPartProps {
listName: string;
}
export interface ISpFxPureWebPartProps {
description: string;
}
export default class FinanceSysAccWebPart extends BaseClientSideWebPart<IFinanceSysAccWebPartProps>
{
private listItemEntityTypeName: string = undefined;
public render(): void {
this.domElement.innerHTML = `
<div class="${ styles.financeSysAcc }">
<div class="${ styles.container }">
<div class="${ styles.row }">
<div class="${ styles.column }">
<span class="${ styles.title }">Finance System Access Request Form</span>
<p class="${styles.description}">Name: ${escape(this.properties.listName)}</p>
<table>
<tr>
<td>First Name: </td>
<td><input id="txtFirstName" name="txtFirstName" type="text" /></td>
</tr>
<tr>
<td>Last Name: </td>
<td><input id="txtSurName" name="txtSurName" type="text" /></td>
</tr>
</table>
<div class="ms-Grid-row ms-bgColor-themeDark ms-fontColor-white ${styles.row}">
<div class="ms-Grid-col ms-u-lg10 ms-u-xl8 ms-u-xlPush2 ms-u-lgPush1">
<div class="status"></div>
<ul class="items"><ul>
</div>
</div>
</div>
</div>
</div>
</div>
`;
//let FirstName = (<HTMLInputElement> document.getElementById("txtFirstName")).value;
this.setButtonsEventHandlers();
}
What's the best way to do this? I can't see script tags in a webpart.
Use native JavaScript in SPFx solution would be more complex than classic page and no compile error/tip.
I would suggest you use #pnp/spfx-controls-react People Picker in your solution.
Demo thread
Delete function no longer works.
I deleted my information from Delete HTTP call and it works but when I used in angular2 part front end it does not work anymore.
error
DELETE http://localhost:3001/formations/undefined 0 ()
EXCEPTION: Response with status: 0 for URL: null
Subscriber.js:246 Uncaught Response {_body: ProgressEvent, status: 0, ok: false, statusText: "", headers: Headers, …}
zone.js:2019 DELETE http://localhost:3001/formations/undefined 0 ()
this is my formation.Service
deleteFormation(id){
return this.http.delete("http://localhost:3001/formations/"+id)
.map(res => res.json());
}
this is my home.ts
import { Component, OnInit } from '#angular/core';
import { FormationService } from '../../services/formation.service';
import { Formation } from '../../../app/formation';
import {Observable} from 'rxjs/Rx';
#Component({
selector: 'app-home1',
templateUrl: './home1.component.html',
styleUrls: ['./home1.component.css']
})
export class Home1Component implements OnInit {
formation: Observable<Formation[]>;
constructor(
public formationService:FormationService
) { };
ngOnInit() {
this.formation = this.formationService.getFormations();
// this.getFormations();
}
getFormations(){
this.formationService.getFormations()
.subscribe(formation=>{
this.formation = this.formation;
})
}
deleteFormation(id) {
this.formationService.deleteFormation(id)
.subscribe(()=>{
this.getFormations();
});
}
}
this my home.html
<app-navbar1></app-navbar1>
<table class="table table-bordered">
<thead>
<tr>
<td><b>Title</b></td>
<td><b>url</b></td>
<td><b>description</b></td>
<td width="275" align="center"><b>Action</b></td>
</tr>
</thead>
<tbody>
<tr *ngFor="let forms of formation | async " >
<td>{{forms.title}}</td>
<td>{{forms.url}}</td>
<td>{{forms.description}}</td>
<td width="275">
<a class="btn btn-info" routerLink="/show/{{formation._id}}">Detail</a>
<a class="btn btn-success" routerLink="/edit/{{formation._id}}" >Edit</a>
<a class="btn btn-danger" (click)="deleteFormation(formation._id)" >Delete</a>
</td>
</tr>
</tbody>
</table>
The problem with your code is that you are enumerating over a collection in *ngFor and instead of passing an instance of an object to deleteFormation method, you are passing collection.
Instead of deleteFormation(formation._id) you need to call deleteFormation(forms._id). _id does not exist on your formation array.
The same problem applies to your Detail and Edit links.
When I have done a function to see the details of a formation already added, nothing is displayed.
Error
GET http://localhost:3001/formations/undefined 0 ()
EXCEPTION: Response with status: 0 for URL: null
Subscriber.js:246 Uncaught Response {_body: ProgressEvent, status: 0, ok: false, statusText: "", headers: Headers, …}
This is my show.html
<div class="panel panel-default" >
<div class="panel-heading">formation detail Form : You can see the detail information of an foration in this page of the EMS Apps.</div>
<div class="panel-body">
<form class="form-horizontal" *ngIf=forms>
<div class="form-group">
<label for="emp_name" class="col-sm-2 control-label">formation's Full title : </label>
<div class="col-sm-9">
<p class="form-control">{{forms.title}}</p>
</div>
</div>
<div class="form-group">
<label for="position" class="col-sm-2 control-label">formation url : </label>
<div class="col-sm-9">
<p class="form-control">{{forms.url}}</p>
</div>
</div>
<div class="form-group">
<label for="department" class="col-sm-2 control-label">formation description : </label>
<div class="col-sm-9">
<p class="form-control">{{forms.description}}</p>
</div>
</div>
</form>
</div>
</div>
this is my show.ts
import { Component, OnInit } from '#angular/core';
import { FormationService } from '../../services/formation.service';
import { Formation } from '../../../app/formation';
import { ActivatedRoute, Params, Router } from '#angular/router';
#Component({
selector: 'app-show',
templateUrl: './show.component.html',
styleUrls: ['./show.component.css']
})
export class ShowComponent implements OnInit {
constructor(
public formationService:FormationService,
public route:ActivatedRoute,
public router:Router
) { }
ngOnInit() {
this.getFormation();
}
formation:Formation;
getFormation(){
var id = this.route.snapshot.params['id'];
this.formationService.getFormation(id)
.subscribe(formation=>{
this.formation = formation;
})
}
}
this is my home.html
<table class="table table-bordered">
<thead>
<tr>
<td><b>Title</b></td>
<td><b>url</b></td>
<td><b>description</b></td>
<td width="275" align="center"><b>Action</b></td>
</tr>
</thead>
<tbody>
<tr *ngFor="let forms of formation | async " >
<td>{{forms.title}}</td>
<td>{{forms.url}}</td>
<td>{{forms.description}}</td>
<td width="275">
<a class="btn btn-info" routerLink="/show/{{forms._id}}">Detail</a>
<a class="btn btn-success" routerLink="/edit/{{forms._id}}" >Edit</a>
<a class="btn btn-danger" (click)="deleteFormation(forms._id)" >Delete</a>
</td>
</tr>
</tbody>
</table>
this is my service file
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class FormationService {
constructor(private http:Http) { }
getFormations(){
return this.http.get("http://localhost:3001/formations")
.map(res => res.json());
}
addFormation(info){
return this.http.post("http://localhost:3001/formations",info)
.map(res => res.json());
}
getFormation(id){
return this.http.get("http://localhost:3001/formations/"+id)
.map(res => res.json());
}
deleteFormation(id){
return this.http.delete("http://localhost:3001/formations/"+id)
.map(res => res.json());
}
updateFormation(id, info){
return this.http.put("http://localhost:3001/formations/"+id,info)
.map(res => res.json());
}
}
I don't realy get you problem, but from what i see, you should
instead of
var id = this.route.snapshot.params['id'];
try to subscribe to the router using queryParams
this.formation = this.route.queryParams.pipe(map(params => {
this.id = params.id;
return this.formationService.getFormation(id)
})
in your html
<tbody *ngIf="formation | async as formation">
<tr *ngFor="let forms of formation">
</tr>
</tbody >
It seems that you send http request without id.
Just add a condition on the id:
getFormation(){
var id = this.route.snapshot.params['id'];
if(id){
this.formationService.getFormation(id)
.subscribe(formation=>{
this.formation = formation;
})
}
}
And it will be a good practice to add this check also in the service layer before sending request with 'id' as a a query param.
I have made a simple Datatable with search input,
Student.js
Vue.component('student-list',{
props: ['students'],
template:`
<div class="table-responsive">
<table class="table table-hover mails m-0 table table-actions-bar">
<thead>
<tr>
<th>10</th>
<th>9</th>
<th>8</th>
<th>7</th>
<th>6</th>
<th>5</th>
<th>4</th>
<th>3</th>
<th>2</th>
<th>1</th>
</tr>
</thead>
<tbody>
<student v-for="student in searchResults" :student="student"></student>
</tbody>
</table>
</div>
`,
computed:
{
searchResults: function(student){
var self = this;
return this.students.filter(
function(student){
if(this.searchQuery == '' || this.searchQuery == null) { return true; }
console.log(this.searchQuery);
return (student.toString().indexOf(this.searchQuery) != -1);
}
);
}
},
// v-if="searchResults(student)"
});
Vue.component('student',{
props: ['student'],
template:`
<tr>
<td>
{{ student.name }} {{ student.lname }}
</td>
<td>
<i class="md-phone"></i>
<i class="ion-woman"></i>
<i class="ion-man"></i>
<i class="ion-android-settings"></i>
</td>
<td>
{{ student.address }}
</td>
<td>
{{ student.totalTopay }}
</td>
<td>
{{ student.lessonsTaken }}
</td>
<td>
{{ student.cancelCount }}
</td>
<td>
{{ student.phone }}
</td>
<td>
{{ student.momPhone }}
</td>
<td>
{{ student.dadPhone }}
</td>
<td>
{{ student.email }}
</td>
</tr>
`
});
new Vue({
el: '#content-page',
data: {
'searchQuery': ''
}
});
HTML
....
<div id="content-page">
<input type="text" id="search" class="form-control" placeholder="Search..." v-model="searchQuery">
</div>
....
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="{{ asset('js/students.js') }}"></script>
...
Now, for some reason whenever I leave the search blank it works as needed, but when I change the input field by typing in the text box(which his v-model is attached to the searchQuery variable), nothing changes, and when I call it (this.searchQuery) on the Dev Tools console, it says undefined. What am I missing?
Thanks in advance everyone!
I've edited your code a little bit to make it work and removed some lines of code, because I don't want to recreate the whole student object.
I passed the search-query as prop and added <student-list /> to your template.
In your filter function I replaced toString with JSON.stringify, because student is an object and toString prints out [object Object].
Also, I changed the props object and added their types.
And one last tiny error:
Interpolation inside attributes has been deprecated. Use v-bind or the
colon shorthand instead.
Don't use href="tel:{{ student.phone }}" use something like this instead :href="'tel:' + student.phone".
Vue.component('student-list',{
props: {
students: {
type: Array
},
searchQuery: {
type: String
}
},
template:`<div class="table-responsive">
searchQuery: {{searchQuery}}
<table class="table table-hover mails m-0 table table-actions-bar">
<thead>
<tr>
<th>10</th>
<th>9</th>
<th>8</th>
<th>7</th>
<th>6</th>
<th>5</th>
<th>4</th>
<th>3</th>
<th>2</th>
<th>1</th>
</tr>
</thead>
<student v-for="student in searchResults" :student="student"></student>
</tbody>
</table>
</div>`,
computed: {
searchResults() {
return this.students.filter(student => JSON.stringify(student).indexOf(this.searchQuery) != -1);
}
},
});
Vue.component('student',{
props: {
student: {
type: Object
}
},
template:`<tr>
<td>
{{ student.name }} {{ student.lname }}
</td>
<td>
<a :href="'tel:' + student.phone" title="התקשר" class="table-action-btn">tel-icon</a>
</td>
</tr>`
});
new Vue({
el: '#content-page',
data: {
'searchQuery': ''
}
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.js"></script>
</head>
<body>
<div id="content-page">
<student-list :search-query="searchQuery" :students="[{
id: 1,
name: 'Peter',
lname: 'Parker',
phone: 12345
},{
id: 2,
name: 'Bat',
lname: 'Man',
phone: 1234
}]"></student-list>
<input type="text" id="search" class="form-control" placeholder="Search..." v-model="searchQuery">
</div>
</body>
</html>
And my second solution
Add this to your computed:
computed: {
searchQuery() {
return this.$parent.searchQuery || '';
},
...
}
...
You access the parent of student-list, but I think this is not a nice way, because you can't see from where do you get this data.
Hope this will help you a little bit.