• Как сделать отмеченную форму checkbox устойчивой к обновлению страницы в браузере на Vue.js?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Зачем отдельный массив checked? Пусть notes вместо массива строк будет массивом объектов, состоящих из двух свойств - text и checked. Наблюдатель c deep: true, сохраняющий данные в localStorage уже есть, так что никаких дополнительных действий предпринимать не придётся.

    UPD. Как это может выглядеть:

    <div id="app">
      <div>
        <input v-model="newTaskText" @keypress.enter="addTask">
        <button @click="addTask">add</button>
      </div>
      <hr>
      <ol v-if="tasks.length">
        <li v-for="(n, i) in tasks">
          <label :class="{ 'task-checked': n.checked }">
            <input type="checkbox" v-model="n.checked">
            {{ n.text }}
          </label>
          <button @click="delTask(i)">del</button>
        </li>
      </ol>
      <strong>Total: {{ tasks.length || 'no tasks fucking exist' }}</strong>
    </div>

    .task-checked {
      text-decoration: line-through;
    }

    Vue.createApp({
      data: () => ({
        newTaskText: '',
        tasks: JSON.parse(localStorage.getItem('tasks')) ?? [],
      }),
      watch: {
        tasks: {
          deep: true,
          handler: val => localStorage.setItem('tasks', JSON.stringify(val)),
        },
      },
      methods: {
        addTask() {
          const text = this.newTaskText.trim();
          if (text) {
            this.tasks.push({
              text,
              checked: false,
            });
    
            this.newTaskText = '';
          } else {
            alert('fuck off');
          }
        },
        delTask(index) {
          if (confirm('really?')) {
            this.tasks.splice(index, 1);
          }
        },
      },
    }).mount('#app');
    Ответ написан
    8 комментариев
  • Как объединить данные из двух массивов по id?

    0xD34F
    @0xD34F Куратор тега React
    const getData = type => fetch(`https://jsonplaceholder.typicode.com/${type}`).then(r => r.json());

    const [ data, setData ] = useState([]);
    
    useEffect(() => {
      Promise
        .all([ 'posts', 'users' ].map(getData))
        .then(([ posts, users ]) => {
          const usersObj = Object.fromEntries(users.map(n => [ n.id, n ]));
          setData(posts.map(n => ({
            post: n,
            user: usersObj[n.userId],
          })));
        });
    }, []);
    
    return (
      <div>
        {data.map(({ post, user }) => (
          <div>
            <h2>{post.title}</h2>
            <h3>{user.name}</h3>
            <p>{post.body}</p>
          </div>
        ))}
      </div>
    );
    Ответ написан
    1 комментарий
  • Как изменить цвет ссылки при наличии в ней определённого текста?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Кого будем перекрашивать, и в какие цвета:

    const className = 'some-link';
    const colors = {
      En:   'red',
      Ru: 'green',
      De:  'blue',
    };

    Перекрашиваем текст в случае точного совпадения:

    for (const n of document.getElementsByClassName(className)) {
      n.style.color = colors[n.textContent];
    }

    Или, смотрим наличие подстроки, регистр не важен:

    document.querySelectorAll(`.${className}`).forEach(function(n) {
      n.style.color = this.find(m => m[0].test(n.innerText))?.[1];
    }, Object.entries(colors).map(n => [ RegExp(n[0], 'i'), n[1] ]));

    Можно и без регулярных выражений:

    - RegExp(n[0], 'i')
    + n[0].toLowerCase()

    - m[0].test(n.innerText)
    + n.innerText.toLowerCase().includes(m[0])
    Ответ написан
    Комментировать
  • Как записать рекурсию (функц. глубокого копирования) в виде цикла?

    0xD34F
    @0xD34F
    Используйте стек.

    Если, обрабатывая данные, натыкаетесь на объект, надо сохранить в стек текущее состояние - ссылку на копируемые данные, индекс текущего элемента, ссылку на объект, куда копируете; затем установить новое состояние - копируемыми данными будет массив пар ключ-значение встреченного объекта, индекс обнуляется (на самом деле надо выставить -1, нулевое значение будет получено при переходе к следующей итерации; конечно, можно и буквально 0 выставлять, но тогда придётся усложнить работу с индексом - тут увеличиваем, а тут нет), пустой объект в качестве копии.

    Если данные закончились, надо восстановить состояние из стека.

    Условий, при которых возможно продолжение цикла, будет два - кроме наличия необработанных данных ещё и непустой стек.

    function clone(value) {
      const clone = {};
      const stack = [];
    
      for (
        let i = 0, source = [ [ '', value ] ], target = clone;
        i < source.length || stack.length;
        i++
      ) {
        if (i === source.length) {
          [ i, source, target ] = stack.pop();
        } else {
          const [ k, v ] = source[i];
          const isObject = v instanceof Object;
    
          target[k] = isObject ? v.constructor() : v;
    
          if (isObject) {
            stack.push([ i, source, target ]);
            [ i, source, target ] = [ -1, Object.entries(v), target[k] ];
          }
        }
      }
    
      return clone[''];
    }

    Или, если в копируемом объекте могут быть циклические ссылки или какой-то из вложенных объектов встречается несколько раз и так же должно быть и в копии, то надо запоминать встреченные объекты, чтобы не обрабатывать их повторно - воспользуемся Map'ом, где ключами будут оригинальные объекты, а значениями их копии.

    Как выглядит получение копии объекта: проверяем, встречался ли указанный объект ранее, если нет - создаём новый объект, сохраняем его в Map, а также кладём его вместе с оригиналом в стек, для последующей обработки; достаём из Map'а копию.

    Крутим цикл, пока стек не пуст, на каждой итерации достаём из стека объект и его копию, перебираем свойства оригинала, записываем в копию копии значений.

    function clone(value) {
      const stack = [];
      const clones = new Map;
      const getClone = val => val instanceof Object
        ? (clones.has(val) || stack.push([ val, clones.set(val, val.constructor()).get(val) ]),
           clones.get(val))
        : val;
    
      for (getClone(value); stack.length;) {
        const [ source, target ] = stack.pop();
        Object.entries(source).forEach(n => target[n[0]] = getClone(n[1]));
      }
    
      return getClone(value);
    }

    как правильнее всего будет это сделать?

    Перестать заниматься ерундой и начать использовать готовые решения - structuredClone или cloneDeep, например.
    Ответ написан
    Комментировать
  • Как в библиотеке chart.js указать в оси Y два значение (минимальное и максимальное)?

    0xD34F
    @0xD34F
    options: {
      scales: {
        y: {
          ticks: {
            callback: (value, index, values) =>
              index > 0 && index < values.length - 1
                ? ''
                : Math[index ? 'max' : 'min'](...values.map(n => n.value)),
            ...
    Ответ написан
    2 комментария
  • Задать класс body при активной переменной во vue?

    0xD34F
    @0xD34F Куратор тега Vue.js
    watch: {
      переменная: {
        immediate: true,
        handler(val) {
          document.body.classList.toggle('класс', val);
        },
      },
    },
    Ответ написан
    Комментировать
  • Почему style background vue3 не применяется?

    0xD34F
    @0xD34F Куратор тега Vue.js
    нужно создать 6 блоков с разными цветами

    :style="{ color: bgColor }"

    Цветами ЧЕГО? Свойство color - это цвет текста, никакого текстового содержимого в блоках нет.

    Делаю как в документации

    v-for="(index, bgColor) in colorArray"

    Ну конечно, как в документации. Там ведь тоже элемент массива и его индекс местами перепутаны. Или всё-таки нет?
    Ответ написан
    Комментировать
  • Как написать этот код легче?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const newData = data.reduce((acc, n) => {
      const k = Object.keys(n)[0];
      (acc.result[acc.keys[k] = (acc.keys[k] ?? -1) + 1] ??= []).push(n);
      return acc;
    }, { result: [], keys: {} }).result.flat();

    или

    const numKeys = new Set(data.flatMap(Object.keys)).size;
    const numObjs = data.length / numKeys;
    const newData = data.map((n, i, a) => a[(i % numKeys) * numObjs + (i / numKeys | 0)]);
    Ответ написан
    2 комментария
  • Если в диве больше 1го элемента то добавить класс к диву?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Кому надо добавить класс, какой класс, сколько вложенных элементов должно быть:

    const selector = 'селектор элементов';
    const className = 'класс';
    const minChildrenCount = 666;

    Добавляем:

    for (const n of document.querySelectorAll(selector)) {
      n.classList.toggle(className, n.children.length >= minChildrenCount);
    }
    
    // или (нет, так делать точно не надо - для 0 результат будет некорректным)
    
    document
      .querySelectorAll(`${selector} > :nth-child(${minChildrenCount})`)
      .forEach(n => n.parentNode.classList.add(className));
    Ответ написан
    Комментировать
  • Одномерный массив в многомерный?

    0xD34F
    @0xD34F Куратор тега JavaScript
    (function toArrays(obj) {
      const arr = Object.values(obj ?? {});
      arr.forEach(n => n.children = toArrays(n.children));
      return arr;
    })(arr.reduce((acc, n) => {
      const path = n.path.replace(/^\/|\/$/g, '').split('/');
      const obj = path.reduce((p, c) => ((p.children ??= {})[c] ??= {}), acc);
      Object.assign(obj, n);
      return acc;
    }, {}).children)
    Ответ написан
    2 комментария
  • Выбор из нескольких компонентов?

    0xD34F
    @0xD34F Куратор тега Vue.js
    computed: {
      component() {
        /*
         * здесь возвращаете имя компонента, в зависимости от... это вам виднее;
         * если действительно будет так, как показано в вопросе - натуральные числа
         * соответствуют компонентам, то можно сложить имена компонентов в массив:
         * return [ 'component-1', 'component-2', 'component-3' ][this.x - 1];
         */
      },
    },

    <component :is="component" />
    Ответ написан
    1 комментарий
  • Как сделать подсчет количества строк?

    0xD34F
    @0xD34F Куратор тега Vue.js
    computed: {
      linesCount() {
        return 1 + (this.text.match(/\n/g)?.length ?? 0);
      },
    },

    <div>{{ linesCount }}</div>
    Ответ написан
    1 комментарий
  • Как переписать функцию со scrollIntoView?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Задать связь между кнопками и блоками с помощью data-атрибутов:

    <div class="mainMenu">
      <button data-scroll-to="calendar">Раз</button>
      <button data-scroll-to="rooms">Два</button>
      <button data-scroll-to="maps">Три</button>
      <button data-scroll-to="contact">Четыре</button>
    </div>
    ...
    <div data-block="calendar">...</div>
    <div data-block="rooms">...</div>
    <div data-block="maps">...</div>
    <div data-block="contact">...</div>

    Сделать функцию, которая получает значение атрибута, находит соответствующий блок и выполняет прокрутку:

    function scrollTo(block) {
      document.querySelector(`[data-block="${block}"]`).scrollIntoView({
        block: 'center',
        behavior: 'smooth',
      });
    }

    Воспользоваться этой функцией при обработке кликов по кнопкам:

    document.querySelector('.mainMenu').addEventListener('click', e => {
      const block = e.target.dataset.scrollTo;
      if (block) {
        scrollTo(block);
      }
    });
    
    // или
    
    document.querySelectorAll('[data-scroll-to]').forEach(function(n) {
      n.addEventListener('click', this);
    }, e => scrollTo(e.target.dataset.scrollTo));
    Ответ написан
    1 комментарий
  • Как сделать алгоритм добавления елементов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Значения в массиве строк уникальны? Если нет, сколько надо добавлять объектов?

    Сколько раз строка повторилась - столько новых объектов будет добавлено:

    const names = new Set(objects.map(n => n.name));
    strings.forEach(n => (names.has(n) || objects.push({ name: n })));

    Два раза одно и то же не добавляем:

    new Set(strings).forEach(function(n) {
      if (!this.has(n)) {
        objects.push({ name: n });
      }
    }, new Set(objects.map(n => n.name)));
    
    // или
    
    for (const name of strings) {
      if (objects.every(n => n.name !== name)) {
        objects[objects.length] = { name };
      }
    }
    
    // или
    
    objects.splice(0, objects.length, ...strings.reduce(
      (acc, n) => acc.set(n, acc.get(n) ?? { name: n }),
      new Map(objects.map(n => [ n.name, n ]))
    ).values());
    Ответ написан
    Комментировать
  • Как в списке удалить весь текст выше 3-рёх подряд строк заканчивающихся на знаки препинания в конце?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const punct = '.,!?;:"\'”“';
    const numStrWithPunctEnd = 3;
    const arr = str.split('\n');
    const index = arr.findIndex(function(n, i, a) {
      return this.every(m => punct.includes(a[i + m]?.slice(-1)));
    }, [...Array(numStrWithPunctEnd).keys()]);
    const result = index !== -1 ? arr.slice(index).join('\n') : str;
    Ответ написан
  • Регулярное выражение находящее текст после n-го значения подстроки?

    0xD34F
    @0xD34F
    results = re.findall(r'(?<=SELECT).+?(?=FROM)', string, re.I | re.DOTALL)
    print(results[2])
    Ответ написан
    Комментировать
  • Почему неправильно парсится дата?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Число перепутано с днём недели; не добавлена единица номеру месяца (они с нуля начинаются).

    Ну и сам формат не везде поддерживается - в фаерфоксе получите Invalid Date. Выдирайте из строки компоненты даты и засовывайте их в конструктор по отдельности, или воспользуйтесь какой-нибудь готовой библиотекой для работы с датами.
    Ответ написан
  • Можно ли передать параметр в computed vue3?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Можно. Если значение этого computed является функцией. Но если вам туда надо передавать параметр, то, наверное, это должен быть метод, а не computed.
    Ответ написан
    Комментировать
  • Как отслеживать смену значений в объекте?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Заворачиваете свой объект в Proxy:

    const obj = {
      a: 69,
      b: 187,
      c: 666,
    };
    
    const proxy = new Proxy(obj, {
      set(target, key, val) {
        console.log('свойство', key, 'изменило своё значение с', target[key], 'на', val);
        target[key] = val;
        return true;
      },
    });

    Дальше вместо исходного объекта работаете с экземпляром Proxy.
    Ответ написан
    2 комментария