initiate to sit

This commit is contained in:
Zuhdan Ubay 2020-05-22 10:25:51 +07:00
parent 947ee4e0c2
commit 9101cdaa87
271 changed files with 990780 additions and 494 deletions

5
package-lock.json generated
View file

@ -12739,11 +12739,6 @@
"integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==",
"dev": true
},
"ng-pick-datetime": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/ng-pick-datetime/-/ng-pick-datetime-7.0.0.tgz",
"integrity": "sha512-SbS+zKX6gOlYpgH8zDSx2EL32ak0Z0y1Ksu1ECP/FiwVBM2mHgbzdfyDYhMmKFB0GKn5yCwXTandR1FCQXe62w=="
},
"ng2-ckeditor": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/ng2-ckeditor/-/ng2-ckeditor-1.2.2.tgz",

View file

@ -58,7 +58,6 @@
"ionicons": "2.0.1",
"leaflet": "1.2.0",
"nebular-icons": "1.1.0",
"ng-pick-datetime": "^7.0.0",
"ng2-ckeditor": "^1.2.2",
"ng2-smart-table": "^1.6.0",
"ngx-echarts": "^4.2.2",

View file

@ -0,0 +1,26 @@
export interface PartnerPriceDetail {
agingType: string,
createTmString: string,
createdBy: string,
createdTm: string,
currencyCarry: string,
currencyType: string,
customerCode: string,
customerName: string,
destCode: string,
flowType: string,
id: number,
invalidDate: string,
invalidString: string,
isSale: number,
modifiedBy: string,
modifiedTm: string,
payCountry: string,
priceCode: string,
productCode: string,
roundTrip: number,
sourceCode: string,
status: number,
validDate: string,
validString: string,
}

View file

@ -0,0 +1,12 @@
export interface PartnerPrice {
createdBy: string,
createdTm: Date,
customerCode: string,
customerName: string,
id: number,
modifiedBy: string,
modifiedTm: Date,
quotationDesc: string,
quotationNo: string,
status: number,
}

View file

