Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Suggestion] Vue 2.0 - Bring back filters please #2756

Closed
smolinari opened this issue Apr 28, 2016 · 116 comments
Closed

[Suggestion] Vue 2.0 - Bring back filters please #2756

smolinari opened this issue Apr 28, 2016 · 116 comments

Comments

@smolinari
Copy link

smolinari commented Apr 28, 2016

Hi,

There was a hot discussion in the Gitter chat and there is a nice post on the forum about people missing the filter feature in 2.0 and it actually being a no-go to upgrading for some. This isn't a positive direction for the community it seems.

So, I'd like to put up this suggestion to bring back filters in 2.0, because they are so loved and, I would agree, smart. Here are some of the arguments for filters (gathered from the different discussions and no guarantee for correctness):

  • They are easier to read in the templates

thing in things | filterBy 'foo' | orderBy 'bar' | limitBy 5

is simply easy to read. In other words, chaining filters helps make sense of what should actually be expected.

  • Filters are global, which is great in a templating/ view system. Currency is a simple example of a great filter that can be used everywhere, just by calling it.
  • Without filters, there will be a ton of boilerplate.
  • Filters allow noobs to learn faster and get a quick and nice winning experience with Vue.
  • Using a mixin for every component to include a self-made "filter" isn't really feasible.

Needless to say, there are probably strong arguments for removing filter from an engineering perspective and why I would suggest this thread be a pros and cons and voting for or against the return of filters.

Scott

@agc93
Copy link

agc93 commented Apr 28, 2016

Clarifying a point that originally came from the Gitter chat: debounce is not a filter, and was just another of the 2.0 changes that people weren't thrilled about losing.

Correction courtesy of @JosephSilber : debounce is both a filter and a v-model param.

@smolinari
Copy link
Author

Thanks @agc93. I've removed that point.

Scott

@posva
Copy link
Member

posva commented Apr 28, 2016

In the worst case we'll come up with tiny plugins to deal with this. About filters, there's https://www.npmjs.com/package/babel-plugin-pipe-operator
The problem is this won't work without babel compilation

@nirazul
Copy link

nirazul commented Apr 28, 2016

I'm not all too stoked about some of the changes either. For filters, there seems to be an easy alternative by registering global mixins. However I'm not too fond of the idea of polluting all of my components' methods scopes for ultra-simple tasks like pluralize and so forth. I've never used two-way filters, however.

Filters were just convenient and beautiful. The two things that vue always did right (so far).

@nilssolanki
Copy link

nilssolanki commented Apr 28, 2016

When I read the post on vue 2.0, I was stoked about all the new possibilities (especially the virtual dom and the server rendering), but I was also surprised and a little sad to that the filters are gone.

They were one of my favorite parts of vue, not only because they were easily usable and chainable, but mainly because they were easily extensible and had a beautiful syntax to use them in the template directly. Especially in combination with v-for loops, they were a perfect match.

Thinking that I'll have to use computed properties to replace the filtering for each and every prop I want, I'm worried that I'll be writing a lot of boilerplate in the future. While mixins might mitigate a part of the problem, I still feel like a part of the elegance and ease of using vue is going to be missing.

@azamat-sharapov
Copy link

Everybody who agrees with it, can you please just click thumbs up under description? it's better than spamming 17 thousand people with +1. Even better, come up with some meaningful use cases.

@rigor789
Copy link

I've never used two-way filters, but I would really miss the filters! I can (and sometimes I do) use computed properties, but in some simple cases, it is a convenience that really speeds up the workflow.

Consider this very simple example

<input type="text" v-model="filter">

<ul>
    <li v-for="item in items | filterBy filter">{{ item }}</li>
</ul>

The above is so much easier to write in cases where it's not required to have some more complex filtering.

Now compare it to the following

<input type="text" v-model="filter">

<ul>
    <li v-for="item in filteredItems">{{ item }}</li>
</ul>
new Vue({
    el: 'body',

    data: {
        items: [],
        filter: ''
    },

    computed: {
        filteredItems() {
            var self = this
            return this.items.filter(function(item) {
                return item.indexOf(self.filter) > -1
            })
        }
    }
})

