Angular: How to filter a dataTable using query params - css

I want to filter my table using query params that I got from the user input in another component.
I am able to get the data that the users send through the input and print it to the console.log. But I don't know how to use it to filter the table.
i have built a filter but for some reason i cant call it.
This is my filter :
import { Pipe, PipeTransform } from "#angular/core";
import { Container } from "./Entites/Container";
#Pipe({
name: 'textFilter'
})
export class textFilter implements PipeTransform {
transform(
containers : Container[],
storageSearch?: any,
clientSearch?: string,
): Container[] {
if (!containers) return [];
if (!storageSearch) return containers;
storageSearch = storageSearch.toLocaleLowerCase();
containers = [...containers.filter(user => user.TAOR_QTSR_EBRI.toLocaleLowerCase() === storageSearch)];
if (!clientSearch) return containers;
clientSearch = clientSearch.toLocaleLowerCase();
containers = [...containers.filter(user => user.LQOCH_SHM_LEOZI_QTSR.toLocaleLowerCase() === clientSearch)];
// if (!roleSearch) return users;
//roleSearch = roleSearch.toLocaleLowerCase();
//users = [...users.filter(user => user.role.toLocaleLowerCase() === roleSearch)];
return containers;
}
}
This is my component ngOnInit i have some other filters there, for example checkbox filter :
ngOnInit() {
this.marinService.getAllContainers().subscribe((result) => {
//Data
this.dataSource = new MatTableDataSource(result);
//Paginator
this.dataSource.paginator = this.paginator;
//AutoFilter Form 1st page
this.clientType = this.route.snapshot.queryParamMap.get('clientType');
this.storageType= this.route.snapshot.queryParamMap.get('storageTypes');
console.log('The Client name is : '+this.clientType+' '+'The storage Facility is : '+this.storageType);
//CheckBox Filter
this.dataSource.filterPredicate = (data: Container, filter: any) => {
return filter.split(',').every((item: any) => data.SOG_MCOLH.indexOf(item) !== -1);
};
this.filterCheckboxes.subscribe((newFilterValue: any[]) => {
this.dataSource.filter = newFilterValue.join(',');
});
});
}
What I want to accomplish is to be able to filter the table using the query params.

We can pass the data which you have received as input (in the parent component) to the material table filtering function applyFilter inside the child component...
relevant parent TS:
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
styles: [`.parent{background:lightgreen; padding:2%;}`],
template: `
Enter string for filtering: <input type='text' [(ngModel)]='inputStr' />
<!-- {{inputStr}} -->
<table-filtering-example [inputStr]='inputStr'>loading</table-filtering-example>
`,
})
export class AppComponent {
inputStr: string = '';
constructor() { }
}
relevant child TS:
export class TableFilteringExample implements OnInit, OnChanges {
displayedColumns: string[] = ['position', 'name', 'weight', 'symbol'];
dataSource = new MatTableDataSource(ELEMENT_DATA);
#Input() inputStr:string;
constructor(){}
ngOnInit(){}
ngOnChanges(){
/* just call the applyFilter button with the data which is passed to your component from it's parent */console.log("(ngOnChanges)this.inputStr:", this.inputStr);
this.applyFilter(this.inputStr);
}
applyFilter(filterValue: string) {
this.dataSource.filter = filterValue.trim().toLowerCase();
}
}
complete working stackblitz here

Related

NGRX Select returning Store not a value

