Ответы пользователя по тегу JavaScript
  • Нужно ли Frontend разработчику мониторить обновления?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    За обновлениями периодически нужно следить, просто, чтобы знать, что меняется, появляется или удаляется из новых версий. Нужно ли обновлять - это уже вопрос потребностей проекта, клиента, команды.

    Я обычно просматриваю изменения ключевых пакетов/библиотек раз в 1-3 месяца.

    В большом проекте ключевые пакеты/библиотеки может быть сложно обновить, особенно если меняется номер старшей (major) версии (например, Bootstrap v4 => v5). Несущественные изменения (minor), в некоторых случаях могут иметь ощутимые последствия, что-то может сломаться или изменится поведение.

    Я бы не стал просто так обновлять такой пакет, как Bootstrap. Если обновление ключевого пакета происходит, то это неизбежно увеличит нагрузку на QA команду (quality assurance - обеспечение качества, тестирование). В случае с Bootstrap, вероятно придется весь проект перетестировать. Без автоматического тестирования будет особенно тяжко.

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

    Если вы используете менеджер пакетов, например npm, то скорее всего он вам будет показывать предупреждения об обнаруженных уязвимостях в пакетах и варианты их устранения. За этим лучше следить, чтобы потом не было неприятных сюрпризов. Как правило, с менеджером пакетов это делать проще, достаточно будет проверить отдельные предупреждения, а не все пакеты.

    Ну и главное, не делать просто так обновления на текущей активной версии проекта. Чем дольше проект с обновленными пакетами будет находиться на тестировании, тем лучше.
    Ответ написан
    Комментировать
  • Как дебажить ajax ошибку?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Отключите обработку ошибок (в web.config и фильтры) и уведите в чем проблема.

    Метод научного тыка говорит, что проблема в кодированных данных (выделено жирным):

    +%26%23171%3B%D0%94%D0%A0%D0%A3%D0%93%D0%90+%D0%91%D0%9E%D0%A0%D0%9E%D0%94%D0%90%26%23187%3B

    %26%23171%3B => «
    %26%23187%3B => »
    Ответ написан
    2 комментария
  • Как добавить строку в таблицу в ReactJS?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Если вы хотите что-то изменить в текущем компоненте, то используйте состояние (setState).

    Если вы хотите изменить дочерний компонент, меняйте состояние текущего (setState) и передавайте измененные данные из состояния (state) дочернему компоненту через props.

    В вашем примере, корневой компонент - Table, в который вы передаете свойства (props): head_names и rows. Если вы хотите сделать возможность добавлять новую строку в Table, то вам следует сделать контейнер, в который поместить Table и кнопку (или другой элемент) для добавления новых строк. В состояние (state) контейнера нужно добавит head_names и rows, на основе которых и будет создаваться таблица.

    class TableManagement extends React.Component {
    
      constructor(props) {
        super(props);
        
        this.state = {
          head_names: ['qwe0', 'qwe1'],
          rows: [
            [1, 2],
            [3, 4]
          ]
        };
      }  
    
      AddRow() {
        let newRows = this.state.rows;
        newRows.push([0, 0]);
        this.setState({rows: newRows});
      }
    
      render() {
        return (
          <div>
            <Table head={this.state.head_names} rows={this.state.rows} />
            <hr />
            <button onClick={ this.AddRow.bind(this) }>Add row</button>
          </div>
        );
      }
    }
    
    class Table extends React.Component {
      render() {
        return (
          <table>
            <thead>
              {this.genHead()}
            </thead>
            <tbody>
              {this.genRow()}
            </tbody>
          </table>
        );
      }
    
      genHead() {
        var head = this.props.head;
    
        return head.map(function(v, i) {
          return (
            <th key={'th' + i}>
              {v}
            </th>
          );
        });
      }
    
      genRow() {
        var rows = this.props.rows;
    
        return rows.map(function(v, i) {
          var tmp = v.map(function(v2, j) {
            return (
              <td key={'td' + i + '_' + j}>
                {v2}
              </td>
            );
          });
    
          return (
            <tr key={'tr' + i}>
              {tmp}
            </tr>
          )
        });
      }
    }
    
    ReactDOM.render(
      <TableManagement />,
      document.getElementById('root')
    );

    https://jsfiddle.net/m11x34fp/

    В процессе разработки, важно определиться, кто будет тупым, а кто умным :-)

    Тупые (простые) компоненты - это компоненты, которые ничего не делают, просто принимают свойства и рисуются.

    Умные компоненты способны управлять состоянием, как своим, так и передавать его своим тупым отпрыскам потомкам :-)

    Если компонентов много, и особенно, если много вложенных компонентов, то следует хорошо продумать роль каждого компонента. Один умный компонент с множеством тупых вложенных компонентов - это самый простой вариант. Если дети тоже будут умными и должны будут общаться с компонентами своего уровня (братьями и сестрами) или верхнего уровня (дедушками, бабушками и выше), то организовать взаимодействие между компонентами будет сложно, будут конфликты, лишние обновления, в худшем случае зацикливание. Для решения проблем таких масштабов можно использования библиотеки управления состоянием, такие как Redux, Flux и т.п.
    Ответ написан
    2 комментария
  • Почему switch не работает?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    У вас двоеточие там, где должна быть точка с запятой:

    case valMetr == 0:
      valMetr = 0: // <<===== ошибка, должна быть точка с запятой
      break;
    case valMetr>1 && valMetr<=49:
      valMetr = 30: // <<===== ошибка, должна быть точка с запятой
      break;

    Вместо подобной конструкции switch лучше использовать if ... else.
    Быстрее будет работать, как минимум :-)
    Ответ написан
    1 комментарий
  • Как вывести данные из бд изменив снова сохранить?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    По указанной вами ссылке ведь есть готовый код:
    var grid, dialog;
    grid = $('#grid').grid({
        // ваш url, который будет передавать данные из вашей бд
        dataSource: '/Grid/GetPlayers',
        uiLibrary: 'bootstrap',
        // имена полей, которые следует выводить в таблице
        columns: [
            { field: 'ID', width: 32 },
            { field: 'Name', sortable: true },
            { field: 'PlaceOfBirth', title: 'Place Of Birth', sortable: true }
        ],
        pager: { limit: 5, sizes: [2, 5, 10, 20] }
    });

    Со стороны сервера можно сделать Action в обычном контроллере, который будет делать запрос к базе и возвращать данные в формате JSON. Но лучше использовать WebAPI, это будет проще.

    Для удаления и сохранения отдельные методы.

    Набросал простой пример: https://github.com/alekseynemiro/examples/tree/mas...
    Возможно для проверки работы примера потребуется переустановить пакеты. Для этого откройте меню Сервис => Диспетчер пакетов Nuget => Консоль диспетчера пакетов и введите следующую команду:

    Update-Package –reinstall

    В контроллере есть метод GetAccounts, который возвращает записи из базы:

    [HttpPost]
    public JsonResult GetAccounts(int page, int limit)
    {
      using (var context = new Database1Entities())
      {
        // получаем записи для указанной страницы
        var result = context.Account.OrderBy(
           row => row.AccountID
        ).Skip((page - 1) * limit).Take(limit).ToArray();
        int total = context.Account.Count();
    
        // возвращаем json
        return Json(new { records = result, total = total });
      }
    }

    В представлении, в коде инициализации jQuery Grid Bootstrap, указывается ссылка на этот метод:

    grid = $('#grid').grid({
      // ссылка на действие GetAccounts в контроллере Home
      // запрос выполняется методом POST
      dataSource: { url: '/Home/GetAccounts', method: 'POST' },
      uiLibrary: 'bootstrap',
      columns: [
        { field: 'AccountID', sortable: true },
        { field: 'FirstName', sortable: true },
        { field: 'LastName', sortable: true },
        { field: 'Company', sortable: true },
        { field: 'Position', sortable: true }
      ],
      pager: { limit: 2, sizes: [2, 5, 10, 20] }
    });
    Ответ написан
    Комментировать
  • Datapicker криво отображает дату?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Используйте русскую локализацию:

    <script src="i18n/datepicker-ru.js"></script>
    $('#datepicker').datepicker($.datepicker.regional[ 'ru' ]);

    Скачать нужные ресурсы можно отсюда:
    https://github.com/jquery/jquery-ui/tree/master/ui/i18n

    Если вам нужен календарь на английском (или другом) языке, то задать первый день недели можно следующим образом:

    $('#datepicker').datepicker({ firstDay: 1 });
    // или
    $('#datepicker').datepicker('option', 'firstDay', 1);

    Посмотреть рабочий пример

    0 - воскресенье (Su - Sunday)
    1 - понедельник (Mo - Monday)
    и т.д.
    Ответ написан
    Комментировать
  • Как получить ответ с fancybox?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Я обычно делаю собственные alert и confirm, которые выводят сообщения в модальных окнах.

    Для обработки закрытия можно использовать либо функции обратного вызова, либо Promise.

    Использование с Promise выглядит так:

    Confirm("Да или Нет?").then((result) => {
      if (result) {
        console.log("Пользователь согласен на всё.");
      }
      else
      {
        console.log("Пользователь что-то подозревает.");
      }
    });

    Внутри все равно без обработчиков не обойтись, но если функция вызова окна будет одна, то это не критично:

    function Confirm(message) {
      let p = new Promise((resolve, reject) => {
        // тут код создания окна
        // $.fancybox({ 
        //   modal: true,
        //   content: '<div>' + message + '</div>'
        // });
        // в обработчики кнопок нужно добавить вызов resolve
        // true - если пользователь нажал Ok
        // resolve(true);
        // false - если пользователь нажал Отмена
        // resolve(false);
      });
    
      return p;
    }

    При желании, можно сделать свой аналог Promise, хотя смысла нет, готовый вполне пригоден для решения подобной задачи.

    Вот более полный рабочий пример:

    function Confirm(message) {
      return new Promise(function(resolve, reject) {
        var buttons = $('<div class="buttons" />');
        var btnOk = $('<button class="btn btn-default">Ok</button>');
        var btnCancel = $('<button class="btn btn-default">Отмена</button>');
        var content = $('<div />');
    
        btnOk.click(function() { 
          resolve(true);
          $.fancybox.close(); 
        });
        
        btnCancel.click(function() { 
          resolve(false); 
          $.fancybox.close();
        });
    
        content.append('<div class="message" />');
        content.append('<hr />');
        content.append(buttons);
        
        $('.message', content).html(message);
        
        buttons.append(btnOk);
        buttons.append(' ');
        buttons.append(btnCancel);
        
        $.fancybox({ 
           modal: true,
           content: content
        });
      });
    }
    
    Confirm('Вы согласны с этим решением?').then(function(result) {
      if (result) {
        $('body').append("<h1>Отлично!</h1>");
      }
      else  {
        $('body').append("<h1>Очень жаль...</h1>");
      }
    });
    Ответ написан
    Комментировать
  • Как найти динамический DOM элемент?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    var element = document.createElement('div');
    element.id = 'test'; // уникальный идентификатор элемента
    element.innerHTML = 'создан новый элемент';
    
    document.body.appendChild(element);
    
    // получаем по идентификатору добавленный элемент
    document.getElementById('test').innerHTML = 'изменено содержимое созданного элемента';

    https://jsfiddle.net/alekseynemiro/x76s8zsy/1/

    В просмотре кода страницы динамически созданных элементов не будет, т.к. содержимое страницы - это то, что было получено от сервера, а JavaScript работает на стороне клиента.

    ---

    Дополню ответ в соответствии с обновлением текста вопроса.

    Получить path можно:

    1. По ссылке, в переменной path:

    console.log('path', path);

    2. Найти с помощью document.querySelector, например по имени класса:

    var path = document.createElement('path');
    path.className = 'anyClass';
    
    console.log('path', document.querySelector('.anyClass'));

    3. По идентификатору, как показано в самом первом примере:

    var path = document.createElement('path');
    path.id= 'path';
    
    console.log('path', document.getElementById('path'));

    4. По имени тега, но придется еще что-то проверять, поскольку на выходе будут все элементы path:

    console.log('path', document.getElementsByTagName('path'));


    PS: У меня ваш код работает без ошибок:
    https://jsfiddle.net/alekseynemiro/voyj5rt1/
    Используется Snap.svg v0.4.1

    649c48abe93b40a8b7efe17f39e7a0b5.gifOpera 39, Chrome 54, IE 11

    ---

    Обновление по результатам обсуждения проблемы в комментариях:

    Проблема в том, что создавать SVG-элементы нужно с указанием пространства имен, с помощью метода document.createElementNS:

    var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');

    https://jsfiddle.net/alekseynemiro/voyj5rt1/4/
    Ответ написан
  • Как быстро отсортировать таблицу?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Узкое место тут в первую очередь jQuery. Нужно уменьшить число запросов к элементам через jQuery. А то получается, что у вас при сортировки для каждой итерации происходит маленький ад :-)

    Возможно, если вы сделаете сортировку, сформируете новый набор данных и затем замените им старые данные, работать будет быстрее.

    Вот небольшой пример: https://jsfiddle.net/alekseynemiro/q3tsw3bt/1/

    var data = [];
    
    // получаем данные из таблицы в массив
    $('tr', 'table>tbody').each(function(index, row) {
      var cells = $('td', row);
      var sum = 0;
      var items = [];
    
      for (var i = 0; i < cells.length; i++)
      {
        if (i == cells.length - 1)
        {
          cells[i].innerHTML = sum / 5;
          items.push(sum / 5);
        }
        else
        {
          sum += parseInt(cells[i].innerHTML, 10);
          items.push(parseInt(cells[i].innerHTML, 10));
        }
      }
    
      data.push(items);
    });
    
    // сортируем массив
    data.sort(function (a, b) {
      return a[5] - b[5];
    });
    
    // создаем новую таблицу на основе массива
    var html = '<table><thead><tr>';
    html += '<th>price</th><th>price</th><th>price</th>';
    html += '<th>price</th><th>price</th><th>avrg</th></tr></thead>';
    html += '<tbody>';
    
    for (var i = 0; i < data.length; i++)
    {
      html += '<tr>';
    
      for (var j = 0; j < data[i].length; j++)
      {
        html += '<td>' + data[i][j] + '</td>';
      }
    
      html += '</tr>';
    }
    
    html += '</tbody></table>';
    
    // выводим таблицу (перезаписываем старую)
    $('#content').html(html);

    Набросал еще вариант со счетчиком и с 50 000 строками, но счетчик показывает неправильное время, видимо на отрисовку данных в браузере уходит больше времени.
    Ответ написан
    Комментировать
  • Как сделать связанные select?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Каскадные списки (cascade dropdown) это называется:
    https://github.com/dnasir/jquery-cascading-dropdown
    www.dnasir.com/github/jquery-cascading-dropdown/de...
    Ответ написан
    Комментировать
  • Как составить регулярное выражение исходя из следующих условий?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    В ситуациях, когда нужно изолировать фрагменты текста от обработки, проще всего перед обработкой их маскировать. Заменить на что-нибудь, что точно не попадет под обработку. А после обработки вернуть назад.

    this.replaceStandardHtmlEntities = (text) => {
      // маскируем ссылки
      text = text.replace(/\<a([^\>]*)\>/g, String.fromCharCode(1) + 'a$1' + String.fromCharCode(1));
      text = text.replace(/\<\/a\>/g, String.fromCharCode(1) + '/a' + String.fromCharCode(1));
    
      // остальной код без изменений
      let regular = /( {2}|&|'|<|>|\\n)/g, translate = {
        '  ': '&nbsp',
        '&': '&amp',
        '\'': '&quot',
        '<': '&lt',
        '>': '&gt',
        '\\n': '<br>'
      };
    
      // результат замены передаем в переменную
      let result = (text.replace(regular, (match, entity) => {
        return translate[entity];
      }));
      
      // возвращаем ссылки обратно
      result = result.replace(/\x01a([^\x01]*)\x01/g, '<a$1>');
      result = result.replace(/\x01\/a\x01/g, '</a>');
      
      return result;
    };

    Смотреть в JSFiddle
    Ответ написан
    5 комментариев
  • Как правильно удалить часть строки?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Раз подстроки разделены запятыми, то проще всего получить массив с помощью функции split, перебрать элементы циклом, собрать новый массив без ненужных данных и потом склеить с помощью join:

    // входящие данные
    var str = 'odborka_vecher_01.jpg,%,podborka_vecher_37.jpg';
    // разбиваем на массив по запятой
    var arr = str.split(',');
    // фильтруем
    var resultArr = [];
    for (var i = 0; i < arr.length; i++)
    {
      if (arr[i] != '%')
      {
        // добавляем в результат
        resultArr.push(arr[i]);
      }
    }
    // склеиваем новый массив в строку
    var result = resultArr.join(',');
    // выводим полученный результат в консоль
    console.log(result);

    Более короткий вариант и без join:

    str.split(',').forEach(function(item) {
      if (item != '%') { 
        if (result != '') { result += ','; }
        result += item; 
      }
    });
    
    console.log(result);

    Еще короче, с использованием filter:

    var arr = str.split(',').filter(function(item) {
      return item != '%';
    });
    
    var result = arr.join(',');

    В качестве альтернативного решения, обычный поиск и замена:

    var result = str.replace(/,\%,/g, '');
    // с учетом того, что % может быть первым или последним
    // str.replace(/(\,*)\%(\,*)/g, '')
    Ответ написан
  • Как взять токен из адресной строки (oauth)?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Вы должны сохранить полученный маркер доступа (access token). В случае с JavaScript это можно сделать в sessionStorage (в рамках текущей сессии) или localStorage (сохранить на длительный срок), либо передать на сервер (более надежный вариант).

    Код сохранения маркера доступа в хранилище браузера может быть примерно таким:
    if (window.location.hash != '') {
      var hash = window.location.hash.substring(1);
      var accessToken = hash.substr(hash.indexOf('access_token=')).split('&')[0].split('=')[1];
      sessionStorage.setItem('access_token', accessToken);
      // для безопасности, из url лучше удалить access_token
      window.location.hash = '';
      // window.location.href = window.location.href.substr(0, window.location.href.indexOf('#'))
    }
    
    // и далее использовать сохраненный маркер доступа
    var currentAccessToken = sessionStorage.getItem('key');
    Ответ написан
    Комментировать
  • Как убрать экранирование с помощью angular?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Поиск и замена?
    return $sce.trustAsHtml(text.replace(/\\/g, ''));
    Можно отдельный фильтр для этого сделать :-)

    Набросал пример, у меня слеш и так не выводится:

    <div ng-app="app">
      <div ng-controller="ExampleController">
        <p ng-bind-html="text | trusted"></p>
      </div>
    </div>

    angular.module('app', []).controller('ExampleController', function($scope) {
      $scope.text = "A GREAT LOT, THIS IS THE ONE YOU\'VE BEEN LOOKING FOR";
    }).filter('trusted', ['$sce', function ($sce) {
      return function (text) {
        return $sce.trustAsHtml(text); // $sce.trustAsHtml(text.replace(/\\/g, ''));
      };
    }]);
    Ответ написан
    Комментировать
  • Как по клику заменить слово на другое и при повторном клике обратно?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    <div class="clickme">
    Нажми на меня!
    </div>

    $('.clickme').click(function(){
      if (!$(this).data('status')) {
        $(this).html('Молодец! А еще раз слабо?');
        $(this).data('status', true);
      }
      else {
        $(this).html('Ну ты гигант! А может еще раз?');
        $(this).data('status', false);
      }
    });

    https://jsfiddle.net/alekseynemiro/bu0zfgz4/
    Ответ написан
    1 комментарий
  • Как задать динамическое значение props?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Использовать state вместо props:
    var Hello = React.createClass({
      getInitialState: function() {
        return { test: 0 };
      },
      componentWillMount: function() {
        setInterval(() => {
          var dynamicData = (Math.random() * 100).toFixed(0);
          this.setState({ test: dynamicData });
        }, 3000);  
      },
      render: function() {
        return <h2>{this.state.test}</h2>;
      }
    });
    
    ReactDOM.render(
      <Hello />,
      document.getElementById('container')
    );

    Если нужны именно props, то родительский компонент может менять состояние и передавать его в нужное свойство дочернего компонента, а дочерний компонент может обрабатывать изменения в componentWillReceiveProps(nextProps).

    Еще можно вызывать forceUpdate(), но лучше так не делать.

    От идеи внешнего управления компонентами лучше сразу отказаться. Будет меньше проблем. Все должно быть внутри компонентов, либо вообще не относиться к компонентам.
    Ответ написан
    2 комментария
  • Как интерпретировать this в данном примере?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    function CoffeeMachine(power) {
      //...
    
      var self = this; // запоминаем ссылку на текущий объект
    
      //...
    
      function getBoilTime() {
        // здесь this будет ссылкой на window, 
        // т.к. вызов метода getBoilTime() происходит из другого контекста
        // доступ к исходному объекту возможен через ссылку, 
        // которая была передана в переменную self
        console.log('this', this);
        console.log('self', self);
        return self.waterAmount * WATER_HEAT_CAPACITY * 80 / power;
      }
    
      // ...
    
      this.run = function() {
        setTimeout(onReady, getBoilTime());
      };
    }


    Привязка контекста и карринг: "bind"

    С передачей контекста код будет примерно таким:

    function CoffeeMachine(power) {
      this.waterAmount = 0;
    
      var WATER_HEAT_CAPACITY = 4200;
    
      function getBoilTime() {
          console.log('getBoilTime', this);
          return this.waterAmount * WATER_HEAT_CAPACITY * 80 / power;
        }
    
      function onReady() {
        alert( 'Кофе готово!' );
      }
    
      this.run = function() {
        console.log('run', this);
        var interval = getBoilTime.apply(this, null);
        setTimeout(onReady, interval);
      };
    
    }
    
    var coffeeMachine = new CoffeeMachine(100000);
    coffeeMachine.waterAmount = 200;
    
    coffeeMachine.run.apply(coffeeMachine, null);
    Ответ написан
    3 комментария
  • Reactjs плагины и requirejs, как?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Не переписывать же плагины, верно? Кто как с этим борется?


    Использую TypeScript и Visual Studio. Итоговый JavaScript сам собирается в нужном виде, достаточно выбрать систему модулей в свойствах проекта.

    Но с некоторыми библиотеками React в AMD возникали проблемы. И да, единственным решением было внесение изменений в код этих компонентов, что не очень хорошо и не факт, что можно будет отделаться парой простых изменений.

    В итоге пришлось использовать Webpack. Опыт работы с RequireJS сильно усложнил миграцию и в какие-то моменты я даже пытался прикрутить RequireJS в Webpack :-) Так что про RequireJS лучше полностью забыть.

    Реализация с Webpack мне нравится больше, но настроек придется написать много и иногда все это дело глючит. С использованием загрузчиков, на выходе можно получить все что угодно из чего угодно.
    Ответ написан
    Комментировать