diff --git a/angular.json b/angular.json
index b255a7e..d926e78 100644
--- a/angular.json
+++ b/angular.json
@@ -112,6 +112,7 @@
},
"defaultProject": "Angular-learnjs-130622",
"cli": {
+ "analytics": false,
"defaultCollection": "@angular-eslint/schematics"
}
}
diff --git a/src/app/app.component.html b/src/app/app.component.html
index 3b61242..8ab0559 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -2,7 +2,11 @@
-
+
+
+
+
+
Item 1
@@ -11,4 +15,4 @@
-
\ No newline at end of file
+
diff --git a/src/app/components/sidenav/sidenav.component.ts b/src/app/components/sidenav/sidenav.component.ts
index eed51ce..d9610ac 100644
--- a/src/app/components/sidenav/sidenav.component.ts
+++ b/src/app/components/sidenav/sidenav.component.ts
@@ -45,5 +45,19 @@ export class SidenavComponent implements OnInit {
private insertList(template: TemplateRef) {
this.drawerViewPortElement.clear();
this.drawerViewPortElement.createEmbeddedView(template);
+
+ // setTimeout(() => {
+ // this.drawerViewPortElement.clear();
+ // this.drawerViewPortElement.createEmbeddedView(template,
+ // {
+ // name: 'Egor',
+ // $implicit: 'Angular LearnJs',
+ // user: {
+ // age: '35',
+ // }
+ // });
+ //
+ // this.changeDetectorRef.markForCheck();
+ // }, 3000)
}
}
diff --git a/src/app/pages/products-list/product-card/product-card.component.html b/src/app/pages/products-list/product-card/product-card.component.html
index 8dec404..c36c7bb 100644
--- a/src/app/pages/products-list/product-card/product-card.component.html
+++ b/src/app/pages/products-list/product-card/product-card.component.html
@@ -3,20 +3,22 @@
{{product?.name}}
{{product?.price}} Р
-
-
-
![Product photo]()
-
-
+
+
+
+
![Product photo]()
+
+
+
-
\ No newline at end of file
+
diff --git a/src/app/pages/products-list/product-card/product-card.component.less b/src/app/pages/products-list/product-card/product-card.component.less
index ce7648f..528d053 100644
--- a/src/app/pages/products-list/product-card/product-card.component.less
+++ b/src/app/pages/products-list/product-card/product-card.component.less
@@ -3,7 +3,8 @@
}
.card {
- width: 100%;
+ width: 500px;
+ margin-bottom: 10px;
box-sizing: border-box;
display: flex;
flex-direction: column;
diff --git a/src/app/pages/products-list/product-card/product-card.component.ts b/src/app/pages/products-list/product-card/product-card.component.ts
index afab068..3c84dc5 100644
--- a/src/app/pages/products-list/product-card/product-card.component.ts
+++ b/src/app/pages/products-list/product-card/product-card.component.ts
@@ -1,4 +1,4 @@
-import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
+import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { IProduct } from '../../../shared/products/product.interface';
@Component({
@@ -7,8 +7,8 @@ import { IProduct } from '../../../shared/products/product.interface';
styleUrls: ['./product-card.component.less'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class ProductCardComponent {
- @Input() product!: IProduct;
+export class ProductCardComponent implements OnInit {
+ @Input() product: IProduct | undefined;
@Output() buyProduct = new EventEmitter();
@@ -17,4 +17,20 @@ export class ProductCardComponent {
this.buyProduct.emit();
}
+
+ // prev!: () => void;
+ // next!: () => void;
+ //
+ // onRegisterPrev(fn: () => void) {
+ // this.prev = fn;
+ // }
+ //
+
+ // get imgSrc(): string {
+ // return this.product?.images[0].url || '';
+ // }
+
+ ngOnInit() {
+ console.log(this.product, 'ProductCardComponent created');
+ }
}
diff --git a/src/app/pages/products-list/products.component.html b/src/app/pages/products-list/products.component.html
index 6e57a9b..6771372 100644
--- a/src/app/pages/products-list/products.component.html
+++ b/src/app/pages/products-list/products.component.html
@@ -1,5 +1,61 @@
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ {{content}}
+ {{product}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/pages/products-list/products.component.less b/src/app/pages/products-list/products.component.less
index 885c68c..e772d39 100644
--- a/src/app/pages/products-list/products.component.less
+++ b/src/app/pages/products-list/products.component.less
@@ -15,3 +15,17 @@
// color: red;
// background-color: red;
// }
+
+.spinner {
+ margin: 0 auto;
+}
+
+.pagination {
+ display: flex;
+ align-items: center;
+ &__button {
+ &--active {
+ color: red;
+ }
+ }
+}
diff --git a/src/app/pages/products-list/products.component.ts b/src/app/pages/products-list/products.component.ts
index 703fd21..e04e3cd 100644
--- a/src/app/pages/products-list/products.component.ts
+++ b/src/app/pages/products-list/products.component.ts
@@ -1,5 +1,7 @@
-import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { productsMock } from '../../shared/products/products.mock';
+import { IProduct } from '../../shared/products/product.interface';
+import { Subject } from 'rxjs';
@Component({
selector: 'app-products',
@@ -7,19 +9,46 @@ import { productsMock } from '../../shared/products/products.mock';
styleUrls: ['./products.component.less'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class ProductsComponent {
- products = productsMock;
+export class ProductsComponent implements OnInit {
+ products: IProduct[] | undefined = undefined;
- // isHovered = true;
+ items = [2, 34, 5, 6, 7, 3, 3, 5, 4];
+ constructor(private changeDetectorRef: ChangeDetectorRef) {}
- // constructor(
- // private changeDetectorRef: ChangeDetectorRef,
- // ) {}
+ ngOnInit() {
+ // setTimeout(() => {
+ // this.products = productsMock.slice(0, 4);
+ // this.changeDetectorRef.markForCheck();
+ // }, 2000)
+ //
+ // setTimeout(() => {
+ // console.log(' ')
+ // }, 3000)
- // ngOnInit() {
- // setTimeout(() => {
- // this.isHovered = false;
- // this.changeDetectorRef.markForCheck();
- // }, 3000)
- // }
+ console.log(this.items.slice(2, 4), 'slice');
+ setTimeout(() => {
+ this.products = productsMock.map((item) => ({ ...item }));
+ this.changeDetectorRef.markForCheck();
+ }, 2000);
+ //
+ // setTimeout(() => {
+ // console.log(' ')
+ // }, 6000)
+
+ setTimeout(() => {
+ // this.products = productsMock.map(item => ({...item, name: item.name + '1245'}));
+ // this.products?.forEach((_, index: number) => {
+ // if (this.products) {
+ // this.products[index].name = '1345';
+ // }
+ // })
+ this.changeDetectorRef.markForCheck();
+ }, 3000);
+ }
+
+ trackBy(_: number, item: IProduct) {
+ return item._id;
+ // return item.name;
+ // return item;
+ }
}
diff --git a/src/app/pages/products-list/products.module.ts b/src/app/pages/products-list/products.module.ts
index 7cc3a97..7e9036e 100644
--- a/src/app/pages/products-list/products.module.ts
+++ b/src/app/pages/products-list/products.module.ts
@@ -6,10 +6,24 @@ import { MatCardModule } from '@angular/material/card';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { NgClassModule } from '../../shared/ng-class/ng-class.module';
+import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
+import { ForModule } from '../../shared/for/for.module';
+import { CarouselModule } from '../../shared/carousel/carousel.module';
+import { PaginationModule } from '../../shared/pagination/pagination.module';
@NgModule({
declarations: [ProductsComponent, ProductCardComponent],
- imports: [CommonModule, MatCardModule, MatButtonModule, MatIconModule, NgClassModule],
+ imports: [
+ CommonModule,
+ MatCardModule,
+ MatButtonModule,
+ MatIconModule,
+ NgClassModule,
+ MatProgressSpinnerModule,
+ ForModule,
+ CarouselModule,
+ PaginationModule,
+ ],
exports: [ProductsComponent],
})
export class ProductsModule {}
diff --git a/src/app/shared/carousel/carousel.directive.spec.ts b/src/app/shared/carousel/carousel.directive.spec.ts
new file mode 100644
index 0000000..9675cbe
--- /dev/null
+++ b/src/app/shared/carousel/carousel.directive.spec.ts
@@ -0,0 +1,8 @@
+import { CarouselDirective } from './carousel.directive';
+
+describe('CarouselDirective', () => {
+ it('should create an instance', () => {
+ const directive = new CarouselDirective();
+ expect(directive).toBeTruthy();
+ });
+});
diff --git a/src/app/shared/carousel/carousel.directive.ts b/src/app/shared/carousel/carousel.directive.ts
new file mode 100644
index 0000000..ac1785d
--- /dev/null
+++ b/src/app/shared/carousel/carousel.directive.ts
@@ -0,0 +1,138 @@
+import { Directive, EventEmitter, Input, OnInit, Output, TemplateRef, ViewContainerRef } from '@angular/core';
+import { BehaviorSubject, filter, map, withLatestFrom } from 'rxjs';
+
+interface ICarouselDirective {
+ $implicit: T;
+ index: number;
+ prev: () => void;
+ next: () => void;
+}
+
+@Directive({
+ selector: '[appCarousel]',
+})
+export class CarouselDirective implements OnInit {
+ @Input() set appCarouselOf(items: T[] | undefined) {
+ console.log(items, 'item in from component');
+ // if (!(items && items.length)) {
+ // this.viewContainerRef.clear();
+ // }
+
+ if (!items?.length) {
+ this.viewContainerRef.clear();
+
+ return;
+ }
+
+ // this.items = items;
+
+ this.items$.next(items);
+ this.currentIndex$.next(0);
+
+ // this.currentIndex = 0;
+ // this.insertTemplateWithCurrentIndex();
+ }
+
+ //
+ // @Output() emitNext = new EventEmitter<() => void>();
+ // @Output() emitPrev = new EventEmitter<() => void>();
+
+ // private items: T[] | undefined = undefined;
+ private readonly items$ = new BehaviorSubject(undefined);
+ // private currentIndex: number = 0;
+
+ private readonly currentIndex$ = new BehaviorSubject(0);
+
+ constructor(private templateRef: TemplateRef>, private viewContainerRef: ViewContainerRef) {}
+
+ ngOnInit() {
+ this.listenCurrentIndexChange();
+ // this.emitPrev.emit(this.prev.bind(this));
+ // this.emitNext.emit(() => {
+ // this.next();
+ // });
+ }
+
+ private listenCurrentIndexChange() {
+ this.currentIndex$
+ .pipe(
+ withLatestFrom(this.items$.pipe(filter(Boolean))),
+ map(([index, items]) => this.getCurrentContext(index, items))
+ )
+ .subscribe((context) => {
+ // this.insertTemplateWithCurrentIndex();
+ this.viewContainerRef.clear();
+ this.viewContainerRef.createEmbeddedView(this.templateRef, context);
+ });
+ }
+
+ // метод вставки темплейта с выбранным индексом
+ // private insertTemplateWithCurrentIndex() {
+ // this.viewContainerRef.clear(); // зачищаем viewContainer.
+ // this.viewContainerRef.createEmbeddedView(this.templateRef, this.getCurrentContext(this.currentIndex, this.items as T[]));
+ // }
+
+ private getCurrentContext(index: number, items: T[]): ICarouselDirective {
+ return {
+ index,
+ $implicit: items[index], // items c необходимым индексом.
+ prev: this.prev.bind(this),
+ next: () => {
+ this.next();
+ },
+ };
+ }
+
+ private next() {
+ // const nextIndex = this.currentIndex + 1;
+ // const itemsLength = this.items?.length;
+
+ const nextIndex = this.currentIndex$.value + 1;
+ const itemsLength = this.items$.value?.length;
+
+ console.log('next method');
+
+ if (!itemsLength) {
+ return;
+ }
+
+ // short version
+ // this.currentIndex = nextIndex < itemsLength ? nextIndex : 0;
+ this.currentIndex$.next(nextIndex < itemsLength ? nextIndex : 0);
+
+ // if (nextIndex < itemsLength) {
+ // this.currentIndex = nextIndex;
+ // this.insertTemplateWithCurrentIndex();
+ // return;
+ // }
+ // this.currentIndex = 0;
+ // this.insertTemplateWithCurrentIndex();
+ }
+
+ private prev() {
+ // const prevIndex = this.currentIndex - 1;
+ // const itemsLength = this.items?.length;
+
+ const prevIndex = this.currentIndex$.value - 1;
+ const itemsLength = this.items$.value?.length;
+
+ console.log('prev method');
+
+ if (!itemsLength) {
+ return;
+ }
+
+ // short version
+ // this.currentIndex = prevIndex >= 0 ? prevIndex : itemsLength - 1;
+
+ this.currentIndex$.next(prevIndex >= 0 ? prevIndex : itemsLength - 1);
+ // if (prevIndex >= 0) {
+ // this.currentIndex = prevIndex;
+ // this.insertTemplateWithCurrentIndex();
+ // return;
+ // }
+
+ // this.currentIndex = itemsLength - 1;
+ // this.insertTemplateWithCurrentIndex();
+ }
+}
diff --git a/src/app/shared/carousel/carousel.module.ts b/src/app/shared/carousel/carousel.module.ts
new file mode 100644
index 0000000..2a3ac43
--- /dev/null
+++ b/src/app/shared/carousel/carousel.module.ts
@@ -0,0 +1,10 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { CarouselDirective } from './carousel.directive';
+
+@NgModule({
+ declarations: [CarouselDirective],
+ imports: [CommonModule],
+ exports: [CarouselDirective],
+})
+export class CarouselModule {}
diff --git a/src/app/shared/for/for.directive.spec.ts b/src/app/shared/for/for.directive.spec.ts
new file mode 100644
index 0000000..53d9e76
--- /dev/null
+++ b/src/app/shared/for/for.directive.spec.ts
@@ -0,0 +1,8 @@
+import { ForDirective } from './for.directive';
+
+describe('ForDirective', () => {
+ it('should create an instance', () => {
+ const directive = new ForDirective();
+ expect(directive).toBeTruthy();
+ });
+});
diff --git a/src/app/shared/for/for.directive.ts b/src/app/shared/for/for.directive.ts
new file mode 100644
index 0000000..5dcee79
--- /dev/null
+++ b/src/app/shared/for/for.directive.ts
@@ -0,0 +1,25 @@
+import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
+
+interface IContextAppFor {
+ $implicit: T;
+}
+
+// IContextAppFor => $implicit: Product;
+// IContextAppFor => $implicit: string;
+
+@Directive({
+ selector: '[appFor]',
+})
+export class ForDirective {
+ @Input() set appForOf(array: T[]) {
+ this.viewContainerRef.clear();
+ if (array) {
+ array.forEach((item) => {
+ const context: IContextAppFor = { $implicit: item };
+ this.viewContainerRef.createEmbeddedView(this.templateRef, context);
+ });
+ }
+ }
+
+ constructor(private templateRef: TemplateRef>, private viewContainerRef: ViewContainerRef) {}
+}
diff --git a/src/app/shared/for/for.module.ts b/src/app/shared/for/for.module.ts
new file mode 100644
index 0000000..f03967c
--- /dev/null
+++ b/src/app/shared/for/for.module.ts
@@ -0,0 +1,10 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { ForDirective } from './for.directive';
+
+@NgModule({
+ declarations: [ForDirective],
+ imports: [CommonModule],
+ exports: [ForDirective],
+})
+export class ForModule {}
diff --git a/src/app/shared/pagination/interfaces/pagination-directive.interface.ts b/src/app/shared/pagination/interfaces/pagination-directive.interface.ts
new file mode 100644
index 0000000..6f41b03
--- /dev/null
+++ b/src/app/shared/pagination/interfaces/pagination-directive.interface.ts
@@ -0,0 +1,8 @@
+export interface IPaginationDirective {
+ $implicit: T | T[];
+ index: number;
+ prev: () => void;
+ next: () => void;
+ allIndexes: number[];
+ selectIndex: (index: number) => void;
+}
diff --git a/src/app/shared/pagination/pagination.directive.spec.ts b/src/app/shared/pagination/pagination.directive.spec.ts
new file mode 100644
index 0000000..8ce8792
--- /dev/null
+++ b/src/app/shared/pagination/pagination.directive.spec.ts
@@ -0,0 +1,8 @@
+import { PaginationDirective } from './pagination.directive';
+
+describe('PaginationDirective', () => {
+ it('should create an instance', () => {
+ const directive = new PaginationDirective();
+ expect(directive).toBeTruthy();
+ });
+});
diff --git a/src/app/shared/pagination/pagination.directive.ts b/src/app/shared/pagination/pagination.directive.ts
new file mode 100644
index 0000000..655ed6a
--- /dev/null
+++ b/src/app/shared/pagination/pagination.directive.ts
@@ -0,0 +1,133 @@
+import {
+ Directive,
+ EventEmitter,
+ Input,
+ OnChanges,
+ OnInit,
+ Output,
+ SimpleChanges,
+ TemplateRef,
+ ViewContainerRef,
+} from '@angular/core';
+import { IPaginationDirective } from './interfaces/pagination-directive.interface';
+
+@Directive({
+ selector: '[appPagination]',
+})
+export class PaginationDirective implements OnInit, OnChanges {
+ @Input() appPaginationOf: T[] | undefined;
+ @Input('appPaginationElementSize') elementSize = 1;
+ @Output() emitSelectedIndex = new EventEmitter<() => void>();
+
+ private items: T[] | Array | undefined = undefined;
+ private currentIndex: number = 0;
+ // private allItems: T[] | undefined = undefined;
+
+ constructor(
+ private templateRef: TemplateRef>,
+ private viewContainerRef: ViewContainerRef
+ ) {}
+
+ ngOnInit() {
+ console.log(this.appPaginationOf, 'appPaginationOf');
+ }
+
+ ngOnChanges({ appPaginationOf, appPaginationElementSize }: SimpleChanges) {
+ if (appPaginationOf || appPaginationElementSize) {
+ if (!(this.appPaginationOf && this.appPaginationOf.length)) {
+ this.viewContainerRef.clear();
+ }
+ }
+
+ this.items = this.elementSize > 1 ? this.getGroupedItemsListByElementsSize() : this.appPaginationOf;
+ this.currentIndex = 0;
+
+ this.insertTemplateWithCurrentIndex();
+ }
+
+ private getGroupedItemsListByElementsSize() {
+ return this.appPaginationOf?.reduce(
+ (acc: Array, item: T): Array => {
+ const lastGroupIndex = acc.length - 1;
+ const lastGroupLength = acc[lastGroupIndex].length;
+
+ if (lastGroupLength === this.elementSize) {
+ return [...acc, [item]];
+ }
+
+ acc[lastGroupIndex].push(item);
+ return [...acc];
+ },
+ [[]]
+ );
+ }
+
+ private insertTemplateWithCurrentIndex() {
+ this.viewContainerRef.clear();
+ this.viewContainerRef.createEmbeddedView(
+ this.templateRef,
+ this.getCurrentContext(this.currentIndex, this.items as T[])
+ );
+ }
+
+ private getCurrentContext(index: number, items: T[]): IPaginationDirective {
+ return {
+ index,
+ $implicit: items[index],
+ prev: () => {
+ this.prev();
+ },
+ next: () => {
+ this.next();
+ },
+ allIndexes: items.map((_, index) => index),
+ selectIndex: (index: number) => {
+ this.activeSelectedIndex(index);
+ },
+ };
+ }
+
+ private activeSelectedIndex(index: number) {
+ // this.emitSelectedIndex(index)
+ console.log(index, 'index from template');
+ }
+
+ prev() {
+ const prevIndex = this.currentIndex - 1;
+ const itemsLength = this.items?.length;
+
+ if (!itemsLength) {
+ return;
+ }
+
+ if (prevIndex >= 0) {
+ this.currentIndex = prevIndex;
+ this.insertTemplateWithCurrentIndex();
+ return;
+ }
+
+ // если он у нас prevIndex меньше нуля, то есть если это бесконечная itemsLength - 1, то есть последний индекс массива.
+ this.currentIndex = itemsLength - 1;
+
+ this.insertTemplateWithCurrentIndex();
+ }
+
+ next() {
+ const nextIndex = this.currentIndex + 1;
+ const itemsLength = this.items?.length;
+
+ if (!itemsLength) {
+ return;
+ }
+
+ if (nextIndex < itemsLength) {
+ this.currentIndex = nextIndex;
+ this.insertTemplateWithCurrentIndex();
+ return;
+ }
+
+ // когда следущуюий индек больше или равен длине массима тогда зануляем currentIndex.
+ this.currentIndex = 0;
+ this.insertTemplateWithCurrentIndex();
+ }
+}
diff --git a/src/app/shared/pagination/pagination.module.ts b/src/app/shared/pagination/pagination.module.ts
new file mode 100644
index 0000000..0a61525
--- /dev/null
+++ b/src/app/shared/pagination/pagination.module.ts
@@ -0,0 +1,10 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { PaginationDirective } from './pagination.directive';
+
+@NgModule({
+ declarations: [PaginationDirective],
+ imports: [CommonModule],
+ exports: [PaginationDirective],
+})
+export class PaginationModule {}