This article gives you the content is about the Vue project data dynamic filtering implementation process, there is a certain reference value, the need for friends can refer to, I hope to help you.
The problem is the actual scenario that is being encountered in doing a Vue project, where I have a record of what I think and how I finally fix it (the old programmer has a bad memory.) -), the process will involve a number of Vue source concepts such as $mount
, and render watcher
so on, if you do not know well, you can see the Vue source Read series of articles ~
The problem is this: the data that the page gets from the background is the key, and the key 0
1
that represents the value 0-女
, for example, 1-男
is to be obtained from another data dictionary interface, similar to the API:
{" sex_type": [ {"Paramvalue": 0, "Paramdesc": "Female"}, {"Paramvalue": 1, "Paramdesc": "Male"} ]}
So if the view gets 0
it, it's going to find its description and show it from the dictionary 女
; the story begins.
1. Thinking
Some people say that this is not filter
the filter to do, the direct vue.filter not on the line, but the problem is that the filter is to wait for the asynchronous data dictionary interface to return to get, if at $mount
the time this filter is not found, Then the rendering (white screen and reporting undefined error) will result in the wrong effect.
There are two solutions that I think of:
The interface becomes synchronous, in beforeCreate
or created
hook to obtain the data dictionary interface synchronously, to ensure that at $mount
the time can get the registered filter, guaranteed timing, but this will block the mount, extended white screen time, so not recommended;
The registration of the filter becomes asynchronous and notifies the update itself after getting the filter so that it render watcher
can take advantage of Vue's own responsive update view, which does not block rendering, so this is the first approach.
2. Implement
Because filter belongs to Asset_types, there are several conclusions about the access chain asset_types in the Vue instance, and the specific code practice can be consulted as follows: Codepen-filter test
asset_types
Include,, filters
components
directives
, and all of the following asset_types
are replaced by their own previous items
The child component asset_types
does not have access to the parent component asset_types
, but can access the mount on the global registration $root.$options.asset_types.__proto__
asset_types
, which corresponds to the source code Src/core/util/options.js
The global registration method Vue.asset_types, such as vue.filters registered asset_types is mounted on the root instance (other instances $root
) $options.asset_types.__proto__
and inherited by all subsequent Vue instances created, that is, All of the Vue instances created later have access to the
The scope of a component's slot is limited to where it is defined, that is, the component it is defined in, not the parent component, asset_types
but can access the globally definedasset_types
Similarly, because the instance in Main.js new Vue()
is the root instance, it is registered to be asset_types
mounted on $root.$options.asset_types
top rather than $root.$options.asset_types.__proto__
According to the above conclusions, we can begin to coding the ~
2.1 Filters using the root component
So the first thing I think about is to mount the filter to be registered to the root component, so that the other components can get to the registered filter by accessing it $root
, the implementation here:
<template> <p> {rootfilters (sexval)}} </p></template> <script type= ' text /javascript ' > import Vue from ' vue ' import {registerfilters} from ' utils/filters ' export default { Data () { return { sexval:1 //Sex } }, methods: {/ * filter on the root component */ rootfilters ( val, id = ' Sex_type ') { const MTH = this. $root. $options. Filters[id] return mth && mth (val) | | val } }, created () { ///filters in the root component responds Vue.util.defineReactive (this. $root. $options, ' filters ', this. $root. $options. filters) }, mounted () { registerfilters.call (The) . Then (data = //Get data from the dictionary here ) } } </script>
JS to register Filter
Utils/filters Import * As API from ' API '/*** get and register filter * registered on $root. $options. Filters is not $root. $options. filters.__proto__ * Note that this is the Vue instance and needs to be called with call or Apply * @returns {Promise}*/export function registerfilters () { return api.sysparams () //Get the API for the data dictionary . Then ({data}) = { Object.keys (data). ForEach (The. $set (this. $root. $ Options.filters, T, val = { Const TAR = data[t].find (item = item[' paramvalue ') = = = val) return tar[' PARAMD Esc '] | | return Data }) . catch (Err = Console.error (err, ' in Utils/filters.js ')}
This makes the filters on the root component reactive and, when rendered, because it is accessed in a rootFilters
method that has already been responded to in the created $root.$options.filters
, triggers the component when the asynchronously acquired data is assigned to $root.$options.filters
render Watcher re-rendering, at rootFilters
this time to obtain the method when the filter can be taken;
Then why not use the Vue.filter method to register directly, because the Object.defineProperty
data can not be monitored __proto__
changes, and the global Vue.filter is to register the filter on the root component $root.$options.asset_types.__proto__
, so its changes can not be responded to.
The code here can be further improved, but this method has some problems, first of all, the use of an unstable Vue.util
method, and in the use of this.$root.$options
such access to the Vue instance inside the properties of the case, not too civilized, read also confusing.
So I thought about it when the project was done waiting to be tested, and who said that the filter would have to be in the filters. -You can also use Mixin to implement
2.2 Using Mixin
One thing to note about using Mixin is that, since all _
$
of the variables in the data in Vue are reserved as internal variables, they are not proxied to the current instance and are therefore directly inaccessible this._xx
and need this.$data._xx
to be accessed through.
Mixins/sysparamsmixin.js Import * As API from ' API ' export default {data () {return {_filterfunc:null, Filter function _sysparams:null,//Get Data dictionary _sysparamspromise:null//Get Sysparams return after promise}}, Meth ODS: {/* Register filter to _filterfunc */_getsysparamsfunc () {Const Thispromise = this. $data. _sysparamspromise ret Urn Thispromise | | Api.sysparams ()//Get the data Dictionary API. Then ({data}) = {this. $data. _filterfunc = {} OBJ Ect.keys (data). ForEach (Paramkey = $data. _filterfunc[paramkey] = val + = {//filter registered to _filterfun C in Const TAR = data[paramkey].find (item = item[' paramvalue ') = = = val) return tar[' Paramdesc ' ] || Return data}). catch (Err = Console.error (err, ' in src/mixins/sysparamsmixin. JS ')},/* Gets a single filter by key value */_rootfilters (val, id = ' Sex_type ') {const FUNC = this. $data. _filterfunc CoNST mth = func && Func[id] return mth && mth (val) | | Val},/* Gets the data dictionary */_getsysparams () {return this. $data. _sysparams}}, mounted () {this. $data. _fil Terfunc | | (this. $data. _sysparamspromise = This._getsysparamsfunc ())}}
Api
Save the promise here, if other places also use the words directly return is already resolved
the state of the promise, there is no need to request data again.
How to use it in our components:
<template> <p> {_rootfilters (sexval)}} </p></template> <script type= ' Text/javascript ' > import * As API from ' API ' import sysparamsmixin from ' mixins/sysparamsmixin ' export Default { mixins: [Sysparamsmixin], data () { return {sexval:1} }, mounted () { this._ Getsysparamsfunc () . Then (data = //Here Gets the database in the dictionary ) } }</script>
Not only did the filter register, but it also exposed the data dictionary to make it easier to display the list in some places, which is a common scenario in real-world projects.