@ -8,53 +8,29 @@ import {
NbRequestPasswordComponent,
NbResetPasswordComponent,
} from '@nebular/auth';
import { AppComponent } from './app.component';
export const routes: Routes = [
{
path: 'pages',
loadChildren: () => import('./pages/pages.module')
.then(m => m.PagesModule),
},
// {
// path: 'pages',
// loadChildren: () => import('./pages/pages.module')
// .then(m => m.PagesModule),
// },
{
path: 'auth',
component: NbAuthComponent,
children: [
{
path: '',
component: NbLoginComponent,
},
{
path: 'login',
component: NbLoginComponent,
},
{
path: 'register',
component: NbRegisterComponent,
},
{
path: 'logout',
component: NbLogoutComponent,
},
{
path: 'request-password',
component: NbRequestPasswordComponent,
},
{
path: 'reset-password',
component: NbResetPasswordComponent,
},
],
loadChildren: () => import('./auth/auth.module').then(m => m.AuthModule),
},
{ path: '', redirectTo: 'pages', pathMatch: 'full' },
{ path: '**', redirectTo: 'pages' },
// { path: '', redirectTo: 'auth', pathMatch: 'full' },
// { path: '**', redirectTo: 'auth' },
];
const config: ExtraOptions = {
useHash: false,
};
// const config: ExtraOptions = {
// useHash: false,
// };
@NgModule({
imports: [RouterModule.forRoot(routes, config)],
// imports: [RouterModule.forRoot(routes, config)],
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {

View file

@ -11,13 +11,5 @@ import { SeoService } from './@core/utils/seo.service';
selector: 'ngx-app',
template: '<router-outlet></router-outlet>',
})
export class AppComponent implements OnInit {
constructor(private analytics: AnalyticsService, private seoService: SeoService) {
}
ngOnInit(): void {
this.analytics.trackPageViews();
this.seoService.trackCanonicalChanges();
}
export class AppComponent {
}

View file

@ -19,8 +19,13 @@ import {
NbSidebarModule,
NbToastrModule,
NbWindowModule,
NbLayoutModule,
NbCardModule,
NbButtonModule,
} from '@nebular/theme';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { NbAuthModule, NbDummyAuthStrategy } from '@nebular/auth';
@NgModule({
declarations: [

View file

@ -0,0 +1,48 @@
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { NbAuthComponent } from '@nebular/auth';
import { LoginComponent } from './login/login.component';
import { AuthComponent } from './auth.component';
export const routes: Routes = [
{
path: '',
component: AuthComponent,
children: [
{
path: 'login',
component: LoginComponent,
},
],
},
{
path: 'auth',
loadChildren: () => import('./auth.module').then(m => m.AuthModule),
},
// {
// path: '',
// component: NbAuthComponent,
// children: [
// {
// path: 'login',
// component: LoginComponent,
// },
// ],
// },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class AuthRoutingModule {
}

View file

@ -0,0 +1,24 @@
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
import { Component } from '@angular/core';
@Component({
selector: 'ngx-app',
template: `
<nb-layout>
<nb-layout-column>
<nb-card>
<nb-card-body>
<a nbButton routerLink="auth/login">Login</a>
</nb-card-body>
</nb-card>
</nb-layout-column>
</nb-layout>
`,
})
export class AuthComponent {
}

View file

@ -0,0 +1,57 @@
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AuthRoutingModule } from './auth-routing.module';
import { NbAuthModule, NbDummyAuthStrategy } from '@nebular/auth';
import { NbAlertModule, NbButtonModule, NbCheckboxModule, NbInputModule, NbLayoutModule, NbCardModule } from '@nebular/theme';
import { LoginComponent } from './login/login.component';
import { HttpClientModule } from '@angular/common/http';
import { AppRoutingModule } from '../app-routing.module';
import { AuthComponent } from './auth.component';
@NgModule({
imports: [
CommonModule,
FormsModule,
NbAlertModule,
NbInputModule,
NbButtonModule,
NbCheckboxModule,
AuthRoutingModule,
NbAuthModule,
CommonModule,
HttpClientModule,
NbLayoutModule,
NbCardModule,
NbButtonModule,
AppRoutingModule,
NbAuthModule.forRoot({
strategies: [
NbDummyAuthStrategy.setup({
name: 'email',
alwaysFail: true,
delay: 1000,
}),
],
}),
],
declarations: [
LoginComponent,
AuthComponent,
],
})
export class AuthModule {
}

View file

@ -0,0 +1,105 @@
<h1 id="title" class="title">Sign In</h1>
<nb-alert *ngIf="showMessages.error && errors?.length && !submitted" outline="danger" role="alert">
<p class="alert-title"><b>Oh snap!</b></p>
<ul class="alert-message-list">
<li *ngFor="let error of errors" class="alert-message">{{ error }}</li>
</ul>
</nb-alert>
<nb-alert *ngIf="showMessages.success && messages?.length && !submitted" outline="success" role="alert">
<p class="alert-title"><b>Hooray!</b></p>
<ul class="alert-message-list">
<li *ngFor="let message of messages" class="alert-message">{{ message }}</li>
</ul>
</nb-alert>
<form (ngSubmit)="login()" #form="ngForm" aria-labelledby="title">
<div class="form-control-group">
<label class="label" for="input-email">Email address:</label>
<input nbInput
fullWidth
[(ngModel)]="user.email"
#email="ngModel"
name="email"
id="input-email"
pattern=".+@.+\..+"
placeholder="Email address"
autofocus
[status]="email.dirty ? (email.invalid ? 'danger' : 'success') : 'basic'"
[required]="getConfigValue('forms.validation.email.required')"
[attr.aria-invalid]="email.invalid && email.touched ? true : null">
<ng-container *ngIf="email.invalid && email.touched">
<p class="caption status-danger" *ngIf="email.errors?.required">
Email is required!
</p>
<p class="caption status-danger" *ngIf="email.errors?.pattern">
Email should be the real one!
</p>
</ng-container>
</div>
<div class="form-control-group">
<label class="label" for="input-password">Password:</label>
<input nbInput
fullWidth
[(ngModel)]="user.password"
#password="ngModel"
name="password"
type="password"
id="input-password"
placeholder="Password"
[status]="password.dirty ? (password.invalid ? 'danger' : 'success') : 'basic'"
[required]="getConfigValue('forms.validation.password.required')"
[minlength]="getConfigValue('forms.validation.password.minLength')"
[maxlength]="getConfigValue('forms.validation.password.maxLength')"
[attr.aria-invalid]="password.invalid && password.touched ? true : null">
<ng-container *ngIf="password.invalid && password.touched ">
<p class="caption status-danger" *ngIf="password.errors?.required">
Password is required!
</p>
<p class="caption status-danger" *ngIf="(password.errors?.minlength || password.errors?.maxlength)">
Password should contains
from {{ getConfigValue('forms.validation.password.minLength') }}
to {{ getConfigValue('forms.validation.password.maxLength') }}
characters
</p>
</ng-container>
</div>
<div class="form-control-group accept-group">
<nb-checkbox name="rememberMe" [(ngModel)]="user.rememberMe" *ngIf="rememberMe">Remember me</nb-checkbox>
<a class="forgot-password" routerLink="../request-password">Forgot Password?</a>
</div>
<button nbButton
fullWidth
status="success"
[disabled]="submitted || !form.valid"
[class.btn-pulse]="submitted">
Sign In
</button>
</form>
<section *ngIf="socialLinks && socialLinks.length > 0" class="links" aria-label="Social sign in">
Or connect with:
<div class="socials">
<ng-container *ngFor="let socialLink of socialLinks">
<a *ngIf="socialLink.link"
[routerLink]="socialLink.link"
[attr.target]="socialLink.target"
[attr.class]="socialLink.icon"
[class.with-icon]="socialLink.icon">{{ socialLink.title }}</a>
<a *ngIf="socialLink.url"
[attr.href]="socialLink.url"
[attr.target]="socialLink.target"
[attr.class]="socialLink.icon"
[class.with-icon]="socialLink.icon">{{ socialLink.title }}</a>
</ng-container>
</div>
</section>
<section class="another-action" aria-label="Register">
Don't have an account? <a class="text-link" routerLink="../register">Sign Up</a>
</section>

View file

@ -0,0 +1,15 @@
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
import { Component } from '@angular/core';
import { NbLoginComponent } from '@nebular/auth';
@Component({
selector: 'ngx-login',
templateUrl: './login.component.html',
})
export class LoginComponent extends NbLoginComponent {
}

View file

@ -1,85 +1,37 @@
<div class="row">
<div class="col-md-4">
<form>
<div class="form-group">
<label for="exampleInputEmail1" class="label">Partner Code</label>
<input nbInput fullWidth id="exampleInputEmail1" [(ngModel)]="data.customerID">
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="label">User Id</label>
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.customerID">
</div>
<div class="form-group">
<label for="exampleInputEmail1" class="label">Promo Type</label>
<input nbInput fullWidth id="exampleInputEmail1" [(ngModel)]="data.promotionType">
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="label">Periode </label>
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.validDay">
</div>
<div class="form-group">
<label for="exampleInputEmail1" class="label">Coupon Code</label>
<input nbInput fullWidth id="exampleInputEmail1" [(ngModel)]="data.code">
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="label">Valid Start</label>
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.validFrom">
</div>
</form>
<div class="form-group row">
<label class="label col-sm-3 col-form-label">Quotation No</label>
<div class="col-sm-9">
<input type="email" nbInput placeholder="Email">
</div>
<div class="col-md-4">
<div class="form-group">
<label for="exampleInputPassword1" class="label">Valid End</label>
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.validTo">
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="label">Promo</label>
<input type="number" nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.value">
</div>
<div class="form-group">
<label for="exampleInputEmail1" class="label">Max. Promo</label>
<input type="number" nbInput fullWidth id="exampleInputEmail1" [(ngModel)]="data.maximumDiscount">
</div>
<div class="form-group">
<label for="exampleInputEmail1" class="label">Quota per User</label>
<input nbInput fullWidth id="exampleInputEmail1" [(ngModel)]="data.maximumPerUser">
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="label">User Type</label>
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.customerType">
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="label">Coverage Pickup</label>
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.coverage">
</div>
</div>
<div class="form-group row">
<label class="label col-sm-3 col-form-label">Partner</label>
<div class="col-sm-9">
<input nbInput placeholder="Password">
</div>
<div class="col-md-4">
<form>
<div class="form-group">
<label for="exampleInputPassword1" class="label">Product</label>
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.product">
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="label">Promo (IDR)</label>
<input type="number" nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.value">
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="label">Promo (%)</label>
<input type="number" nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.value">
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="label">Max. Promo</label>
<input type="number" nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.maximumDiscountValue">
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="label">Budget</label>
<input type="number" nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.maximumDiscount">
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="label">Quota</label>
<input type="number" nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.quota">
</div>
<button (click)="onSubmit()" type="submit" nbButton status="primary">Submit</button>
</form>
</div>
<div class="form-group row">
<label class="label col-sm-3 col-form-label">Status</label>
<div class="col-sm-9">
<label>*SDS price is for first 5kg, each additional kg is additional Rp.</label>
<input nbInput placeholder="Password">
<label>for shipment total up to 10kg</label>
</div>
</div>
</div>
<!-- <div class="form-group row">
<div class="offset-sm-3 col-sm-9">
<button (click)="onSubmit()" type="submit" nbButton status="primary">Sign in</button>
</div>
</div> -->
<div class="form-group row">
<label class="label col-sm-3 col-form-label">Add</label>
<div class="col-sm-9">
<input nbInput placeholder="Password">
<label>Flow(s)</label>
<!-- (click)="onSubmit()" -->
<button type="submit" nbButton status="primary">Go</button> </div>
</div>
<ng2-smart-table [settings]="settings" [source]="data" (deleteConfirm)="onDeleteConfirm($event)"
(createConfirm)="onCreateConfirm($event)" (userRowSelect)="openWindowForm($event)">
</ng2-smart-table>

View file

@ -1,30 +1,132 @@
import { Component, Inject, Input } from '@angular/core';
import { NbWindowRef, NB_WINDOW_CONTEXT } from '@nebular/theme';
import { PromotionList } from '../../../@core/data/promotion';
import { Component, Inject, Input, OnInit } from '@angular/core';
import { NbWindowRef, NB_WINDOW_CONTEXT, NbWindowService } from '@nebular/theme';
import { PartnerPriceService } from '../partner-price.service';
import { PartnerPriceDetail } from '../../../@core/data/partner-price-detail';
import { PartnerPrice } from '../../../@core/data/partner-price';
@Component({
selector: 'ngx-partner-price-detail',
templateUrl: './partner-price-detail.component.html',
styleUrls: ['partner-price-detail.component.scss'],
})
export class PartnerPriceDetailComponent {
export class PartnerPriceDetailComponent implements OnInit {
@Input() data: PromotionList;
settings = {
mode: 'inline',
pager: {
perPage: 2,
},
add: {
addButtonContent: '<i class="nb-plus"></i>',
createButtonContent: '<i class="nb-checkmark"></i>',
cancelButtonContent: '<i class="nb-close"></i>',
confirmCreate: true,
},
edit: {
editButtonContent: '<i class="nb-edit"></i>',
saveButtonContent: '<i class="nb-checkmark"></i>',
cancelButtonContent: '<i class="nb-close"></i>',
},
delete: {
deleteButtonContent: '<i class="nb-trash"></i>',
confirmDelete: true,
},
columns: {
productCode: {
title: 'Product',
type: 'string',
},
flowType: {
title: 'Pricing Level',
type: 'string',
},
sourceCode: {
title: 'Origin',
type: 'string',
},
destCode: {
title: 'Destination',
type: 'string',
},
priceCode: {
title: 'Price',
type: 'string',
},
agingType: {
title: 'SLA',
type: 'string',
},
validDate: {
title: 'Valid Date Start',
type: 'string',
},
invalidDate: {
title: 'Valid Date End',
type: 'string',
},
},
};
constructor(private service: PartnerPriceService, public windowRef: NbWindowRef, @Inject(NB_WINDOW_CONTEXT) context) {
@Input() data: PartnerPriceDetail[];
headerData: PartnerPrice;
constructor(private service: PartnerPriceService, public windowRef: NbWindowRef, @Inject(NB_WINDOW_CONTEXT) context, private windowService: NbWindowService) {
if (context != null) {
this.data = Object.assign({}, context.data);
this.headerData = Object.assign({}, context.data);
}
}
onSubmit(){
this.service.postPromotion(this.data).subscribe((value) => {
this.close();
filter: any = {
customerCode: '',
}
ngOnInit(): void {
this.filter.customerCode = this.headerData.customerCode;
this.getData();
}
getData(){
this.service.getDetailPartnerPrice(this.filter).subscribe((result) => {
this.data = Object.assign([], result);
});
}
onSubmit() {
// this.service.postPromotion(this.data).subscribe((value) => {
// this.close();
// });
}
close() {
this.windowRef.close();
}
onCreateConfirm(event) {
if (window.confirm('Are you sure you want to save?')) {
event.newData.status = 1;
this.service.getHeaderPartnerPrice(event.newData).subscribe((value) => {
// this.initData();
});
event.confirm.resolve(event.newData);
} else {
event.confirm.reject();
}
}
onDeleteConfirm(event): void {
if (window.confirm('Are you sure you want to delete?')) {
event.confirm.resolve();
} else {
event.confirm.reject();
}
}
openWindowForm(event) {
this.windowService.open(PartnerPriceDetailComponent, {
title: 'Partner Price Detail',
context: {
data: event.data,
},
});
}
}

View file

@ -1,11 +1,11 @@
<nb-card>
<nb-card-header>
Partner Price
Partner Price - dev 90%
</nb-card-header>
<nb-card-body>
<ng2-smart-table [settings]="settings" [source]="source" (deleteConfirm)="onDeleteConfirm($event)"
(createConfirm)="onCreateConfirm($event)" (userRowSelect)="openWindowForm($event)">
(createConfirm)="onCreateConfirm($event)" (userRowSelect)="openWindowForm($event)" (create)="openCreateDialog($event)">
</ng2-smart-table>
</nb-card-body>
</nb-card>

View file

@ -1,8 +1,8 @@
import { Component, OnInit } from '@angular/core';
import { PromotionList } from '../../@core/data/promotion';
import { PartnerPriceService } from './partner-price.service';
import { PartnerPriceDetailComponent } from './partner-price-detail/partner-price-detail.component';
import { NbWindowService } from '@nebular/theme';
import { PartnerPrice } from '../../@core/data/partner-price';
@Component({
selector: 'ngx-partner-price',
@ -11,7 +11,7 @@ import { NbWindowService } from '@nebular/theme';
})
export class PartnerPriceComponent implements OnInit {
settings = {
mode: 'inline',
mode: 'external',
add: {
addButtonContent: '<i class="nb-plus"></i>',
createButtonContent: '<i class="nb-checkmark"></i>',
@ -28,26 +28,39 @@ export class PartnerPriceComponent implements OnInit {
confirmDelete: true,
},
columns: {
code: {
title: 'Coupon Code',
customerCode: {
title: 'Partner Code',
type: 'string',
},
validDay: {
title: 'Period',
customerName: {
title: 'Partner Name',
type: 'string',
},
validFrom: {
title: 'Valid Start',
quotationNo: {
title: 'Reference',
type: 'string',
},
validTo: {
title: 'Valid End',
modifiedBy: {
title: 'Modified by',
type: 'string',
},
modifiedTm: {
title: 'Modified Date',
type: 'string',
},
status: {
title: 'Status',
type: 'string',
},
},
};
source: PromotionList[] = [];
filter: any = {
start: 0,
column: 'customerCode',
}
source: PartnerPrice[] = [];
constructor(private service: PartnerPriceService, private windowService: NbWindowService) {
@ -57,8 +70,8 @@ export class PartnerPriceComponent implements OnInit {
this.initData();
}
initData(){
this.service.getPromotion().subscribe((result) => {
initData() {
this.service.getHeaderPartnerPrice(this.filter).subscribe((result) => {
this.source = Object.assign([], result);
});
}
@ -66,7 +79,7 @@ export class PartnerPriceComponent implements OnInit {
onCreateConfirm(event) {
if (window.confirm('Are you sure you want to save?')) {
event.newData.status = 1;
this.service.postPromotion(event.newData).subscribe((value) => {
this.service.getHeaderPartnerPrice(event.newData).subscribe((value) => {
this.initData();
});
event.confirm.resolve(event.newData);
@ -83,9 +96,14 @@ export class PartnerPriceComponent implements OnInit {
}
}
openCreateDialog(event) {
//logic
}
openWindowForm(event) {
this.windowService.open(PartnerPriceDetailComponent, {
title: 'Promotion Detail',
title: 'Partner Price Detail',
context: {
data: event.data,
},

View file

@ -4,6 +4,8 @@ import { Observable, throwError } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
import { HttpErrorResponse } from '@angular/common/http';
import { PromotionList } from '../../@core/data/promotion';
import { PartnerPrice } from '../../@core/data/partner-price';
import { PartnerPriceDetail } from '../../@core/data/partner-price-detail';
@Injectable({
providedIn: 'root',
@ -12,28 +14,40 @@ export class PartnerPriceService {
constructor(private http: HttpClient) {
}
getPromotion(): Observable<any> {
const url = 'http://localhost:8011/api/promotions/all';
return this.http.get(url).pipe(
getHeaderPartnerPrice(filter: any): Observable<any> {
const url = 'http://34.87.6.140:8011/api/productFlow/pagingFlowHeader?start=0&sort=asc&length=10&column=' + filter.column;
return this.http.post(url, null).pipe(
map(this.extractData),
catchError(this.handleError),
);
}
postPromotion(data: PromotionList): Observable<any> {
const headers = new HttpHeaders({
'Content-Type': 'application/json',
'X-Requested-Method': 'POST',
});
const options = { headers: headers };
const url = 'http://localhost:8011/api/promotions/add';
return this.http.post(url, JSON.stringify(data), options).pipe(
getDetailPartnerPrice(filter: any): Observable<any> {
const url = 'http://34.87.6.140:8011/api/productFlow/pagingByCustomerCode?start=0&sort=asc&length=10&column=modifiedTm&customerCode=' + filter.customerCode;
return this.http.post(url, null).pipe(
map(this.extractData),
catchError(this.handleError),
);
}
private extractData(body: any): PromotionList[] {
return Object.assign(body.promotionList);
// postPromotion(data: PromotionList): Observable<any> {
// const headers = new HttpHeaders({
// 'Content-Type': 'application/json',
// 'X-Requested-Method': 'POST',
// });
// const options = { headers: headers };
// const url = 'http://localhost:8011/api/promotions/add';
// return this.http.post(url, JSON.stringify(data), options).pipe(
// catchError(this.handleError),
// );
// }
private extractData(body: any): PartnerPrice[] {
return Object.assign(body.content);
}
private extractDataDetail(body: any): PartnerPriceDetail[] {
return Object.assign(body.content);
}
private handleError(error: HttpErrorResponse | any) {

View file

@ -1,80 +1,92 @@
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label for="exampleInputEmail1" class="label">Partner Code</label>
<input nbInput fullWidth id="exampleInputEmail1" [(ngModel)]="data.customerID">
<label class="label">Partner Code</label>
<input nbInput fullWidth [(ngModel)]="data.customerID">
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="label">User Id</label>
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.customerID">
<label class="label">User Id</label>
<input nbInput fullWidth [(ngModel)]="data.customerID">
</div>
<div class="form-group">
<label for="exampleInputEmail1" class="label">Promo Type</label>
<input nbInput fullWidth id="exampleInputEmail1" [(ngModel)]="data.promotionType">
<label class="label">Promo Type</label>
<!-- <input nbInput fullWidth [(ngModel)]="data.promotionType"> -->
<nb-card-body>
<nb-select [(selected)]="data.promotionType">
<nb-option value="Discount">Discount</nb-option>
<nb-option value="Cashback">Cashback</nb-option>
</nb-select>
</nb-card-body>
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="label">Periode </label>
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.validDay">
<label class="label">Periode </label>
<input nbInput fullWidth [(ngModel)]="data.validDay">
</div>
<div class="form-group">
<label for="exampleInputEmail1" class="label">Coupon Code</label>
<input nbInput fullWidth id="exampleInputEmail1" [(ngModel)]="data.code">
<label class="label">Coupon Code</label>
<input nbInput fullWidth [(ngModel)]="data.code">
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="label">Valid Start</label>
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.validFrom">
<label class="label">Valid Start</label>
<!-- <input nbInput fullWidth [(ngModel)]="data.validFrom"> -->
<input nbInput fullWidth placeholder="Select Date" [nbDatepicker]="formpickerstart" [(ngModel)]="data.validFrom">
<nb-datepicker #formpickerstart></nb-datepicker>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="exampleInputPassword1" class="label">Valid End</label>
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.validTo">
<label class="label">Valid End</label>
<!-- <input nbInput fullWidth [(ngModel)]="data.validTo"> -->
<input nbInput fullWidth placeholder="Select Date" [nbDatepicker]="formpickerend" [(ngModel)]="data.validTo">
<nb-datepicker #formpickerend></nb-datepicker>
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="label">Promo</label>
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.value">
<label class="label">Quota per User</label>
<input (keypress)="numberOnly($event)" nbInput fullWidth [(ngModel)]="data.maximumPerUser">
</div>
<div class="form-group">
<label for="exampleInputEmail1" class="label">Max. Promo</label>
<input nbInput fullWidth id="exampleInputEmail1" [(ngModel)]="data.maximumDiscount">
<label class="label">User Type</label>
<input nbInput fullWidth [(ngModel)]="data.customerType">
</div>
<div class="form-group">
<label for="exampleInputEmail1" class="label">Quota per User</label>
<input nbInput fullWidth id="exampleInputEmail1" [(ngModel)]="data.maximumPerUser">
<label class="label">Coverage Pickup</label>
<input nbInput fullWidth [(ngModel)]="data.coverage">
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="label">User Type</label>
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.customerType">
<label class="label">Product</label>
<!-- <input nbInput fullWidth [(ngModel)]="data.promotionType"> -->
<nb-card-body>
<nb-select [(selected)]="data.product">
<nb-option value="Regular">Regular</nb-option>
<nb-option value="Next Day">Next Day</nb-option>
<nb-option value="Same Day">Same Day</nb-option>
<nb-option value="Same Day Service">Same Day Service</nb-option>
<nb-option value="Instant Delivery">Instant Delivery</nb-option>
<nb-option value="Free Ongkir Regular">Free Ongkir Regular</nb-option>
</nb-select>
</nb-card-body>
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="label">Coverage Pickup</label>
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.coverage">
<label class="label">Promo (IDR)</label>
<input (keypress)="numberOnly($event)" nbInput fullWidth [(ngModel)]="data.value">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="exampleInputPassword1" class="label">Product</label>
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.product">
<label class="label">Promo (%)</label>
<input (keypress)="numberOnly($event)" nbInput fullWidth [(ngModel)]="data.value">
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="label">Promo (IDR)</label>
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.value">
<label class="label">Max. Promo</label>
<input (keypress)="numberOnly($event)" nbInput fullWidth [(ngModel)]="data.maximumDiscountValue">
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="label">Promo (%)</label>
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.value">
<label class="label">Budget</label>
<input (keypress)="numberOnly($event)" nbInput fullWidth [(ngModel)]="data.maximumDiscount">
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="label">Max. Promo</label>
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.maximumDiscountValue">
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="label">Budget</label>
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.maximumDiscount">
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="label">Quota</label>
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.quota">
<label class="label">Quota</label>
<input (keypress)="numberOnly($event)" nbInput fullWidth [(ngModel)]="data.quota">
</div>
<button (click)="onSubmit()" type="submit" nbButton status="primary">Submit</button>
</div>

View file

@ -2,7 +2,6 @@ import { Component, Inject, Input } from '@angular/core';
import { NbWindowRef, NB_WINDOW_CONTEXT } from '@nebular/theme';
import { PromotionList } from '../../../@core/data/promotion';
import { PromotionService } from '../promotion.service';
import { NgForm } from '@angular/forms';
@Component({
selector: 'ngx-promotion-detail',
@ -19,7 +18,7 @@ export class PromotionDetailComponent {
}
}
onSubmit(){
onSubmit() {
this.service.postPromotion(this.data).subscribe((value) => {
this.close();
});
@ -28,4 +27,14 @@ export class PromotionDetailComponent {
close() {
this.windowRef.close();
}
numberOnly(event): boolean {
const charCode = (event.which) ? event.which : event.keyCode;
if (charCode > 31 && (charCode < 48 || charCode > 57)) {
return false;
}
return true;
}
}

View file

@ -1,6 +1,6 @@
<nb-card>
<nb-card-header>
Promotion
Promotion - dev 100%
</nb-card-header>
<nb-card-body>

View file

@ -58,7 +58,7 @@ export class PromotionComponent implements OnInit {
this.initData();
}
initData(){
initData() {
this.service.getPromotion().subscribe((result) => {
this.source = Object.assign([], result);
});
@ -78,6 +78,9 @@ export class PromotionComponent implements OnInit {
onDeleteConfirm(event): void {
if (window.confirm('Are you sure you want to delete?')) {
this.service.deletePromotion(event.data).subscribe((value) => {
this.initData();
});
event.confirm.resolve();
} else {
event.confirm.reject();

View file

@ -6,7 +6,9 @@ import {
NbTreeGridModule,
NbButtonModule,
NbCheckboxModule,
NbRadioModule
NbRadioModule,
NbSelectModule,
NbDatepickerModule
} from '@nebular/theme';
import { Ng2SmartTableModule } from 'ng2-smart-table';
@ -28,6 +30,8 @@ import { FormsModule } from '@angular/forms';
NbCheckboxModule,
Ng2SmartTableModule,
FormsModule,
NbSelectModule,
NbDatepickerModule,
],
declarations: [
PromotionComponent,

View file

@ -12,8 +12,10 @@ export class PromotionService {
constructor(private http: HttpClient) {
}
// 34.87.6.140
getPromotion(): Observable<any> {
const url = 'http://localhost:8011/api/promotions/all';
const url = 'http://34.87.6.140:8011/api/promotions/all';
return this.http.get(url).pipe(
map(this.extractData),
catchError(this.handleError),
@ -26,12 +28,25 @@ export class PromotionService {
'X-Requested-Method': 'POST',
});
const options = { headers: headers };
const url = 'http://localhost:8011/api/promotions/add';
const url = 'http://34.87.6.140:8011/api/promotions/add';
return this.http.post(url, JSON.stringify(data), options).pipe(
catchError(this.handleError),
);
}
deletePromotion(data: PromotionList): Observable<any> {
const id = data.id;
const headers = new HttpHeaders({
'Content-Type': 'application/json',
'X-Requested-Method': 'PUT',
});
const options = { headers: headers };
const url = 'http://34.87.6.140:8011/api/promotion/delete';
return this.http.put(url, id, options).pipe(
catchError(this.handleError),
);
}
private extractData(body: any): PromotionList[] {
return Object.assign(body.promotionList);
}

View file

@ -1,36 +0,0 @@
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ServerDataSource } from 'ng2-smart-table';
@Component({
selector: 'ngx-advanced-example-server',
styleUrls: ['./examples.component.scss'],
template: `
<ng2-smart-table [settings]="settings" [source]="source"></ng2-smart-table>
`,
})
export class AdvancedExampleServerComponent {
settings = {
columns: {
id: {
title: 'ID',
},
albumId: {
title: 'Album',
},
title: {
title: 'Title',
},
url: {
title: 'Url',
},
},
};
source: ServerDataSource;
constructor(http: HttpClient) {
this.source = new ServerDataSource(http, { endPoint: 'https://jsonplaceholder.typicode.com/photos' });
}
}

View file

@ -1,45 +0,0 @@
import { Component } from '@angular/core';
import { LocalDataSource } from 'ng2-smart-table';
import { BasicExampleLoadService } from './basic-example-load.service';
@Component({
selector: 'ngx-basic-example-load',
providers: [BasicExampleLoadService],
styleUrls: ['./examples.component.scss'],
template: `
<ng2-smart-table [settings]="settings" [source]="source"></ng2-smart-table>
`,
})
export class BasicExampleLoadComponent {
source: LocalDataSource;
settings = {
columns: {
id: {
title: 'ID',
editable: false,
addable: false,
},
name: {
title: 'Full Name',
},
username: {
title: 'User Name',
},
email: {
title: 'Email',
},
},
};
constructor(protected service: BasicExampleLoadService) {
this.source = new LocalDataSource();
this.service.getData().then((data) => {
this.source.load(data);
});
}
}

View file

@ -1,34 +0,0 @@
import { Injectable } from '@angular/core';
@Injectable()
export class BasicExampleLoadService {
static DATA_SIZE = 500;
// emulating request to the server
getData(): Promise<any> {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(this.generateData());
}, 2000);
});
}
getNewExampleObj(n?: number): any {
n = typeof n !== 'undefined' ? n : Math.random() * 1000;
return {
id: n,
name: `Jack London ${n}`,
username: `jack_london_${n}`,
email: `jack_london_${n}@example.com`,
};
}
protected generateData(): Array<any> {
const data = [];
for (let i = 0; i < BasicExampleLoadService.DATA_SIZE; i++) {
data.push(this.getNewExampleObj(i));
}
return data;
}
}

View file

@ -1,65 +0,0 @@
.with-sidebar {
position: relative;
.main-content-body {
padding-left: 16rem;
}
.fixed-sidebar {
display: block;
padding: 0 1rem;
margin-top: 2rem;
position: fixed;
top: 0;
padding-top: 290px;
width: 16rem;
font-size: 0.875rem;
.back-top {
display: none;
margin-bottom: 1rem;
font-weight: bold;
}
ul {
padding-left: 1rem;
list-style: none;
margin-bottom: 0.875rem;
}
&.scrolled {
position: fixed;
top: 0;
.back-top {
display: block;
}
}
.examples-menu {
a.active {
font-weight: bold;
}
}
}
}
@media screen and (max-width: 64em) {
.with-sidebar {
.fixed-sidebar {
display: none;
}
}
}
@media screen and (min-width: 42em) and (max-width: 64em) {
.with-sidebar {
padding: 2rem 4rem;
}
}
@media screen and (max-width: 42em) {
.with-sidebar {
padding: 2rem 1rem;
}
}

View file

@ -1,19 +0,0 @@
import {NgModule} from '@angular/core';
import {HttpClientModule} from '@angular/common/http';
import {ServerExamplesComponent} from './server-examples.component';
import {BasicExampleLoadComponent} from './basic-example-load.component';
import {AdvancedExampleServerComponent} from './advanced-example-server.component';
import {Ng2SmartTableModule} from 'ng2-smart-table';
@NgModule({
imports: [
HttpClientModule,
Ng2SmartTableModule,
],
declarations: [
ServerExamplesComponent,
BasicExampleLoadComponent,
AdvancedExampleServerComponent,
],
})
export class SampleModule { }

View file

@ -1,17 +0,0 @@
<h2>Get data from external source example</h2>
<h3><a id="from-server" class="anchor" href="#from-server" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Loading From Server Example</h3>
<p>
This example shows how to pass the data loaded from some API to the table DataSource.
</p>
<div class="with-source">
<ngx-basic-example-load></ngx-basic-example-load>
</div>
<h3><a id="server" class="anchor" href="#server" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Server Data Source Example</h3>
<p>
An example on how to load data user Server DataSource:
</p>
<div class="with-source">
<ngx-advanced-example-server></ngx-advanced-example-server>
</div>

View file

@ -1,8 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: 'ngx-server-examples',
styleUrls: ['./examples.component.scss'],
templateUrl: './server-examples.component.html',
})
export class ServerExamplesComponent { }

View file

@ -1,48 +0,0 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { LocalDataSource } from 'ng2-smart-table';
import { map } from 'rxjs/operators';
@Injectable()
export class CustomServerDataSource extends LocalDataSource {
lastRequestCount: number = 0;
constructor(protected http: HttpClient) {
super();
}
count(): number {
return this.lastRequestCount;
}
getElements(): Promise<any> {
let url = 'https://jsonplaceholder.typicode.com/photos?';
if (this.sortConf) {
this.sortConf.forEach((fieldConf) => {
url += `_sort=${fieldConf.field}&_order=${fieldConf.direction.toUpperCase()}&`;
});
}
if (this.pagingConf && this.pagingConf['page'] && this.pagingConf['perPage']) {
url += `_page=${this.pagingConf['page']}&_limit=${this.pagingConf['perPage']}&`;
}
if (this.filterConf.filters) {
this.filterConf.filters.forEach((fieldConf) => {
if (fieldConf['search']) {
url += `${fieldConf['field']}_like=${fieldConf['search']}&`;
}
});
}
return this.http.get(url, { observe: 'response' })
.pipe(
map(res => {
this.lastRequestCount = +res.headers.get('x-total-count');
return res.body;
}),
).toPromise();
}
}

Binary file not shown.

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 732 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 758 KiB

Binary file not shown.

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 738 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 760 KiB

Binary file not shown.

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 726 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 750 KiB

Binary file not shown.

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 736 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 761 KiB

Binary file not shown.

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 716 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 743 KiB

Binary file not shown.

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 726 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 756 KiB

View file

@ -0,0 +1,352 @@
[
{
"title": "Fan of Angular-In-Depth and my writings? Support us on Twitter!",
"link": "https://blog.angularindepth.com/fan-of-angular-in-depth-and-my-writings-support-us-on-twitter-e3bfcbabb4b1",
"text": "A few weeks ago I ran a poll on Twitter to understand why Angular account has 280k followers on Twitter while Angular-In-Depth has only a fraction of that on Medium (11k). The poll showed that 50% of those who responded dont use Medium, 17% find stories too complicated, 27% have no time to read and there are people (7%) who find stories not interesting."
},
{
"title": "Boosting performance of Angular applications with manual change detection",
"link": "https://blog.angularindepth.com/boosting-performance-of-angular-applications-with-manual-change-detection-42cb396110fb",
"text": "Angular uses NgZone/Zone.js to know when to trigger UI update (change detection) when our app data state changes. It brilliantly utilized the events emitted by Zone.js when async operations are performed to detect when to run a change detection cycle."
},
{
"title": "Learn how Angular Elements transmits Components @Outputs outside Angular",
"link": "https://blog.angularindepth.com/how-angular-elements-uses-custom-events-mechanism-to-transmit-components-outputs-outside-angular-7b469386f6e2",
"text": "In our last article we described how Angular Elements works under the hood. We identified that Angular Elements is a bridge to connect Custom Elements to Angular Components."
},
{
"title": "Angular CDK Tables",
"link": "https://blog.angularindepth.com/angular-cdk-tables-1537774d7c99",
"text": "In this article: Angular CDK Tables, Bootstrap 4 with Angular CDK Tables, Client Side searching/paging/sorting."
},
{
"title": "One-way template expression binding mechanism in Angular",
"link": "https://blog.angularindepth.com/becoming-an-angular-environmentalist-45a48f7c20d8",
"text": "Angular is the most popular and widely used JavaScript framework after React.js. It abstracts many complexities away from developers to enable them to develop apps with ease."
},
{
"title": "The Extensive Guide to Creating Streams in RxJS",
"link": "https://blog.angularindepth.com/how-to-unit-test-angular-components-with-fake-ngrx-teststore-f0500cc5fc26",
"text": "For most developers the first contact with RxJS is established by libraries, like Angular. Some functions return streams and to make use of them the focus naturally is on operators."
},
{
"title": "RxJS: Avoiding Unbound Methods",
"link": "https://blog.angularindepth.com/rxjs-avoiding-unbound-methods-fcf2648a805",
"text": "When unbound methods are passed to RxJS, they will be invoked with an unexpected context for this. If the method implementations dont use this, they will behave as you would expect."
},
{
"title": "Angular Elements: how does this magic work under the hood?",
"link": "https://blog.angularindepth.com/angular-elements-how-does-this-magic-work-under-the-hood-3684a0b2be95",
"text": "The Angular Elements project is generating lots of hype in the community right now, and rightly so! Angular Elements provides a wealth of awesome features out of the box."
},
{
"title": "RxJS: Testing with Fake Time",
"link": "https://blog.angularindepth.com/rxjs-testing-with-fake-time-94114271eed2",
"text": "Angular, Jasmine, Jest and Sinon.JS all provide APIs for running tests with fake time. Their APIs differ, but they are broadly similar. Running tests with fake time avoids having to wait for actual time to elapse and it also makes the tests much simpler, as they run synchronously. So what does this have to do with RxJS?"
},
{
"title": "How do CDK Portals work?",
"link": "https://blog.angularindepth.com/how-do-cdk-portals-work-7c097c14a494",
"text": "In the last article we were exploring how to leverage the Angular Material CDK portals for placing some piece of template from a component to some other location within our app. CDK portals make this a no-brainer. Wondering how they work? In this article we dive deeper to uncover how its internals work and how we could simply implement it by ourselves."
},
{
"title": "How I test my NgRx selectors",
"link": "https://blog.angularindepth.com/how-i-test-my-ngrx-selectors-c50b1dc556bc",
"text": "In this post Im going to show you how I test my selectors by putting the selectors from a previous post “Clean NgRx reducers using Immer”, where we created a small shopping cart application, under test. In the application there is a collection of products (the catalog) and the cart items, together they form the state of the application."
},
{
"title": "Angular 5 or Angular 6? Yes please!",
"link": "https://blog.angularindepth.com/angular-5-or-angular-6-yes-please-d71b08b5e59b",
"text": "And, Im glad you asked: YES, you should move all your projects to Angular 6 now or sooner! But … and it is a big but. Like me, you may be in the situation where you are working on multiple projects and many of them are going to be stuck in Angular 5 for a while. So, you need to support a development environment where you can work on and even create new Angular applications in both Angular 5 and Angular 6."
},
{
"title": "Total Guide To Dynamic Angular Animations That Can Be Customized At Runtime",
"link": "https://blog.angularindepth.com/total-guide-to-dynamic-angular-animations-that-can-be-toggled-at-runtime-be5bb6778a0a",
"text": "From route transitions to small details like feedback when clicking on a button or displaying a tooltip, animations give your project that nice sleek look. Well crafted animations communicate that you or your organization care enough to put effort into details and create best possible experience for your users."
},
{
"title": "RxJS: How to Observe an Object",
"link": "https://blog.angularindepth.com/rxjs-how-to-observe-an-object-20c47cf51571",
"text": "A while ago, John Lindquist published a package named rx-handler. With it, you can create event handler functions that are also observables. When it was published, I noticed a few queries about whether something similar could be done with Angulars Input propertiesso that they, too, could be treated as observables."
},
{
"title": "A curious case of the @Host decorator and Element Injectors in Angular",
"link": "https://blog.angularindepth.com/a-curios-case-of-the-host-decorator-and-element-injectors-in-angular-582562abcf0a",
"text": "As you know, Angulars dependency injection mechanism includes a bunch of decorators like @Optional and @Self which impact the way dependencies are resolved. And while most of them are pretty straightforward and self-explanatory, the @Host decorator has puzzled me for a long time."
},
{
"title": "Simple state mutations in NGXS with Immer",
"link": "https://blog.angularindepth.com/simple-state-mutations-in-ngxs-with-immer-48b908874a5e",
"text": "NGXS is a state management pattern + library for Angular. Just like Redux and NgRx its modeled after the CQRS pattern. NGXS uses TypeScript functionality to its fullest extent and because of this it may feel more Angular-y."
},
{
"title": "Upgrading a project without CLI to Angular 6",
"link": "https://blog.angularindepth.com/upgrading-a-project-without-cli-to-angular-6-b07b105adc02",
"text": "In the following article, Im going to describe the challenging process of updating an Angular application with custom Webpack configuration, which our team had to pull through 3 weeks ago. I guess our experience would be useful for those who use Angular with acustom Webpack config. For others, it is an illustration of where modern front-end could lead us and how to live with that."
},
{
"title": "Power of RxJS when using exponential backoff",
"link": "https://blog.angularindepth.com/power-of-rxjs-when-using-exponential-backoff-a4b8bde276b0",
"text": "Most of the modern-day Angular web apps make Ajax requests to the servers. These requests involve multiple network components (such as routers, switches, etc) as well as servers state and everything has to go just right for them to succeed. However, sometimes it doesnt."
},
{
"title": "Clean NgRx reducers using Immer",
"link": "https://blog.angularindepth.com/clean-ngrx-reducers-using-immer-7fe4a0d43508",
"text": "This weeks post is inspired by another great This Dot Media event and the topic this time was state management. There was a small segment about Immer which I found interesting (video is linked at the bottom of this post), so I decided to give it a shot with NgRx."
},
{
"title": "The Angular Library Series - Creating a Library with the Angular CLI",
"link": "https://blog.angularindepth.com/creating-a-library-in-angular-6-87799552e7e5",
"text": "Angular 6 was just released. Many of the improvements were to the Angular CLI. The one I have really been looking forward to is the integration of the Angular CLI with ng-packagr to generate and build Angular libraries. ng-packagr is a fantastic tool created by David Herges that transpiles your library to the Angular Package Format."
},
{
"title": "RxJS: Avoiding takeUntil Leaks",
"link": "https://blog.angularindepth.com/rxjs-avoiding-takeuntil-leaks-fb5182d047ef",
"text": "Using the takeUntil operator to automatically unsubscribe from an observable is a mechanism thats explained in Ben Leshs Dont Unsubscribe article. Its also the basis of a generally-accepted pattern for unsubscribing upon an Angular components destruction."
},
{
"title": "Use <ng-template>",
"link": "https://blog.angularindepth.com/use-ng-template-c72852c37fba",
"text": "Render Props have been making waves in the React community recently, but the corresponding pattern in the Angular world hasnt been getting nearly as much press. Ive written before that TemplateRefs are Angulars Render Props and I hope to give you a good simple example of that here."
},
{
"title": "RxJS: Improving the Static pipe Function",
"link": "https://blog.angularindepth.com/rxjs-improving-the-static-pipe-function-81146fbb14b6",
"text": "My previous article looked at using the static pipe function to compose reusable combinations of operators. Most of the time, the pipe functions TypeScript overload signatures will infer the desired type for the returned function. However, sometimes its desirable to have a generic type inferred and the current overload signatures will not do that."
},
{
"title": "Angular Ivy change detection execution: are you prepared?",
"link": "https://blog.angularindepth.com/angular-ivy-change-detection-execution-are-you-prepared-ab68d4231f2c",
"text": "While new Ivy renderer is not feature completely yet, many people wonder how it will work and what changes it prepares for us. In this article I am going to visualize Ivy change detection mechanism, show some things I am really excited about and also build simple app based on instructions, similar to angular Ivy instructions, from scratch."
},
{
"title": "Ivy engine in Angular: first in-depth look at compilation, runtime and change detection",
"link": "https://blog.angularindepth.com/ivy-engine-in-angular-first-in-depth-look-at-compilation-runtime-and-change-detection-876751edd9fd",
"text": "I usually finish my talks with the philosophical phrase that nothing stays the same. And as you probably know its more then true with Angular. The current rendering engine is being rewritten with the new much enhanced version called Ivy. The current status of Ivy can be tracked here."
},
{
"title": "RxJS: Combining Operators",
"link": "https://blog.angularindepth.com/rxjs-combining-operators-397bad0628d0",
"text": "In version 5.5, pipeable operators were added to RxJS. And in version 6, their non-pipeable namesakes were removed. Pipeable operators have numerous advantages. The most obvious is that they are easier to write. A less obvious advantage is that they can be composed into reusable combinations."
},
{
"title": "A modern solution to lazy loading images using Intersection Observer",
"link": "https://blog.angularindepth.com/a-modern-solution-to-lazy-loading-using-intersection-observer-9280c149bbc",
"text": "Performance of a web application has become a key factor in deciding conversion rates for e-commerce websites. The faster a page loads, the better the conversion rate. According to the recent mobile page speed benchmarks released by Google, the bounce probability increases as page load time increases."
},
{
"title": "Working with DOM in Angular: unexpected consequences and optimization techniques",
"link": "https://blog.angularindepth.com/working-with-dom-in-angular-unexpected-consequences-and-optimization-techniques-682ac09f6866",
"text": "I recently gave a talk on advanced DOM manipulations in Angular in a form of a workshop at NgConf. I went from the basics like using template references and DOM queries to access DOM elements to using a view container to render templates and components dynamically."
},
{
"title": "The benefits of application state normalization in Angular",
"link": "https://blog.angularindepth.com/the-benefits-of-application-state-normalization-in-angular-f93392ca9f44",
"text": "Imagine we have a recursive data structure in the store, let us say, information about a products category in an e-commerce application. Category is the classification of which type of product it is. For example, Mobile Phones category can have subcategories such as Google, Apple, Samsung and so on and each subcategory can in turn have further subcategories..."
},
{
"title": "RxJS: TSLint Rules for Version 6",
"link": "https://blog.angularindepth.com/rxjs-tslint-rules-for-version-6-d10e2482292d",
"text": "Earlier this week, RxJS version 6 was released and, with its release, managing RxJS imports has become much, much easier. Last year, I wrote a bunch of TSLint rules for managing RxJS imports. Theyre distributed in the rxjs-tslint-rules package."
},
{
"title": "Angular Universal & Firebase functions: The missing guide",
"link": "https://blog.angularindepth.com/angular-5-universal-firebase-4c85a7d00862",
"text": "Lucky you, Ive written this simplified guide to configure Angular 5 Universal in your Angular project. Moreover, Im gonna give you also a bonus track on how to run Universal in a serverless environment like Firebase Cloud Functions."
},
{
"title": "Angular and Internet Explorer",
"link": "https://blog.angularindepth.com/angular-and-internet-explorer-5e59bb6fb4e9",
"text": "You installed the Angular CLI and used it to generate your new application. But, when you try to view it in Internet Explorer (IE), you see nothing. Now what? The bad news: Angular CLI applications require a few more steps in order to support Internet Explorer."
},
{
"title": "Gestures in an Angular Application",
"link": "https://blog.angularindepth.com/gestures-in-an-angular-application-dde71804c0d0",
"text": "In this post I will attempt to explain how to use hammerjs gesture recognizers provided by the @angular/platform-browser package. Ill be referencing @angular/platform-browser@5.2.0 within my code samples, but there are some changes coming to 6.0.0 that will be discussed later."
},
{
"title": "Deploy an Angular Application to IIS",
"link": "https://blog.angularindepth.com/deploy-an-angular-application-to-iis-60a0897742e7",
"text": "The Angular Router is a fantastic module for Single Page Apps. However, to deploy it in a Production scenario you will typically need to do some configuration to make it work. This article details the steps necessary to deploy an Angular Router application anywhere on Internet Information Services (IIS)."
},
{
"title": "Super Charging an Angular CLI App",
"link": "https://blog.angularindepth.com/super-charging-an-angular-cli-app-fc496a6c100",
"text": "A standard Angular CLI application comes with a terrific set of of tooling to prepare you to get developing quickly. However, theres a few additional steps you should take to really prepare your project for success. In this article Ill break down all the additional features you can add to your project without ejecting (exporting the WebPack Config)."
},
{
"title": "What you always wanted to know about Angular Dependency Injection tree",
"link": "https://blog.angularindepth.com/angular-dependency-injection-and-tree-shakeable-tokens-4588a8f70d5d",
"text": "If you didnt dive deep into angular dependency injection mechanism, your mental model should be that in angular application we have some root injector with all merged providers, every component has its own injector and lazy loaded module introduces new injector."
},
{
"title": "RxJS: When to Use switchMap",
"link": "https://blog.angularindepth.com/when-to-use-switchmap-dfe84ac5a1ff",
"text": "In a response to RxJS: Avoiding switchMap-Related Bugs, Martin Hochel mentioned a classic use case for switchMap. For the use case to which he referred, switchMap is not only valid; its optimal. And its worth looking at why."
},
{
"title": "RxJS: Understanding Expand",
"link": "https://blog.angularindepth.com/rxjs-understanding-expand-a5f8b41a3602",
"text": "RxJS has a lot of operators. Lots and lots of them. It takes time to learn what they all do and how they can be used. Some operators are straightforward; others, less so. One operator that developers often find confusing is expand."
},
{
"title": "RxJS: Composing Subscriptions",
"link": "https://blog.angularindepth.com/rxjs-composing-subscriptions-b53ab22f1fd5",
"text": "RxJS code involves making subscriptions to observables. Lots of subscriptions. If each subscription is assigned to its own variable or property, the situation can be difficult to manage."
},
{
"title": "Handle Template Reference Variables with Directives",
"link": "https://blog.angularindepth.com/handle-template-reference-variables-with-directives-223081bc70c2",
"text": "Ive been using template reference variables pretty liberally in my examples so far, and its high time I dive in a bit into how to use them to reference specific directives."
},
{
"title": "Avoid Namespace Clashes with Directives",
"link": "https://blog.angularindepth.com/avoid-namespace-clashes-with-directives-1f00d62de445",
"text": "Not only can the selector for a directive clash with another directive, but Inputs and Outputs for those directives can clash with each other. When they have the same name, Angular doesnt complainit just applies the logic to both directives. In some cases, this is exactly what we want. However, sometimes it can cause unexpected behavior."
},
{
"title": "Dynamically Loading Components with Angular CLI",
"link": "https://blog.angularindepth.com/dynamically-loading-components-with-angular-cli-92a3c69bcd28",
"text": "When moving from a multi-page application to a SPA, one of the problems that presents itself is the payload size upon initial load. By default, in an Angular application everything is bundled into one payload, which means as the application grows, so does the time that it takes to load."
},
{
"title": "Insiders guide into interceptors and HttpClient mechanics in Angular",
"link": "https://blog.angularindepth.com/insiders-guide-into-interceptors-and-httpclient-mechanics-in-angular-103fbdb397bf",
"text": "You probably know that Angular introduced a new powerful HTTP client in version 4.3. One of its major features was request interceptionthe ability to declare interceptors which sit in between your application and the backend."
},
{
"title": "Enhance Components with Directives",
"link": "https://blog.angularindepth.com/enhance-components-with-directives-58f16c4ca1f",
"text": "One element of part 4 of Kent C. Dodds series that I didnt touch on in the previous article is the fact that the withToggle higher order component is able to pull common logic out of the <toggle-on>, <toggle-off>, and <toggle-button> components. There wasnt very much logic happening in those components in the last article, but what if there were?"
},
{
"title": "Communicate Between Components Using Dependency Injection",
"link": "https://blog.angularindepth.com/communicate-between-components-using-dependency-injection-d7280567faa7",
"text": "There is another problem weve found with our <toggle> component. We cant have more than one <toggle-on> or <toggle-button> component in the same <toggle> and a <toggle-on> that is inside of another custom component wont be picked up by the @ContentChild decorator."
},
{
"title": "Build a Toggle Component",
"link": "https://blog.angularindepth.com/build-a-toggle-component-6e8f44889c2c",
"text": "Just like in Kent C. Dodds Advanced React Component Patterns, we will use a relatively simple <toggle> component to illustrate these patterns. The <toggle> component is responsible for managing a singleboolean property: on."
},
{
"title": "Introducing Advanced Angular Component Patterns",
"link": "https://blog.angularindepth.com/introducing-advanced-angular-component-patterns-13e102e6bbfc",
"text": "This series of posts is my small attempt to broaden my own view by providing a translation of Kent C. Dodds Advanced React Patterns in Angular. My goal is to foster learning and sharing rather than criticism."
},
{
"title": "Top 10 Angular articles in 2017 from Angular-In-Depth you really want to read",
"link": "https://blog.angularindepth.com/top-10-angular-articles-in-2017-from-angularindepth-you-really-want-to-read-153ae6e497d4",
"text": "Almost one year ago I started Angular-In-Depth medium publication with the goal to become the largest and most technical Angular publication on medium. I was lucky to get on board very talented and knowledgeable guys Uri Shaked, Nicholas Jamieson and Chaz Gatian."
},
{
"title": "Practical RxJS In The Wild 🦁— Requests with concatMap() vs mergeMap() vs forkJoin() 🥊",
"link": "https://blog.angularindepth.com/practical-rxjs-in-the-wild-requests-with-concatmap-vs-mergemap-vs-forkjoin-11e5b2efe293",
"text": "I would like to share with you experience acquired by working on a yet another Hacker News client (code name HAKAFAKA 😂 still in alpha). I have been on the road for couple months now and realized that a small coding project wouldnt hurt."
},
{
"title": "He who thinks change detection is depth-first and he who thinks its breadth-first are both usually right",
"link": "https://blog.angularindepth.com/he-who-thinks-change-detection-is-depth-first-and-he-who-thinks-its-breadth-first-are-both-usually-8b6bf24a63e6",
"text": "I was once asked if change detection in Angular is depth or breadth first. This basically means whether Angular first checks siblings of the current component (breadth-first) or its children (depth-first). I hadnt given any prior thought to this question so I just went with my gut and the knowledge of internals."
},
{
"title": "Learn to combine RxJs sequences with super intuitive interactive diagrams",
"link": "https://blog.angularindepth.com/learn-to-combine-rxjs-sequences-with-super-intuitive-interactive-diagrams-20fce8e6511",
"text": "When working on a sufficiently complex application you usually have data coming from more than one data source. It can be some multiple external data points like Firebase or several UI widgets interacting with a user. Sequence composition is a technique that enables you to create complex queries."
},
{
"title": "React Call Return in Angular",
"link": "https://blog.angularindepth.com/react-call-return-in-angular-32a1c9751d6",
"text": "This article continues in the theme of taking React articles and reimagining them in Angular. See TemplateRefs are Angulars Render Props and Content Directives Are Angulars Prop Getters."
},
{
"title": "Do you really know what unidirectional data flow means in Angular",
"link": "https://blog.angularindepth.com/do-you-really-know-what-unidirectional-data-flow-means-in-angular-a6f55cefdc63",
"text": "Most architectural patterns are not easy to grasp especially when the information that describes them is scarce. One of such patterns in Angular is unidirectional data flow. Theres no clear explanation of what that means in the official documentation and its only briefly mentioned in the expression guidelines and template statements sections."
},
{
"title": "How to Reduce Action Boilerplate",
"link": "https://blog.angularindepth.com/how-to-reduce-action-boilerplate-90dc3d389e2b",
"text": "I use Redux for my application development and, to take advantage of RxJS, I use NgRx in Angular projects and redux-observable in React projects. I also use TypeScript."
},
{
"title": "These 5 articles will make you an Angular Change Detection expert",
"link": "https://blog.angularindepth.com/these-5-articles-will-make-you-an-angular-change-detection-expert-ed530d28930",
"text": "In the last 8 months Ive spent most of my free time reverse-engineering Angular. The topic that fascinated me the most was change detection. Id argue that its the most important part of the framework since its responsible for the “visible” job like DOM updates, input bindings and query list updates."
},
{
"title": "Angular CDK Portals",
"link": "https://blog.angularindepth.com/angular-cdk-portals-b02f66dd020c",
"text": "The @angular/cdk contains a concept called portals. In this post Ill attempt to explain the concepts of a Portal, and when they should be applied. The example code in this post is referencing @angular/cdk@2.0.0-beta.12."
},
{
"title": "Content Directives Are Angulars Prop Getters",
"link": "https://blog.angularindepth.com/content-directives-are-angulars-prop-getters-360fdae60576",
"text": "Kent C. Dodds wrote a piece about using prop getters in React. Along with render props (see TemplateRefs Are Angulars Render Props), prop getters allow component library authors to give users as much control of the rendering as possiblethe component only needs to do its job."
},
{
"title": "Using TransferState API in an Angular v5 Universal App",
"link": "https://blog.angularindepth.com/using-transferstate-api-in-an-angular-5-universal-app-130f3ada9e5b",
"text": "You can get a more up-to-date version at https://leanpub.com/angular-universal. Lets illustrate this article with a concrete example. We have a weather app, displaying a list of cities in its sidebar. When you click on a city name, the app displays the current weather in this city."
},
{
"title": "Do you still think that NgZone (zone.js) is required for change detection in Angular?",
"link": "https://blog.angularindepth.com/do-you-still-think-that-ngzone-zone-js-is-required-for-change-detection-in-angular-16f7a575afef",
"text": "Most articles I have seen strongly associate Zone(zone.js) and NgZone with change detection in Angular. And although they are definitely related, technically they are not part of one whole. Yes, Zone and NgZone is used to automatically trigger change detection as a result of async operations."
},
{
"title": "As busy as a beelazy loading in the Angular CLI",
"link": "https://blog.angularindepth.com/as-busy-as-a-bee-lazy-loading-in-the-angular-cli-d2812141637f",
"text": "Angular has a programmatic API for lazy loading NgModules. In the Angular CLI, it has a direct dependency upon webpacks underlying toolchain for chunk splitting and lazy loading. Its thus (almost) impossible to use it outside of an ordinary router set-up. Custom lazy loading strategies need to use SystemJS."
},
{
"title": "TemplateRefs are Angulars Render Props",
"link": "https://blog.angularindepth.com/templaterefs-are-angulars-render-props-a2b97cbcc362",
"text": "As a developer that spends most of my time building Angular apps, I still love reading about what the React community is doing. Were generally solving the same problems and innovation in one community can be leveraged in another."
},
{
"title": "RxJS: How to Use Lettable Operators with Promises",
"link": "https://blog.angularindepth.com/rxjs-how-to-use-lettable-operators-and-promises-2e717313bf76",
"text": "Converting observables to promises is an antipattern. Unless you are integrating observables with a promise-based API, there is no reason to convert an observable into a promise."
},
{
"title": "RxJS: Pipelining Lettable Operators",
"link": "https://blog.angularindepth.com/rxjs-pipelining-lettable-operators-f92f6843d817",
"text": "Earlier this week, a TC39 proposal for a pipeline operator moved to stage-1. If the proposal is eventually accepted and included in the ECMAScript standardit has a long way to goit will offer a new syntax for lettable operators."
},
{
"title": "I reverse-engineered Zones (zone.js) and here is what Ive found",
"link": "https://blog.angularindepth.com/i-reverse-engineered-zones-zone-js-and-here-is-what-ive-found-1f48dc87659b",
"text": "Zones is a new mechanism that helps developers work with multiple logically-connected async operations. Zones work by associating each async operation with a zone."
},
{
"title": "RxJS: Understanding Lettable Operators",
"link": "https://blog.angularindepth.com/rxjs-understanding-lettable-operators-fe74dda186d3",
"text": "Lettable operators offer a new way of composing observable chains and they have advantages for both application developers and library authors. Lets look briefly at the existing composition mechanisms in RxJS and then look at lettable operators in more detail."
},
{
"title": "The essential difference between Constructor and ngOnInit in Angular",
"link": "https://blog.angularindepth.com/the-essential-difference-between-constructor-and-ngoninit-in-angular-c9930c209a42",
"text": "One of the most popular Angular questions on stackoverflow is Difference between Constructor and ngOnInit with over 100k views. I gave my answer to this question there but also decided to expand on it in this article."
},
{
"title": "RxJS: How to Use refCount",
"link": "https://blog.angularindepth.com/rxjs-how-to-use-refcount-73a0c6619a4e",
"text": "My previous articleUnderstanding the publish and share Operatorslooked only briefly at the refCount method. Lets look at it more closely here."
},
{
"title": "The essential difference between pure and impure pipes in Angular and why that matters",
"link": "https://blog.angularindepth.com/the-essential-difference-between-pure-and-impure-pipes-and-why-that-matters-999818aa068",
"text": "When writing a custom pipe in Angular you can specify whether you define a pure or an impure pipe. Angular has a pretty good documentation on pipes that you can find here. But as it often happens with documentation the clearly reasoning for division is missing."
},
{
"title": "RxJS: Understanding the publish and share Operators",
"link": "https://blog.angularindepth.com/rxjs-understanding-the-publish-and-share-operators-16ea2f446635",
"text": "Im often asked questions that relate to the publish operator: Whats the difference between publish and share? How do I import the refCount operator? When should I use an AsyncSubject? Lets answer these questionsand moreby starting with the basics."
},
{
"title": "If you think `ngDoCheck` means your component is being checkedread this article",
"link": "https://blog.angularindepth.com/if-you-think-ngdocheck-means-your-component-is-being-checked-read-this-article-36ce63a3f3e5",
"text": "Theres one question that comes up again and again on stackoverflow. The question is about ngDoCheck lifecycle hook that is triggered for a component that implements OnPush change detection strategy."
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

Some files were not shown because too many files have changed in this diff Show more