I'm not saying the second one is hard to write, but when you use it in many places, you will start repeating yourself, and it just takes some extra time you could perhaps use on some other, more useful features!

Either way I will stay a happy Vue user, just sharing my opinion on the filters being deprecated!

@OEvgeny
Copy link

OEvgeny commented Apr 28, 2016

Filters are reusable. I can create function to format my data once, register it as a filter and just use from all instances. How can I do it in 2.0?

@azamat-sharapov
Copy link

How can I do it in 2.0?

  • Mixin
  • Separate module with method
  • Separate module with computed prop function

@JosephSilber
Copy link

JosephSilber commented Apr 28, 2016

I just left a comment on the announcement thread, so instead of duplicating it all here I'll simply link to it:

http://archive.forum.vuejs.org/topic/3891/announcing-vue-js-2-0-public-preview/8

raywill added a commit to raywill/raywill.github.io that referenced this issue Apr 28, 2016
@yyx990803
Copy link
Member

I totally understand the feeling of something super convenient being taken away from you. But first please take a moment to read @chrisvfritz 's comment in the forum thread above. To make discussion easier I'm just copy pasting it here:


@theotherzach Thanks for your passion for Vue! I'd like to explain the deprecation a bit better, but first, I should introduce myself. I'm a member of the Vue core team, I use Vue all the time for my freelance UI and data visualization work, and I'm also an educator that teaches people to use Vue and Rails, among other web technologies. I run a code school, so I help people learn to use these tools (and use them together) almost every day.

I was also one of the big proponents for removing filters in Vue 2.0.

The problem with filters for beginners to Vue

A big part of the reason I was in favor of deprecating filters was actually for beginners. When working with students, this is a conversation that's come up more than once:

  • Student: "So a filter is basically a function?"
  • Mentor: "Yes!"
  • Student: "OK, so can I use it normally with function parentheses?"
  • Mentor: "Well, no. It's a special kind of function."
  • Student: "Can I use it in other places? Like in a computed value?"
  • Mentor: "No, you can only use it in templates and only with the special pipe syntax."
  • Student: "... why?"

One of the big things that trips up beginners is exceptions. Filters are just functions, except they require a special syntax and can't be used everywhere. And they use a pipe syntax that's different from the pipe syntax that may be integrated into ES7, meaning it won't be long until people have two very similar operators to do something very similar, but they're not quite the same. And only one of them is actually JavaScript.

Util libraries are useful, but Vue isn't one

In the case of filterBy, transforms for strings and numbers, and other specific filters, yes, they are useful in applications where they come up. Util libraries in general are useful. And there are dozens of great util libraries to choose from, but Vue isn't a utility library. And frankly, none of the utilities we've offered have been best-in-class.

Handling currencies, dates, or even filtering arrays - these aren't our focus. Many apps don't require them and most of the apps that I've worked on that do face these problems require a more robust solution than Vue currently offers (or could offer without introducing significant bloat and wheel reinvention).

In my apps, Accounting.js has handled currency superbly, Moment.js (as you mentioned) handles dates and times, pluralize doesn't handle many pluralizations well, so a custom computed value is often more desirable, and json is little more than JSON.stringify.

The advantages of computed properties

Using computed properties in place of filters also offers the advantage that the processed value can be easily reused in a DRY way anywhere in the component. I find myself having to do this in my apps all the time. The computed property also moves more implementation details out of the template, leaving only a clean description of what the component does. And an advantage over globally defined filters is that one need only look at the function for that computer value to see and tweak exactly how it's working. On arrays, chaining JavaScript's map and filter methods even provides the same linear list of processing that pipes do, but in an even more declarative and easily manipulated way.

The usefulness of globally defined whatever

If you need to define a function or anything else that you want accessible in all of your components, Vue.prototype.whateverIWant = mySuperCoolFunction is a great way to do it. Personally, I've never wanted to do it. I've always preferred to put a helper method into a module and import that module where I need it. But it's still important to note that registering globals - of any kind - isn't made any harder.

The case of the debounce directive

