Set 9: Implementing a set of ui component libraries for pc-side vue from scratch (Pager Components)

Posted by baby_g on Mon, 05 Aug 2019 18:10:25 +0200

Episode 9: Implementation from scratch (Pager Component)

This episode locates:
Pager is also an old friend. Remember when I first learned js, writing a pager required 300 lines of code. If I could get back through it, I would have to teach myself design patterns.

As the status of mobile phones has been improved, most people spend more time online on their mobile phones. The pc side is really much less. Pager is a kind of component, which is not really suitable for mobile phones. It is no longer suitable for human experience. People are now paging driven by scratch or pull. PagerIn fact, you can have a good feeling with the mouse. People's finger clicks are not precise, and they will cover the field of view when clicking. Anyway, I would recommend using a page splitter on the pc side to jump. The mobile side should not play so much. Think more about the operation that makes users more comfortable.

This time I wrote a video that references Famine Valley and also looked at the source of element ation, but eventually I built this component my own way.

  1. It's interesting to add some understanding to your own way of doing it. After all, code is not the same thing. More ways of playing make programming more dynamic.
  2. If you remove an input box, for example, you can jump to a fixed number of pages. This function I did a background management system for half a year last year, but it didn't work at all.
  3. Instead of thinking about the traditional components, the total number of user incoming pages, I then divide them into pages, using how many pages the user's own incoming pages divide.
  4. This activated page will interact with users in the form of v-model, that is, this variable is bidirectional, the two functions removed above are very good to achieve, but will not do this time, after all, practical experience tells me that what I do is really useless

I. Basic Structure
This structure is somebody else's source code for reference... and it's nice, although the dom is a bit ugly, it's logical and easy to maintain.

The same as usual
vue-cc-ui/src/components/Pagination/index.js

import Pagination from './main/pagination.vue';

Pagination.install = function(Vue) {
    Vue.component(Pagination.name, Pagination);
  };

export default Pagination;

Body

  1. Write the first and last one separately, and by default they will always be presented to the user for selection
  2. ... this symbol was written separately, after all, it was written directly at the beginning and end, so he can do it as well.
  3. Two buttons on the left and right allow users to insert their own code
  4. The advantage of this structure is that it makes the problem specific, regardless of the others. The current core issue is how to find the data of the intermediate for loop, which is the focus of this collection.
<template>
  <div class='cc-pagination'>
    <button class="btn-prev">
      <slot name='previous'> &lt; </slot>
    </button>
    <ul class='cc-pagination__box'>
      <li>1</li>
      <li>··</li>
      <li ....>{{item}}</li>
      <li>··</li>
      <li v-if="PageCount!== 1">{{PageCount}}</li>
    </ul>
    <button class="btn-prev">
      <slot name="next"> &gt; </slot>
    </button>
  </div>
</template>

Show me the basic look first

css aspects


@include b(pagination) {
    cursor: pointer;
    color: #606266; //This color is very soft black
    align-items: center;
    display: inline-flex;
    justify-content: center;
    .btn-prev { // Button to remove default style
        border: none;
        outline: none;
        background-color: transparent;
        &:hover { // This nomal is a soft blue
            color: $--color-nomal
        }
    }
}

2. Definition of functions

  1. pageTotal: The total number of pages, that is, 700 pages more than the current data, that's 700 pages in.
  2. pageSize: Display up to how many page markers, for example, 3, pageTotal 6, which is
    Only these three numbers are shown on pages 1,2... 6. After many experiments, this number must be passed down as low as 5, otherwise the experience will be poor, the maximum can be unlimited, and friends have the opportunity to try it themselves.
  3. value: Basic element for implementing v-model
  4. validator: This function is a check function when the sub-component receives parameters. It can not modify parameters here. It is just responsible for telling the user that the right one is passed. Don't have too many functions, it is not easy to maintain if the logic is scattered.
  5. There are three duplicate functions in the code below, so you must encapsulate a common tool function
