Страницы

Поиск по вопросам

Показаны сообщения с ярлыком постраничный-вывод. Показать все сообщения
Показаны сообщения с ярлыком постраничный-вывод. Показать все сообщения

вторник, 31 декабря 2019 г.

Vuejs Grid с Постраничным разделением (пагинация)

#vuejs #grid #постраничный_вывод


Есть Grid из официального примера:
Как к нему можно сделать "постраничное разделение"?
Данных всегда может быть разное количество.

Vue.component('demo-grid', {
  template: '#grid-template',
  props: {
    data: Array,
    columns: Array,
    filterKey: String
  },
  data: function () {
    var sortOrders = {}
    this.columns.forEach(function (key) {
      sortOrders[key] = 1
    })
    return {
      sortKey: '',
      sortOrders: sortOrders
    }
  },
  computed: {
    filteredData: function () {
      var sortKey = this.sortKey
      var filterKey = this.filterKey && this.filterKey.toLowerCase()
      var order = this.sortOrders[sortKey] || 1
      var data = this.data
      if (filterKey) {
        data = data.filter(function (row) {
          return Object.keys(row).some(function (key) {
            return String(row[key]).toLowerCase().indexOf(filterKey) > -1
          })
        })
      }
      if (sortKey) {
        data = data.slice().sort(function (a, b) {
          a = a[sortKey]
          b = b[sortKey]
          return (a === b ? 0 : a > b ? 1 : -1) * order
        })
      }
      return data
    }
  },
  filters: {
    capitalize: function (str) {
      return str.charAt(0).toUpperCase() + str.slice(1)
    }
  },
  methods: {
    sortBy: function (key) {
      this.sortKey = key
      this.sortOrders[key] = this.sortOrders[key] * -1
    }
  }
})

// bootstrap the demo
var demo = new Vue({
  el: '#demo',
  data: {
    searchQuery: '',
    gridColumns: ['name', 'power'],
    gridData: [
      { name: 'Chuck Norris', power: Infinity },
      { name: 'Bruce Lee', power: 9000 },
      { name: 'Jackie Chan', power: 7000 },
      { name: 'Jet Li', power: 8000 }
    ]
  }
})

    


Ответы

Ответ 1



Вариант @OlegShleif адаптированный на vue2: // register the grid component Vue.component('demo-grid', { template: '#grid-template', props: { data: Array, columns: Array, filterKey: String, rowsPerPage: Number }, data: function() { var sortOrders = {} this.columns.forEach(function(key) { sortOrders[key] = 1 }) return { sortKey: '', sortOrders: sortOrders, startRow: 0 } }, computed: { filteredData: function() { var sortKey = this.sortKey var filterKey = this.filterKey && this.filterKey.toLowerCase() var order = this.sortOrders[sortKey] || 1 var data = this.data this.startRow = 0 // reset to start if (filterKey) { data = data.filter(function(row) { return Object.keys(row).some(function(key) { return String(row[key]).toLowerCase().indexOf(filterKey) > -1 }) }) } if (sortKey) { data = data.slice().sort(function(a, b) { a = a[sortKey] b = b[sortKey] return (a === b ? 0 : a > b ? 1 : -1) * order }) } return data }, dataPerPage: function() { return this.filteredData.filter((item, index) => index >= this.startRow && index < (this.startRow + this.rowsPerPage)) } }, filters: { capitalize: function(str) { return str.charAt(0).toUpperCase() + str.slice(1) } }, methods: { sortBy: function(key) { this.sortKey = key this.sortOrders[key] = this.sortOrders[key] * -1 }, movePages: function(amount) { var newStartRow = this.startRow + (amount * this.rowsPerPage); if (newStartRow >= 0 && newStartRow < this.filteredData.length) { this.startRow = newStartRow; } } } }) var baseData = [{ name: 'Chuck Norris', power: Infinity }, { name: 'Bruce Lee', power: 9000 }, { name: 'Jackie Chan', power: 7000 }, { name: 'Jet Li', power: 8000 }]; var gridData = Array(100).fill(null).map(function() { return Object.assign({}, baseData[Math.floor(Math.random() * 4)]); }); // bootstrap the demo var demo = new Vue({ el: '#demo', data: { searchQuery: '', gridColumns: ['name', 'power'], gridData: gridData } }) body { font-family: Helvetica Neue, Arial, sans-serif; font-size: 14px; color: #444; } table { border: 2px solid #42b983; border-radius: 3px; background-color: #fff; } th { background-color: #42b983; color: rgba(255, 255, 255, 0.66); cursor: pointer; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } td { background-color: #f9f9f9; } th, td { min-width: 120px; padding: 10px 20px; } th.active { color: #fff; } th.active .arrow { opacity: 1; } .arrow { display: inline-block; vertical-align: middle; width: 0; height: 0; margin-left: 5px; opacity: 0.66; } .arrow.asc { border-left: 4px solid transparent; border-right: 4px solid transparent; border-bottom: 4px solid #fff; } .arrow.dsc { border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 4px solid #fff; } #page-navigation { display: flex; margin-top: 5px; } #page-navigation p { margin-left: 5px; margin-right: 5px; } #page-navigation button { background-color: #42b983; border-color: #42b983; color: rgba(255, 255, 255, 0.66); }


