Vue: [Suggestion] Vue 2.0 - Bring back filters please

Created on 28 Apr 2016  ·  116Comments  ·  Source: vuejs/vue

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

discussion

Most helpful comment

Here's the final decision:

  1. Filters will be supported, but only inside text interpolations. This limits them to text formatting purposes while enforcing other logic into JavaScript land.
  2. Vue 2.0 will ship with no built-in filters. The community can create their own filter packs if needed.
  3. The filter syntax will change to use function invocation syntax for arguments instead of space-delimitered. This brings it more inline with JavaScript and other popular templating languages (Jinja2, swig, twig...):

html {{ date | formatDate('YY-MM-DD') }}

All 116 comments

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.

Thanks @agc93. I've removed that point.

Scott

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

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).

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.

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.

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!

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?

How can I do it in 2.0?

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

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

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. :-)

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.

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.

@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.

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.

@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.

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

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>

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

@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

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.

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.

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

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

@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!

this pipeline operator is not even in stage 0

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

@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.

@yyx990803 How does Vue perform in the following cases, when one user's name is updated?

<div v-for="user in users">
  <p>{{ user.name |> capitalize }}</p>
</div>
Vue.extend({
  computed: {
    displayableUsers() {
      for (user of this.users)
        user.name = capitalize(user.name);
    },
  },
});

It seems like the former would only re-render the one object, while the latter would recompute the entire list?

@rpkilby that doesn't seem to be the equivalent. It'd be more like:

Vue.extend({
  methods: {
    capitalize () { ... }
  }
})
<div v-for="user in users">
  <p>{{ capitalize(user.name) }}</p>
</div>

Still don't like the idea of using methods as filters (as a gut feeling it just seems wrong, but can't really explain it). Either way you discussed other methods of making filters available, so +1.

Nothing suggested in this thread comes even close to the expressiveness and succinctness of filters. It just doesn't.

And that makes me real sad. Like I said in the forum thread: to me, this removes a big chunk of what makes Vue Vue.

If you're looking for me, you can find me in the corner sobbing audibly 😟

No way, this change encourages good Javascript coding, deal with it :)

So I've just had a long discussion with @yyx990803, where I – from a user's perspective – suggested to keep the _syntax_ support, because it does feel elegant and natural. My imagination was something like this:

<li v-for="item in items | filterBy 'name' | orderBy 'title' 1">{{ item.name }}</li>
...
<script>
  ...
  methods: {
    filterBy (items, field) { return filtered; },
    orderBy (items, field, order) { return filtered; }
  }
  ...
</script>

I was under the impression that this would approach the best of both worlds.

In the end though, I'm convinced that removing filters as a whole is actually a better thing: like @thelinuxlich just said, it encourages better JavaScript and logic thinking. We don't introduce logic in Laravel's Blade or any other framework's view layer, and we shouldn't in Vue's templates.

That said, @JosephSilber if you look at the other corner, you'll find me there.

For me filters feel are very beautiful, the syntax is exactly what a filter syntax should look like.

Also one attractive thing about Vue for me is that it comes with (some) batteries included. It would be really sad to lose either of these things -- both which, in my opinion, make Vue stand out.

Reading through the thread, it seems most of the concern with filters was the fact Vue had default filters, and not really the function itself (or is it? I still am not sure).

I like the filter plug-in thinking from @blake-newman and there should be prebuilt examples. If other people come up with other filters, they should be plug and play. That would be great. I absolutely agree that creating filters is a userland responsibility.

What is still wanted is the pipe and chaining abilities and the globalness of the original filter feature. The concerns about globalness were covered by @yyx990803. How about the chaining with the pipe? It helps a ton in reading the templates and understanding what is to be expected as an output. The pipe and chaining was discussed above. Can it still be done? Why is it a bad thing at all? For the designer, it is gold! The filter is a designer's tool, not the JS programmer's. So, the argument of writing better JS falls off the board, in my book, but I can understand why it would be wanted. But, as a designer, I want to write better code too, and filters allows me to, beautifully. :smile:

Scott

Here's the thing regarding chaining - filters are primarily used for two purposes:

  1. formatting text;
  2. processing an array.

In the case of formatting text, 90% or more of the time only a single utility method is used. Chaining is not really that much of a problem in this case.

As for arrays: I have already pointed out that array processing is in fact logic and is better suited in JavaScript. Having multiple chained array filters may look okay when it's simple, but can get ugly when you use more than 1 arguments for each. It is also encouraging you to put too much logic in your template when you really shouldn't. They are also inflexible (cannot easily retrive the processed value). In comparison, with ES2015 the original example

<div v-for="thing in things | filterBy 'foo' | orderBy 'bar' | limitBy 5">

can be written as:

computed: {
  filteredThings () {
    return this.things
      .filter(item => item.title.indexOf('foo') > -1)
      .sort((a, b) => a.bar > b.bar ? 1 : -1)
      .slice(0, 5)
  }
}

It's just JavaScript, it has no magic, no alternative syntax and filter-specific API to learn. And you can access the processed value (which is intelligently cached). You are also free to add sugar on top of this as shown in previous comments. Plus, your template looks cleaner:

<div v-for="filteredThings">

I know this is like a handy thing being taken away, but honestly, the arguments for filters right now sounds to me like just trying to keep the syntax for the syntax's sake. In my opinion, it takes more than just "because it's elegant" to justify a feature - it needs to provide objective value. This was the case for inline-templates, but I don't see it for filters.