pageSize: {
      type: Number,
      default: 5,
      validator: function(value){
      if (value < min || value !== ~~value) {
        throw new Error(`Integer with a minimum of 5`);
       }
       return true;
      }
    },
    value: {
      // Selected Page
      type: Number,
      required: true,
      validator: function(value){
      if (value < min || value !== ~~value) {
        throw new Error(`An integer with a minimum of 1`);
       }
       return true;
      }
    },
    pageTotal: {
      // Total
      type: Number,
      default: 1,
      required: true,
      validator: function(value){
      if (value < 1 || value !== ~~value) {
        throw new Error(`An integer with a minimum of 1`);
       }
       return true;
      }
    }

Pull Tool Function
vue-ui/my/vue-cc-ui/src/assets/js/utils.js

// The inspect word is the meaning of the test, and only a minimum value needs to be passed in for a temporary business.
export function inspect(min) {
// Returns a function as a true check function
  return function(value) {
// Error if less than this minimum or not an integer
// ~~This bit operator is written to mean rounding, which is equal to no rounding, not floating point, of course
// ~Operators are bitwise inversions, 1 becomes 0,0 becomes 1, that is, the inversion of binary
    if (value < min || value !== ~~value) {
      throw new Error(`Minimum is ${min}Integer`);
    }
    return true;
  };
}

After pulling it out, I can simplify and refresh a lot here

    pageSize: {
      type: Number,
      default: 5,
      validator: inspect(5)
    },
    value: {
      type: Number,
      required: true,
      validator: inspect(1)
    },
    pageTotal: {
      type: Number,
      default: 1,
      required: true,
      validator: inspect(1)
    }

3. Improve the display of page numbers (focus)
Analyse one by one:

  1. As mentioned earlier, the first and last page numbers are written directly, so if the user-defined pageSize is 5, I'll take out the middle three.
    For example, if the user is currently on page 6 and the total number of pages is 12, then 1,..., 5, 6, 7,..., the 567 in the middle of 12 is what I want to get.
  2. This selection uses computational attributes to monitor the real-time changes of the v-model. The name is showPages for li to loop through.
  3. Error in compatible value
  4. This is certainly offset, for example, if the user enters pageSize 4, there are two situations where you can choose
    1,..., 5, 6,..., 12, 1,..., 6, 7,..., 12 When 6 is activated, whether it is 5 or 7 is unnecessary to tangle. Just write one, because I tangle and feel meaningless.
  5. The idea is to get the current page number value to activate, and then extend it to the left and right, for example, if the value is 6, then the left and right are 5,7. This way, you keep traversing to get the value, and ultimately within the specified number, without touching the boundary conditions.
showPages() {
// The variable returned by the customary definition
      let result = [],
      // Get the variable you want
        value = this.value,
        pageTotal = this.pageTotal,
        // Because you want to remove the end, SO-2
        pageSize = this.pageSize - 2;
      // Prevent confusion caused by user input errors, such as the user's cache, from being returned to the user for the user to handle, as it is likely that the v-model will loop dead
      if (value > pageTotal) {
      // Friendly Trigger an Error Event
        this.$emit("error", value, pageTotal);
        value = pageTotal;
      }
      // If the active page is between 1 and end, the value is put in the array, otherwise a duplicate value will appear
      if (value > 1 && value < pageTotal) result.push(value);
      // Open your bow left and right to find the data about the currently active page number
      for (let i = 1; i <= pageSize; i++) {
      // Add, so detect less than the total
        if (value + i < pageTotal) {
          result.push(value + i);
          // Whenever the qualifications have been checked, the exit will occur if the value is sufficient.
          if (result.length >= pageSize) break;
        }
      // Subtraction, as long as the detection is greater than 1
        if (value - i > 1) {
          result.unshift(value - i);
          if (result.length >= pageSize) break;
        }
      }
      return result;
    },

The li tag above has been safely traversed

 <li v-for="item in showPages"
          :key='item'
          :class="{'is-active':value === item}">{{item}}</li>

4. Defining events