I have to say, I'm actually with you on this one! I recently looked over all my Vue projects and every single one that had an input somewhere also used debounce. In fact, only one of my past applications didn't use debounce. While technically a utility - lodash and many other robust projects offer debounce solutions - this problem is pretty universal to UI development, for any kind of app. Writing your own debounce function is also non-trivial enough that I'd probably want to use lodash's implementation for nearly every project.

There remains some internal debate over this, so we'll see where it goes. But yes, for me personally and it seems also some others, removing debounce removes most of the convenience offered by v-model.

Again, thanks for your passion!

Seriously, we love how much you love Vue and are really glad you're voicing your concerns - and especially in such a kind and respectful way! We're hearing you. The core team is all designers, front-end developers, and data visualization specialists. We all use Vue for our own work, which is pretty diverse, so we're definitely dedicated to pushing out dogfood we'll want to eat ourselves. :-)

@raywill
Copy link

raywill commented Apr 28, 2016

Talk is cheap, let's code to see if without filter, how we apply filterBy and reverse:

write global pure filter functions in a seperate file for code reuse

//
// filters.js
//
function filterBy(list, value) {
  return list.filter(function(item) {
    return item.indexOf(value) > -1;
  });
}

function findBy(list, value) {
  return list.filter(function(item) {
    return item == value
  });
}

function reverse(value) {
  return value.split('').reverse().join('');
}

export {filterBy, reverse, findBy}

use filters in App.vue template

<template>
  <div id="app">
    <h1> Reverse Demo </h1>
    <p> {{ reverse(msg) }}</p>

    <hr />

    <h1> Filter Demo </h1>
    <input v-model="userInput" />
    <h2> Prefix Matched Words: </h2>
    <ul v-for="word in filterBy(words, userInput)">
      <li>{{word}}</li>
    </ul>

    <h2> Exact Matched Words: </h2>
    <ul v-for="word in findBy(words, userInput)">
      <li>{{word}}</li>
    </ul>
  </div>

</template>

<script>
import {reverse, filterBy, findBy} from './filters.js'
export default {
  data() {
    return {
      userInput: '',
      msg: 'Hello Vue!',
      words: ['Black', 'Block', 'Blue', 'Alpha'],
    }
  },
  methods : {
    reverse,
    filterBy,
    findBy,
  },
}
</script>

see the result here http://raywill.github.io/vuefilter/


If filter used, following vue code would do the same:

<template>
  <div id="app">
    <h1> Reverse Demo </h1>
    <p> {{ msg | reverse }}</p>

    <hr />

    <h1> Filter Demo </h1>
    <input v-model="userInput" />
    <h2> Prefix Matched Words: </h2>
    <ul v-for="word in words | filterBy userInput">
      <li>{{word}}</li>
    </ul>

    <h2> Exact Matched Words: </h2>
    <ul v-for="word in words | findBy userInput">
      <li>{{word}}</li>
    </ul>
  </div>

</template>

<script>
export default {
  data() {
    return {
      userInput: '',
      msg: 'Hello Vue!',
      words: ['Black', 'Block', 'Blue', 'Alpha'],
    }
  },
}

Apparently, with filter supported we could write less trivial code.
Personally I prefer the vue 1.0 way, which makes coding more enjoyable.

@yyx990803
Copy link
Member

yyx990803 commented Apr 28, 2016

As for the boilerplate concerns - there are several ways to deal with it.

1. Explicitly import/export

Like @raywill demonstrated above. It's a bit more verbose, but the benefits is that:

  1. You don't have to lookup Vue's filter documentation to understand how it works. It's super explicit where the functions are coming from and how they are implemented.
  2. The functions themselves are just JavaScript. You can alter/compose them to fit special uses cases unlike built-in filters which you cannot touch.
  3. You can import and programmatically reuse these functions in methods, computed properties and anywhere you write JavaScript.

2. Attach them to Vue.prototype

Vue.prototype.filters = {
  filterBy: ...,
  orderBy: ...
}
<ul v-for="word in filters.filterBy(words, userInput)">
    <li>{{word}}</li>
 </ul>

3. Go functional (for advanced users)

computed: {
  filteredThings () {
    return this.things
       .filter(contains(this.foo))
       .sort(by(thing => thing.bar))
       .slice(0, 10)
  }
}