Ответ 2



Добрый день Евгений, вот вам пример с пагинацией HTML
{{key | capitalize}}
{{entry[key]}}
JS var baseData = [{ name: 'Chuck Norris', power: Infinity }, { name: 'Bruce Lee', power: 9000 }, { name: 'Jackie Chan', power: 7000 }, { name: 'Jet Li', power: 8000 }]; var gridData = Array(100).fill(null).map(function() { return Object.assign({}, baseData[Math.floor(Math.random() * 4)]); }); // register the grid component Vue.component('grid', { template: '#grid-template', props: { data: Array, columns: Array, movePages: Function, startRow: Number, rowsPerPage: Number } }); // Create the view-model var gridViewModel = new Vue({ el: '#grid-view-model', data: { searchQuery: '', gridColumns: ['name', 'power'], gridData: gridData, startRow: 0, rowsPerPage: 10 }, methods: { movePages: function(amount) { var newStartRow = this.startRow + (amount * this.rowsPerPage); if (newStartRow >= 0 && newStartRow < gridData.length) { this.startRow = newStartRow; } }, resetStartRow: function() { this.startRow = 0; } }, filters: { orderByBusinessRules: function(data) { return data.slice().sort(function(a, b) { return a.power - b.power; }); } } })

пятница, 27 декабря 2019 г.

Лучшие практики для пагинации контента

#ajax #база_данных #постраничный_вывод


Есть определенные записи в базе данных(mysql, но не принципиально), которые выводятся
постранично или подгружаются через ajax(в рамках задачи это не имеет значения). 

Записи выводятся в зависимости от рейтинга(по убыванию). Рейтинг записи могут изменять
пользователи ресурса.

Каким образом лучше всего избежать дублирования вывода записей при переходе на другую
страницу или подгрузке новых? 

Пример.

Допустим, у нас в базе следующий список записей:

Запись №1 - рейтинг 15

Запись №2 - рейтинг 14

Запись №3 - рейтинг 13

Запись №4 - рейтинг 12

Запись №5 - Рейтинг 11

Запись №6 - рейтинг 0

Допустим, на странице выводится за раз по 5 записей. 
То есть при первом заходе на страницу у нас будут выведены следующие записи и в таком
порядке - №1,№2,№3,№4,№5(в порядке убывания рейтинга). Если теперь, когда пользователь
ещё находится на этой странице, изменится рейтинг записи №6, например, на 20, то на
второй странице будет выведена запись №5 снова, что не правильно. 

Как решить данную проблему лучше всего?

Мне в голову приходит вариант с передачей каждый раз номеров уже выведенных записей
и второй - кэширование, но оба кажутся не совсем верными. Как решают подобные проблемы
на крупных проектах с большой активностью пользователей? Язык и БД особо не играет
роли, интересен только подход.



Голоса пользователей хранятся в отдельной таблице: 

id user_id record_id date

Записи соответственно в таблице record:

id rating 

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


Ответы

Ответ 1



