• Динамические классы в CSS и HTML используя Angular 9?

    Xuxicheta
    @Xuxicheta Куратор тега Angular
    инженер
    При стандартных настройках ангуляр добавляет хтмл-элементам компонента свою метку, типа _nghost-lli-c18 или _ngcontent-rja-c18
    Всем стилям компонента автоматически добавляется эта приписка, например в браузере будет выглядеть как
    a[_ngcontent-rja-c18] {
        color: red;
    }

    В Ангуляре это зовется "инкапсуляция стилей", является дефолтным поведением и может быть выключено или изменено на использование ShadowDOM.
    Таким образом стили компонента накладываются только на тэги в компоненте. Что-то там отдельно синхронизировать не нужно.
    Ответ написан
    1 комментарий
  • Стоит ли сразу начинать с Typescript и Angular? Или начинать с малого?

    Xuxicheta
    @Xuxicheta
    инженер

    Стоит ли начинать писать сразу с помощью Typescript?

    На нем проще писать, чем на js, язык сам подсказывает что можно, а что нет.

    Насчет Angular, он сложнее в освоении за счет своей комплексности. Если React это просто продвинутый шаблонизатор и вместе с ним для серьезной работы вам придется осваивать еще много всего, то Angular включает в себя практически все что нужно. Однако в нем нет стейт-менеджера, а концепция rxjs для многих требует много времени для понимания.
    Vue находится где-то посередине, это уже фреймворк, многое дано по дефолту, но без подпорок тоже не обойтись, .
    Ответ написан
    1 комментарий
  • Когда не стоит использовать стрелочные функции?

    Xuxicheta
    @Xuxicheta
    инженер
    ок
    el.addEventListener('click', (evt) => {
      console.log(evt)
    })


    не ок
    el.addEventListener('click', function (evt) {
      console.log(evt)
    })


    ок
    function mySuperFunc(evt) {
      // еще десяток строк
      console.log(evt)
    }


    не ок
    const mySuperFunc = (evt) =>  {
      // еще десяток строк
      console.log(evt)
    }


    ок
    const mySuperFunc = (evt) => evt.target;

    не ок
    function mySuperFunc(evt) {
      return evt.target;
    }


    в целом как-то так. Это не догма, просто так лучше читается. Ну и конечно в случаях с действиями с this, там все однозначно, где лямбда, а где нет.
    Ответ написан
    5 комментариев
  • Вопрос по form control в ангуляре?

    Xuxicheta
    @Xuxicheta Куратор тега Angular
    инженер

    Мне нужно, чтобы при самом первом запросе это поле не участвовало в запросе я этого добился тем, что убрал значение по дефолту в этом поле.

    поясните более развернуто, почему такое поведение?

    Пока выглядит так, что вам нужно проверять это в запросе, форма тут роли вообще не должна играть.
    И дефолтное значение у формы всегда есть, если не указали то это undefined.
    Ответ написан
  • Почему вылетает error 'Cannot read property 'subscribe' of undefined'?

    Xuxicheta
    @Xuxicheta Куратор тега Angular
    инженер
    getCerts(): Observable<any> {
      // ...
      return new Observable(observer => {
        chrome.runtime.sendMessage(editorExtensionId, message, res=> {
          // ...
           observer.next(result);
           observer.complete();
        })
    }
    })


    Домашняя работа - сократить код используя https://rxjs.dev/api/index/function/bindCallback
    Ответ написан
    Комментировать
  • Чем отличаются импорты компонентов во Vue?

    Xuxicheta
    @Xuxicheta
    инженер
    По втором случае Layout будет помещен в отдельный файл и загрузится при надобности.
    Читать тут
    Ответ написан
    Комментировать
  • Какой тип должен быть у переменной, которая передается как ключ для доступа к значению, находящимуся в HTMLElement?

    Xuxicheta
    @Xuxicheta
    инженер
    offset и singleSlider должны быть нормально типизированы.
    offset хотя бы как keyof HTMLElement, но вообще лучше конкретно перечень возможных значений.
    Ответ написан
    2 комментария
  • JS Функции декораторы?

    Xuxicheta
    @Xuxicheta
    инженер
    обертка возвращает функцию с аргументом x. return function(x) {.
    Результат выполнения функции decorate - тоже функция, она сохраняется в переменной fac
    И вызываете вы потом эту функцию.

    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
    Ответ написан
    Комментировать
  • JS, циклы в PUG. Надо ли?

    Xuxicheta
    @Xuxicheta
    инженер
    pug не инструмент для ускорения верстки.
    Это шаблонизатор. Он по шаблону и данным строит страницу. И циклы там для построения стилизованных списков из массивов данных.
    Тут не вопрос надо или не надо, без цикла вы просто не сделаете нужный шаблон.
    Ответ написан
    3 комментария
  • Как сгенерировать разные поля input?

    Xuxicheta
    @Xuxicheta Куратор тега Angular
    инженер
    вот примерно так ток добавьте материал и чтение/сохранение в ls
    Ответ написан
    Комментировать
  • Как получить доступ к chrome в Angular?

    Xuxicheta
    @Xuxicheta Куратор тега Angular
    инженер
    не надо слушать вредные советы :)
    Установите типы для хрома https://www.npmjs.com/package/@types/chrome

    а потом где-нибудь создаем токен для инжектора.
    export const CHROME = new InjectionToken('CHROME');

    в провайдерах апп-модуля по этому токену провайдим значение
    providers: [
      ...
      {
        provide: CHROME,
        useValue: window.chrome,
      }
      ...
    ]

    В конструкторе вашего компонента или сервиса
    constructor(
      @Inject(CHROME) private chrome: chrome,
    )


    Это делается для того чтобы иметь возможность когда нужно эту зависимость подменить. Например подсовывать моки для тестирования.
    Но вообще можно и просто chrome или window.chrome вызвать, если вы не паритесь по поводу подмен и вообще слежением за зависимостями. Главное объяснить тайпскрипту что это валидное имя. Ну т.е. доустановить типы.

    Я могу ошибиться в мелочах, т.к. не проверял, но правильный способ в общих чертах описал.
    Ответ написан
    2 комментария
  • Как использовать this в стрелочной функции в typescript?

    Xuxicheta
    @Xuxicheta
    инженер
    Судя по данному PR в typescript можно использовать this в стрелочных функциях

    эм.. я может не туда смотрю, но там написано как раз что это бессмысленно. И ни одного примера типизации this в стрелочных нет.
    This formulation is OK because lambda (=>) doesn't bind this, so this comes from the class instead of from the implementing function.


    А вообще можно объявив сначала тип для функции, вот так
    https://www.typescriptlang.org/play/?noImplicitThi...
    Но оно тоже работает криво.
    А зачем вам вообще это понадобилось?
    Ответ написан
    6 комментариев
  • Как прокручивать меню?

    Xuxicheta
    @Xuxicheta Куратор тега Angular
    инженер
    Определить ширину контейнера в ngAfterViewInit
    При свайпе выставлять transform: translateX(ширина * индекс) с помощью Renderer2
    Анимацию на transform в стили.
    Ответ написан
    Комментировать
  • Почему не происходит запись в базу indexedDB?

    Xuxicheta
    @Xuxicheta Куратор тега Angular
    инженер
    На вид все вроде ок, надо дебажить, перерести куда-нибудь в песочницу.
    Пока могу предложить свою обертку к indexedDB, вроде работает, не тестировал толком. Может наведет на размышление.
    DbService

    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$;
      }
    }

    Ответ написан
  • Как лучше работать с localStorage?

    Xuxicheta
    @Xuxicheta
    инженер

    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.


    https://developer.mozilla.org/en-US/docs/Web/API/W...
    Ответ написан
    Комментировать
  • Как настроить/использовать модули для typescript?

    Xuxicheta
    @Xuxicheta
    инженер
    Простейший конфиг webpack.config.js

    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'),
      },
    };


    исходники должны лежать в папке src, стартовый файл index.ts
    сборка webpack --mode=production
    разработка с hmr
    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"
      }


    tsconfig.json (думаю не очень хороший, но рабочий)
    {
      "compilerOptions": {
        "allowJs": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "incremental": true,
        "module": "es6",
        "noImplicitAny": false,
        "outDir": "./dist/",
        "sourceMap": true,
        "target": "es2016"
      }
    }


    tslint.json - правила для линтера
    {
      "extends": "tslint-config-airbnb",
      "rules": {}
    }


    в html
    <script src="dist/bundle.js"></script>
    Ответ написан
    Комментировать
  • Как сделать переворот стрелки при наведении?

    Xuxicheta
    @Xuxicheta
    инженер
    Ответ написан
    Комментировать
  • Лучше писать функции через const?

    Xuxicheta
    @Xuxicheta
    инженер
    Лучше все писать через const
    Ответ написан
    9 комментариев
  • Зачем столько асинхронных инструментов в JavaScript?

    Xuxicheta
    @Xuxicheta
    инженер
    промисы и async это одно и тоже.
    И все это лишь сахар для коллбэков разной степени удобности. И вы еще евенты не упомянули. А так же цикл событий, таски/микротаски, квази-асинхронность.

    В язык входят те вещи, которые tc39 посчитают достойными этого. Промисы позволили писать цепочки более понятно, асинки избавили от обилия скобочек. Генераторы не прижились и редко где используются, хотя иногда я их применяю, по назначению разумеется, а не для асинхронного выполнения.

    И этого еще не хватает, неплохо было бы иметь поддержку Observable на уровне языка, многие браузерные api стали бы удобнее.
    Ответ написан
    2 комментария
  • Как с помощью RxJs показать лоадер для медленного запроса?

    Xuxicheta
    @Xuxicheta Куратор тега Angular
    инженер
    https://stackblitz.com/edit/angular-ws6pee
    задержки увеличены до 1000 и 2500 для наглядности
    В случае множественных загрузок возможны коллизии, надо будет дорабатывать.
    Лоадер, дебаунсер и таймауты можно и нужно изолировать в отдельном компоненте (или директиве), который будет получать текущее значение лоадера (тут оно setLoader) как инпут параметр.
    В принципе вообще можно сделать даже пайп.


    - если запрос длится более 500 мс, то показываем лоадер
    - если лоадер уже показан, то отображать его не менее 300 мс

    Если бы эти два числа были одинаковы, можно было обойтись одним debounceTime на лоадере.
    Ответ написан