@yyx990803 - Your example exemplifies the issue, I think. Again, I'm not the best JS programmer, but I don't need to be a JS programmer to know filteredThings says nothing about what it really does. To me, that is a bad practice. :smile: The fact the filtering method is global also means I must go searching in docs, the code or decipher the output to find out what the method does. Not really elegant. Right?

So, ok. we could have a method name like `filterByOrderByAndLimit'. What about the arguments?

<div v-for="filterByOrderByAndLimit(things,thing,'foo','bar',5)">

Would that be correct?

If it is, that might be feasible. Still, it doesn't look good.

And, now I want only filterBy and OrderBy. Do I need a separate method to do that too? And then I want to add currency formatting. Another method? The filter chaining in the templates makes manipulating the presentation flexible and very expressive. This concern has yet to be properly addressed with methods (and my lack of knowledge isn't allowing me to understand methods can be better).

Can this be possible?

<div v-for="filterBy(things, thing, 'foo').OrderBy('bar').Limit(5)">

I agree that doing too much logic in a template is something to avoid. But, filter chaining and the ability to simply pop in a filter anywhere in an app's components/ templates is a fantastic concept and there was a reason why you added it to begin with. What was that reason or reasons? If you can name them, then I will bet money they outweigh "but it promotes bad practices" by a mile.

Anything that allows flexibility can be used improperly, just like Vue itself. The main argument for inline-template was it was flexible and allowed Vue to be used in different ways. Filters isn't much different, other than it is Vue internal and doesn't affect how Vue might be used externally. Still, that doesn't devalue its importance. Remember, a lot of people have also said this would be a no-go for them to upgrade. It is that important! :smile:

The new way just needs the same attributes the old way had.

  • chainable
  • global
  • add to templates to nicely express how the data in the templates will be manipulated

(did I forget anything?)

If it can do all that, then I am sure everyone will be pretty much satisfied. I personally can't see it happening with methods (yet).

Scott

@smolinari

  1. I've clearly stated that you should put less logic in your templates. This is my opinion whether you like it or not. Obviously you are free to disagree, but I can't help you if you want to work against recommended best practice.
  2. Given (1) - I've explained why chaining is not an issue because complex logic should be done in JavaScript.
  3. I've also given examples of how you can add globally available methods.
  4. See @rigor789 's example on a custom chaining syntax similar to what you want.
  5. The goal of the framework is to provide what _I_ believe is the best way to develop front end applications, not to please everyone. So please don't use "I will not upgrade if you don't give me this feature back" - it doesn't work.

Why don't you guys try this new alpha release for a week and then come with some really valuable comments (based on your practice)? Maybe try to refactor your existing app and let us know what was impossible, what was improved, what got worse, so we can discuss it better. That would be useful for everybody I think.

@yyx990803 - I agreed to 1. So we agree. :smile: I just don't agree it being a proper reason for taking out something which had gained so much love.

So, I guess you've decided over time that pragmatic JS is better than pragmatic HTML?

The argument of a no-go for not upgrading is what others have said. I am just trying to communicate and mitigate that.

One last argument from myself. Look at this from the user's user's eyes. Say I have a system like Laravel or some other MVC or MVVM system that incorporates Vue, which also allows the user of that system to build their own components. The filter, as it was, simplifies the learning curve and it allows the users of that system to get a lot done, without touching any JS. I am a fan of Vue, because it did allow non-JS programmers to still get a lot done. This is the same reason why I don't like React and JSX. That combination will have a lot smaller user base over Vue as time goes on. I'll bet money on it.

I also understand that the real flexibility lies in JS. Still, please don't rely solely on JS for the flexibility in Vue. Keep it in mind, not everyone is a killer JS programmer. In fact, most people aren't. The filter was a nice way to get a lot done for those people and it was a nice stepping stone towards further JS manipulation. Filters can't get it done? Go deeper to JS methods.

Ok. Enough from me..... I am done. And, thanks for listening at any rate. Vue is still awesome! :smile:

@azamat-sharapov - good point.

Scott

Seeing people trying to justify bad practices inside JS makes me sad. Really, you don't need to be a pro, just do the basic homework(or isn't it basic anymore these days?)

The problem I have with filters-inside-methods is of semantics.

In oops term, filters are like static functions while methods are non-static function.

Filters convey extremely different semantics than methods. the biggest difference being that filters can not use this, but methods can.

@yyx990803 while using Vue.prototype.filters can work but changing Vue does not look like good practice to me. I would rather advocate creating a separate (global) object which will contain all filters.

It doesn't look like good practice because globals is not good practice. The recommended approach is explicitly importing helper methods. Attaching to prototype is a workaround for those who do not want to explicitly import methods.

As for semantics - filters are a coined concept anyway. In JavaScript you wouldn't invent a different semantics just to uppercase a string - you call a function. And that's what they are, JavaScript functions.

I am going to make one more comment to try and make my personal point clearer, mainly because of the "trying to justify bad practices inside JS " comment. I am certainly not trying to do that.

I realize Vue is more than just a templating system, but, it is a templating system too and I am afraid it is trying to move away from that role, which I feel it shouldn't. So as a templating engine/system, take the Twig templating engine as a very successful example of what one could expect from a templating system. They have a "For the template designers" section and a "For the developers" section in their docs. As a templating system, Twig is powerful for the template designer too, because it is chock full of default behaviors, including filters. It allows non-developers to do a ton with the template system, without directly knowing PHP. They just have to learn what Twig has to offer and how to use it. I am looking for this in Vue too. I'd like to see a "Vue for template designers" section in the docs too. :smile:

Also, very important is the extensibility of Twig. If something isn't available in the stock version, it can be added. That is when the developer steps in. The nice thing is too, extensions can be shared, which means, it only has to be done once.

The other nice thing about these two levels designer/ developer is, you get a much broader base of users. Much, much broader and this is the same with . And when the behavior defaults aren't enough, that is when they start to learn, willingly, the underlying language. And that is when they learn best practices and the rest.

If you are saying, Vue can't be a template engine without having to learn JS, then I'd say, it is lowering its market value considerably. If you say, you will leave the door open to let others make those tools for the templating engine as plug-ins, great. But, then everybody will be fighting for what should be in the templating system. That is also counterproductive IMHO.

In your student talking about filters example Evan, was it someone learning JS or someone learning a template engine? If it was the latter, I bet the conversation would go differently.

At any rate. I still think Vue is a winning system. I hope my thoughts might make others think differently about Vue's roles in some way. :smile:

Scott

@yyx990803 if filters are coined concept, then why were they coined in first place?

I believe its because inside <template> all external variables like $data or abc are converted to this.$data and this.abc and hence plain js functions defined outside the component can't be referenced. Filters were introduced to overcome this limitation.

That limitation still exists ---I am assuming I am right--- to remove the limitation would require devs to explicitly code this.abc for referencing this.abc and access js functions as they would be referenced in js.

But that will be too powerful feature, prone to abuse and migrating old code will be a pain. the current syntax looks way better than that to me.

I agree with you that filters are basically js functions and should be imported to component. I just don't like that they are defined in same place as methods.

Hi everyone. I read the whole thread and this is what I think.

  • No one would miss the built in filters like orderBy filterBy and any other
  • What everyone want to still have around is the pipe operator in templates
  • The core team says that the logic should keep in javascript, not in templates, besides there is a coming es7 feature with the pipe operator.

Having that as a quick summary what I think it could be a good solution until js implement a native pipe operator, would be to have the pipe operator as a plugin and keeping the 2.0 version as it is. So, users can do

Vue.use(require('vue-pipe'))

While we wait for the new feature implementation, it could be included with some warning about coming deprecation or something. I know that anyone could implement that plugin, but I think that would be better if it is provided and keeped by the Vue team, could that be possible?

I could be wrong, but I don't think Vue allows plugins mess with it's compiler..

@azamat-sharapov Of course not. I just didn't think it would mess with Vue compiler :open_mouth:

@YerkoPalma I absolutely _love_ the pipe operator. I use it obsessively in functional languages and can't wait to have it in JavaScript! _But_, imagine Vue had never had filters. Would you be requesting that a frontend framework extend JavaScript's syntax? That's the domain of Babel or a compile-to-JS language, not a UI framework. If you prefer to use a language like LiveScript, as I often do, you can. But the problem here isn't with Vue. It's with JavaScript itself and we're not here to fix that.

Additionally, if we're able to pipe the results of the render in 2.0 through Babel as we hope, then you'll even be able to use the non-TC39 tracked plugin if you want - consistently, everywhere in your JavaScript, instead of just in the template. So if you just want a pipe, it's likely you'll be able to have it. And you _can_ have it today in 1.x - just be warned that the pipe you'd be using in your templates is likely to have a different precedence and behavior than the one in your Babel.

@smolinari and others. There are two phrases (and variations thereof) I keep hearing:

  • "Think of developers / the user's perspective"
  • "Think of beginners"

Both imply an assumption - that we're _not_ thinking of these groups.

I've mentioned this before, but I guess it needs reiteration. _Everyone_ on the core team is either a designer, frontend developer, or combination of both. We use these tools every day for our own work. We'll be using Vue 2.0 too. Believe me, we _are_ thinking of that. 😉

As for beginners, I made a case here, but I guess I'll go into more detail. I'm an educator. And I was a proponent of eliminating filters, _thinking of beginners_. I've personally taught hundreds of people - maybe even more than a thousand - how to practice web development, usually from scratch. No former coding experience. I've done this with middle schoolers, high schoolers, university students, professional adults, and senior citizens.

From that perspective, I can tell you that while filters can seem like exciting magic at first, they ultimately slow down a student's learning by introducing more complexity for limited convenience. If they had never been in Angular or Vue and this conversation were reversed - we were trying to _introduce_ them in 2.0 - we'd have a hard time explaining why they were needed and when you should use them.

Before there was any talk of a deprecation in 2.0, I had eliminated filters from the curriculum at our code school, because we'd gathered enough evidence that they were doing more harm than good for beginners. We'd rather they gain experience with more versatile Vue features, like methods and computed properties. We even discouraged the use of filters, because they made it easier to pick up bad practices.

Hope that puts these two complaints to bed. We'll see. 😃

But, imagine Vue had never had filters. Would you be requesting that a frontend framework extend JavaScript's syntax?

Of course I will, as filters are very natural to _templates_ as in Twig, mentioned before. I feel that having a pipe operator is as natural as having the mustache syntax in templates, I mean, mustaches are not html, and not javascript are you guys going to remove them too? whats the different with the pipe operator?

The "think about the children" argument is just dumb. As far as I know, Vue is not designed for teaching JavaScript, but for front-end developers to get shit done. For the latter filters are great.

If they had never been in Angular or Vue and this conversation were reversed - we were trying to introduce them in 2.0 - we'd have a hard time explaining why they were needed and when you should use them.

I very much disagree. I've been using a Python framework called Django since 2005 and its templating language -- which has been the inspiration for most of the later born templating languages out there -- has had filters from the day one. After using them with front-end developers over ten years, I've come to appreciate their beauty and usefulness. It would be sad to see this syntax go away.

(Here's what Django does with filters: https://docs.djangoproject.com/en/1.9/ref/templates/builtins/#built-in-filter-reference )

@Uninen please mind your tone - calling others' arguments dumb is not a constructive way to participate in a discussion.

For those drawing analogies to server-side templating languages - one important aspect to note is that server-side template languages do not have the same amount of flexibility that Vue templates have (no computed properties, limited expressions). Also - they are built for completely different purposes: outputting static strings. Vue templates are representations of interactive DOM - there are two-way bindings, event handlers, component props and more. Filters only fit in a very limited use case in the Vue context: today I believe it is a bad idea to allow filters everywhere, e.g. filters on v-model, v-for and v-on introduces more complexity than good.

One possible alternative is keeping filters but only for text interpolations - i.e. you can only use it inside {{ }}s, not in directives.

As an interesting reference: Angular 2 still has filters (renamed "pipes") but they also intentionally removed the list filtering ones.

Sorry for my language, not meaning to insult anyone.

For me the purpose of a framework _is_ to do the hard stuff and make it look easy for the developers who use these tools. I still argue that the syntax just can't be beat and, again, it would be sad to see it go.

I don't know or understand that much of the under the hood mechanisms, but from a users point of view, practicality beats purity :)

In a related note, it's been interesting to see how much passion there seems to be in this community. For me Vue has been a fresh and invigorating tool, maybe that's why I myself had to take part in painting this particular shed :)

For another datapoint, Ember doesn't have filters nor does it allow component methods, though it does have computed properties.

For the use case of pure functions performing in-template transforms, you have to register a handlebar helper / ember helper.
http://emberjs.com/api/classes/Ember.Helper.html

Handlebar helpers are syntactically distinct from things that aren't handlebar helpers which is why I bring it up. They've got that in common with the piping filter syntax. However handlebar templates are "logic-less" which means that they require special syntax for an in-template function call. An issue that Vue doesn't have.

Filters is simple for beginners, there see some magic by using for pretty output or filter\ordersearch in arrays

 {{ article.date.created_at | moment 'MM.DD.YYYY HH:mm:ss' }}

and ease define it

Vue.filter('moment', (str, format) => moment(str).format(format));

it's simple, clear at templates, for 2.x you define filters in external module and in templates get

import moment from 'moment'

methods:{
   moment(date, format){
       return moment(str).format(format)
  }
}

and in template

 {{ moment(article.date.created_at, 'MM.DD.YYYY HH:mm:ss') }}

As suggestion, why not leave old filters in template, but move filters it in separate module and use it from methods section?

So if you need it, just do

npm install vue-utils

and use specific filters from utils package

import {moment} from 'vue-utils/date'
import {price} from 'vue-utils/numbers'

methods:{
   moment, price
}

and use as common filter

 {{ article.date.created_at | moment 'MM.DD.YYYY HH:mm:ss' }}
 {{ product.price | price }}

in result it be translated to simple function call

moment(article.date.created_at, 'MM.DD.YYYY HH:mm:ss')
price(product.price)

_Note_
For me, filters most use to format data, like dates, numbers or strings. For filtering arrays\objects I think better use computed and function from common vue module:

import _ from 'vue-utils/array'

computed:{
   ordersTable(){
         return _(this.orders)
                        .filterBy(this.filter)
                        .sortBy('date', -1)
                        .limit(10)
   }
}

Benefits:
Beginners can ease use old filters, but in computed and use pretty function in their templates to format data output and programmers can write own functionsmodules for it and ease to use.

_Why separate module?_
I think it's no need be in core of Vue, but Vue must have some utils for developerstemplate designer, so no need all time require lodash, moment or else, just install utils from npm and ease to use it, but save old call syntax for templates.
But one important think must be done for filters, there must be pure functions, like getters in vuex.

It's ease to support, ease to usereuseextend and it have good look at templates.

What you guys need is a clear upgrade path and the desire to learn how to modularise your Javascript code.

@thelinuxlich We speak about filters.

 {{ article.date.created_at | moment 'MM.DD.YYYY HH:mm:ss' }}

just syntax sugar for

{{ moment(article.date.created_at, 'MM.DD.YYYY HH:mm:ss') }}

But completely exclude filters for beginners in Vue\javascript is bad. Why Laravel community like Vue? Because it's simple and powerful.

If we completely remove filters, need give alternative for _"For the template designers"_, there no need to know how sort array or filter it, there want just enter code form example and get result.

Programers must know how _modularise_ Javascript code, but rest who use it for small stuff no need to know it.

If we speak about _"For the template designers"_ there can just put

<script src="vue-utils.js"></script>

and inside code use

Vue.use(VueUtils)

computed:{
   ordersTable(){
         return this.utils.array(this.orders)
                        .filterBy(this.filter)
                        .sortBy('date', -1)
                        .limit(10)
   }
}

from example and proud themselves, there no need to know js to filter and sort arrays, lot of people want do some things, but there hard to understand code like

computed: {
  filteredThings () {
    return this.things
      .filter(item => item.title.indexOf('foo') > -1)
      .sort((a, b) => a.bar > b.bar ? 1 : -1)
      .slice(0, 5)
  }
}

so no need make complicated things for this type of people, if we can give simple solution for there and it be good for developers to.

One possible alternative is keeping filters but only for text interpolations - i.e. you can only use it inside {{ }}s, not in directives.

I think this could be a very good compromise. While it will not satisfy everyone it is all I ever used filters for and I actually find it weird that they currently can do more than that.

It would allow for convenient text formatting if you want to use filters and reduce complexity by effectively removing two-way filters.

In my opinion, your momentjs filter example is already passing too much logic to the templates.

Seriously, you should direct all this "pipe love" to the spec repository until it reaches TC39 :)

I like what both @yyx990803 and @VitaliyLavrenko have suggested. That could be a good middle ground.

Scott

I like @VitaliyLavrenko proposal, but when I say something similar, about having the pipe operator in a plugin @azamat-sharapov said that plugins shouldn't mess with the compiler... So I'm confuse if it's even possible or if I just misunderstood the comment?

@thelinuxlich

What better for you to read?

<div v-for="thing in things | filterBy 'foo' | orderBy 'bar' | limitBy 5">

or

computed: {
  filteredThings () {
    return this.things
      .filter(item => item.title.indexOf('foo') > -1)
      .sort((a, b) => a.bar > b.bar ? 1 : -1)
      .slice(0, 5)
  }
}

Think as developer and ask someone who don't know javascript.

As for me, for _not developers_ better something like this:

Vue.use(VueUtils)

computed:{
   ordersTable(){
         return this.utils.array(this.orders)
                        .filterBy(this.filter)
                        .sortBy('date', -1)
                        .limit(10)
   }
}

It's middle, and used like vue-resource just common lib.


About pipe, I it's syntax sugar

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

for

thing in limitBy(orderBy(filterBy(things, 'foo'), 'bar'), 5)

What ease to read?

If you need pass data to 2 diff templates, you store 2 or 3 modifications for one data?

If it be some like:

padLeft(capitalize(title), 10)


padRight(upper(title), 5)

I'ts abstract situation, but if it use in one or two templates? You need store data for 10 or 100 objects? Increase memory usage? Yes we can use helper method and use it in template, but for _"For the template designers"_ who far away from javascript better something like title | capitalize | padLeft 10 and it should be translated to function call, it's ease and functional.

Think about diff people, developers can ease do it in diff way, but other people want do it simple.

I will say it again, too much logic in templates is a anti-pattern. Instead of having to learn a specific DSL(filters) you should learn Javascript.

@thelinuxlich Just use JSX and we get React with 2-way binding and few other things.

But with Vue we get, people that don't know javascript, but with google can do something and if you dislike pipe, don't use it. We can just add something like

Vue.config.pipeFuncCall = true

And you can turn on\off it, but for some people it be ease.

_I will say again, not only JS developers can use Vue, it's the strongest side of Vue._

Standart common filters need, but there better be in separate lib and ease to add it to vue and use.

Or you want Vue be something like ASM and only good devs can use it?

Guys, I think decision Vue core team took has been explained very well. Can we not repeat same thing again and again and comment only with something new, valuable, if any?

If it is part of the Vue core principles to be easier to non-JS developers, even if it hurts language principles, then I will agree with you, but probably it isn't the case.

As I said before I'm mostly fine with filters being deprecated, I will surely miss them for the sake of their convenience, but I'm more than sure that a very similar filter system can be introduced as a plugin.

I've given a proof of concept example a little earlier, which could be used in computed props, as well as inline

<ul>
    <li v-for="item in f(items).filterBy(foo).orderBy(bar).limitBy(5).apply()">
        {{ item.foo }}
    </li>
</ul>

On the other hand, If someone wants the pipe, I'm sure it can be done, with something like this

<ul>
    <li v-for="item in p(items, 'filterBy foo | orderBy bar | limitBy 5')">
        {{ item.foo }}
    </li>
</ul>

Or perhaps, if there would be a hook which would allow plugins to parse expressions, and process them however they want, that could be a possible way too, but that solely depends on how complex it is to implement, and what effects it would have on the framework (performance / size), and if @yyx990803 would actually want to go in this direction or not!

I feel like, if we are given elegant alternatives, that are easy to use (Plugins or something in the core), we will get used to it fairly quickly.

@rigor789 for filtering arrays and sort it, etc, better use computed, it's much clean. But i think problem to display data in diff format:

if we have field created_at and need in template render it as dd.mm.YYYY or in links

<a href="dd">dd</a>.<a href="mm">mm</a>.<a href="YYYY">YYYY</a>

for this filters used good now, what alternative we can get from 2.x?

For now only use

 {{ date(created_at, 'dd') }}

and define date method, but it's make template dirty, if store additional 3 fields it cost memory, and if it 100-200 objects it became heavy.

Here's the final decision:

  1. Filters will be supported, but only inside text interpolations. This limits them to text formatting purposes while enforcing other logic into JavaScript land.
  2. Vue 2.0 will ship with no built-in filters. The community can create their own filter packs if needed.
  3. The filter syntax will change to use function invocation syntax for arguments instead of space-delimitered. This brings it more inline with JavaScript and other popular templating languages (Jinja2, swig, twig...):

html {{ date | formatDate('YY-MM-DD') }}

Thanks for listening Evan. That's what makes Vue great. It has a great leader. :smile:

Scott

@yyx990803 & @chrisvfritz

One thing that seems to have been buried in this discussion is two-way filters. Two-way filters are the ones that are the hardest to replicate with other proposed solutions.

  1. Consider this simple example, which I've posted in that forum thread. Using computed properties for this would have 2 main drawbacks:

    1. You'll have to create a new computed property for each value, resulting in a ton of boilerplate code.

    2. To make matters worse, computed properties can't even be nested. If you have a more complex object graph, all your computed properties will have to be at the top level. You'd end up with long, verbose property names requiring more cognitive overload to constantly keep track of which computed property tracks which nested value.

Dismissing this use-case as "trivial" or "less complicated" is counter-productive. I've used this on both medium- and large-scale applications. It's worked wonders! The demo is necessarily simple, but the technique is sound.

  1. This is further confounded when dealing with an array in v-for, such as in this example (again, intentionally simple). Iterating over an array you'll find no recourse in computed properties. The only way to work around this is by having a separate component for each item in the array, adding even more boilerplate.

Compare this example with filters:

``` js
import currency from 'some-currency-filter';
import products from 'products-stub';

