Datatables not initializing after reload operation in angular 7 app - node.js

i implemented the jquery.datatables in my Angular 7, i installed all the node modules and did the necessary configuration, and included the necessary files in the angular.json file, and it works perfectly after the first run, but when i refresh the page or component, or move from one link to another and come back to the products page, the data loads from the api successfully but the datatable refuses to initialize properly...
and i'm lost on what to do.
Products Controller
import { Component, OnInit, ViewChild } from '#angular/core';
import { Router } from '#angular/router';
import { AccountService } from 'src/app/services/account.service';
import { ProductsService } from 'src/app/services/products.service';
import { Observable, Subject } from 'rxjs';
import { Product } from '../../interfaces/product';
import { DataTableDirective } from 'angular-datatables';
#Component({
selector: 'app-products',
templateUrl: './products.component.html',
styleUrls: ['./products.component.css']
})
export class ProductsComponent implements OnInit {
product$ : Observable<Product[]>;
products : Product[] = [] ;
//angular-datatables options..
dtOptions: DataTables.Settings = {};
dtTrigger: Subject<any> = new Subject();
#ViewChild(DataTableDirective, {static:true}) dtElement: DataTableDirective;
constructor(
private productService:ProductsService
){}
ngOnInit(): void {
this.productService.getAllProducts().subscribe(rst => {
this.products = rst; //assign the value of the observable to the array...
this.dtTrigger.next();
this.dtOptions = {
pageLength : 5,
autoWidth : true,
pagingType : 'full_numbers',
order : [[ 0, 'desc']]
}
});
}
}
the Product.component.html file
this is the code with the binding expressions
<table datatable [dtOptions]="dtOptions" [dtTrigger]="dtTrigger" class="table table-bordered row-border hover" style="width:100%">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Description</th>
<th>In Stock</th>
<th>Price( CAD) </th>
<th>Image</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let p of products" class="text-center">
<td>{{p.productId}}</td>
<td>{{p.name | uppercase}}</td>
<td>{{p.description}}</td>
<td>{{ (true == p.outOfStock)?'No':'Yes' }}</td>
<td>{{p.price | currency: 'CAD'}}</td>
<td> <img [src]="'/imgs/'+p.imageUrl" *ngIf="p.imageUrl" style="float:right" width="75" /> </td>
<td>
<div class="btn btn-group mt-2" role="group">
<button type="button" class="btn btn-success"><i class="fa fa-list"></i></button>
<button type="button" class="btn btn-primary"><i class="fa fa-pencil-square-o"></i></button>
<button type="button" class="btn btn-danger"><i class="fa fa-trash-o"></i></button>
</div>
</td>
</tr>
</tbody>
</table>
but on the first glance after logging in, everything works perfectly, but when i move go back to the page, only the data shows on the table shows after the ngFor loop, but the datatable refuses to initialize.. what do i do, and please whats my mistake..

You need to destroy datatable instance before triggering it again.
import { DataTableDirective } from 'angular-datatables';
dtElement: DataTableDirective;
isDtInitialized:boolean = false
this.products = rst;
if (this.isDtInitialized) {
this.dtElement.dtInstance.then((dtInstance: DataTables.Api) => {
dtInstance.destroy();
this.dtTrigger.next();
});
} else {
this.isDtInitialized = true
this.dtTrigger.next();
}
Using this, for the first time it will go to the else block and trigger datatable directly. After that when you refresh, it will first destroy the datatable and then trigger.

Destroy the datatables instance before re-initializing. Make sure to also destroy the instance on destroy of the component using OnDestroy, like so:
public dt: DataTableDirective;
public isDtInit: boolean = false;
public dtInit(): void {
if (this.isDtInit) {
this.dt.dtInstance.then((dtInstance: DataTables.Api) => {
dtInstance.destroy();
this.dtTrigger.next();
});
} else this.isDtInit = true;
}
ngOnDestroy() {
this.dtInit();
}
Using this, for the first time it will go to the else block and trigger datatable directly. After that when you refresh, it will first destroy the datatable and then trigger.

Related

Pagination links is not working properly in Laravel Livewire

I am using Laravel 9 and Livewire 2.x. Pagination links are not working correctly; it only changes one item. I tried to change the link on the address bar like /search?query=example&page=2, and that worked perfectly.
Component Class
use Livewire\Component;
use App\Models\Product;
use Livewire\WithPagination;
class Search extends Component
{
use WithPagination;
protected $paginationTheme = 'bootstrap';
public $query;
protected $queryString = ['query'];
public function render()
{
$products = $this->searchQuery($this->query);
return view('livewire.products.search', [
'products' => $products->paginate(6),
])->extends('layouts.buyer')->section('content');
}
public function mount()
{
}
public function searchQuery($input){
return Product::where('name','like','%' . $input . '%' );
}
}
View
<div class="container my-5">
#if($products->isNotEmpty())
<div class="row g-4">
#foreach($products as $product)
<div class="col-lg-3">
#livewire('products.buyer-product-item', ['product' => $product])
</div>
#endforeach
</div>
#else
<div class="alert alert-danger">Product Was Not Found !!!</div>
#endif
<nav class="mt-5" aria-label="Page navigation example">
{{ $products->links() }}
</nav>
</div>
Hey After viewing the console I found there is an error.
Cannot read property 'fingerprint' of null
After including the key for the nested component the issue was solved.
#livewire('products.buyer-product-item', ['product' => $product], key($product->id))

