• Как перенести коммит?

    @askhat
    git checkout master
    git rev-parse HEAD <- Ctrl+C
    git checkout dev
    git cherry-pick [Ctrl+V]
    git checkout master
    git reset HEAD~1 <- сбрасываем последний коммит
    git checkout -- . <- сбрасываем состояние директории
    Ответ написан
    Комментировать
  • С чем связана ошибка unknown key adress?

    @askhat
    Addresses

    dd
    Ответ написан
    Комментировать
  • Как преобразовать строку в дату?

    @askhat
    Не благодари

    const monthToInt = val => {
      val = val.toLowerCase();
      switch (true) {
        case !!val.match(/^янв/):  return 0;
        // TODO добавить больше кейсов
      }
    }
    
    const dateFromLocaleString = date =>
      new Date(
        ...date
          .split(" ")
          .reduce(
            (acc, val) =>
              (acc = [val.match(/\D/) ? monthToInt(val) : parseInt(val), ...acc]),
            []
          )
      )
    
    dateFromLocaleString('07 янв 2019')
    Ответ написан
    Комментировать
  • Docker swarm вопросы по архитектурое?

    @askhat
    > По идее nginx должен быть поднят как для боя, так и на машинке для разработчиков

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

    example.com
    dev.example.com // как я понимаю слушает только 192.168...
    
    или
    
    example.com/
    example.com/dev // imho не удобно
    Ответ написан
    3 комментария
  • Нормально ли использование VUE JS только для работы с формами, придания реактивности и тд?

    @askhat
    Vue примечателен хотя бы тем, с какой легкостью он интегрируется в уже существующие проекты. Набор возможностей этой библиотеки, на мой взгляд лучшая реализация MVVM. Допустим на вашем сайте есть jQuery слайдер–калькулятор, вы добавили плагин для чата с онлайн–консультантом и вам нужно передавать данные в чат. Добавив модель представления, в виде Vue приложения, вы можете легко управлять данными и моделью представления:

    <body>
      <div id="app">
        <p> {{ calculationResult | rub }}</p>
        <!-- Плагин range slider инициализируется через директиву
               В кавычка объект из binding.value из сигнатуры хука -->
        <div v-range-slider="{ min: 0, max: 10 }"></div>
        <!-- Подписка на события осуществляется прямо в html -->
        <button @click="sendToChat">Отправить в чат</button>
        <button @click="toggleChat">Открыть чат</button>
        <button @click="toggleChat">Закрыть чат</button>
        <!-- Чат изолирован от остальной разметки в отдельный компонент -->
        <chat-app v-show="isChatOpen" :result="calculationResult" />
      </div>
    </body>


    Vue.component('chat-app', {
      // Шаблон это HTML + суперспособности vue, например ref
      template: '<div ref="chat"></div>',
      // Аргумненты компонента, позволяет делать
      // - аннотации типов
      // - валидация
      // - дефолтные значения
      props: {
        result: { type: Number }
      },
      mounted() {
        // Здесь нет смысла использовать директиву, инициализируем чат когда компонент окажется в ДОМе
        // Вместо селекта элемента по например id, мы напрямую получаем из ранее созданной рефки
        this.chatInstance = new SomeChatPlugin(this.$refs.chat)
      },
      watch: {
        // При изменении пропа result, в текстовое поле чата будет добавлен текст
        result(newValue) {
          this.chatInstance.fillText(newValue)
        }
      }
    })
    
    new Vue({
      el: '#app',
      filters: {
        // Фильтры это особый способ вызова функции
        // rub(value) === value | rub
        rub(v) {
          const value = Math.round(v)
          switch(value) {
            case 1: return `${value} рубль`
            case 2:
            case 3:
            case 4: return `${value} рубля`
            default: return `${value} рублей`
          }
        }
      },
      directives: {
        rangeSlider: {
          // Директивы имеют свой жизненный цикл
          // Хук inserted это ближайшее к $(document).ready() событие
          /**
            * @param {HTMLElement} el
            * @param {Object} binding https://ru.vuejs.org/v2/guide/custom-directive.html#Аргументы-хуков
            * @param {Vue} context vue instance
            */
          inserted(el, binding, context) {
            $(el).someFancyRangeSlide(binding.value)
              .on('move', newValue => context.updateValue(newValue))
              // События выпускаемые jquery будут обработаны vue
          }
        }
      },
      data() {
        return {
          value: 0,
          calculationResult: 0,
          isChatOpen: false,
        }
      },
      methods: {
        updateValue(newValue) {
          this.value = newValue
        },
        thoggleChat() {
          this.isChatOpen = !this.isChatOpen
        },
        sendToChat() {
          this.calculationResult = this.value
        }
      }
    })


    Пример синтетический и упрощённый. Однако, ярко иллюстрирует как и зачем интегрировать Vue в любое MVC приложение, или даже в статический сайт. В прочем всё это не плохо покрывается jQuery, однако решать вам.
    Ответ написан
    7 комментариев
  • Чем отличаются Ubuntu, Xubuntu, Kubuntu, Lubuntu?

    @askhat
    Различаются оболочкой рабочего стола

    Ubuntu — Unity (раньше Gnome)
    Lubuntu — LXDE
    Xubuntu — XFCE
    Kubuntu — KDE
    Ответ написан
    5 комментариев
  • Чем заменить функцию .map() в моем случае?

    @askhat
    На вопрос ответил автор вопроса, я в свою очередь попытаюсь объяснить что произошло.

    Рендер в реакте всегда идёт по наименее трудозатратному пути, а именно вычисляет дельту (разницу) между новым стейтом и предыдущим, если таковой имеется. Например:

    class TodoList extends React.Component {
      state = {
        todos: [
          'Commit',
          'Push'
        ]
      }
      render() {
        return <ul>
          {this.state.todos.map(item => {
            return <li>{ todo }</li>
          }
        </ul>
      }
    }


    Если стейт компонента изменится, скажем при добавление элемента в начало списка todos, так что он станет таким:

    const todos = [
      'Init',
      'Commit',
      'Push'
    ]


    Реакт вычислит два древа VirtualDOM:

    // Начальный стейт
    <ul>
      <li>Commit</li>
      <li>Push</li>
    </ul>
    // Добавлен элемент
    <ul>
      <li>Init</li> // <- разница начинается здесь и до конца древа
      <li>Commit</li>
      <li>Push</li>
    </ul>


    Здесь выполняется работа которой можно было бы избежать. К примеру если бы элемент был добавлен в конец списка:

    const todos = [
      'Commit',
      'Push',
      'Merge'
    ]


    То реакт получил бы на сравнение другие два древа элементов:

    // Начальный стейт
    <ul>
      <li>Commit</li>
      <li>Push</li>
    </ul>
    // Добавлен элемент
    <ul>
      <li>Commit</li>
      <li>Push</li>
      <li>Merge</li> <- разница начинается здесь, от начала и до сих по ничего не менялось
    </ul>


    Дельта этих двух списков меньше, а значит и работы нужно сделать меньше.

    Совершенно очевидно что <li>Commit</li> и <li>Push</li> не менялись, однако реакт недостаточно умён чтобы это понять. Чтобы помочь ему следует воспользоваться специальным пропом key={}. Он может быть значением любого типа, единственно требование — значение должно стабильно идентифицировать соответствующие данные.

    Если бы компонент выглядел так:

    class TodoList extends React.Component {
      state = {
        todos: [
          { id: 0, text: 'Commit' },
          { id: 1, text: 'Push' }
        ]
      }
      render() {
        return <ul>
          {this.state.todos.map(item => {
            return <li key={todo.id}>{ todo.text }</li>
          }
        </ul>
      }
    }


    То добавление элемента в начало массива, породило бы следующий стейт:

    const todos = [
      { id: 2, text: 'Init' },
      { id: 0, text: 'Commit' },
      { id: 1, text: 'Push' }
    ]


    И, снова, два древа элементов:

    // Начальный стейт
    <ul>
      <li>Commit</li> // id 0
      <li>Push</li> // id 1
    </ul>
    // Добавлен элемент
    <ul>
      <li>Init</li> // id 2 новый элемент отобразится в начале
      <li>Commit</li> // id 0
      <li>Push</li> // id 1 
    </ul>


    Благодаря подсказке, реакт не учтёт в дельте элементы чьи идентификаторы не изменились, и, как следствие, не будет делать лишних действий.

    Таким образом использовать индекс в массиве в качестве ключа — не лучшая идея, особенно если массив будет меняться. По той же причине не следует использовать Math.random() в качестве ключа, так вы почти гарантировано будете всегда получать нестабильные идентификаторы.

    Подробнее об этом можно почитать здесь — Reconciliation.
    Ответ написан
    5 комментариев
  • Как лучше сделать на Vue.js?

    @askhat
    1. События change на поле, модифицируют стейт компонента, от которых зависят вычислимые свойства определяющие валидна форма или нет.

    Пример
    <template>
      <form>
        <input v-model="text" />
        <button :disabled="!isValid">Submit</button>
      </form>
    </template>
    <script>
    export default {
      data() {
        return {
          text: ''
        }
      },
      computed: {
        isValid() {
          return validator(this.text)
        }
      }
    }
    </script>



    2. Зависит от того должны ли все поля быть отрендерены безусловно или опционально.

    Разница между v-if и v-show
    v-if не отрендерит разметку никогда, если её биндинг не вернёт true.
    v-show отрендерит разметку, но в зависимости от своего биндинга установит display: hidden на элемент.
    Ответ написан
  • Как деструктуризовать объект без отдельных свойств?

    @askhat
    const { name, email } = obj

    Просто не создавайте идентификатор под это свойство
    Ответ написан
    2 комментария
  • Как изменить состояние во всех экземплярах компонента React?

    @askhat
    Для этого каждому экземпляру нужно передать ссылку на одно значение

    class Buttons extends Component {
      state = {
        foo: 'bar'
      }
    
      changeText = () => {
        if (this.state.foo === 'bar') {
          this.setState({ foo: 'foo' })
        } else {
          this.setState({ foo: 'bar' })
        }
      }     
    
      render() {
        return (
          <Button text={this.foo} />
          <Button text={this.foo} />
          <Button text={this.foo} />
          <Button text={'Изменить текст'} onClick={this.changeText} />
        )
      }
    }


    Пример глупый, но принцип иллюстрирует.
    Ответ написан
    Комментировать
  • Как пишут frontend на более менее больших проектах?

    @askhat
    Сниппет кода который вы привели, очень похож на общее представление о веб-компоненте. Если класс CategoiesFilter расширит класс React.Component, вам будет нужно реализовать метод render. Тогда вы сможете избавиться от селекторов по заранее написанному html.
    Опционально вы сможете реализовать методы т.н. хуки жизненного цикла, тогда вы получите более тонкий контроль.
    Говорят пользователи Vue используют очень похожий подход к моделированию, но пользуются более гибким синтаксисом рендер функции (они просто пишут html и css.)
    Если речь идёт о более крупных приложениях, часто испозуют redux и mobx. Они реализуют похожие паттерны flux и observable. Но это уже совсем другая история.
    Смысл всех этих технологий в Virtual DOM. Он позволяет не писать все состояния вашего приложения сразу в html, а вычислят его на основе данных в JavaScript и только тогда создавать html наиболее оптимальным образом.
    Ответ написан
    3 комментария
  • Как удалить файл .swp?

    @askhat
    В корневой директории проекта:
    killall vim
    find ./ -name *.swp -exec rm {} +


    Более надёжно решение:
    mkdir -p ~/.vim/backup
    echo 'set dir=$HOME/.vim/backup' >> ~/.vimrc
    vim
    [выйти из вима]
    find ./ -name *.swp -exec rm {} +
    Ответ написан
    1 комментарий
  • XMLHttpRequest или fetch?

    @askhat
    В идеальном мире у вас есть абстракция http клиента в которой вы легко меняете реализацию без изменений в остальном коде
    Ответ написан
    Комментировать
  • Локальное хранение данных в ElectronJS, что лучше?

    @askhat
    Рекомендую PouchDB

    • Встраиваемая БД с поддержкой различных бекендов (SQLite, LevelDB, IndexedDB, WebSQL, LocalStorage)
    • Мастер–мастер репликация с удалённым CouchDB/PouchDB
    • Версионирование и CRDT
    • Красивенький админ интерефейс
    Ответ написан
    5 комментариев
  • Docker: Как увидеть локальную сеть из контейнера?

    @askhat
    Успешная попытка получить доступ к eth0 из контейнера без использования network: host, будет тем же самым что и использование network: host.

    Через CLI флаг:
    docker run --network host ...

    Через Compose:
    some-service:
      network_mode: 'host'


    https://docs.docker.com/network/host/
    Ответ написан
    1 комментарий
  • Как при подключении компонентов во Vue избавиться от длинного пути компонента?

    @askhat
    WebPack Alias. Для Vue версии ниже 3, в webpack.config.js, выше 2 соответственно в vue.config.js
    Ответ написан
    Комментировать
  • Можно ли узнать размер файла, его тип, не скачивая целиком?

    @askhat
    Конечно, первые несколько байт файла уникальны для каждого типа, по ним можно вычислить тип. Благодаря этому свойству работает например библиотека libmagic.

    https://gist.github.com/leommoore/f9e57ba2aa4bf197ebc5
    Ответ написан
    Комментировать
  • С помощью чего можно реализовать подобный эффект?

    @askhat
    Canvas или SVG
    Ответ написан
    Комментировать
  • Как вывести img в массиве?

    @askhat
    <img v-for="item in menu" :src="item.image"/>
    Ответ написан
    Комментировать
  • Можно ли задать соотношение сторон изображения?

    @askhat
    .some-picture {
      background: url('some-picture.png');
      background-size: fill;
      height: 300px;
      width: 400px;
      /* или вместо этих трёх строк */
      background-size: 300px 400px;
    }


    Для <img> есть правило object-fit
    Ответ написан
    Комментировать