With all this said, the structure is ready and event driven is needed.

  1. This event is responsible for notifying the parent of the change in value and checking accordingly.
  2. Parameter is which page you want to activate currently
  3. Each event informs the parent that there will be duplicate activations, so this method compares the page number that you want to activate with the page number that is currently active, and puts it in the second parameter of the thrown event. Users will know if they want to request new data as long as they judge whether isNoChange is true or false. Users can also use this prompt toUser "You are already on page xx"
 handlClick(page) {
      if (page < 1) page = 1;
      if (page > this.pageTotal) {
        page = this.pageTotal;
      }
      let isNoChange = this.value === page;
      this.$emit("input", page);
      // Current value, whether there is a change from the current value
      this.$emit("onChange", page, isNoChange);
    }
  1. Forward and backward, just + 1-1
  2. ... the button needs to be processed because it involves adding or subtracting forward and backward.
  3. ... button clicks I designed to jump to a page I can't see right now, just what I can't see right now
  4. Divided into two functions to handle
 // On the left...
    previous() {
      // First not shown on left
      let page = this.showPages[0];
      this.handlClick(page - 1);
    },
    // On the right...
    next() {
    // First not shown on right
      let len = this.showPages.length,
        page = this.showPages[len - 1] + 1;
      this.handlClick(page + 1);
    },

Put time on the dom

<template>
  <div class='cc-pagination'>
    <button class="btn-prev"
            @click="handlClick(value-1)"
            // The end is to prompt the user to show a click-off style
            :style="{'cursor': (value === 1)?'not-allowed':'pointer'}">
      <slot name='previous'> &lt; </slot>
    </button>
    <ul class='cc-pagination__box'>
      <li @click="handlClick(1)"
          :class="{'is-active':value === 1}">1</li>
      <li v-if='showLeft'
          @click="previous">··</li>
      <li v-for="item in showPages"
          :key='item'
          @click="handlClick(item)"
          :class="{'is-active':value === item}">{{item}}</li>
      <li v-if='showRight'
          @click="next">··</li>
      <li v-if="pageTotal !== 1"
          @click="handlClick(pageTotal)"
          :class="{'is-active':value === pageTotal}">{{pageTotal}}</li>
    </ul>
    <button class="btn-prev"
            @click="handlClick(value+1)"
            // The end is to prompt the user to show a click-off style
            :style="{'cursor': (value === pageTotal)? 'not-allowed':'pointer'}">
      <slot name="next"> &gt; </slot>
    </button>
  </div>
</template>

In order to make a judgment about.. whether or not to show, we also need to pull out the logic of judgment.
For example, if the elements on both sides of the middle array are connected, they will not be shown.. Otherwise, they will appear.

showLeft() {
      let { showPages, pageTotal, pageSize } = this;
      // The left side is not 2, and pageTotal is shown beyond the required number, otherwise 1...... 2 is embarrassing
      return showPages[0] !== 2 && pageTotal > pageSize;
    },
    showRight() {
      let { showPages, pageTotal, pageSize } = this,
        len = showPages.length;
      return showPages[len - 1] !== pageTotal - 1 && pageTotal > pageSize;
    }

So far, the functional thing has come to an end

5. Enrich Styles and Effects

  1. Users can pass backgrounds to activate the background color effect for a comparison


  1. Comparing the beauty of large amounts of data, Hahaha, the feature of this component is that it can be infinite


Code implementation

// Backgroundis the key word, especially when it comes to css not being used directly
// js for user convenience, can be used appropriately
<ul class='cc-pagination__box'
        :class="{'ground-box':background}">

css
Pull out the ground style separately to prepare for future extensions


.ground {
        background-color: #f4f4f5;
        ;
        border-radius: 4px;
    
        &:hover {
            background-color: $--color-nomal;
            color: white;
        }
    }
    .ground-box { // Background color is the key word
        &>li {
            @extend .ground;
        }
        &>.is-active{
            background-color: $--color-nomal;
            color: white;
        }
    }

hideOne property, do not show components when opening only one page

// Outermost parent definition is fine
 <div class='cc-pagination'
       v-if='!(hideOne && pageTotal === 1)'>

Tot: Turn on left display bar mode
What I do is different from others. You pass me on and I show that it doesn't matter if you don't pass it on. There are no additional functions.

<p v-if="total"
       class="total-number">In total <span> {{total}}</span> strip</p>

Although the sparrows are small and have all five zang-organs, it takes half a day to do this. Many problems have been detected and improved one by one.

end
In addition, recently planned to do a series of vue,vuex, vue-router, webpack principle analysis, is also a bit from scratch, we look forward to continuing to learn together, progress together, and achieve self-value!!
Next episode is ready to talk about counters.
For more fun results, follow your personal blog: Link Description
github address: Link Description

Topics: Front-end Vue Mobile less Programming