catalogueSelection in Store Image
I have the data I require in state (catalogueSelection: searchTextResult & categoryCheckboxResult) and need to pass the string 'SearchTextResult' into one component and the Array 'categoryCheckboxResult' into another.
When I try to retrieve the required values I am retrieving the whole store. I have looked at numerous websites and entries here but getting very confused now.
Model:
export class SearchTextResult {
searchTextResult: string;
}
export class CategoryCheckboxResult {
categoryCheckboxResult:Array<CategoryCheckboxResult>;
}
Actions:
import { Action } from '#ngrx/store';
import { SearchTextResult, CategoryCheckboxResult } from 'app/#core/services/products/products.model';
export enum UserCatalogueSelectionTypes {
AddSearchTextResult = '[SearchTextResult] AddResult',
AddCategoryCheckboxResult = '[CategoryCheckboxResult] AddResult',
GetSearchTextResult = '[SearchTextResult] GetResult',
}
export class AddSearchTextResult implements Action {
readonly type = UserCatalogueSelectionTypes.AddSearchTextResult;
constructor(public payload: SearchTextResult){
}
}
export class AddCategoryCheckboxResult implements Action {
readonly type = UserCatalogueSelectionTypes.AddCategoryCheckboxResult;
constructor(public payload: CategoryCheckboxResult){
}
}
export class GetSearchTextResult implements Action {
readonly type = UserCatalogueSelectionTypes.GetSearchTextResult;
}
export type UserCatalogueSelectionUnion =
| AddSearchTextResult
| AddCategoryCheckboxResult
| GetSearchTextResult
Reducers:
import { SearchTextResult, CategoryCheckboxResult} from "app/#core/services/products/products.model";
import { UserCatalogueSelectionTypes, UserCatalogueSelectionUnion} from "../actions/products.actions";
export interface UserCatalogueSelectionState {
searchTextResult: SearchTextResult | null;
categoryCheckboxResult: CategoryCheckboxResult | null;
}
export const initialState: UserCatalogueSelectionState = {
searchTextResult: null,
categoryCheckboxResult: null,
}
export function reducer(state:UserCatalogueSelectionState = initialState, action: UserCatalogueSelectionUnion ): UserCatalogueSelectionState{
switch (action.type) {
case UserCatalogueSelectionTypes.AddSearchTextResult:
return {
...state,
searchTextResult: action.payload,
};
case UserCatalogueSelectionTypes.AddCategoryCheckboxResult:
return {
...state,
categoryCheckboxResult: action.payload,
};
case UserCatalogueSelectionTypes.GetSearchTextResult: {
return state;
}
default: {
return state;
}
}
}
Selectors:
import { createSelector,createFeatureSelector } from "#ngrx/store";
import {UserCatalogueSelectionState} from '../../store/reducer/products.reducer';
export const fetchSearchTextResults = createFeatureSelector<UserCatalogueSelectionState>("searchTextResult");
export const fetchSearchTextResult = createSelector (
fetchSearchTextResults,
(state:UserCatalogueSelectionState) => state.searchTextResult.searchTextResult
);
export const fetchCatalogueCheckBoxResults = createFeatureSelector<UserCatalogueSelectionState>("catalogueCheckboxResult");
export const fetchCatalogueCheckBoxResult = createSelector (
fetchCatalogueCheckBoxResults,
(state: UserCatalogueSelectionState) => state.categoryCheckboxResult.categoryCheckboxResult
);
My Component 1
Observable:
public searchTextResult: Observable<String>;
Contructor: (part of)
private store: Store<fromCatalogueSelection.UserCatalogueSelectionState>
Code Snippet: (asking for the data)
this.searchTextResult = this.store.select('SearchTextResult');
console.log('TESTING SEARCH TEXT: ', this.searchTextResult);
Console:
TESTING SEARCH TEXT: StoreĀ {_isScalar: false, actionsObserver: ActionsSubject, reducerManager: >ReducerManager, source: Store, operator: DistinctUntilChangedOperator}
My Component 2
Observable
searchTextResult$: Observable<CatalogueSelectionActions.GetSearchTextResult>;
Code Snippet: (asking for the data)
this.searchTextResult$ = this.store.select('GetSearchTextResult');
console.log('TESTING SEARCH TEXT: ', this.searchTextResult$);
Console:
TESTING SEARCH TEXT: StoreĀ {_isScalar: false, actionsObserver: ActionsSubject, reducerManager: > ReducerManager, source: Store, operator: DistinctUntilChangedOperator}
I've given up on the Selectors for the moment. Any help much appreciated as I'm going a round in circles.
You are almost there, the value from the console log is the observable object, everytime you select something from the store, you will get the value wrapped within an observable. You just need to subscribe to it:
this.searchTextResult$ = this.store.select('GetSearchTextResult');
this.searchTextResult$.subscribe((yourData) => console.log(yourData));
Also, since you are working with selectors, use them, you don't have to write the state/selector name:
selector
...
export const fetchCatalogueCheckBoxResult = createSelector (
fetchCatalogueCheckBoxResults,
(state: UserCatalogueSelectionState) =>
state.categoryCheckboxResult.categoryCheckboxResult
);
component
import * as YourSelectors from './store/something/selectors/yourthing.selectors'
...
...
this.searchTextResult$ = this.store
.select(YourSelectors.fetchCatalogueCheckBoxResult)
.subscribe(console.log);
Additionally, try to subscribe using the async pipe delegating that to your template html so you don't have to deal with the subscription in the code, for example:
component
...
export class Component {
searchTextResult$!: Observable<any> // your data type here
...
...
this.searchTextResult$ = this.store
.select(YourSelectors.fetchCatalogueCheckBoxResult)
}
html
<ng-container *ngIf="(searchTextResult$ | async) as result">
<p>Your result value: {{ result }}</p>
</ng-container>