new Vue({
el: document.body,
data: { products },
filters: { currency },
});
```

To one without filters:

``` js
import currency from 'some-currency-filter';
import products from 'products-stub';

new Vue({
el: document.body,
data: { products },
components: {
product: {
props: ['product'],
computed: {
price: {
get() {
return currency.read(this.product.price);
},
set(value) {
this.product.price = currency.write(value)
}
},
shipping: {
get() {
return currency.read(this.product.shipping);
},
set(value) {
this.product.shipping = currency.write(value)
}
},
handling: {
get() {
return currency.read(this.product.handling);
},
set(value) {
this.product.handling = currency.write(value)
}
},
discount: {
get() {
return currency.read(this.product.discount);
},
set(value) {
this.product.discount = currency.write(value)
}
}
}
}
}
});
```

I know which of the above I'd want to write.

(this could maybe be cleaned up a little by creating computed property factories, but the point remains that these are far more verbose than need be)


Since the core team seems steadfastly opposed to the filter pipe syntax, would it be possible to introduce a filter param for the v-model directive?

With a filter param we'll be able to keep the first elegant version of the JS above, and simply rewrite the template to use the param instead of the magic pipe syntax:

<tr v-for="product in products">
  <td><input type="text" filter="currency" v-model="product.price"></td>
  <td><input type="text" filter="currency" v-model="product.shipping"></td>
  <td><input type="text" filter="currency" v-model="product.handling"></td>
  <td><input type="text" filter="currency" v-model="product.discount"></td>
</tr>

This would strike the right balance of not having magic syntax while retaining the ability to easily convert input back-and-forth without much boilerplate.

Thanks for your consideration.

I've rewritten your example to incorporate your filter as a custom directive: jsfiddle.
Although not as concise as a filter, it isn't nearly as painful as I imagined it to be. And it hasn't taken me much time to put the pieces together out of stuff that's already available to all of us.

@Nirazul creating dedicated directives for each filter has been proposed before. Needing to create a new directive that re-implements all of the v-model functionality for each type of filter you use in your app is simply preposterous.

In addition, your directive doesn't even fully work like filters; blurring the field after changing the value doesn't reformat it. Furthermore, v-model does way more than you do in that directive.

The directive can obviously be amended with more features and fixes, but the point remains: these are all band-aids on top of something that should be really simple right out-of-the-box.

I haven't seen any proposition in this thread mentioning custom directives. Would you please point me to this comment?

As you can imagine, I haven't written and tested it for you to use in your app. I just wanted to show, what's possible right now as an alternative. If this is possible so easily, maybe there's room for a plugin to automate the process even more.
It is obvious, that this quickly written directive lacks the full functionality of v-model. Maybe one custom filter directive combined with params and/or modifiers would be sufficient to replace filters.

Let's remain constructive, as I've tried it to be to solve your problem. It's always easier to bash on a solution instead of making a contribution and moving forward.

@JosephSilber you need to chill out. "preposterous" is a strong word.

Hi, Is removing filter motivated by reducing template cluttering or is it seriously affecting
the technical implementation of 2.0? If it is former visual effect issue, how about allowing
filter like now (but with the new javascript form)
but limiting it only for one per expression, like v-for="i of list | orderBy('key')", (no more than one pipe sign)
this could make filter processing in 2.0 simpler and predictable and visually least cluttered.
Since there will be filter parsing for {{}}, allowing it in other expressions would make sense.
this would accommodate all existing use cases/functions (like two way filtering, item within v-for).
Any existing usages of more than one filter could be easily merged/upgraded into one by owners.

@tomsmithgroup What you've suggested (and more than that) have been decided to remain supported in 2.0.

@yyx990803 & @chrisvfritz would love to hear your thoughts regarding two-way filters.

@JosephSilber So with the computed property factory you alluded to, there's not even much boilerplate:

import currenciesFactory from 'some-computed-currencies-factory'
import products from 'products-stub'

new Vue({
  el: 'body',
  data: { products },
  components: {
    product: {
      props: ['product'],
      computed: currenciesFactory(['price', 'shipping', 'handling', 'discount'])
    }
  }
})

Though personally, I wouldn't create a factory for this example, because each of these properties are actually separate concerns that only _appear_ the same right now. When you remove filters, it becomes much more obvious how complex that one component was. That's what you're seeing as boilerplate.

And this is a huge reason why components _exist_. To divide complexity into manageable concerns. When I look at a component, everything it's doing needs to easily fit into my working memory or else development becomes much slower and more error-prone. Hiding complexity behind the syntax of filters doesn't remove that complexity.

To demonstrate this, let's look at what happens when the problem meets real-world changing requirements: Say you decide the discount should not be allowed to be larger than the price + shipping + handling. Or the handling should not exceed X amount. Or the discount field should be a percentage, instead of a currency. Or if the total exceeds a certain amount, a minimum discount should automatically be applied.

You may be tempted to make the filter more complex with extra options and conditional logic. As you update the filter's API and internal logic, you now have to think about how your changes might affect anything else consuming the filter - possibly even outside the current component, if this is a shared filter. So now you're in a situation where making changes to your app starts to feel daunting. And it's all because a shared, two-filter has allowed you to delegate internal component state to outside the component, violating component boundaries, increasing coupling, and hiding away complexity that you still need to think about every time you work on the component.

By breaking things down into multiple, individually less complex components, with separately defined computed properties or bound props, you often update one line to adjust to a changing requirement. No extra options or conditionals. No refactoring to satisfy scaling complexity. You don't have to think about anything outside the component _or even outside that single property_. Your application was from the beginning and still remains easy to think about and update.

I think what makes filters initially seductive in this case, is they're an easy-to-reach-for cure for duplication. But duplication is not boilerplate when it's incidental (i.e. code just happens to be the same right now, before the real-world problems inevitably start rolling in).

And I can't put it better than Sandi Metz:

duplication is far cheaper than the wrong abstraction

@chrisvfritz Filters have absolutely nothing to do with business requirements. Filters just format the data. That's it. They don't manipulate it in any way, shape or form. filter="currency" is conceptually the same as the number param. Business requirements should obviously never be handled in the filters.

Say you decide the discount should not be allowed to be larger than the price + shipping + handling. Or the handling should not exceed X amount.

Then you would use some proper validation module, à la vue-validator. The currency filter will stay in place regardless, simply displaying the underlying value.

the discount field should be a percentage, instead of a currency.

Then obviously its type is no longer currency. No different than if you swap out a type="text" for type="email" when the username requirements change. Or even when you remove the number param when a field is no longer a number.

if the total exceeds a certain amount, a minimum discount should automatically be applied.

Again, the minimum will be applied, but the currency filter will stay in place. You'd still want to display it as currency.

As you update the filter's API and internal logic, you now have to think about how your changes might affect anything else consuming the filter [...] two-filter has allowed you to delegate internal component state to outside the component

No. You _never_ put any logic or state into the filter itself. The filter is an idempotent function strictly for formatting values. Ideally, it shouldn't even be executed in the context of the VM; it should have no this from which to pull additional data.

duplication is far cheaper than the wrong abstraction

...when, as you said, you're abstracting code that just happens to be the same right now. Filters, on the other hand, are simple idempotent functions that format a value. They're not an abstraction at all.


tl;dr filters are not an abstraction, and they're not in place of explicit business requirement. Filters never have any internal logic, and never should. They're conceptually similar to the HTML's type attribute and v-model's number param.

Two way filter is certainly an abstraction. The current behavior of two-way filters on v-model hides a lot of magic inside - specifically it allows the input's DOM value to be temporarily out-of-sync with the underlying model, and only "syncs" them when you blur the field. BTW @JosephSilber this behavior was implemented specifically due to your request, so I can understand why you feel so strongly about it.

However, the biggest problem I see with two-way filters is that the underlying logic is a black box, and the user has no way to further customize it other than making feature requests. To me, the better option is providing the proper building blocks for implementing such behavior yourself. And let me show you how that is possible in 2.0:

https://jsfiddle.net/yyx990803/5bnu6xb6/

Here we have a base CustomInput component that implements the "out-of-sync-until-blur" behavior of current two-way filters. Due to the changes in 2.0, you can directly use v-model on custom components as long as the component accepts a value prop and $emits input events. In addition, it also formats the value on change events, which is currently not possible with two-way filters. You can further tweak this component to get any desired behavior you want without being tied to how two-way filters are implemented by the framework.

The base component doesn't implement the parse/formatting logic - you extend it with the parse and format methods to get custom input components tailored for specific use cases. In this case, a CurrencyInput component.

The base component is more complex than two-way filters, because we are now implementing the previous magic behavior ourselves. However you only need to define this component once. In return you get full control over how it should behave in case you want to change it later. I believe this is a worthy tradeoff.

This is an example of how we should think more in components because it is a unified abstraction for reusability that gives you the most control. Also see the 2.0 select2 example - previously in 1.0 this was implemented as a directive, but now it's a component with the same v-model interface.

To me, the better option is providing the proper building blocks for implementing such behavior yourself.

This is great and most definitely what Vue needs, however I would have liked to have read this

To me, the better option is providing the proper building blocks for starting with Vue and to also extend Vue with new filtering behavior yourself.

In other words, couldn't the currency input component along with a number of other default filtering components be a part of Vue? Because, everyone uses currencies at some point, right? Why leave it up to everyone else to do the same work again and again? Currency formatting is standard enough to make it part of Vue's own filtering component tool bag and they are a great basic example for others to learn to create their own filtering components from too.

And.....there is only one thing missing from the example to be a finished currency input component and that is an i18n feature, which I believe Vue should also entail. The currency number formatting should change depending on the person's (the viewer's) selected Locale. I mean locale data i.e . "DE_de" to format the currency and other numbers as shown in this Locale Explorer (which stems from a standard). Add that into Vue and you have a great currency / number formatting component system built right into Vue, that the 1000's of other devs who use Vue don't need to create themselves, but rather can use right on the spot, when they get Vue. This makes Vue a powerful templating system!

If using this "i18n formatting component" can't be used in a chainable fashion as a filter like in 1.0, it should be pluginable. That would be fine too. That is what Aurelia is doing and it is what Twig does too with its extensions . The currency input component could still be <currency-input>.

This i18n and other standard templating filters just really need to be something others don't need to constantly reproduce, if they don't have to. If you show me how to make such a plug-in, I'd even do the i18n plug-in for you. I am that interested in this as whole!:smile:

Scott

Because, everyone uses currencies at some point, right?

Not really, no. I, for one, have never really used it :) The same would apply to all other built-in filters – some of them are useful to some of us, when the others are just useless. While we're on this topic, most of the times I wanted to use a built-in filter, I ended up writing my own version anyway, rendering the built-in version dead code.

an i18n feature, which I believe Vue should also entail.

This is, again, very debatable. A comprehensive i18n support can be pretty complex, and IMO doesn't belong in Vue, especially when most of the users won't need it anyway.

Now, I would like to raise this: every time we attempt to make a comparison between Vue and another template/framework, there's one critical thing to keep in mind: Unlike Django/Laravel/Twig, Vue is essentially a client library, and thus needs to be as lightweight as possible. It would make more sense to keep it simple enough – while retaining all the _core_ features of course – instead of adding "features" and "utils" just for the sake of so-called "usefulness" or "elegance." If you want anything other than core functions, the more preferred way would be to use a plugin or even develop your own. Take this as an example: For a commonly seen SPA, is i18n/currency more vital than a router or a state manager? I don't think so. And yet, vue-router and vuex don't belong in Vue, but have their own repositories. In other words, Vue is progressive.

If you show me how to make such a plug-in, I'd even do the i18n plug-in for you. I am that interested in this as whole!

I believe this is a more sensible approach, and thank you for your interest :)

if much will need to use i18n in some areas)) I think I can go into the code and manually change a few names of some things, for beauty.
For example: just a few hours ago, it took me butstrap.datepikker - I do not connect, i18n despite the fact that there is such a possibility. Quick 60 seconds. "Manually" replaced the names of the days of the week and months, and all - ok! ))

@phanan - Your argumentation is why I mentioned "plugins". I agree, not everyone will want the currency or i18n functionality and those that don't can keep their app light, but as a templating system, I believe (and this is very much my personal opinion) Vue should also have such standard functionality available too. More importantly, and my concern, is it shouldn't be that everyone has to reinvent the i18n or currency filter component wheels. In other words, just like Vue-Router and Vuex are available additions to Vue, a good number of standard filters like currency and i18n could be too.

Oh, and interestingly enough, Angular has i18n built in it seems.

https://docs.angularjs.org/guide/i18n

Angular supports i18n/l10n for date, number and currency filters.

Ember and React seem to have gone the "let the dev world do it on their own" route.
Interesting video.
https://www.youtube.com/watch?v=Sla-DkvmIHY

Maybe Vue only needs an integration to FormatJS? VueIntl? :smile:

Edit: It can't be that simple, can it? https://github.com/learningequality/vue-intl/blob/master/index.js

Scott

Angular has i18n built in it seems.

Angular also comes with ng-router which, from what i have heard, is shunned by ng-community in favor of ui-router.

Now, if Angular team replaces ng-router with ui-router, it will result into breaking changes and pain.

if they try to improve ng-router, they are wasting their time as better solution(ui-router) already exists.

If Vue started supporting i18n, currency etc in its core, then same situation will happen to vue as well.

I do find the in-built filters convenient, but in this matter I agree with vuejs team..

Then we all agree on the filters not being in Vue core. I am also a fan of componentization actually. But, couldn't Vue offer the filters as core plugins too? Like Vuex and Vue-Router?

Scott

But, couldn't Vue offer the filters as core plugins too? Like Vuex and Vue-Router?

That's exactly what @blake-newman shared earlier.

Hi: the final decision page indicated only allowing filter for text
display {{}} (v-text)
not for other expressions like v-for="item of list | sortBy"
Or did I misunderstand ?
What I meant was keep having filter as in 1.0 but limiting only one filter
per expression

On Sun, May 1, 2016 at 10:24 PM, Phan An [email protected] wrote:

@tomsmithgroup https://github.com/tomsmithgroup What you've suggested
(and more than that) have been decided
https://github.com/vuejs/vue/issues/2756#issuecomment-215868244 to
remain supported in 2.0.


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
https://github.com/vuejs/vue/issues/2756#issuecomment-216093937

@yyx990803 May I conclude from your reply that two-way filters are not going to be supported in 2.0?

@fnlctrl that's correct. Just build your own components.

I'll go a little off-topic: are those two-way filters really that useful in production?
I mean, is it really okay to let user type anything into an input then reformat it after it's blurred?
My business team always demands our inputs to act like this: you just can't type anything wrong. So even now with Vue we use jquery.inputmask for that purpose since instant two-way change got deprecated (that one I really miss...)
I mean, is there really a problem with those two-way filters or people just overthinking it? :)

@fullfs Two-way filters are extensively used in our production app (lots of <input>s) for convenience, but as I see it now, why filter the values real-time instead of just filter them once before they are POST-ed to the server? It'll take some time to refactor the code, but I think I won't miss them :D

If you want optimistic updating of the UI (which is the norm today IMHO, especially for mobile), you'll want the input formatted properly practically at the time the input was given.

Scott

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.

@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: https://github.com/vuejs/vue/issues/2873

Scott

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

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.

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.

@ecmel agreed. This is the way to go.

Open a feature request issue then :)

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

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

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!

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>

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.

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

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?

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

Scott

Computed vs. filters

why not both

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

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

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.

Was this page helpful?
0 / 5 - 0 ratings