Trying to write a custom pipe to hide some items.
import { Pipe } from '#angular/core';
// Tell Angular2 we're creating a Pipe with TypeScript decorators
#Pipe({
name: 'showfilter'
})
export class ShowPipe {
transform(value) {
return value.filter(item => {
return item.visible == true;
});
}
}
HTML
<flights *ngFor="let item of items | showfilter">
</flights>
COMPONENT
import { ShowPipe } from '../pipes/show.pipe';
#Component({
selector: 'results',
templateUrl: 'app/templates/results.html',
pipes: [PaginatePipe, ShowPipe]
})
My item has the property of visible, which can be true or false.
However nothing showing, is there something wrong with my pipe?
I think my pipe is working because when I change the pipe code to:
import { Pipe } from '#angular/core';
// Tell Angular2 we're creating a Pipe with TypeScript decorators
#Pipe({
name: 'showfilter'
})
export class ShowPipe {
transform(value) {
return value;
}
}
It will show all items.
Thanks
I'm pretty sure this is because you have an initial value of [] for items. When you then later add items to items, the pipe is not reexecuted.
Adding pure: false should fix it:
#Pipe({
name: 'showfilter',
pure: false
})
export class ShowPipe {
transform(value) {
return value.filter(item => {
return item.visible == true;
});
}
}
pure: false has a big performance impact. Such a pipe is called every time change detection runs, which is quite often.
A way to make a pure pipe being called is to actually change the input value.
If you do
this.items = this.items.slice(); // create a copy of the array
every time after items was modified (added/removed) makes Angular recognize the change and re-execute the pipe.
It is not recommended to use impure pipe. I will impact your performance.
It will be called even source has not been changed.
So the correct alternative to be is change the reference of your returning object.
#Pipe({
name: 'showfilter'
})
export class ShowPipe {
transform(value) {
value = value.filter(item => {
return item.visible == true;
});
const anotherObject = Object.assign({}, value) // or else can do cloning.
return anotherObject
}
}
Related
I created a breadcrumb component, I have a service BreadcrumbService that has a function that reads the url path and converts them to an array of segments (this.breadService.getUrlPathSegments()). When breadcrumbs is loaded or updated, I get the following error:
ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value for 'click-enabled': 'true'. Current value: 'false'.
What is the correct way to handle this? The code works the way I intended, but I need to handle the error message.
What I am trying to do is disable click events on the last item in the breadcrumb list, so when you click on it, none of the events fire. This all works even though I receive the error message.
What I am doing is when the view is checked, update the value of each breadcurmb's clickable state. This is done just like this:
#Component({
selector: 'breadcrumbs',
styleUrls: ['./breadcrumbs.component.scss'],
template: `
<ng-content select="breadcrumb"></ng-content>
`,
encapsulation: ViewEncapsulation.None
})
export class Breadcrumbs implements AfterViewChecked {
#Input() disableLast = true;
#ContentChildren(Breadcrumb, { descendants: false })
breadcrumbs!: QueryList<Breadcrumb>;
ngAfterViewChecked() {
this.enableDisableLast();
}
enableDisableLast() {
if (this.breadcrumbs && this.breadcrumbs.length > 0) {
this.breadcrumbs.forEach(item => { item.clickable = true; });
this.breadcrumbs.last.clickable = !this.disableLast;
}
}
}
Next in the breadcrumb I have a #HostBinding(), that updates the class of the element. Which is done like this:
#Component({
selector: 'breadcrumb',
styleUrls: ['./breadcrumb.component.scss'],
template: `
<button>{{label}}</button>
`
})
export class Breadcrumb {
#HostBinding('class.click-enabled')
get clickEnabled() { return this.clickable; }
}
I then combine the two in the component that I am using them with a forEach to create the child breadcrumbs. I also listen for navigation changes to re-generate the array of breadcrumb segments to keep the breadcrumb display up-to-date with the current path.
#Component({
selector: 'app-root',
templateUrl: `
<breadcrumbs>
<breadcrumb *ngFor="let crumb of breadcrumbs" [label]="crumb.label|titlecase" [routerLink]="crumb.uri"></breadcrumb>
</breadcrumbs>
`,
styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit {
breadcrumbs: BreadcrumbSegment[] = [];
constructor(
private router: Router,
private breadService: BreadcrumbService
) { }
ngOnInit() {
this.router.events.subscribe(val => {
if (val instanceof NavigationEnd) {
// Returns an array formatted as: {label:string; uri:string;}[]
this.breadcrumbs = this.breadService.getUrlPathSegments();
}
});
}
}
I am not sure if this is the optimal solution, but it is working for my needs. When the Breadcrumbs component view is initialized, I set the QueryList to dirty, then pipe a delay before I subscribe to the changes. This stops the error from showing up and runs the change detection.
#Component({...})
export class Breadcrumbs implements AfterViewChecked {
ngAfterViewInit() {
// Set to dirty so the changes emit at least one time.
this.breadcrumbs.setDirty();
this.breadcrumbs.changes.pipe(delay(1)).subscribe(() => {
this.enableDisableLast();
});
}
}
I am not a developer by training, so it is likely that my question reveals some basic misunderstanding. I have tried searching for this topic, but am really not sure what phrases to even look for. (Is this something to do with scope? Permissions?)
I am working on an Angular 6 app. In it, I have a service with a function that calls a function from another service.
The nested function manipulates various fields on the objects that have been passed into it as parameters.
Something like this:
parentFunction(person:Person, scenario:CalculationScenario){
person.age = 1
scenario.value = 0
this.otherService.childFunction(person, scenario)
console.log(person.age) //logs "2"
console.log(scenario.value) //logs "100"
}
And in the other service:
childFunction(person:Person, scenario:CalculationScenario){
person.age = person.age + 1
scenario.value = scenario.value + 100
//Does not return anything
}
I had previously thought that in order for the values of person.age and scenario.value (within parentFunction) to reflect the changes made within childFunction, childFunction had to return those objects (and the parentFunction would have to set its "child" and "scenario" objects to be equal to what is being returned by childFunction).
But if I console.log them immediately after the call to childFunction, their values have in fact been changed.
Is is still preferable to have childFunction return "person" and "scenario" for some reason?
Again, apologies if similar discussions exist elsewhere. I'm struggling to find them as I'm not sure what terms even refer to the topic I'm asking about.
It might be preferable depending on the use cases of this childFunction().
It's a good idea for services methods to return values to improve its reusability.
Look at the getHeroes() method of this service:
import { Injectable } from '#angular/core';
import { Observable, of } from 'rxjs';
import { Hero } from './hero';
import { HEROES } from './mock-heroes';
import { MessageService } from './message.service';
#Injectable({
providedIn: 'root',
})
export class HeroService {
constructor(private messageService: MessageService) { }
getHeroes(): Observable<Hero[]> {
this.messageService.add('HeroService: fetched heroes');
return of(HEROES);
}
}
Instead of changing the value of a variable declared in your service, it returns some heroes.
It's usefull because in some component, I could do something like the getHeroes() method does here:
import { Component, OnInit } from '#angular/core';
import { Hero } from '../hero';
import { HeroService } from '../hero.service';
#Component({
selector: 'app-heroes',
templateUrl: './heroes.component.html',
styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
heroes: Hero[];
constructor(private heroService: HeroService) { }
ngOnInit() {
this.getHeroes();
}
getHeroes(): void {
this.heroService.getHeroes()
.subscribe(heroes => this.heroes = heroes);
}
}
I could easily customize the output to be used on some view by just changing the getHeroes() component method, without changing the service and being sure that any component could use the same getHeroes() service method.
Another good reason for having your service methods returning something, is to make easier to test them.
As shown by Balázs Takács, expecting values can be super simple to write:
it('should return correct sum', () => {
expect(service.myMethod(1, 2))
.toEqual(3);
});
I would suggestion to use the solution where you return a new object because in that case you can write unit test to your childFunction easily.
childFunction(person:Person,
scenario:CalculationScenario): [Person, CalculationScenario] {
person.age = person.age + 1
scenario.value = scenario.value + 100
return [person, scenario];
}
service.spec.ts
it('should return updated person and scenario in an array', () => {
const mockPerson = {age: 0};
const mockScenario = {value: 0};
const expectedReturnValue = [{age: 1}, {value: 100}];
expect(service.childFunction(mockPerson, mockScenairio))
.toEqual(expectedReturnValue);
});
Furthermore I suggest you to read articles about immutability in javascript which helps a lot in things like that.
Not needed, as Person and Calculationscenario are looks like your interface/class they already have referential access, It means you can directly access age and scenario.value from your ParentFunction. Its proper way to do it.No harm or no coding standard breakdown.
We have the async pipe to resolve Observables within template *ngFor expressions. Is there a pipe that can resolve / call a function that returns an array to be looped over.
For example if we have something like this:
<app-todo *ngFor="let todo of todosFunction | call"
Then angular would use the call pipe to resolve the todosFunction into the array of todos it returns.
Use Case
export enum VISIBILITY_FILTER {
SHOW_COMPLETED = 'Completed',
SHOW_ACTIVE = 'Active',
SHOW_ALL = 'All'
}
export function VISIBILITY_FILTER_VALUES():string[] {
return Object.keys(VISIBILITY_FILTER).map(k => VISIBILITY_FILTER[k]);
}
I'd like to be able to loop over the VISIBILITY_FILTER_VALUES directly simply y importing and using it (without declaring it as a property on the component). Is this possible?
Or perhaps it's possible to create a pipe that processes the enum FILTER_VALUES directory and creates the array out of it?
You can simply do
<app-todo *ngFor="let todo of todosFunction()"> and define todosFunction(), which returns an array, in your ts file.
There aren't any built in ones but writing one is pretty simple:
Pipe
#Pipe({
name: 'call'
})
export class CallPipe implements PipeTransform {
transform(value: any, args?: any): any {
if(typeof value === 'function') {
return value(args);
}
}
}
Component.ts
export class AppComponent {
data(times) {
return [1, 2, 3].map(x => x*times);
}
}
Component.html
<p *ngFor="let i of data | call: 3">{{i}}</p>
This is a list of built-in pipes if you wonder.
You can just call the function like this? , there is no need of another pipe to do the same.
<app-todo *ngFor="let todo of todosFunction()">
however it is recommended to prepare your array inside the component and bind it to variable instead of calling a function.
Call function in template is really bad as function will be executed on each Change Detection cycle.
Pipe in other hand is pure (by default) so it will be called only when input parameters was changed.
#Pipe({
name: 'apply',
})
export class ApplyPipe implements PipeTransform {
transform<T, U extends any[]>(fn: (...fnArgs: U) => T, ...args: U): T {
return fn(...args);
}
}
Consider an Angular 2 component that has an #Input meant to be a boolean, where its presence indicates true and its absence indicates false. Right now, I might manage this either with a custom getter/setter, or by having a function that tells me whether the attribute is present:
#Component({
selector:'foo',
template:`<div [class.error]="hasError()">Hello world</div>`
})
class Foo {
#Input() error:string;
public hasError() {
return error === '' || !!error;
}
}
And then I can use it in another component like this:
<foo></foo> <!-- no error class -->
<foo error></foo> <!-- has error class -->
What I really want to do is this, but have the same behavior:
#Component({
selector:'foo',
template:`<div [class.error]="error">Hello world</div>`
})
class Foo {
#Input() error:boolean;
}
Is there a common pattern for creating this behavior without the boilerplate in my first example?
What you need is a decorator that wraps your boolean property as a getter/setter and handles all the logic.
It's quite simple and save that boilerplate.
This feature is already implemented by the material team in google, they build the material library for Angular 2 and they work closely with the angular team.
Currently it's implemented in their repo, not in the angular repo but if high demand will raise I suppose they might consider migrating it to angular, it has been done in some cases before.
Anyway, this is dead simple and it's around 15-20 LOC.
/**
* Annotation Factory that allows HTML style boolean attributes. For example,
* a field declared like this:
* #Directive({ selector: 'component' }) class MyComponent {
* #Input() #BooleanFieldValueFactory() myField: boolean;
* }
*
* You could set it up this way:
* <component myField>
* or:
* <component myField="">
*/
function booleanFieldValueFactory() {
return function booleanFieldValueMetadata(target: any, key: string): void {
const defaultValue = target[key];
const localKey = `__md_private_symbol_${key}`;
target[localKey] = defaultValue;
Object.defineProperty(target, key, {
get() { return (<any>this)[localKey]; },
set(value: boolean) {
(<any>this)[localKey] = value != null && `${value}` !== 'false';
}
});
};
}
export { booleanFieldValueFactory as BooleanFieldValue };
You can see the implementation in THIS LINK
This works for me (Angular 8.2):
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'my-component',
templateUrl: './my-component.component.html',
styleUrls: ['./my-component.component.scss']
})
export class MyComponent implements OnInit {
#Input() inverse = false;
ngOnInit() {
this.inverse = !(this.inverse === false);
}
}
If you want more than a presence/absence check and really check for empty string, you can do that, too:
this.inverse = (this.inverse === "");
let's say I have a component which has a child component. For example this simple calendar:
Here is its template:
<month-board [current-month]="currentMonth$"></month-board>
<button (click)="decrement()">Prev</button>
<button (click)="increment()">Next</button>
when the buttons are clicked the month-board which subscribed to currentMonth$ is subscribed is changing the displayed month.
currentMonth$ type is Observable<Moment>.
My question is: is it a good practice to pass Observables to child components in Angular2? is there any better way to do this?
Parent full code:
#Component({
selector: 'month-view',
templateUrl: 'app/components/month-view/month-view.html',
styleUrls: ['app/components/month-view/month-view.css'],
directives: [MonthBoard],
})
export class MonthView {
currentMonth: Moment = moment();
currentMonth$: Observable<Moment>;
currentMonthObserver: Observer<Moment>;
decrement: Function;
increment: Function;
constructor() {
this.currentMonth$ = Observable.create((observer: Observer<Moment>) => {
this.currentMonthObserver = observer;
});
const decrementCounter$: Observable<Function> = Observable.create((observer) => {
this.decrement = () => {
observer.next();
};
});
const incrementCounter$: Observable<Function> = Observable.create((observer) => {
this.increment = () => {
observer.next();
};
});
this.currentMonth$.subscribe();
Observable
.merge(
decrementCounter$.map(() => - 1),
incrementCounter$.map(() => + 1)
)
.startWith(0)
.scan((currentCount: number, value: number) => currentCount + value)
.subscribe((count: number) => {
this.currentMonthObserver.next(this.currentMonth.clone().add(count, 'M'));
});
}
}
Child full code:
#Component({
selector: 'month-board',
templateUrl: 'app/components/month-board/month-board.html',
styleUrls: ['app/components/month-board/month-board.css'],
directives: [DayCell]
})
export class MonthBoard implements OnInit{
#Input('current-month') currentMonth: Observable<Moment>;
weeks: Moment[][];
constructor(private calendarHelper: CalendarHelper) {
this.weeks = this.calendarHelper.getMonthWeeks();
}
ngOnInit() {
this.currentMonth.subscribe((month: Moment) => {
this.weeks = this.calendarHelper.getMonthWeeks(month);
});
}
}
You can do it that way, the other way is with #input. It's very easy to pass values from parent to child with it.
https://angular.io/docs/ts/latest/api/core/Input-var.html
I don't think it's necessarily bad to pass observables that way to your child component. For example I have a service that uses an observable that my whole application uses to watch for logged in events. But for a Calendar you might find yourself wanting to pass different values in different places on the same observable. If you do that you can always make another observable. Or manipulate it in different ways for each component.
But for readability I would definitely just use #input, that way I only have to go to the parent component to figure out what I am passing around.