1 вариант. Можно кешировать результат. (например memcache).Следующим образом. В ИД кеша используется временная метка. Т.е. я зашел на первую и поучил временную метку. При дальнейшем моем обращении я получаю закешированные данные с этой временной меткой. При изменении рейтинга организуется новый кеш с новой временной меткой. Если развить этот подход, то можно попробовать только затронутые изменением страницы. В любом случае это не полное описание реализации, а идея, которую можно развить 2 вариант. Хранить историю рейтинга с временными метками в отдельной странице. И опять же обеспечить передачу на сервер (параметр, кука или переменная в сессии) временной метки с которой мы работаем. Так же можно скидывать куда то метки с которыми сейчас работают посетители. А другим скриптом на кроне чистить историю, которая ни кому не нужна. Но, на мой взгляд, все это лишние сложности. А может лучше показать посетителю: "Дружище изменился рейтинг"? Например, по простому, хранить в сессииИД записей что были на посещенной странице и если какаято из них окажется на странице на которую перешли то выдать меседж... Либо все теже мемкеш. В него записываем ИД записи у которой изменился рейтинг. При открытии новой страницы показываем сообщение" У записи ссылка изменился рейтинг..... Это заодно повысит "глубину просмотра" ;)

Ответ 2



Для больших проектов есть инструменты. Например, у нас много таблиц с большим количеством записей: ag-grid + OData. Это готовые, проверенные инструменты на все случаи жизни. В описанном примере вижу два решения: 1) Статика. При обновлении записи кидать нотификацию пользователю, что рейтинг изменился. Не пускать новые данные - сделать кнопку обновить таблицу, при нажатии на которую произойдёт обновление. 2) Динамика. При выводе записей сохранять на стороне клиента Id выведенных записей. Если на сервере произошло обновление рейтинга, то сервер шлёт нотификацию клиенту, в которой содержатся Id записи, для которой обновился рейтинг и значение рейтинга. Если этот Id среди выведенных, то на клиенте производим обновление текущей страницы, иначе игнорируем. Обновление можно сделать либо с помощью ещё одного запроса на сервер, либо с помощью клиентского кода(js,ts,...). Выбор одного из вариантов обуславливается использованием, например, 1-й хорошо подойдёт, если предполагается, что пользователь делает снапшот рейтинга на момент времени, 2-й если нужна страница с постоянными актуальными рейтингами. Можно поставить чекбокс: статика/динамика и реализовать оба варианта. Вариантов использования придумать можно гораздо больше. Следующий вопрос будет к нагрузке на сервер. 1-й в отличии от второго не требует нотификации браузера со стороны сервера, что является плюсом, и если динамическое обновление не важно, то стоит выбрать его. Кстати, не думаю, что вывод номера 5 на 2-й страницу это неправильно, потому что какой толк в сортировке, если она не выводит записи на своих местах?

среда, 19 июня 2019 г.

ЧПУ постраничной навигации в Yii2

Есть у меня на главной странице в контроллере sitecontroller экшен
public function actionIndex() { $searchModel = new CourseSearch(); $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [ 'searchModel' => $searchModel, 'dataProvider' => $dataProvider, ]); }
CourseSearch
public function search($params) { $query = Course::find()->active()->with(['category'])->orderBy(['id' => SORT_DESC]);
// add conditions that should always apply here
$dataProvider = new ActiveDataProvider([ 'query' => $query, 'pagination' => [ 'pageSize' => 2, ],
]); .............
Который выводит список постов через вьюху
$dataProvider, 'layout' => "{items}
{pager}", 'itemView' => '_item', 'emptyText' => 'Ничего не найдено.', 'emptyTextOptions' => [ 'tag' => 'div class="alert alert-danger"' ], 'pager' => [ 'firstPageLabel' => '««', 'lastPageLabel' => '»»', 'nextPageLabel' => false, 'prevPageLabel' => false, 'maxButtonCount' => 5, ], ]); ?>
И постраничная навигация когда переходишь например на вторую страницу выдает вот такой вид:
mysite.ru/?page=2&per-page=2
Вот как сделать чтобы страницы выводились mysite.ru/page-2
'urlManager' => [ 'class' => 'yii\web\UrlManager', // Disable index.php 'showScriptName' => false, // Disable r= routes 'enablePrettyUrl' => true, 'rules' => [ 'page--' => 'site/index', '' => 'site/index', '' => 'site/', '' => 'site/category', '/' => 'site/view',


Ответ

Открываем config/web.php и добавляем в components:
'components' => [ 'urlManager' => [ 'class' => 'yii\web\UrlManager', // Disable index.php 'showScriptName' => false, // Disable r= routes 'enablePrettyUrl' => true, 'rules' => [ '//page-' => '/', '/' => '/', ], //Далее какой-то код. ],
Код рабочий, если возникнут какие-то проблемы, выложите config/web.php и мы поможем.