Lit-Element - Can't get Id of item from Object

I am learning lit-element and have run into a small problem I am trying to setup the ability to remove an Item from my list but I am unable to get the id of my Item it is coming across as undefined when I test it with console.log. I have three components the add-item.js which adds items to the list that is working fine. app.js is the main component that handles the auto refresh of the page aswell as the main rendering of the page this is where I have the event listeners for the addItem and the removeItem. Then I have todo-item component which is where I have the object that I am trying to get the ID for the remove functionality. Im at a loss at what I am doing wrong here and was hoping some one could take a look and point me in the right direction
here is the code so far .
add-item.js
```
import {LitElement, html} from 'lit-element';
class AddItem extends LitElement{
static get properties(){
return{
todoList: Array,
todoItem: String
}
}
constructor(){
super();
this.todoItem = '';
}
inputKeypress(e){
if(e.keyCode == 13){
e.target.value="";
this.onAddItem();
}else{
this.todoItem = e.target.value;
}
}
onAddItem(){
if(this.todoItem.length > 0){
let storedTodoList = JSON.parse(localStorage.getItem('todo-
list'));
storedTodoList = storedTodoList === null ? [] : storedTodoList;
storedTodoList.push({
id: new Date().valueOf(),
item: this.todoItem,
done: false
});
localStorage.setItem('todo-list',
JSON.stringify(storedTodoList));
this.dispatchEvent(new CustomEvent('addItem',{
bubbles: true,
composed: true,
detail: {
todoList: storedTodoList
}
}));
this.todoItem = '';
}
}
render(){
return html `
<div>
<input value=${this.todoItem}
#keyup="${(e) => this.inputKeypress(e)}">
</input>
<button #click="${() => this.onAddItem()}">Add Item</button>
</div>
`;
}
}
customElements.define('add-item', AddItem)
```
app.js
```
import {LitElement, html} from 'lit-element';
import './add-item';
import './list-items';
class TodoApp extends LitElement{
static get properties(){
return{
todoList: Array
}
}
constructor(){
super();
let list = JSON.parse(localStorage.getItem('todo-list'));
this.todoList = list === null ? [] : list;
}
firstUpdated(){
this.addEventListener('addItem', (e) => {
this.todoList = e.detail.todoList;
});
this.addEventListener('removeItem', (e) => {
let index = this.todoList.map(function(item) {return
item.id}).indexOf(e.detail.itemId);
this.todoList.splice(index, 1);
this.todoList = _.clone(this.todoList);
localStorage.setItem('todo-list', JSON.stringify(this.todoList));
})
}
render(){
return html `
<h1>Hello todo App</h1>
<add-item></add-item>
<list-items .todoList=${this.todoList}></list-items>
`;
}
}
customElements.define('todo-app', TodoApp)
```
todo-item.js
```
import {LitElement, html} from 'lit-element';
class TodoItem extends LitElement{
static get properties(){
return{
todoItem: Object
}
}
constructor(){
super();
this.todoItem = {};
}
onRemove(id){
this.dispatchEvent(new CustomEvent('removeItem',{
bubbles: true,
composed: true,
detail:{
itemId: id
}
}));
}
render(){
console.log(this.todoItem.id);
return html `<li>${this.todoItem}</li>
<button #click="${() =>
this.onRemove(this.todoItem.id)}">Remove</button>`;
}
}
customElements.define('todo-item', TodoItem);
```
I am looking to get the Id of the item so that I can remove it from the list for example if i have 5 items, one, two , three , four, five and I click the button to remove the third Item it should be removed and the list updated with the remaining items .. right now it is deleting the items but it is the last one on the list which is what I do not want to happen.
Looking forward to some help on this so that I can move forward with the project
thank you .
The issues has been resolved,
I was not supplying the entire array, just one element. After fixing the code I was able to get the Id of the object and move forward with my project as expected.

Get list as array from Firebase with angularfire2(v5) and ngrx effects

I have to tell you I'm getting crazy with it. I'm trying to get data from Firebase with AngularFire2(v.5) then work with it on #ngrx/effects and store it on #ngrx/store. Well, as I need the data with the keys, my code of effects looks like this:
spaces.effects.ts
#Effect()
getSpaces$ = this.actions$.ofType(SpacesActions.GET_SPACES_REQUEST)
.switchMap((action: SpacesActions.GetSpacesRequest) => {
return this.afs.list<Space>('/spaces').snapshotChanges()
.switchMap(actions => {
console.log('action is ', actions);
return actions.map(space => {
const $key = space.payload.key;
const data: Space = { $key, ...space.payload.val() };
console.log('snapshot is: ', data);
return new SpacesActions.GetSpacesSuccess(data);
});
}
);
My "actions" comes with the data and the key, then I get the key for each item because then I could update and delete items easily. My database has 3 items with 3 keys. If I run this code and log it, first I can see all items in 1 array with their payloads and with the second log I see each payload as snapshot.
When I call GetSpacesSuccess, I'd like to send all snapshots I got (with key and item) then store it. The way I'm doing now dispatch this action 3 times and I can see only 2 items on the screen because the first one is overridden by the second one.
So, two questions: Is there any easier way to get the items from firebase with their keys then store them with #ngrx? If not, what am I doing wrong that my first item is being overridden and my action is being dispatched 3 times?
Please, I'm doing my best with it as I'm learning. Thank you!
spaces.reducers.ts
case SpacesActions.GET_SPACES_REQUEST:
return {
state,
spaces: null,
loading: true
};
case SpacesActions.GET_SPACES_SUCCESS:
return {
...state,
...action.payload,
spaces: [state, action.payload],
loading: false
};
spaces.actions.ts
export class GetSpacesRequest implements Action {
readonly type = GET_SPACES_REQUEST;
}
export class GetSpacesSuccess implements Action {
readonly type = GET_SPACES_SUCCESS;
constructor(public payload: Space) {} <<<<<HERE I'D LIKE TO GET THE FULL ARRAY WITH EACH KEY
}
Thanks #AndreaM16 for the most complete answer. I went through the night working on it and I ended up doing it different. Actually, in the learning process we make mistakes in order to get the knowledge. Probably your solution is better than mine and I'll study that, thanks. Please, if possible, I'd love to hear your comments about my solution.
Finally, after reading lots of documentation, my effects is now this one, I don't have any error catcher though:
private spacesList = 'spaces/';
#Effect()
getSpaces$ = this.actions$.ofType(SpacesActions.GET_SPACES_REQUEST)
.switchMap(payload => this.afs.list(this.spacesList).snapshotChanges()
.map(spaces => {
return spaces.map(
res => {
const $key = res.payload.key;
const space: Space = {$key, ...res.payload.val()};
return space;
}
);
})
.map(res =>
new SpacesActions.GetSpacesSuccess(res)
));
Reducer
case SpacesActions.GET_SPACES_REQUEST:
return Object.assign({}, state, {
spaces: null,
loading: true
});
case SpacesActions.GET_SPACES_SUCCESS:
return Object.assign({}, state, {
spaces: action.payload,
loading: false
});
Actions
export class GetSpacesRequest implements Action {
readonly type = GET_SPACES_REQUEST;
}
export class GetSpacesSuccess implements Action {
readonly type = GET_SPACES_SUCCESS;
constructor(public payload: Space[]) {}
}
And, in my component, where I need the list:
constructor(private store: Store<fromSpaces.FeatureState>) {}
ngOnInit() {
this.store.dispatch(new SpacesActions.GetSpacesRequest());
this.spacesState = this.store.select('spaces');
}
If I understood your question correctly, you would like to store for each Item also store its key. You are looking for Map.
I would structure your feature as follows.
spaces.actions.ts:
Loading spaces requires no payload, while success has only an array of Space. I think you should build your Map<string,Space> in your reducer (string is your key).
import { Action } from '#ngrx/store';
/** App Models **/
import { Space } from './space.model';
export const GET_SPACES = '[Spaces] Spaces get';
export const GET_SPACES_SUCCESS = '[Start] Spaces get - Success';
export class GetSpacesAction implements Action {
readonly type = GET_SPACES;
}
export class GetSpacesActionSuccess implements Action {
readonly type = GET_SPACES_SUCCESS;
constructor(public payload: Space[]) {}
}
export type All
= GetSpacesAction
| GetSpacesActionSuccess;
spaces.effects.ts:
I'm assuming you just need a method to fetch spaces. If you need to do other stuff, just edit this piece of code. spaceService.getSpaces() is supposed to return only an array of Spaces. So, create a new Space model and, on your service, map each json entry to a new Space().
import { Injectable } from '#angular/core';
import { Actions, Effect } from '#ngrx/effects';
/** rxjs **/
import {map} from 'rxjs/operators/map';
import {mergeMap} from 'rxjs/operators/mergeMap';
import {catchError} from 'rxjs/operators/catchError';
/** ngrx **/
import * as spacesActions from './spaces.actions';
/** App Services **/
import { SpacesService } from './spaces.service';
#Injectable()
export class SpacesEffects {
#Effect() getSpaces$ = this.actions$
.ofType(spaceActions.GET_SPACES)
.pipe(
mergeMap(() => {
return this.spaceService.getSpaces()
.pipe(
map((spaces) => {
return new spacesActions.GetSpacesActionSuccess(spaces);
}),
catchError((error: Error) => {
// Handle erro here
})
);
})
)
;
constructor(private spacesService: SpacesService, private actions$: Actions) { }
}
spaces.reducer.ts
Here you build your map and you can also create a new action to return, for instance, a space given its key. I dont think you need any loading parameter here, I guess you are using it for some loading handling in your views, just use AsyncPipe in your view and handle a loading animation with an *ngIf checking if there are spaces or not.
/** ngrx **/
import {createFeatureSelector} from '#ngrx/store';
import {createSelector} from '#ngrx/store';
import * as spacesActions from './spaces.actions';
export type Action = spacesActions.All;
/** App Models **/
import { Space } from './space.model';
export interface SpaceState {
keySpaces: Map<string, Space>;
spaces: Space[];
keys: string[];
}
export const initialState: SpaceState = {
keySpaces: new Map<string, Space>(),
spaces: [],
keys: []
};
// Selectors
export const selectSpace = createFeatureSelector<SpaceState>('space');
export const getKeySpaces = createSelector(selectSpace, (state: StartState) => {
return state.keySpaces;
});
export const getSpaces = createSelector(selectSpace, (state: SpaceState) => {
return state.spaces;
});
export const getKeys = createSelector(selectSpace, (state: SpaceState) => {
return state.keys;
});
export function spacesReducer(state: SpaceState = initialState, action: Action): SpaceState {
switch (action.type) {
case startActions.GET_SPACES_SUCCESS:
// Since we return this from effect
const fetchedSpaces = action.payload;
const fetchedKeys = [];
const keySpacesMap = new Map<string, Space>();
fetchedSpaces.forEach( (space: Space) => {
fetchedkeys = fetchedKeys.concat(space.key);
keySpacesMap.set(space.key, new Space(space));
}
returns {
...state,
keySpaces: keySpacesMap,
spaces: fetchedSpaces,
keys: fetchedkeys
}
default: {
return state;
}
}
}
And, finally, you should be able to get such parameters in your components like:
. . .
keySpaces$ = Observable<Map<string, Space>>;
spaces$ = Observable<Array<Space>>;
keys$ = Observable<Array<string>>;
constructor(private _store: Store<AppState>) {
this.keySpaces$ = this._store.select(getKeySpaces);
this.space$s = this._store.select(getSpaces);
this.keys$ = this._store.select(getKeys);
}
. . .
ngOnInit() {
this._store.dispatch(new spacesActions.GetSpacesAction);
}
. . .
Of course add the new state to AppState:
. . .
export interface AppState {
. . .
space: SpaceState;
}

How to pass CSS styles to my Angular 2 component?

I've built an Angular2/4 component that is, basically, a masked date input. I use it in place of a textbox input, and have some code behind it to treat date conversions. It works well enough, but now i want to apply a CSS style and have it changing the input.
I want to write <app-date-input class='someCssClass'></app-date-input> and that class be attributed to my internal input.
My code for the component follows:
date-input.component.html
import { Component, Input, forwardRef } from '#angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '#angular/forms';
import { DatePipe } from "#angular/common";
import * as moment from 'moment';
date-input.component.ts
import { AppConstantsService } from "../../_services";
#Component({
selector: 'app-date-input',
templateUrl: './date-input.component.html',
styleUrls: ['./date-input.component.css'],
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DateInputComponent),
multi: true
}]
})
export class DateInputComponent implements ControlValueAccessor {
#Input()
public valor: Date;
#Input()
public readonly: boolean;
public dataString: string;
constructor(public appConstants: AppConstantsService) {
}
atribuirData(dataEntrada: string) {
if (!dataEntrada || dataEntrada == '') {
this.valor = null;
this.propagateChange(this.valor);
return;
}
let parts = dataEntrada.split('/');
try {
let newDate = moment(parts[2] + '-' + parts[1] + '-' + parts[0]).toDate();
// let newDate = new Date(+parts[2], +parts[1]-1, +parts[0]);
this.valor = newDate;
this.propagateChange(this.valor);
} catch (error) {
// return dataEntrada;
console.log(error);
}
}
writeValue(value: any) {
this.valor = value;
const datePipe = new DatePipe('pt-BR');
this.dataString = datePipe.transform(this.valor, 'dd/MM/yyyy');
}
propagateChange = (_: any) => { };
registerOnChange(fn) {
this.propagateChange = fn;
}
registerOnTouched() {
}
}
Although there are multiple ways to achieve this
Setting styles in the Child Component
<app-date-input [childClass]='someCssClass'></app-date-input>
Now in DateInputComponent, create an #Input() property and in the HTML for the component, you can do something like
'<input [class]="input Variable Name">'

React props using Meteor Apollo

I am playing with the Meteor Apollo demo repo.
I am having difficulty passing variables down to children with React. I am getting an error
imports/ui/Container.jsx:10:6: Unexpected token (10:6)
The below code is the Container.jsx component:
import React from 'react';
import { Accounts } from 'meteor/std:accounts-ui';
class Container extends React.Component {
render() {
let userId = this.props.userId;
let currentUser = this.props.currentUser;
}
return (
<Accounts.ui.LoginForm />
{ userId ? (
<div>
<pre>{JSON.stringify(currentUser, null, 2)}</pre>
<button onClick={() => currentUser.refetch()}>Refetch!</button>
</div>
) : 'Please log in!' }
);
}
}
It is passed props via the Meteor Apollo data system (I have omitted some imports at the top):
const App = ({ userId, currentUser }) => {
return (
<div>
<Sidebar />
<Header />
<Container userId={userId} currentUser={currentUser} />
</div>
)
}
// This container brings in Apollo GraphQL data
const AppWithData = connect({
mapQueriesToProps({ ownProps }) {
if (ownProps.userId) {
return {
currentUser: {
query: `
query getUserData ($id: String!) {
user(id: $id) {
emails {
address
verified
}
randomString
}
}
`,
variables: {
id: ownProps.userId,
},
},
};
}
},
})(App);
// This container brings in Tracker-enabled Meteor data
const AppWithUserId = createContainer(() => {
return {
userId: Meteor.userId(),
};
}, AppWithData);
export default AppWithUserId;
I would really appreciate some pointers.
I believe the error is that you accidentally ended the render function before the return statement.
render() { // <- here it starts
let userId = this.props.userId;
let currentUser = this.props.currentUser;
} // <- here it ends
Another error is that your return statement doesn't return a single DOM element, but two of them: an Accounts.ui.LoginForm and a div. The return function should only return one element. Just put the entire thing into a single <div>.

Resources