Where the helper functions look like:

// a function that returns a predicate function for array.filter()
function contains (value) {
  return thing => thing.indexOf(value) > -1
}

function by (getValue) {
  return (a, b) => {
    return getValue(a) > getValue(b) ? 1 : -1
  }
}

Again, a very important design consideration is that built-in filters can be useful, but they lack the flexibility of pure JavaScript. When a built-in function doesn't suit your needs, you either end up re-implementing something similar (and shipping both in your final code, where the built-in becomes useless, dead code), or have to wait for Vue to update them and release a new version.

@rigor789
Copy link

@yyx990803 I like the prototype approach, but how about cases where you need multiple filters?

It's pretty common to use orderBy along filterBy

<ul v-for="word in filters.orderBy(filters.filterBy(words, userInput), column, -1)">
    <li>{{word}}</li>
 </ul>

How about cases where you would add a limitBy too?

<ul v-for="word in filters.limitBy(filters.orderBy(filters.filterBy(words, userInput), column, -1), limit)">
    <li>{{word}}</li>
 </ul>
Does this approach make the code less readable?

Yes, at least in my opinion.

I guess we could have combined filters like filterAndOrderBy but that doesn't feel right either.

I'm open to changes, just want to come up with an almost as easy way to handle it!

Also, the benefit of being able to use the filters anywhere is a really good point too.

@blake-newman
Copy link
Member

Just to note, the @vuejs/collaborators have been discussing an option to provide a utilities package of the current integrated filters. There is plenty of resources out there that will provide you with utility tools for your code base.

One good thing about removing the core filters, is that you can now customise/implement them yourself. Which gives you lots more flexibility.

@blake-newman
Copy link
Member

blake-newman commented Apr 28, 2016

@rigor789 template expressions should be as simple as possible. Ideally just like <div v-for="filteredData"> </div>. This can be a computed prop, that can hold the logic for creating the filtered data. This is way more readable, and is better than something like:

<div v-for="item in filters.orderBy(filters.filterBy(words, userInput), column, -1)"> </div>
or
div v-for="item in data | filterBy | orderBy " ect

It is much more reusable to assign as a computed property, and can even be passed down to child components. What if you wanted to use the filtered data in two places? It's easier, simpler, reliable and more readable for templates to have simple expressions, compared to having chained filters in expressions.

@JosephSilber
Copy link

For anyone following along, I answered @chrisvfritz here: http://forum.vuejs.org/topic/3891/announcing-vue-js-2-0-public-preview/17

@theotherzach
Copy link

The following solves my use case of a very few globally available pure view helper functions. I withdraw my filter removal concerns. Burn 'em! 🔥 Congratulations to @chrisvfritz and @yyx990803 for changing somebody's mind (mine) on the Internet!

Vue.prototype.filters = {
  filterBy: ...,
  orderBy: ...
}
<ul v-for="word in filters.filterBy(words, userInput)">
    <li>{{word}}</li>
 </ul>

@thelinuxlich
Copy link

I totally agree with @yyx990803 , remove filters and stick to plain JS functions.

@rigor789
Copy link

@blake-newman That's a very good point, I'm mostly convinced at this point, and thinking about readibility, I think something like this can be achieved

computed: {
    filteredItems() {
        return f(this.items).filterBy(this.filter /* string or function*/)
                            .orderBy(this.field, -1)
                            .limitBy(10)
                            .apply()
    }
}

Here is a quick jsfiddle of the concept

@JosephSilber
Copy link

One good thing about removing the core filters, is that you can now customise/implement them yourself. Which gives you lots more flexibility.

You were always able to customize/implement them yourself.

Vue isn't a utility library. And frankly, none of the utilities we've offered have been best-in-class.

What we're concerned about is removing the filter functionality in the templates. Removing the built-in filters does make a lot of sense – they can be easily recreated by proxying them to underscore/other util libraries. Then, someone could even release a single plugin that recreates all the current built-in filters.

Using computed properties in place of filters also offers the advantage that the processed value can be easily reused in a DRY way anywhere in the component.

Of course you can use computed properties if you have to reuse it elsewhere. But if you don't, a filter is still much more convenient.


