i have a React App which worked fine, both on mobile and pc.
i am using 'react-bootstrap' listgroup and modal inside a material-ui tabs component.
it all worked fine until a few days ago on mobile the modal stopped rendering and the list items started to wrap instead of filling the entire witdh of the screen.
on pc chrome works fine, if i activate in VS Code chrome debugger the issue appears.
my code added below:
CatalogListItem.js
import React,{ Component } from 'react';
import {ListGroupItem} from 'react-bootstrap';
class CatalogListItem extends Component {
render() {
return (
<ListGroupItem bsStyle={(this.props.catalogItem.isCount)? null :"danger"}
align="right"
header={this.props.catalogItem.itemName + " - " + this.props.catalogItem.measureId.measureName}
onClick={() => {this.props.onClickHandler(this.props.catalogItem)}}
>
<img src={this.props.catalogItem.itemPicPath}
height="70" width="70" alt="item" align="left" />
<br/>
<span align="right">
מלאי קבוע: {this.props.catalogItem.itemStock} ,
מחיר: {this.props.catalogItem.itemPrice[0].price} ש"ח
פיקדון: {this.props.catalogItem.itemDeposit} ש"ח
<br/>
הנחה: {this.props.catalogItem.itemDiscount}%,
הנחה צוברת: {this.props.catalogItem.additionalDiscount}%
</span>
</ListGroupItem>
);
}
}
export default CatalogListItem;
CatalogTabs.js
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
import React,{Component} from 'react';
import {Tabs,Tab} from 'material-ui/Tabs';
import {List} from 'material-ui/List';
import PropTypes from 'prop-types';
import FloatingActionButton from 'material-ui/FloatingActionButton';
import ContentAdd from 'material-ui/svg-icons/content/add';
import CatalogListItem from '../catalogPage/CatalogListItem';
import CatalogItemFormModal from './CatalogItemFormModal';
// eslint-disable-next-line
const propTypes = {
//tabsTitles: PropTypes.object.isRequired,
pathName: PropTypes.string.isRequired
};
const fabStyle = {
bottom: 20,
left: 10,
position: 'fixed'
};
const emptyCatalogItem = {
"itemId": "",
"itemName": "",
"supplierId": {},
"measureId": {},
"itemStock": 0,
"itemPrice": [{price:0}],
"itemDeposit": 0,
"itemDiscount": 0,
"additionalDiscount": 0,
"itemPicPath": "http://freevector.co/wp-content/uploads/2012/04/70626-label-
beer-bottle.png",
"isCount": true
}
class CatalogTabs extends Component {
constructor(props) {
super(props);
this.state = {focusedCatalogItem:emptyCatalogItem,
showModal:false};
}
showModal(catalogItem){
this.setState({focusedCatalogItem:JSON.parse(JSON.stringify(catalogItem))});
this.setState({showModal: true});
}
hideModal(){
this.setState({showModal: false});
}
Listitemsfactory(supplier){
return(
<div>
{ this.props.catalogData.catalog
.sort((a, b) => a.itemName.localeCompare(b.itemName))
.map((catalogItem)=> {
if (supplier._id === catalogItem.supplierId._id)
return <CatalogListItem key={catalogItem._id}
catalogItem={catalogItem}
onClickHandler=
{this.showModal.bind(this)} />
return false;
}
)}
</div>);
}
render(){
return(
<div>
<MuiThemeProvider>
<Tabs>
{this.props.suppliersData.suppliers.map((supplier)=>
<Tab label={(supplier.supplierName.length > 6)?
supplier.supplierName.substring(0,6)+"..."
:
supplier.supplierName}
key={supplier._id}>
<List>
{this.Listitemsfactory(supplier)}
</List>
</Tab>
)}
</Tabs>
<FloatingActionButton onClick={()=>this.showModal(emptyCatalogItem)}
style={fabStyle}>
<ContentAdd/>
</FloatingActionButton>
</MuiThemeProvider>
<CatalogItemFormModal
suppliers={this.props.suppliersData.suppliers}
measures={this.props.measuresData}
heading={"פרטי פריט"}
onItemNameChange={this.onItemNameChange.bind(this)}
onItemSupplierChange={this.onItemSupplierChange.bind(this)}
onItemMeasureChange={this.onItemMeasureChange.bind(this)}
onItemPriceChange={this.onItemPriceChange.bind(this)}
onItemDepositChange={this.onItemDepositChange.bind(this)}
onItemDiscountChange={this.onItemDiscountChange.bind(this)}
onAdditionalDiscountChange={this.onAdditionalDiscountChange.bind(this)}
onItemStockChange={this.onItemStockChange.bind(this)}
onIsCountChange={this.onIsCountChange.bind(this)}
onItemDelete={this.startDeleteItem.bind(this)}
itemData = {this.state.focusedCatalogItem}
showModal={this.state.showModal}
onHide={this.hideModal.bind(this)}
onOk={this.startUpdateItem.bind(this)}/>
</div>
);}
onItemNameChange(event,newValue){
let item = this.state.focusedCatalogItem;
item.itemName = newValue;
this.setState({focusedCatalogItem:item});
}
onItemSupplierChange(event, key, payload){
let item = this.state.focusedCatalogItem;
item.supplierId._id = payload;
this.setState({focusedCatalogItem:item});
}
onItemMeasureChange(event,key,payload){
let item = this.state.focusedCatalogItem;
item.measureId._id = payload;
this.setState({focusedCatalogItem:item});
}
onItemPriceChange(event,newValue){
let item = this.state.focusedCatalogItem;
item.itemPrice = newValue;
this.setState({focusedCatalogItem:item});
}
onItemDepositChange(event,newValue){
let item = this.state.focusedCatalogItem;
item.itemDeposit = newValue;
this.setState({focusedCatalogItem:item});
}
onItemDiscountChange(event,newValue){
let item = this.state.focusedCatalogItem;
item.itemDiscount = newValue;
this.setState({focusedCatalogItem:item});
}
onAdditionalDiscountChange(event,newValue){
let item = this.state.focusedCatalogItem;
item.additionalDiscount = newValue;
this.setState({focusedCatalogItem:item});
}
onItemStockChange(event,newValue){
let item = this.state.focusedCatalogItem;
item.itemStock = newValue;
this.setState({focusedCatalogItem:item});
}
onIsCountChange(isInputChecked){
let item = this.state.focusedCatalogItem;
item.isCount = isInputChecked;
this.setState({focusedCatalogItem:item});
}
startUpdateItem(catalogItem){
console.log("item that is sent to server:" +
JSON.stringify(catalogItem));
let catalogForSend = JSON.parse(JSON.stringify(catalogItem));
catalogForSend.supplierId = catalogItem.supplierId._id;
catalogForSend.measureId = catalogItem.measureId._id;
console.log("item that is sent to server:" +
JSON.stringify(catalogForSend));
this.props.updateCatalogItem(catalogForSend);
}
startDeleteItem(catalogItem){
this.props.deleteCatalogItem(catalogItem);
}
}
export default CatalogTabs;
CatalogPage.js
import React, { Component } from 'react';
import {bindActionCreators} from 'redux';
import {connect,} from 'react-redux';
import { ToastContainer } from 'react-toastify';
import Header from '../common/Header';
import CatalogTabs from './CatalogTabs';
import LoadingIndicator from '../common/Loading';
import {setFocusedCatalogItem,updateCatalogItem,deleteCatalogItem} from
'../../store/actions/catalogActions'
// this class detirme what page to display - cataloglist or an item form
// this is decided by the url - if provided an item id or not
class CatalogPage extends Component {
render() {
return (
<div>
<Header pageTitle="קטלוג"
isUserLoggedIn = {this.props.userData.isUserLoggedIn}/>
<CatalogTabs
pathName={this.props.location.pathname}
catalogData = {this.props.catalogData}
suppliersData = {this.props.suppliersData}
measuresData = {this.props.measuresData.measures}
userData = {this.props.userData}
setFocusedCatalogItem={this.props.setFocusedCatalogItem}
updateCatalogItem={this.props.updateCatalogItem}
deleteCatalogItem={this.props.deleteCatalogItem}/>
<LoadingIndicator isLoading =
{this.props.catalogData.fetchingCatalogData}/>
<ToastContainer/>
</div>
);
}
}
function mapStateToProps(state){
return{
userData: state.userData,
catalogData: state.catalogData,
suppliersData: state.suppliersData,
measuresData: state.measuresData
}
}
function matchDispachToProps(dispatch){
return bindActionCreators({setFocusedCatalogItem:setFocusedCatalogItem,
updateCatalogItem:updateCatalogItem,
deleteCatalogItem:deleteCatalogItem},dispatch)
}
export default connect(mapStateToProps,matchDispachToProps)(CatalogPage);
Related
hi guys and thanks for help
for my last year in university i need to build some project to graduate with mern stack so i chose to make an e-commerce website everything was working fine until i start to create a component for the category of each product : when i click to an category to display all product related to that category the page always display to me empty i dont know how to fix it the categrory button dont fetch for me the products that it is related to an specific category
this is the component of category list
import React from 'react';
import { LinkContainer } from 'react-router-bootstrap';
import { Nav, NavItem } from 'react-bootstrap';
import { useDispatch, useSelector } from 'react-redux';
import SearchBox from './SearchBox';
import Product from './Product';
import HomeScreen from '../screens/HomeScreen';
const CategoryList = ({list}) => {
const productList = useSelector((state) => state.productList);
const { loading, error, products, page, pages } = productList;
{products.map((product) => {
return (
console.log(product.category+"this one ")
)
})}
return (
<Nav className='mb-4'>
{products.map((product) => {
const {category} = product
console.log(category+"this is category")
return (
<Nav.Item>
<a onClick={<SearchBox match={product.category}/>}>{category}</a>
<LinkContainer
to={`/search/${category}`}>
<Nav.Link>{product.category}</Nav.Link>
</LinkContainer>
</Nav.Item>
)
})}
{/* add more categories as needed */}
</Nav>
);
};
export default CategoryList;
and this is the component of the Home screen :
import React, { useEffect } from 'react'
import { Link } from 'react-router-dom'
import { useDispatch, useSelector } from 'react-redux'
import { Row, Col } from 'react-bootstrap'
import Product from '../components/Product'
import Message from '../components/Message'
import Loader from '../components/Loader'
import Paginate from '../components/Paginate'
import ProductCarousel from '../components/ProductCarousel'
import Meta from '../components/Meta'
import { listProducts } from '../actions/productActions'
import CategoryList from '../components/CategoryList'
const HomeScreen = ({ match }) => {
const keyword = match.params.keyword
const pageNumber = match.params.pageNumber || 1
const dispatch = useDispatch()
const productList = useSelector((state) => state.productList)
const { loading, error, products, page, pages } = productList
{products.map((product) => {
return (
console.log(product.category)
)
})}
useEffect(() => {
dispatch(listProducts(keyword, pageNumber))
}, [dispatch, keyword, pageNumber])
return (
<>
<Meta />
{!keyword ? (
<ProductCarousel />
) : (
<Link to='/' className='btn btn-light'>
Go Back
</Link>
)}
<h1>Latest Products</h1>
<CategoryList/>
{loading ? (
<Loader />
) : error ? (
<Message variant='danger'>{error}</Message>
) : (
<>
<Row>
{products.map((product) => (
<Col key={product._id} sm={12} md={6} lg={4} xl={3}>
<Product product={product} />
</Col>
))}
</Row>
<Paginate
pages={pages}
page={page}
keyword={keyword ? keyword : ''}
/>
</>
)}
</>
)
}
export default HomeScreen
I am new to nativescript!
I am trying to implement a search filter with listview in nativescript-angular, but I am not getting result filter from listview. My HTML component files' code follows.
Here I take searchbar component and listview for showing on screen (home.html):
<ActionBar title="Search" class="action-bar">
<SearchBar hint="Enter search term here"
[text]="searchTerm" (textChange)="onTextChanged($event)"
(clear)="onClear($event)" (submit)="onSubmit($event)" >
</SearchBar>
</ActionBar>
<StackLayout backgroundColor="#66cdaa" padding="5">
<!-- <Label text="Select country"></Label> -->
<listView [items]="dataItems"
suggestMode="Suggest" displayMode="Tokens" backgroundColor="yellow" hint="serch suggestion">
<SuggestionView tkAutoCompleteSuggestionView>
<ng-template tkSuggestionItemTemplate let-item="item">
<StackLayout orientation="vertical" padding="10">
<Label [text]="item.text"></Label>
</StackLayout>
</ng-template>
</SuggestionView>
</listView>
</StackLayout>
My home.component.ts file code:
import { Component, ViewChild,OnInit } from "#angular/core";
import { ObservableArray } from "tns-core-modules/data/observable-array";
import { TokenModel, AutoCompleteCompletionMode, AutoCompleteDisplayMode, AutoCompleteSuggestMode } from "nativescript-ui-autocomplete";
import { SearchBar } from "tns-core-modules/ui/search-bar";
export class BrowseComponent {
data = [];
name="countries"
searchPhrase: string;
private _items: ObservableArray<TokenModel>;
private countries = ["Australia", "Albania", "Austria", "Argentina", Maldives",
"Bulgaria", "Belgium", "Cyprus", "Italy", "Japan", "Denmark", "Finland",
"France", "Germany", "Greece", "Hungary", "Ireland", "Saudi Arabia",
"Senegal","Serbia","Seychelles","Sierra Leone","Singapore","Slovakia",
"Slovenia","South Africa","South Korea","Spain","Sri Lanka",
"St Kitts & Nevis","St Lucia","St Vincent","St Lucia","Sudan",
"Suriname", "Swaziland", "Sweden", "Switzerland", "Syria", "Taiwan",
"Tajikistan","Tanzania","Thailand","Timor","Togo","Tonga",
"Trinidad & Tobago","Tunisia","Turkey","Turkmenistan",
"Turks & Caicos","Uganda","Ukraine","United Arab Emirates",
"United Kingdom","Uruguay","Uzbekistan","Venezuela","Vietnam",
"Virgin Islands (US)","Yemen","Zambia","Zimbabwe"];
Here, I take country array and I am trying to filter this country list from html but I am not getting how to implement:
constructor() {
this.initDataItems();
// Use the component constructor to inject providers.
}
These are the functions from searchbar:
onSumit(args) {
const searchBar = args.object as SearchBar;
console.log(`Searching for ${searchBar.text}`);
}
onTextChanged(dataItem) {
const searchBar = dataItem as SearchBar;
console.log(`Input changed! New value: ${dataItem}`);
}
onClear(args) {
const searchBar = args.object as SearchBar;
console.log(`Clear event raised`);
}
get dataItems(): ObservableArray<TokenModel> {
return this._items;
}
Here this is observable Array I take:
private initDataItems() {
this._items = new ObservableArray<TokenModel>();
for (let i = 0; i < this.countries.length; i++) {
this._items.push(new TokenModel(this.countries[i], undefined));
}
ngOnInit(): void {
// Init your component properties here.
}
}
GlobalCompanyArrList: Array<Company>;
onTextChanged(dataItem) {
const searchBar = dataItem as SearchBar;
const searchValue = searchBar.text.toLowerCase();
let arrayItems=this.GlobalCompanyArrList; //Set Main countries Arraylist data to Local Arraylist for Searching
this.companylist_arr=new Array();
if (searchValue !== "") {
for (let i = 0; i < arrayItems.length; i++) {
if (arrayItems[i].NAME.toLowerCase().indexOf(searchValue) !== -1) {
this.companylist_arr.push(arrayItems[i]);
}
}
}
console.log("SearchBar text changed! New value: " + searchBar.text);
}
I have a RadList view which am trying to populate with data from a web service. The data returned from the service is in JSON format. So am trying to create a service which i will use to get the information by passing the respective URL. Below are the codes:-
interface class (cps-admin.interface.ts)
export class AddTenantInterface {
tenant_id: string;
tenant_names: string;
tenant_contact: string;
tenant_email: string;
starting_date: string;
rent_amount: string;
initial_water_reading: string;
water_unit_cost: string;
}
the service (CPSAdminService.ts)
import { Injectable } from "#angular/core";
import { AddTenantInterface } from "../interfaces/cps-admin.interface";
import { Observable } from "rxjs";
import { HttpClient, HttpHeaders, HttpResponse } from "#angular/common/http";
#Injectable()
export class CPSAdminService {
public _fetchTenantListUrl: string = "http://192.168.137.1/cps/fetchTenantsObj.php"; // fetch tenants list api url
constructor(private _http: HttpClient) {}
fetchTenantsList() {
let headers = this.createRequestHeader();
return this._http.get(this._fetchTenantListUrl, { headers: headers });
}
private createRequestHeader() {
// set headers here e.g.
let headers = new HttpHeaders({
"AuthKey": "my-key",
"AuthToken": "my-token",
"Content-Type": "application/json",
});
return headers;
}
}
Here is the tenants-component.ts
import { Component, OnInit, ChangeDetectorRef, ViewChild, ChangeDetectionStrategy} from "#angular/core";
import { AddTenantInterface } from "./../../interfaces/cps-admin.interface";
import { CPSAdminService } from "./../../services/cps-admin.service";
import * as ApplicationSettings from "application-settings";
import { ObservableArray } from "tns-core-modules/data/observable-array";
import { RadListViewComponent } from "nativescript-ui-listview/angular";
import { ListViewLinearLayout, ListViewEventData, RadListView, LoadOnDemandListViewEventData } from "nativescript-ui-listview";
import { setTimeout } from "tns-core-modules/timer";
import { TextField } from "ui/text-field";
#Component({
selector: "tenants-list",
moduleId: module.id,
templateUrl: "./tenants-list.component.html",
styleUrls: ["./tenants-list.css"]
})
export class TenantListComponent implements OnInit {
public rentItems: ObservableArray<AddTenantInterface>;
private _sourceDataItems: ObservableArray<AddTenantInterface>;
private layout: ListViewLinearLayout;
public searchPaymentsList: string;
private _myFilteringFunc: (item: any) => any;
#ViewChild("myListView") myListViewComponent: RadListViewComponent;
constructor(private _cPSAdminService: CPSAdminService, private _changeDetectionRef: ChangeDetectorRef) {}
ngOnInit(): void {
this.layout = new ListViewLinearLayout();
this.layout.scrollDirection = "Vertical";
this.initDataItems();
this._changeDetectionRef.detectChanges();
this.rentItems = new ObservableArray<AddTenantInterface>();
this.addMoreItemsFromSource(6);
}
get myFilteringFunc(): (item: any) => any {
return this._myFilteringFunc;
}
set myFilteringFunc(value: (item: any) => any) {
this._myFilteringFunc = value;
}
public onTextChanged(args) {
let searchBar = <TextField>args.object;
let listView = this.myListViewComponent.listView;
this.myFilteringFunc = (item: AddTenantInterface) => {
return item.tenant_names.includes(searchBar.text) || item.starting_date.includes(searchBar.text);
};
if (!listView.filteringFunction) {
listView.filteringFunction = this.myFilteringFunc;
} else {
listView.filteringFunction = undefined;
}
}
get rentDataItems(): ObservableArray<AddTenantInterface> {
return this.rentItems;
}
public addMoreItemsFromSource(chunkSize: number) {
let newItems = this._sourceDataItems.splice(0, chunkSize);
this.rentDataItems.push(newItems);
}
public onLoadMoreItemsRequested(args: LoadOnDemandListViewEventData) {
const that = new WeakRef(this);
if (this._sourceDataItems.length > 0) {
setTimeout(function () {
const listView: RadListView = args.object;
that.get().addMoreItemsFromSource(2);
listView.notifyLoadOnDemandFinished();
}, 1000);
} else {
args.returnValue = false;
}
}
// ===== **PROBLEM IS HERE**
private initDataItems() {
this._sourceDataItems = new ObservableArray(this._cPSAdminService.fetchTenantsList());
}
}
Note where i have marked "PROBLEM IS HERE", Here is the error image clip
I just don't what the problem. When i place "any" as the method return in fetchTenantsList() like so fetchTenantsList(): any , the error disappears but nothing is displayed in list.
And when i hard code the data there like below, it works;
tenantData: AddTenantInterface[] = [
{
"tenant_id":"542948",
"tenant_names":"Jane Doe",
"tenant_contact":"0787916686",
"tenant_email":"jane.doe#ymail.com",
"starting_date":"2004-08-09",
"rent_amount":"850000",
"initial_water_reading":"100",
"water_unit_cost":"250"
},
{
"tenant_id":"575550",
"tenant_names":"Emily Clarke",
"tenant_contact":"07752654868",
"tenant_email":"emily.clarke#gmail.com",
"starting_date":"2007-07-04",
"rent_amount":"700000",
"initial_water_reading":"400",
"water_unit_cost":"250"
},
{
"tenant_id":"422031",
"tenant_names":"John Doe",
"tenant_contact":"0772485364",
"tenant_email":"john.doe#ymail.com",
"starting_date":"2008-12-14",
"rent_amount":"700000",
"initial_water_reading":"120",
"water_unit_cost":"250"
}
];
fetchTenantsList(): AddTenantInterface[] {
return this.tenantData;
}
Here is my component html:
<GridLayout class="page page-content custom_font_family m-5" rows="50, *">
<StackLayout class="input-field" row="0">
<TextField
hint="search..."
[(ngModel)]='searchPaymentsList'
secure="false"
returnKeyType="done"
(textChange)="onTextChanged($event)"
autocorrect="false"
autocapitalizationType="allCharacters"
focus="onFocus"
blur="onBlur"
class="input input-border"
color="navy"
textFieldHintColor="#bfbfbf"></TextField>
</StackLayout>
<GridLayout tkExampleTitle tkToggleNavButton row="1" rows="*">
<RadListView [items]="rentDataItems" loadOnDemandMode="Manual" (loadMoreDataRequested)="onLoadMoreItemsRequested($event)" [filteringFunction]="myFilteringFunc" #myListView row="0">
<ng-template tkListItemTemplate let-item="item" let-i="index" let-odd="odd" let-even="even">
<StackLayout [class.odd]="odd" [class.even]="even" class="list-group-item p-y-10 m-y-2 t-17 p-x-5">
<Label [text]='item.starting_date | date: "d-M-y"'></Label>
<Label [text]='item.tenant_id + ". "'></Label>
<Label [text]='item.tenant_names'></Label>
<Label [text]='item.tenant_contact'></Label>
<Label [text]='item.tenant_email'></Label>
<Label [text]='item.rent_amount | currency:"UG ":"Ug. ": "3.1-1"'></Label>
</StackLayout>
<!--</GridLayout>-->
</ng-template>
<ng-template tkListViewHeader>
<GridLayout class="header" rows="*" columns="30, auto, auto, auto, auto, auto">
<Label row="0" col="0" text='Date'></Label>
<Label row="0" col="1" text='No '></Label>
<Label row="0" col="2" text='Names'></Label>
<Label row="0" col="3" text='Contact'></Label>
<Label row="0" col="4" text='Email'></Label>
<Label row="0" col="5" text='Rent'></Label>
</GridLayout>
</ng-template>
<ListViewLinearLayout *tkIfIOS tkListViewLayout itemHeight="120"></ListViewLinearLayout>
<div *tkIfIOS>
<GridLayout *tkListLoadOnDemandTemplate class="loadOnDemandItemGridLayout">
<Label text="Load More" horizontalAlignment="center" verticalAlignment="center"></Label>
</GridLayout>
</div>
</RadListView>
</GridLayout>
</GridLayout>
Any help is appreciated.
this._cPSAdminService.fetchTenantsList()
This will return an Observable that emits the result from server when the request is completed. You can't simply pass it to Observable Array.
It must be something like this,
this._cPSAdminService.fetchTenantsList()
.subscribe((result) => {
this._sourceDataItems = new ObservableArray(result);
});
Alright my bad. I was supposed to remove
this.addMoreItemsFromSource(6) from ngOnInit() and put it inside the subscribe.
Here is the solution.
import { Component, OnInit, ChangeDetectorRef, ViewChild, ChangeDetectionStrategy} from "#angular/core";
import { AddTenantInterface } from "./../../interfaces/cps-admin.interface";
import { CPSAdminService } from "./../../services/cps-admin.service";
import * as ApplicationSettings from "application-settings";
import { ObservableArray } from "tns-core-modules/data/observable-array";
import { RadListViewComponent } from "nativescript-ui-listview/angular";
import { ListViewLinearLayout, ListViewEventData, RadListView, LoadOnDemandListViewEventData } from "nativescript-ui-listview";
import { setTimeout } from "tns-core-modules/timer";
import { TextField } from "ui/text-field";
#Component({
selector: "tenants-list",
moduleId: module.id,
templateUrl: "./tenants-list.component.html",
styleUrls: ["./tenants-list.css"]
})
export class TenantListComponent implements OnInit {
public rentItems: ObservableArray<AddTenantInterface>;
private _sourceDataItems: ObservableArray<AddTenantInterface>;
private layout: ListViewLinearLayout;
public searchPaymentsList: string;
private _myFilteringFunc: (item: any) => any;
#ViewChild("myListView") myListViewComponent: RadListViewComponent;
constructor(private _cPSAdminService: CPSAdminService, private _changeDetectionRef: ChangeDetectorRef) {}
ngOnInit(): void {
this.layout = new ListViewLinearLayout();
this.layout.scrollDirection = "Vertical";
this.initDataItems();
this._changeDetectionRef.detectChanges();
this.rentItems = new ObservableArray<AddTenantInterface>();
//this.addMoreItemsFromSource(6); // remove this and put it inthe initDataItems method
}
get myFilteringFunc(): (item: any) => any {
return this._myFilteringFunc;
}
set myFilteringFunc(value: (item: any) => any) {
this._myFilteringFunc = value;
}
public onTextChanged(args) {
let searchBar = <TextField>args.object;
let listView = this.myListViewComponent.listView;
this.myFilteringFunc = (item: AddTenantInterface) => {
return item.tenant_names.includes(searchBar.text) || item.starting_date.includes(searchBar.text);
};
if (!listView.filteringFunction) {
listView.filteringFunction = this.myFilteringFunc;
} else {
listView.filteringFunction = undefined;
}
}
get rentDataItems(): ObservableArray<AddTenantInterface> {
return this.rentItems;
}
public addMoreItemsFromSource(chunkSize: number) {
let newItems = this._sourceDataItems.splice(0, chunkSize);
this.rentDataItems.push(newItems);
}
public onLoadMoreItemsRequested(args: LoadOnDemandListViewEventData) {
const that = new WeakRef(this);
if (this._sourceDataItems.length > 0) {
setTimeout(function () {
const listView: RadListView = args.object;
that.get().addMoreItemsFromSource(2);
listView.notifyLoadOnDemandFinished();
}, 1000);
} else {
args.returnValue = false;
}
}
public initDataItems() {
//this._sourceDataItems = new ObservableArray(this._cPSAdminService.fetchTenantsList());
this._cPSAdminService.fetchTenantsList()
.subscribe( (result) => {
this._sourceDataItems = new ObservableArray(result);
this.addMoreItemsFromSource(6); // put it here
},
error => {
console.log("Error: ", error);
});
}
}
Thanks #Manoj
I've been struggling to make this react virtualized table example work & seriously starting to doubt my sanity. I've created a react app and I'm just trying to render the example table inside App.js with this:
class App extends Component {
render() {
var data = [1,2,3,4,5,6,7,8,9,10];
return (
<TableExample
list={data}
/>
);
}
}
React keeps saying list isn't defined - it seems obvious I'm not getting the data into the component the right way. I haven't been able to understand the example code, what props need to be passed in and what they should be named. Sorry for the stupid question but I've been stuck forever not knowing where else to find an answer. The table example code is below:
/** #flow */
import Immutable from 'immutable';
import PropTypes from 'prop-types';
import * as React from 'react';
import {
ContentBox,
ContentBoxHeader,
ContentBoxParagraph,
} from '../demo/ContentBox';
import {LabeledInput, InputRow} from '../demo/LabeledInput';
import AutoSizer from '../AutoSizer';
import Column from './Column';
import Table from './Table';
import SortDirection from './SortDirection';
import SortIndicator from './SortIndicator';
import styles from './Table.example.css';
export default class TableExample extends React.PureComponent {
static contextTypes = {
list: PropTypes.instanceOf(Immutable.List).isRequired,
};
constructor(props, context) {
super(props, context);
const sortBy = 'index';
const sortDirection = SortDirection.ASC;
const sortedList = this._sortList({sortBy, sortDirection});
this.state = {
disableHeader: false,
headerHeight: 30,
height: 270,
hideIndexRow: false,
overscanRowCount: 10,
rowHeight: 40,
rowCount: 1000,
scrollToIndex: undefined,
sortBy,
sortDirection,
sortedList,
useDynamicRowHeight: false,
};
this._getRowHeight = this._getRowHeight.bind(this);
this._headerRenderer = this._headerRenderer.bind(this);
this._noRowsRenderer = this._noRowsRenderer.bind(this);
this._onRowCountChange = this._onRowCountChange.bind(this);
this._onScrollToRowChange = this._onScrollToRowChange.bind(this);
this._rowClassName = this._rowClassName.bind(this);
this._sort = this._sort.bind(this);
}
render() {
const {
disableHeader,
headerHeight,
height,
hideIndexRow,
overscanRowCount,
rowHeight,
rowCount,
scrollToIndex,
sortBy,
sortDirection,
sortedList,
useDynamicRowHeight,
} = this.state;
const rowGetter = ({index}) => this._getDatum(sortedList, index);
return (
<ContentBox>
<ContentBoxHeader
text="Table"
sourceLink="https://github.com/bvaughn/react-virtualized/blob/master/source/Table/Table.example.js"
docsLink="https://github.com/bvaughn/react-virtualized/blob/master/docs/Table.md"
/>
<ContentBoxParagraph>
The table layout below is created with flexboxes. This allows it to
have a fixed header and scrollable body content. It also makes use of{' '}
<code>Grid</code> for windowing table content so that large lists are
rendered efficiently. Adjust its configurable properties below to see
how it reacts.
</ContentBoxParagraph>
<ContentBoxParagraph>
<label className={styles.checkboxLabel}>
<input
aria-label="Use dynamic row heights?"
checked={useDynamicRowHeight}
className={styles.checkbox}
type="checkbox"
onChange={event =>
this._updateUseDynamicRowHeight(event.target.checked)
}
/>
Use dynamic row heights?
</label>
<label className={styles.checkboxLabel}>
<input
aria-label="Hide index?"
checked={hideIndexRow}
className={styles.checkbox}
type="checkbox"
onChange={event =>
this.setState({hideIndexRow: event.target.checked})
}
/>
Hide index?
</label>
<label className={styles.checkboxLabel}>
<input
aria-label="Hide header?"
checked={disableHeader}
className={styles.checkbox}
type="checkbox"
onChange={event =>
this.setState({disableHeader: event.target.checked})
}
/>
Hide header?
</label>
</ContentBoxParagraph>
<InputRow>
<LabeledInput
label="Num rows"
name="rowCount"
onChange={this._onRowCountChange}
value={rowCount}
/>
<LabeledInput
label="Scroll to"
name="onScrollToRow"
placeholder="Index..."
onChange={this._onScrollToRowChange}
value={scrollToIndex || ''}
/>
<LabeledInput
label="List height"
name="height"
onChange={event =>
this.setState({height: parseInt(event.target.value, 10) || 1})
}
value={height}
/>
<LabeledInput
disabled={useDynamicRowHeight}
label="Row height"
name="rowHeight"
onChange={event =>
this.setState({
rowHeight: parseInt(event.target.value, 10) || 1,
})
}
value={rowHeight}
/>
<LabeledInput
label="Header height"
name="headerHeight"
onChange={event =>
this.setState({
headerHeight: parseInt(event.target.value, 10) || 1,
})
}
value={headerHeight}
/>
<LabeledInput
label="Overscan"
name="overscanRowCount"
onChange={event =>
this.setState({
overscanRowCount: parseInt(event.target.value, 10) || 0,
})
}
value={overscanRowCount}
/>
</InputRow>
<div>
<AutoSizer disableHeight>
{({width}) => (
<Table
ref="Table"
disableHeader={disableHeader}
headerClassName={styles.headerColumn}
headerHeight={headerHeight}
height={height}
noRowsRenderer={this._noRowsRenderer}
overscanRowCount={overscanRowCount}
rowClassName={this._rowClassName}
rowHeight={useDynamicRowHeight ? this._getRowHeight : rowHeight}
rowGetter={rowGetter}
rowCount={rowCount}
scrollToIndex={scrollToIndex}
sort={this._sort}
sortBy={sortBy}
sortDirection={sortDirection}
width={width}>
{!hideIndexRow && (
<Column
label="Index"
cellDataGetter={({rowData}) => rowData.index}
dataKey="index"
disableSort={!this._isSortEnabled()}
width={60}
/>
)}
<Column
dataKey="name"
disableSort={!this._isSortEnabled()}
headerRenderer={this._headerRenderer}
width={90}
/>
<Column
width={210}
disableSort
label="The description label is really long so that it will be truncated"
dataKey="random"
className={styles.exampleColumn}
cellRenderer={({cellData}) => cellData}
flexGrow={1}
/>
</Table>
)}
</AutoSizer>
</div>
</ContentBox>
);
}
_getDatum(list, index) {
return list.get(index % list.size);
}
_getRowHeight({index}) {
const {list} = this.context;
return this._getDatum(list, index).size;
}
_headerRenderer({dataKey, sortBy, sortDirection}) {
return (
<div>
Full Name
{sortBy === dataKey && <SortIndicator sortDirection={sortDirection} />}
</div>
);
}
_isSortEnabled() {
const {list} = this.context;
const {rowCount} = this.state;
return rowCount <= list.size;
}
_noRowsRenderer() {
return <div className={styles.noRows}>No rows</div>;
}
_onRowCountChange(event) {
const rowCount = parseInt(event.target.value, 10) || 0;
this.setState({rowCount});
}
_onScrollToRowChange(event) {
const {rowCount} = this.state;
let scrollToIndex = Math.min(
rowCount - 1,
parseInt(event.target.value, 10),
);
if (isNaN(scrollToIndex)) {
scrollToIndex = undefined;
}
this.setState({scrollToIndex});
}
_rowClassName({index}) {
if (index < 0) {
return styles.headerRow;
} else {
return index % 2 === 0 ? styles.evenRow : styles.oddRow;
}
}
_sort({sortBy, sortDirection}) {
const sortedList = this._sortList({sortBy, sortDirection});
this.setState({sortBy, sortDirection, sortedList});
}
_sortList({sortBy, sortDirection}) {
const {list} = this.context;
return list
.sortBy(item => item[sortBy])
.update(
list => (sortDirection === SortDirection.DESC ? list.reverse() : list),
);
}
_updateUseDynamicRowHeight(value) {
this.setState({
useDynamicRowHeight: value,
});
}
}
Looking at some previous questions, it seems that the example is using some components that are not included inside dist package. Thats probably why you are getting undefined error.
Here is the most basic example of Tables in react virtulized:
import React from 'react';
import ReactDOM from 'react-dom';
import { Column, Table } from 'react-virtualized';
import 'react-virtualized/styles.css'; // only needs to be imported once
// Table data as an array of objects
const list = [
{ name: 'Brian Vaughn', description: 'Software engineer' }
// And so on...
];
// Render your table
ReactDOM.render(
<Table
width={300}
height={300}
headerHeight={20}
rowHeight={30}
rowCount={list.length}
rowGetter={({ index }) => list[index]}
>
<Column
label='Name'
dataKey='name'
width={100}
/>
<Column
width={200}
label='Description'
dataKey='description'
/>
</Table>,
document.getElementById('example')
);
When learning new library, its always best to start with the most simple example then expand on it. Here is the link to the full table docs.
The issue with the example is that in order for contextTypes to work, the parent component needs to define corresponding contextTypes and a getChildContext function.
In the parent component:
class App extends React.Component {
static childContextTypes = {
list: PropTypes.instanceOf(Immutable.List).isRequired
};
getChildContext() {
return {
list
};
}
render() {
return <TableExample />;
}
}
Mentioned in this issue; see lines 48-53 and 68-76 of the react-virtualized demo Application.js.
I am building a 1-1 chat using Angular4 and Firebase and I am pretty new to Angular.
In order to initiate a conversation, I am trying to display all available users form '/users' subcollection. So, I need to get user/{user.uid}/username.
This is my chat.component.ts:
import { Component, OnInit } from '#angular/core';
import {AngularFireDatabase, FirebaseListObservable} from 'angularfire2/database';
import { AngularFireAuth } from 'angularfire2/auth';
import { UserSessionService } from '../_services/user-session.service';
#Component({
selector: 'app-chat',
templateUrl: './chat.component.html',
styleUrls: ['./chat.component.css']
})
export class ChatComponent implements OnInit {
items: FirebaseListObservable<any[]>;
other_users: FirebaseListObservable<any[]>;
user_id: any;
from: any;
msgVal: string = '';
constructor(public afAuth: AngularFireAuth, public af: AngularFireDatabase, public logged_user: UserSessionService ){ }
ngOnInit() {
this.from= this.logged_user.getFirebaseUid();
this.user_id= this.logged_user.getFirebaseUid();
this.items = this.af.list('/personalMessages', {
query: { limitToLast: 5 }
});
this.other_users= this.af.list('/users');
}
findChat(){
this.other_users= this.other_users;
this.user_id = this.user_id;
}
chatSend(theirMessage: string) {
this.items.push({ text: theirMessage, from: this.logged_user.getFirebaseUid(), isRead: false, timestamp: + new Date() });
this.msgVal = '';
this.user_id = this.user_id;
this.other_users= this.other_users;
}
}
And this is my chat.component.html:
<div class="users-chat-container" *ngFor="let other_user of other_users| async">
<div id="circle" style="background-color:pink;">
</div>
<br/> {{other_user.username}}
</div>
<div class="chat-container" *ngFor="let item of items | async">
<div id="circle" style="background-image:url( http://www.ics.forth.gr/mobile/female.png);">
</div>
<br/> {{item.from}}
<p>{{item.text}}</p>
</div>
<input type="text" id="message" placeholder="Type a message..." (keyup.enter)="chatSend($event.target.value)" [(ngModel)]="msgVal" />
How can I iterate over the array of objects I get from '/users' collection? Thank you! :)
you need use ForkJoin. ForkJoin will take users list as input and fire parallel request for all users list
try some thing like this
this.af.list('/users')
.mergeMap((users) => {
if (users.length > 0) {
return Observable.forkJoin(
users.map((user) => this.af.database
.object(`user/${user.$uid}/username`)
.first()
),
(...values) => { // here you can assign username
users.forEach((user, index) => { user.username = values[index]; });
return users;
}
);
}
return Observable.of([]);
});
more info about forkJoin
https://www.learnrxjs.io/operators/combination/forkjoin.html
You need an array for *ngFor. With object.keys you can create an array of the objects. In the example below I have done this with players from a group, coming as objects from firebase.
private getPlayersPerGroup(group: Group) {
this.playersInGroup = [];
if (group["players"]) {
Object.keys(group["players"]).forEach((key) => {
this.groupService.getPlayerById(key).then((player) => {
this.playersInGroup.push(player);
});
});
}
}