How to set a SharePoint list field using a pnp.sp People Picker

I do not know the code to add the user, selected in a People Picker from the pnp.sp library.
I've tried the below code example (by using State) but this I understand is not saving the users selection.
private _getPeoplePickerItems() {
pnp.sp.web.siteUsers.get().then((data) =>{
this.setState({
DeptContact: data
});
});
}
And the people picker in the render:
<PeoplePicker
context={this.props.context}
titleText="People Picker"
personSelectionLimit={3}
groupName={''}
showtooltip={false}
isRequired={false}
disabled={false}
selectedItems={this._getPeoplePickerItems}
showHiddenInUI={false}
principalTypes={[PrincipalType.User]}
resolveDelay={1000}
/>
</div>
</div>
I expect a user to be able to enter a user into the people picker and resolve it and then submit it. This user is then added to a 'Person' column in a sharepoint list.
My test code for your reference(react framework).
import * as React from 'react';
import styles from './PnpReact.module.scss';
import { IPnpReactProps } from './IPnpReactProps';
import { escape } from '#microsoft/sp-lodash-subset';
import pnp from "#pnp/pnpjs";
import { PeoplePicker, PrincipalType } from "#pnp/spfx-controls-react/lib/PeoplePicker";
export interface IDefaultData{
PeoplePickerDefaultItems:string[];
}
export default class PnpReact extends React.Component<IPnpReactProps, IDefaultData> {
public constructor(props: IPnpReactProps,state: IDefaultData){
super(props);
this.state = {
PeoplePickerDefaultItems:[]
};
}
//get users from peoplepicker
private _getPeoplePickerItems(items: any[]) {
console.log(items);
}
public componentDidMount(){
this.GetDefaultUsers();
}
private GetDefaultUsers() {
pnp.sp.web.siteUsers.get().then((items: any[]) =>{
var defaultUsers:string[]=[];
//get last 2 users
for(var i=items.length-1;i>items.length-3;i--){
defaultUsers.push(items[i].Email);
}
this.setState({
PeoplePickerDefaultItems:defaultUsers
});
});
}
public render(): React.ReactElement<IPnpReactProps> {
return (
<div className={ styles.pnpReact }>
<div className={ styles.container }>
<div className={ styles.row }>
<PeoplePicker
context={this.props.context}
titleText="People Picker"
personSelectionLimit={3}
groupName={''}
showtooltip={false}
isRequired={false}
disabled={false}
selectedItems={this._getPeoplePickerItems}
defaultSelectedUsers={this.state.PeoplePickerDefaultItems}
showHiddenInUI={false}
principalTypes={[PrincipalType.User]}
resolveDelay={1000}
/>
<div className={ styles.column }>
<span className={ styles.title }>Welcome to SharePoint!</span>
<p className={ styles.subTitle }>Customize SharePoint experiences using Web Parts.</p>
<p className={ styles.description }>{escape(this.props.description)}</p>
<a href="https://aka.ms/spfx" className={ styles.button }>
<span className={ styles.label }>Learn more</span>
</a>
</div>
</div>
</div>
</div>
);
}
}

*ngIf-else not showing else results

I have no idea why this isn't working and I'm stuck. Can anyone spot what is wrong with my code?
Im trying to show a Not found message when there are no tickets to show. I tried getting the result via tickets array lenght, but it always shows that the length is 0.
HTML
<ng-container *ngIf="tickets; else elseTemplate">
<div class="container">
<div class="row justify-content-md-center">
<div class="col-sm-4" *ngFor="let ticket of tickets">
<div class="card">
<h5 class="card-header"><span class="fa fa-ticket"></span> Pileti nr. {{ticket._id}}</h5>
</div>
</div>
</div>
</div>
</ng-container>
<ng-template #elseTemplate>
<div><h1>Ticket not found.</h1></div>
</ng-template>
TS
import { Component, OnInit } from '#angular/core';
import { TicketService } from '../../ticket.service';
import { ActivatedRoute } from '#angular/router';
import * as moment from 'moment';
import {Location} from '#angular/common';
#Component({
selector: 'app-reg-ticket-list',
templateUrl: './reg-ticket-list.component.html',
styleUrls: ['./reg-ticket-list.component.css']
})
export class RegTicketListComponent implements OnInit {
reg: string;
public tickets = [];
constructor(private ticketService: TicketService, private route: ActivatedRoute, private _location: Location) { }
ngOnInit() {
this.reg = this.route.snapshot.params.reg;
this.ticketService.getTicketsByReg(this.reg)
.subscribe(data => this.tickets = data);
}
dateFormat(date) {
moment.locale('et');
return moment(date).format("Do MMMM YYYY HH:mm:ss")
}
goBack() {
this._location.back();
}
}
When using *ngIf="tickets;, this will only check if tickets is falsey, and an empty array is not falsey (although null is).
Instead, you can use *ngIf="tickets && ticket.length; which will also check that there's the length is not 0