There are some other points I posted in that link I shared above.

@young-steveo
Copy link

young-steveo commented Apr 28, 2016

Why not support a syntax for filters that operates like the ES7 proposed syntax? That would allow people to keep using their beloved filters, and bring it in line with what the future may hold. Eventually when we have ES7 pipes, you can switch the internal implementation without changing the api.

@thelinuxlich
Copy link

Are ES7 pipes approved or subject to a lot of changes?

@rpkilby
Copy link

rpkilby commented Apr 28, 2016

It's theoretically subject to change, but seems... stable?
Status: tc39/proposal-pipeline-operator#33

@chrisvfritz
Copy link
Contributor

@JosephSilber @young-steveo @thelinuxlich I think we're on the same page regarding the value of pipes in general. 😃 One advantage of the new compiler in 2.0 is we can pipe the generated render function code through Babel. This still needs to be further explored, but it's not inconceivable that once |> gains more momentum and a Babel plugin for it is developed, you could happily chain methods with pipes again - everywhere in your app. As a huge fan of LiveScript and other functional languages, I definitely recognize the value!

@thelinuxlich
Copy link

this pipeline operator is not even in stage 0

@chrisvfritz
Copy link
Contributor

@thelinuxlich Yes, I believe they're still waiting for a champion on TC39. 😞

@yyx990803
Copy link
Member

@rigor789 that's one of the alternatives I wanted to mention too! The power of JavaScript allows you to achieve expressiveness of your choice, and imo it's better than putting the filtering logic inside templates.

@franciscolourenco
Copy link
Contributor

franciscolourenco commented Jun 10, 2016

2 way filters are really nice in vue. You can always build your own customised components, with or without filters available, but that takes time. The library should provide flexibility but also a faster and convenient way to do things. Including 2-way filters wouldn't stop people from building their own components if they have to, but would allow faster development when the existing functionality fits the needs, which is most of the time. Its always a compromise between API surface and conveniency. The balance has to be found, and Vue already had a great one. Getting rid of filters seems to be a step back from the right point in balance. The library becomes smaller, but the codebase and boilerplate of every projects which use it grows bigger.

If filters are really to be deprecated maybe it would be good to provide an alternative be able to specify parser and a formaters for v-model directives, in a easy way. With filters are able to write just this part of the example @yyx990803 provided:

const CurrencyInput = CustomInput.extend({
  methods: {
    parse(val) {
      var number = +val.replace(/[^\d.]/g, '')
      return isNaN(number) ? 0 : number
    },
    format(val) {
      return '$' + Number(val).toFixed(2)
    }
  }
})

Without 2-way filters one also has to write

const CustomInput = Vue.extend({
  template: `
    <input type="text"
      @focus="onFocus"
      @blur="onBlur"
      @input="onInput"
      @change="setDisplayValue">
  `,
  props: ['value'],
  watch: {
    value() {
      if (!this.focused) {
        this.setDisplayValue()
      }
    }
  },
  ready() {
    this.setDisplayValue()
  },
  methods: {
    onInput() {
      this.$emit('input', {
        target: {
          value: this.parse(this.$el.value)
        }
      })
    },
    onFocus() {
      this.focused = true
    },
    onBlur() {
      this.focused = false
      this.setDisplayValue()
    },
    setDisplayValue() {
      this.$el.value = this.format(this.value)
    }
  }
})

So, more flexibility, but also more code for the same result.

@smolinari
Copy link
Author

@aristidesfl - filters aren't being deprecated. We won the battle, partially. LOL! They are going to be limited to only being used in text interpolations. See here: #2873

Scott

@franciscolourenco
Copy link
Contributor

Thanks @smolinari . I was referring particularly to 2-way filters though.

@aviggngyv
Copy link

please come back! we need it, I agree all you said. but it's so easy, we are lazy user ,we just want a lazy, easy way. just like keep-alive.

@ecmel
Copy link

ecmel commented Sep 11, 2016

May be there should be a way to add custom v-model modifiers (http://rc.vuejs.org/guide/forms.html#Modifiers)

They work like two-way filters.

@franciscolourenco
Copy link
Contributor

@ecmel agreed. This is the way to go.

@LinusBorg
Copy link
Member

Open a feature request issue then :)

