return function(x) {
.function decorator(func) {
let cache = new Map();
return function decoratedFunc(x) {
if (cache.has(x)) {
return cache.get(x);
};
let result = func.call(this, x);
cache.set(x, result);
return result
}
}
obj.fac = decorator(obj.fac); // obj.fac === decoratedFunc
export const CHROME = new InjectionToken('CHROME');
providers: [
...
{
provide: CHROME,
useValue: window.chrome,
}
...
]
constructor(
@Inject(CHROME) private chrome: chrome,
)
Судя по данному PR в typescript можно использовать this в стрелочных функциях
This formulation is OK because lambda (=>) doesn't bind this, so this comes from the class instead of from the implementing function.
export interface DbConfig {
/** prefix for indexed db name */
name: string;
/** keyPath in indexed db */
keyPath?: string;
/** keyPath of indexed db */
objectName: string;
}
@Injectable()
export class DbService<T = any> {
private version = 1;
private db$: Observable<IDBDatabase> = this.createDb(this.config.name, this.version);
constructor(
@Inject(DB_CONFIG) private config: DbConfig,
) { }
private createDb(name: string, version?: number): Observable<IDBDatabase> {
const openRequest: IDBOpenDBRequest = indexedDB.open(name, version);
openRequest.onupgradeneeded = (evt: IDBVersionChangeEvent) => this.onUpgradeNeeded(openRequest);
return this.fromIDBRequest(openRequest).pipe(
shareReplay(1),
);
}
private onUpgradeNeeded(openRequest: IDBOpenDBRequest): void {
const db = openRequest.result;
if (db.objectStoreNames.contains(this.config.objectName)) {
return;
}
db.createObjectStore(this.config.objectName, { keyPath: this.config.keyPath });
}
public save(value: T, key?: IDBValidKey): Observable<IDBValidKey> {
return this.db$.pipe(
mergeMap((db: IDBDatabase) => this.fromIDBRequest(
this.createIDBObjectStore(db, 'readwrite').put(value, key)
)),
);
}
public saveAll(values: T[]): Observable<IDBValidKey[]> {
return from(values).pipe(
mergeMap((value: T, index: number) => this.save(value, index)),
toArray(),
);
}
public delete(key: string): Observable<undefined> {
return this.db$.pipe(
mergeMap(db => this.fromIDBRequest(
this.createIDBObjectStore(db, 'readwrite').delete(key),
))
);
}
public clear() {
return this.db$.pipe(
mergeMap(db => this.fromIDBRequest(
this.createIDBObjectStore(db, 'readwrite').clear(),
))
);
}
public retreive(key: string): Observable<T> {
return this.db$.pipe(
mergeMap(db => this.fromIDBRequest(
this.createIDBObjectStore(db, 'readonly').get(key)
)),
);
}
public retreiveAll(): Observable<T[]> {
return this.db$.pipe(
mergeMap(db => this.fromIDBRequest(
this.createIDBObjectStore(db, 'readonly').getAll()
)),
);
}
private createIDBObjectStore(db: IDBDatabase, mode: IDBTransactionMode): IDBObjectStore {
const transaction: IDBTransaction = db.transaction(this.config.objectName, mode);
return transaction.objectStore(this.config.objectName);
}
private fromIDBRequest<R>(idbRequest: IDBRequest<R>): Observable<R> {
return new Observable<R>(observer => {
idbRequest.onsuccess = (evt: Event) => {
observer.next(idbRequest.result);
observer.complete();
evt.stopPropagation();
};
idbRequest.onerror = (evt: Event) => {
observer.error(idbRequest.error);
evt.stopPropagation();
};
});
}
public selectDb() {
return this.db$;
}
}
Note: It's recommended to use the Web Storage API (setItem, getItem, removeItem, key, length) to prevent the pitfalls associated with using plain objects as key-value stores.
const path = require('path');
module.exports = {
entry: './src/index.ts',
devtool: 'inline-source-map',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
webpack --mode=production
webpack-dev-server --mode development --inline --hot
"devDependencies": {
"css-loader": "^3.0.0",
"ts-loader": "^6.0.4",
"tslint": "^5.18.0",
"tslint-config-airbnb": "^5.11.1",
"typescript": "^3.5.2",
"webpack": "^4.35.0",
"webpack-cli": "^3.3.5",
"webpack-dev-server": "^3.7.2"
}
{
"compilerOptions": {
"allowJs": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"incremental": true,
"module": "es6",
"noImplicitAny": false,
"outDir": "./dist/",
"sourceMap": true,
"target": "es2016"
}
}
{
"extends": "tslint-config-airbnb",
"rules": {}
}
<script src="dist/bundle.js"></script>
- если запрос длится более 500 мс, то показываем лоадер
- если лоадер уже показан, то отображать его не менее 300 мс
@Injectable()
export class UserService {
isAuthenticated$ = new ReplaySubject(1);
constructor(private http: HttpClient) { }
getUser() {
return this.http.get('/server/api/userService').pipe(
tap(data => this.isAuthenticated$.next(!!data['login'])),
);
}
}
export class HeaderComponent implements OnInit {
user$: Observable<User>;
isAuthenticated$: Observable<boolean>;
constructor(private userService: UserService) { }
ngOnInit() {
this.user$ = this.userService.getUser();
this.isAuthenticated$ = this.userService.isAuthenticated$;
}
}