react server side return null and just do browser rendering

See the code below,I use SSR in project, but some components just can render in browser-end since it use window or document object.So I do a switch, in server side: return null,and in browser-end return the real component. But I got a checksum error. What should I do?
render() {
if (!process.env.BROWSER) {
return null;
}
else {
const OwlCarousel = require('react-owl-carousel').default;
return <OwlCarousel
className="owl-theme"
loop margin={10} nav
>
<div className="item" style={{backgroundColor:'#3EA5E9'}}><h4>1</h4></div>
<div className="item"><h4>2</h4></div>
<div className="item"><h4>3</h4></div>
<div className="item"><h4>4</h4></div>
<div className="item"><h4>5</h4></div>
<div className="item"><h4>6</h4></div>
<div className="item"><h4>7</h4></div>
<div className="item"><h4>8</h4></div>
<div className="item"><h4>9</h4></div>
<div className="item"><h4>10</h4></div>
<div className="item"><h4>11</h4></div>
<div className="item"><h4>12</h4></div>
</OwlCarousel>;
}
}
example component
import { Component } from 'react'
export default class MyComponent extends Component {
state = { render: false }
componentDidMount() {
if (process.env.BROWSER) this.setState({ render: true })
}
render() {
return this.state.render && <ComponentToRender />
}
}

Viewmodel IEnumerable property is empty

I'm in the middle of making an ASP .NET MVC4 based app. I'm a complete newb in that field. The idea is quite simple - have a some members in DB, show them listed, select desired ones via check boxes and redirect to some other controller which would do something with the previously selected members.
Problem is passing the list of members from View to the Controller. I've thought it would work with ViewModel. It certainly works from Controller to the View, but not the other way.
My ViewModel:
public class MembersViewModel
{
public IEnumerable<Directory_MVC.Models.Member> MembersEnum { get; set; }
public string Test { get; set; }
}
Snippet of my Controller:
public class MembersController : Controller
{
private MainDBContext db = new MainDBContext();
public ActionResult Index()
{
var model = new Directory_MVC.ViewModels.MembersViewModel();
// populating from DB
model.MembersEnum = db.Members.Include(m => m.Group).Include(m => m.Mother).Include(m => m.Father);
model.Test = "abc";
return View(model);
}
[HttpPost]
public ActionResult GoToSendEmail(Directory_MVC.ViewModels.MembersViewModel returnedStruct)
{
if (ModelState.IsValid)
{
// it is valid here
return Redirect("http:\\google.com");
}
}
Snippet of my View:
#model Directory_MVC.ViewModels.MembersViewModel
#{
ViewBag.Title = "Members listing";
var lineCount = 0;
string lineStyle;
}
#using (Html.BeginForm("GoToSendEmail", "Members", FormMethod.Post))
{
<table>
#foreach (var item in Model.MembersEnum)
{
lineCount++;
// set styling
if (lineCount % 2 == 1)
{
lineStyle = "odd-line";
}
else
{
lineStyle = "even-line";
}
<tr class="#lineStyle">
<td>
#Html.EditorFor(modelItem => item.Selected)
</td>
<td>
#Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Mother.FirstName) #Html.DisplayFor(modelItem => item.Mother.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Father.FirstName) #Html.DisplayFor(modelItem => item.Father.LastName)
</td>
<!-- other print-outs but not all properties of Member or Mother/father are printed -->
</tr>
}
</table>
<input type="submit" value="Send E-mail" />
}
The data are shown OK in the View. However, when I submit that form the returnedStruct.MembersEnum and Test string are both null in the Controller's method GoToSendEmail.
Is there a mistake or is there another possible way how to pass that members structure and check their Selected property?
Model binding to a collection works a little differently. Each item has to have an identifier so that inputs don't all have the same name. I've answered a similar question here.
#for (int i = 0; i < Model.MembersEnum.Count(); i++)
{
#Html.EditorFor(modelItem => modelItem.MembersEnum[i].FirstName)
}
...which should render something like...
<input type="text" name="MembersEnum[0].FirstName" value="" />
<input type="text" name="MembersEnum[1].FirstName" value="" />
<input type="text" name="MembersEnum[2].FirstName" value="" />
...which should then populate the collection in your ViewModel when picked up by the controller...
public ActionResult GoToSendEmail(ViewModels.MembersViewModel model)
As mentioned in the other answer, I'd have a look at some related articles from Scott Hansleman and Phil Haack.
You also mentioned that your string called Test is null when you submit to your POST action. You haven't added a field for this property anywhere within your form, so there's nothing for the model binder to bind to. If you add a field for it within your form then you should see the value in the POST action:
#Html.EditorFor(modelItem => modelItem.Test)
Html.BeginCollectionItem() helper did the job - BeginCollectionItem.

Resources