@rpkilby
Copy link

rpkilby commented Sep 11, 2016

@ecmel a similar idea, although described as type modifiers, was discussed here.

@ecmel
Copy link

ecmel commented Sep 12, 2016

@rpkilby That discussion is closed as well, may be we should open an new one for custom v-model modifiers.

@fergaldoyle
Copy link
Contributor

No longer able to do this:

<span :title="item.Modified | date('dd-mmmm-yyyy H:MM:ss')">{{item.Modified | date('dd-mmmm-yyyy')}}</span>

Limiting filters to {{ }} seems strange, there's plenty of times you'd want to use a filter in an attribute binding (not passing a prop), and we can't use {{ }} in attributes anymore!

@ecmel
Copy link

ecmel commented Sep 13, 2016

@LinusBorg Opened #3666

@LinusBorg
Copy link
Member

LinusBorg commented Sep 13, 2016

Limiting filters to {{ }} seems strange, there's plenty of times you'd want to use a filter in an attribute binding (not passing a prop), and we can't use {{ }} in attributes anymore!

They got removed because they can be easily replaced by computed properties and methods, while themselves having a more limited API an no possibility of caching results.

So:

  • one API less
  • use the same methods in the template and the JS code, no need to duplicate filter behaviour to use in app code.
  • computed props can be cached
  • more flexibility because it's just JS

pseudocode ahead:

<!-- method, suitable in v-if  -->
<span :title=" date(item.Modified, 'dd-mmmm-yyyy H:MM:ss')>

<!-- computed prop, more suitable for single values -->
<span :title="formattedModified">
<script>
computed: {
  formattedModified () { return date(this.item.Modified, 'dd-mmmm-yyyy H:MM:ss') }
}
</script>

@Andre741
Copy link

Sorry, I am only "new".
But coming from Angular 1 not having filters feels really strange and counter intuitive why would it be better to have every developer develop his own set of filters and setting the computed entries themselves etc. better than having it build in?
And what's now considered best practice to filter a really large array, by objects in the element. With angular this was only one line of code. It was possible to filter a array for a search input and it would do all the magic, which should be the function of a framework.

@smolinari
Copy link
Author

smolinari commented Dec 28, 2016

And what's now considered best practice to filter a really large array

What is a really large array? Theoretically a computed property would do good for filtering too.

https://jsfiddle.net/sat25z51/3/

why would it be better to have every developer develop his own set of filters and setting the computed entries themselves etc. better than having it build in?

I used to think the same way (and why I started the thread), but since I've learned it is basically a piece of cake to create these kinds of filters on your own, and that there are sooo many things each developer might want as a filter (or computed property), I've come to realize it isn't all that big a deal not having built in filters.

Scott

@Andre741
Copy link

Andre741 commented Dec 29, 2016

In my case it equals to >10.000 lines in json provided by a firebase database.
I see why you might think that way, and decide to drop the feature, nonetheless it's quite a additional hassle for devs.

I forked the fiddle to be in-line with my Data Structure:
https://jsfiddle.net/nw5yhLwv/
So I need to code the search a string in my object by hand?

@smolinari
Copy link
Author

I don't understand your question. Sorry. The fiddle looks like the one I posted.

Scott

@gkiely
Copy link

gkiely commented Jan 27, 2017

Computed vs. filters

why not both

@smolinari
Copy link
Author

Filters are possibly bad for performance, since they are computed for every render (just like methods) and computed properties are easy as cake to do, and are only recomputed, when necessary otherwise the results are cached, which means Vue can fly.

Oh. and we do have both. 😄

Scott

@Andre741
Copy link

Hey, I did some fiddling and now I get it, it's a great concept! 😄

@LinusBorg
Copy link
Member

I will lock this thread now, as the discussion about this is long finished - Vue 2.0 has been out for a couple of months, and critique about the drop of filters has largely subsided.

Further discussion in this thread is not productive. If you see a new angle to the topic that you think needs discussion, please open a new issue.

@vuejs vuejs locked and limited conversation to collaborators Jan 29, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests