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

1.0 binding syntax (discussion thread) #1308

Closed
yyx990803 opened this issue Sep 13, 2015 · 137 comments
Closed

1.0 binding syntax (discussion thread) #1308

yyx990803 opened this issue Sep 13, 2015 · 137 comments

Comments

@yyx990803
Copy link
Member

Note: This post may contain outdated content. Latest reference is now maintained at #1325


Here's yet another long post on the new binding syntax. I know we have gone through a lot of changes during the alpha phase, but please, bear with me - I want to make sure we land on something that is good enough to be called a 1.0 - something that is consistent, explicit, and most importantly, stable. We are going to strictly follow semver post 1.0 - I don't want to have to release 2.0 in a few months ;)

I've been thinking about all the discussion on consistent v-, and I feel that the new syntax may indeed be a bit too different from what we have in 0.12. Some of the justifications about the additional prefixes was based on the fact that Angular 2 and Aurelia are both introducing custom syntax for bindings; but then I realize Vue is not Angular or Aurelia, because these two are full-stack frameworks, where Vue aims to be flexible in as many use cases as possible.

So, let's take a step back and think about the original problems that we set out to solve with the new syntax, and evaluate what we have so far:

  1. Confusion about expressions vs. literals for directives

    Example:

    <div v-text="msg"></div>
    <div v-link="/a/b/c"></div>

    Here msg is an expression but a/b/c is a literal string. There's no way to tell which should be literal except looking at the directive's documentation.

    Current solution in alpha.4:

    Explicit syntax for literal directives: v-link#="123". This also simplifies implementation of custom directives. I think this is a good change.

  2. Confusion about where mustaches are allowed.

    Example:

    <!-- these works... -->
    <a v-link="/a/b/{{abc}}"></a>
    <a href="/a/b/{{abc}}"></a>
    
    <!-- these don't -->
    <a v-model="{{abc}}">
    <a v-attr="href: 'a/b/{{abc}}'">

    The general rule is that mustaches are only allowed in literal strings but not in expressions. But mustaches just make people think in the string template mindset and assume it should work everywhere.

    In addition, mustaches inside attributes have some subtle gotchas, for example when used in src it causes a 404 error; when used in style it causes IE to ignore it.

    Current solution in alpha.4:

    No more mustaches inside attribute values. Attribute bindings use the bind- special prefix.

    As someone pointed out, this introduces an additional prefix and isn't as distinctive and consistent as v-. But v-attr also has its own warts, mostly related to the "arguments + multiple clauses" micro-syntax. We will discuss this in more details below.

  3. Directive micro-syntax

    Example:

    <div v-on="click: doThis, keyup: doThat"></div>
    <div v-attr="href: address, src: imgSrc"></div>

    The problem here is that it looks like an object, but it is not; it also becomes awkward to format (different indentation, no syntax highlight) when you have a lot of handlers/attributes:

    <div
      other-attribute-a
      other-attribute-b
      v-on="
        click: doThis,
        keyup: doThat | key 'esc',
        keyup: doSomethingElse | key 'enter'">

    Current Solution in alpha.4:

    <div
      bind-href="address"
      bind-src="imgSrc"
      on-click="doThis"
      on-keyup-esc="doThat"
      on-keyup-enter="doSomethingElse">
    </div>

    I think breaking each binding into a single attribute is definitely the right move. But we also introduced yet another prefix: on-.

  4. Not-so-good prop syntax.

    Example:

    <component
      literal="string"
      dynamic="{{something}}"
      two-way="{{@something}}"
      one-time="{{*something}}">
    </component>

    First, this looks like normal attribute bindings. Second, as we noted above, mustache bindings indicates that it evaluates into a literal string. However, the 0.12 prop syntax is ambiguous in this aspect:

    <component prop="{{ anObject }}">

    ^ This passes down the actual object.

    <component prop="abc-{{ anObject }}">

    ^ This will try to concatenate "abc-" and the object, resulting in "abc-[object Object]".

    The binding indicator can also be confusing:

    <!-- what does this do? -->
    <component prop="abc-{{@something}}">

    Current Solution in alpha.4:

    <component
      literal="string"
      bind-dynamic="something"
      bind-two-way@="something"
      bind-one-time*="something">

    By getting rid of mustaches and move the binding type indicator into the attribute name, I think we've solved most of the issues above, but it relies on the extra bind- prefix.


Recap

I think with the current 1.0.0-alpha.4 syntax we've addressed most of the original issues, but also introduced some new ones. Most of the negative feedback is concerned with the fact that we now have three prefixes instead of one: v-, on- and bind-. Do event handlers and attribute bindings really deserive their own special prefix?

My original intention was that on- and bind- would allow us to get rid of the "micro-syntax" issue mentioned above. But I also agree that more top-level prefixes also introduce additional cognitive overhead. So here's an update that attempts to address this issue:

<!-- 0.12 directive syntax -->
<div v-dirname="arg1: expression1, arg2: expression2"></div>

<!-- 1.0 directive syntax -->
<div
  v-dirname:arg1="expression1"
  v-dirname:arg2="expression2">
<div>

So, instead of what we currently have in 1.0.0-alpha.4:

<div
  bind-href="address"
  bind-src="imgSrc"
  on-click="doThis"
  on-keyup-esc="doThat">
</div>

We would write:

<div
  v-bind:href="address"
  v-bind:src="imgSrc"
  v-on:click="doThis"
  v-on:keyup:esc="doThat">
</div>

A little more verbose, but more consistent, more Vue-specific, and maps to 0.12 concepts better.

When we are using Vue alone to build an SPA, the v- prefix may not be that important anymore. So it is still possible to provide optional, more concise sugar on top of v-bind and v-on:

<!-- : for bind -->
<div
  :href="address"
  :src="imgSrc">
</div>

<!-- @ for on -->
<input
  @click="doThis"
  @keyup:esc="doThat">

You probably would prefer the shorthand for component props as well (changing two-way indicator to & instead of @):

<component
  :prop="something"
  :two-way&="something"
  :one-time*="something">
</component>
@nervgh
Copy link

nervgh commented Sep 13, 2015

@yyx990803 , thanks for answer =)

Directive syntax

https://github.com/yyx990803/vue/issues/1173#issuecomment-137326936 👍
https://github.com/yyx990803/vue/issues/1173#issuecomment-138063652 👍
best for me 😏

Component syntax

@yjeroen

What about changing the literal/dynamic/two-way/one-time towards the vue javascript instead of the template?
https://github.com/yyx990803/vue/issues/1173#issuecomment-139917897

I have suggested this option. It pretty good for me 😋

@ghost
Copy link

ghost commented Sep 13, 2015

Looks good! I like the possibility to have both the v- prefix and the shorthand option. I also like the : and ^ being a prefix and not an affix.

Maybe we can have a consistency between the full notation and the shorthand? Example:

<div
  v-bind:href="address"
  v-bind:src="imgSrc"
  v-on^click="doThis"
  v-on^keyup:esc="doThat">
</div>

I'm not sure if it's an improvement. Maybe it makes it harder. What do you think?

Another (maybe stupid) question about expressions & literals; can we use quotes for literals, and leave the quotes for expressions? Something like;

<div v-text=msg></div>
<div v-link="/a/b/c"></div>

@acasar
Copy link

acasar commented Sep 13, 2015

I really like the latest proposal. It's clear that there are two major groups of Vue users - some of us are mostly back-end developers and are using Vue as a front-end library (and we really wish to keep the consistent v- syntax) and then there are others developing full-stack SPA apps, who would prefer shorter more and concise syntax. And the latest proposal caters very well to both groups.

I can't speak for everyone, but I came to Vue because I really disliked how verbose all of those other front-end frameworks got (Angular 2 is a good example) by introducing a ton of new syntax, which can be very confusing for a back-ender like me. And a simple rule like v-dirname:argument="expression" would mean a lot and it would make templates very clear and readable.

@nervgh
Copy link

nervgh commented Sep 13, 2015

I am new Vue user, but I use Angular almost 3 year. One of my public angular modules placed here (sorry for ad). I can create a long list of what I like in Vue 😊

@yyx990803
Copy link
Member Author

@dennisver : in the full syntax denotes arguments, so it's best to keep it consistent. Non-quoted attribute values only work when the value doesn't contain spaces, which is impractical.

@ghost
Copy link

ghost commented Sep 14, 2015

@yyx990803 I agree, thanks! When I tried Vue for the first time, one of the first things that were not clear to me, was whether I should use quotes or not. I'm coming from Ember, haven't really tried Angular and I've always associated quotes with literal strings. Could you also explain what the downside is of using mustaches for expressions?

@yyx990803
Copy link
Member Author

@dennisver In Ember, all templates are parsed as strings, so it can actually be non-valid HTML - e.g. attr={{ a + 1 }} would work; but in Vue, you can use the real DOM as your template, so all Vue templates must also be valid HTML. In real HTML, you can write attribute without quotes, but any space encountered when parsing non-quoted attribute value will be treated as the end of that value - so attr={{ a }} would actually be parsed as attr="{{" a="" }}="".

So in short, always quote your attribtues in Vue.

@thelinuxlich
Copy link

I like the new proposal but I prefer @ for events instead of ^, although it clashes with two-way syntax. The literal,dynamic,two-way,one-time shortcuts, I think they should be enforced on the prop definition instead, dynamic props would be the standard, then literal could reuse the "#" shortcut and two-way and one-time on the component prop "schema"

@yyx990803
Copy link
Member Author

@thelinuxlich yeah, I like @ better too, especially after trying it in some real templates. It's much easier to spot. I also think it might make more sense to use &= for two-way bindings.

Dynamic props by default would imo be confusing because there's nothing that differentiate them from normal attributes (directives have v- prefix); also - I think it's beneficial to be able to tell whether a child component can mutate parent state without having to look at its implementation details (which is what the binding type indicator does)

@thelinuxlich
Copy link

Then we should have another symbol for two-way binding to avoid misunderstanding, maybe like Ember "mut var"

@nervgh
Copy link

nervgh commented Sep 14, 2015

... @ ... clashes with two-way syntax
https://github.com/yyx990803/vue/issues/1308#issuecomment-139944633

yeap

The literal,dynamic,two-way,one-time shortcuts, I think they should be enforced on the prop definition instead
https://github.com/yyx990803/vue/issues/1308#issuecomment-139944633

+1. ie in javascript. I think about component like about function:

// definition
/**
 * @param {Number} a
 * @param {Object} b
 * @param {Array} c
 */
function foo(a, b, c) {
    // some code here
}

// using
foo(1, {}, []);

You need know how it work when you deal with it.

@nkovacs
Copy link

nkovacs commented Sep 14, 2015

That's not a correct analogy. It's more like this:

foo('literal')
var a = 'dynamic'
foo(a)

@karevn
Copy link

karevn commented Sep 14, 2015

This topic made me sleepless... So, I came to the next.

Problem

First off, what are main goals of all these changes? I see two:

  1. Make learning curve as low as possible. Avoid confusions.
  2. Make the syntax as flexible as possible.

And I'm Captain Obvious, yep. And, in my opinion, next things are confusing now:

  1. Three types of directives: literal, non-literal, terminate. I can't find good reasons why, but it just feels wrong for me. It's too complicated to work well, and a good design is always dead simple. Now, I see lots of questions at gitter and Github issues regarding v-show vs v-if and don't always see v-if or v-for in my own code.
  2. Several types of binding expressions: bind-whatever, :whatever, whatever* , whatever@ on-whatever. And we should remember about them, so they can be combined (or not?) Should it be v-for or v-for@? Is v-for@ acceptable at all?

Proposal

Step 1. Let's replace terminal directives with control-flow tags:

v-if and v-repeat never felt good for me, because I just don't see them in my code most of the time - they don't create an indentation, whatsoever to stand out.
Vue also has <component> and <slot> tags. So we can make one step further and extract control flow directives into separate tags:

<v-if condition="{something}">
  <!-- conditional code here -->
</v-if>
<v-unless condition="{some condition}">
  <!-- conditional code here -->
</v-else>
<!-- there will be no v-else, as one can put something between <v-if> closing tag and <v-else> --> 

<v-for collection="{arrayVariable}" item="currentItem">
  <!-- each collection item will be exposed as a local variable currentItem -->
  <!-- repeated part here -->
</v-for>

It's much more readable IMO, and should feel more intuitive for people who are familiar with any template engine.

Step 2. Make directive expressions uniform.

Now we have "literal" and "non-literal" directives. Let's get rid of the term "directive" for now and call them just "expressions". We need 3 types of expressions:

  • Direct value assignment. Simple attribute="value" should work fine. value is a literal string, number, or boolean.
  • Assign an evaluated expression. attribute="{expression} | filter" should work fine too. In this case, an "expression" will be a free-form javascript expression evaluated in Vue instance's context, or "property path"- like this.child.grandChild.property which is a valid JS expression (and this. can be optional). Or, it can be this.child['grand-child'].property, which is a valid JS expression too. In last two cases, expressions are assignable by their nature, which gives us item 3. For one-time assignments we can use attribute*="{expression}" or attribute="*{expression}". First syntax looks better to me, because it follows the concept "everything in double quotes is an expression".
  • Two-way assignments when the expression is writable: attribute@="{expression} | twoWayFilter" To avoid any confusion when working with what was v-model directive, we can use the next syntax to it:
    <input v-bind@="{user.email} | sanitizeEmail">, which can be evaluated two-way in current Vue instance's context.
  • Event handling. No new ideas, v-on-click, etc sounds good. For consistency, it should be an expression too: <button v-on-click="{clickHandler}"> will take current instance's clickHandler property and bind it to the event.

Possible problems and solution

I see some room for errors:

<!-- String instead of the function reference -->
<button v-on-click="clickHandler">

<!-- Readonly expression for binding -->
<input v-bind@="{'mr.' + user.name}">

<!-- Binding to literal value. "user.name" -->
<input v-bind@="user.name">

But, these errors can all be caught automatically at the directive code and appropriate warnings can be displayed.

Example

Let's doodle a little bit and build a shopping cart sketch:

<script>
module.exports =
  data: function () {
    items: [
      {name: 'iPhone 6s', quantity: 1, price: 299},
    ],
    /* validation errors object, name: value format */
    validationErrors: {},
    checkoutForm: {
      name: '',
      email: '',
      /* so on */

    }
  },
  methods: {
    validateForm: function () {/* some validation code here */}
  },
  filters: {
    nameFormatter: {
      read: function(value) {return value;}
      write: function(Value) {return value;}
    }
  }
</script> 

<template>
<ul class="cart-items">
    <!-- collection binding is two-way, because we can click at "delete item" link at the list and the list will be modified -->
  <v-for collection@="{items}" item="product">
   <!-- at this point a "product" local variable is defined and we pass it to the <product> -->component
    <cart-item item@="{product}"></cart-item>
  </v-for>
</ul>
<form id="checkout" v-on-submit="{validateForm}">
<label>Name</label><input type="text" v-bind="{checkoutForm.name} | nameFormatter" />
<label>Email</label><input type="email" v-bind="{checkoutForm.name} | nameFormatter" />
<!-- so on -->
<v-if condition="{validationErrors.length > 0}" transition="fade">
    <div class="validation-errors">
    <v-for object@="{validationErrors}" item="error" key="field">
        <div class="error">
            <!-- the only thing that worries me is that these mustache bindings may be confused with assignment syntax -->
            {{field}}:{{error}}
        </div>
    </v-for>
    </div>
</v-if>
<v-pre>
Some freeform markup that will not be compiled
</v-pre>
</form>


</template>

@yyx990803
Copy link
Member Author

@karevn I appreciate your ideas, but:

  1. The concept of "terminal" directive is not really exposed to the user. To a normal user there's only reactive vs. literal. And every directive is reactive by default. That seems simple enough to me. Plus, you can already do it like this everywhere if you prefer:

    <template v-if="condition">
      <div>123</div>
    </template>
  2. Binding type is a concept specifically designed for component props - component is a first class concept and props is the mechanism for data flow between them. Components and directives are different. Mixing the binding type indicator with normal directives doesn't sound like a good idea to me.

  3. Using curlies to indicate expressions sounds unnecessary when we have already clearly defined where expressions may appear. (only in v- attributes or the shorthand notations)

Finally, please note that we are trying to "fix issues with as few changes as possible", which is why I toned down the original proposal to this one. We want 1.0 to be a natural progression of 0.12, and there should be a clear upgrade path. We are not trying to "reinvent the syntax" (which is a mistake I made with the original proposal).

yyx990803 added a commit that referenced this issue Sep 14, 2015
@karevn
Copy link

karevn commented Sep 14, 2015

@yyx990803 From tech standpoint, v-if works more or less fine. But from conceptual and readability standpoints - it just feels illogical, because in most languages if statements are container control blocks, not attributes.

  1. Why? I offered a straightforward syntax that is universal for native HTML tags and components. It's closer to what other contemporary frameworks (Polymer and React) are doing, and with lower learning curve than existing solution.
  2. In my examples, curlies are used to tell expressions VS literal values. I don't particularly like curlies (because they may be confused with mustache bindings), so if there will be any more elegant option - why not?

Basically, my thougts are: 1.0-alpha breaks ALL the existing code that relies on Vue. So, if rewriting all the dependent code is inevitable already - it's a very good time to polish the syntax not to revisit it once again in future. I don't have any experience with Angular (came from Backbone directly), but when I took a look at it the first time - the impression was "what the hell is going on with the syntax"? While Polymer and React's syntax look much more elegant and "natural". Taking in account Angular hype is over, and React / Polymer are taking over - may be it's the sign not everything was good with the syntax and Vue should learn this lesson too?

@acasar
Copy link

acasar commented Sep 14, 2015

@karevn I would argue that with the latest proposal, the migration process from 0.12 to 1.0 has been drastically simplified. And it's certainly not true that "it breaks all the existing code". Some changes in the view layer will be required for sure, but very few in the actual logic itself. Furthermore, the current syntax has proven to be very popular with a lot of developers due to its simplicity and clarity. And judging by myself, the learning curve is already very low compared to some other frameworks like React or Angular2 which I was never able to fully comprehend.

@yyx990803
Copy link
Member Author

@karevn

  1. The <template v-if> syntax is pretty much the same with what you proposed. Polymer offers almost the same syntax in terms of if and repeat logic, except it requires you to use a wrapper <template> element, whereas in Vue it is optional and you can use it directly on the element. In React/JSX it's a JavaScript Array.map(fn) expression, which is a completely different construct. I don't see how your proposal is "closer to what others are doing", and I don't see it offering any substantial improvement other than catering to your personal preference.
  2. You are more or less trying to force Polymer's "expressions are always in mustaches" mindset on to Vue, where in Vue the basic rule is "expressions are always in v- prefixed attributes". Which one is better? It's subjective, and I don't see a convincing reason to suddenly jump ship.
  3. 1.0-alpha doesn't break anything. You can seamlessly upgrade your app by just following the deprecation warnings. And it would be ideal if the process can be simpler for people.

@karevn
Copy link

karevn commented Sep 14, 2015

@yyx990803 I think you misunderstood me. My proposal's key points are:

  1. Move "control logic" to custom tags to improve readability. It's simple to oversee v-for and v-if currently. As an implication, I think Vue core code will become cleaner after this change .
  2. Yes, that is what I'm thinking (minus specific symbol choice - I would be equally happy with () or [] or whatever works best). And I really think it's better than "v-anything", because that is how most of other systems work (Polymer, React, and even Windows Presentation Foundation), so it's just more "natural". Why reinvent the wheel if it works well for other frameworks? And I also think Vue internal directive code will be more straightforward in this case (not 100% sure).
  3. @acasar Let me describe how it worked for me. I switched from 0.12.13 to 1.0.0.alpha3 a few days ago. Webpack re-compiled everything. I refreshed the page and have seen about a hundred of warnings with no references to line numbers in my code (because Vue warnings show stacktraces to Vue.js code). Also, there were JS errors, again in Vue core code (caused by syntax incompatibility). I spent whole two days replacing the syntax and re-debugging the code. So even if it's simple syntax change - it forces you to review all the templates. Or use regex-based global replacement and pray your regex was good enough. And then not forget to replace v-repeat with v-for. And then not forget to explicitly declare variables when you use v-for. In other words - even if it is not the end of the world, its a decent work. Simple, yet manual mostly.

@acasar you were totally right - React's syntax is a nightmare sometimes. But I am offering to steal it's best properties (simple attr binding syntax), not worst ones like mixing HTML and JS syntax.

@yyx990803
Copy link
Member Author

@karevn

  1. As I said, you can already do that by always using <template v-if> everywhere. It's almost exactly the same with Polymer.
  2. As I said, it's subjective - It's more "natural" to you doesn't necessarily mean it's more natural to me. I personally find curlies or expression wrapper symbols noisy and annoying to type, and make Vue templates look more like string templates when they are in fact HTML with attribute annotations. "That's how most other systems work" is not a valid argument, and I'd argue that Polymer is far, far away from "taking over".
  3. The in-progress versions are unstable, so it definitely doesn't represent the best experience possible. The final 1.0 will ship together with a 1.0-migration version which focuses on ensuring a smooth upgrade experience. With the latest proposal, that experience has already improved; it wouldn't make sense to make that experience worse by introducing even bigger syntax changes with arguable improvements.

I want to make it clear that the syntax change is not because "the current syntax sucks": in fact, it's been working well for many of us. What we are trying to do is "fix the small issues within the current syntax", not "let's just move to something completely new". That sounds more like a goal for maybe Vue 2.0.

@acasar
Copy link

acasar commented Sep 14, 2015

@karevn Regarding the upgrade experience - we upgraded a medium sized app to 1.0.alpha1 and it took a few hours. I can't say it went completely smoothly because there were some frustrations along the way. But overall not anything drastic. We still have a bigger app waiting for the upgrade, but I'm going to hold off until 1.0 is finished.

With your proposals I'm afraid that the upgrade process would be even harder. Also, I'm personally not a big fan of the syntax you proposed, but I do acknowledge that we come from different backgrounds, so it probably seems a lot more natural to you than it does to me. :)

@azamat-sharapov
Copy link

Make learning curve as low as possible. Avoid confusions

It's closer to what other contemporary frameworks (Polymer and React) are doing, and with lower learning curve than existing solution.

Don't fix it, it was not broken!

It took me 2 minutes to memorize new changes. I thougth you have good memory. If you mean "general learning curve" - I still think Vue's API makes perfect sense and learnable in a day or two. It's funny for me how you are comparing Vue to those libraries mentioning learning curve. New API is to bring more sense, explicity and I think it makes it easier to learn compared to those libraries' API.

@yyx990803
Copy link
Member Author

@azamat-sharapov calm down please, it's just discussion ;)

@azamat-sharapov
Copy link

Sorry, forgot to insert smiley 😄

@karevn
Copy link

karevn commented Sep 14, 2015

@acasar two accounts? :)
@azamat-sharapov you don't seem to get the point. I was against 1.0 changes before, because I don't think current 1.0 syntax worths efforts. It's not about laziness learning anything, it's about spending time efficiently. But if changes are inevitable - I would like to have the best syntax possible in 1.0 release. And I keep referring to other libraries not because I like them, but because when newbies having some other background see the syntax - they'll feel more familiar from the beginning, which increases Vue's chances to succeed. And to succeed, the library not should only have a nice architecture and pretty stable code (and Vue is good from these points unquestionably), but should look familiar based on the experience most people have.

Imagine I went from Ruby On Rails, Backbone or WPF world, and I see `:attr="src: 'something"'. Hmm, what does this colon mean?? I have to read the docs to make sure.

Now, with the same background, when i see attr="{src: 'something'}". Hmmm. It looks like I assign a Ruby hash or JS object. I think I got it!

@azamat-sharapov
Copy link

I have to read the docs to make sure.

Personally me, I took like 3 days off just to read Vue docs first, so that I won't annoying to the public with simple questions :) because it was my first JS library. Yeah I also did Backbone in the past, but probably not much as you.. I think it's mandatory to learn docs first and Vue docs seem to be easy to understand.

Yeah I understand you mean "people with different backgrounds", but that colon thing is optional, like if/else vs ternary, so use it if you are sure about it. Or did I miss something? (I personally wouldn't use that short-syntax, I better type "v-bind/v-on" so I can later read HTML like plain english).

@acasar
Copy link

acasar commented Sep 14, 2015

@karven Posted from company account by mistake. It seems it didn't get unnoticed :)

I'll just add one more comment about familiarity. I came from Angular v1 and I felt right at home with Vue. Not only that, it even felt much easier and more intuitive to use. And judging by how popular Angular v1 was (and still is) there are many who feel the same. It seems that changing the syntax completely in v2 didn't end well for them... but well, I guess the jury is still out on that.

@azamat-sharapov I also prefer the longer syntax but I see no harm in offering a more concise shorter one. It seems there are some valid use-cases for that. And from what I understand it is optional.

@karevn
Copy link

karevn commented Sep 14, 2015

@azamat-sharapov it just indicates that you are not like other 95% of people. If you'll take a look at Vue gitter thread, you'll see that most of question would not appear if people have read docs.

@acasar I think it is not new syntax that is the problem in Angular, but the fact that syntax was changed. People just don't trust Angular anymore.

@yjeroen
Copy link

yjeroen commented Sep 17, 2015

@yyx990803 Could you maybe add a changelog to #1325 the next time you change it? I don't think we can see a timestamp of when you did edit it the last time, can we?

@acasar
Copy link

acasar commented Sep 17, 2015

@yyx990803 After some thought I am still rooting to make adding a class with condition a little easier. Right now v-class="error: hasError" is all over my code and while v-class="{ error: hasError }" is not so bad, it still is a tiny downgrade (in length and readability) and object literals are still some form of micro-syntax.

I understand that class is natively a single attribute but so is onclick and every other event attribute and you can still add multiple handlers on a single element:

<input v-on:keyup.enter="doSomething" v-on:keyup.esc="doSomethingElse"  />

So I think that v-class:error="hasError" would be pretty much the same.

By the way, is it also possible to define multiple event handlers using object literals or is that exclusive to v-class and v-style?

@yyx990803
Copy link
Member Author

@acasar I wouldn't call the Object literal microsyntax - because it's a JavaScript expression. If you know how to write JavaScript, you know how to write an Object literal. The point about "removing microsyntax" is that now we can always safely assume "a directive takes a single expression."

For event handlers - you use multiple attributes to handle different events with the native syntax too, e.g. onclick="doThis()" onkeyup="doThat()". The fact that we allow the key modifier is because the native syntax would force you to do key detection in JavaScript, so it's a strict improvement.

On the other hand, I don't see how v-class:error="hasError" offers any readability improvement over the object literal. And it has the problem that it allows you to split class-manipulation logic into multiple attributes, which is a mismatch with the native counterpart.

I think there should only be "two ways of doing the same thing" when there's a clear benefit for each (for example v- vs. shorthand); any alternative syntax that doesn't offer specific benefits should be removed. That's why I'm considering removing v-class and v-style altogether and just use v-bind:class or :class.

@JosephSilber
Copy link

Since curly braces are used for mustache expressions, seeing single braces used for object literals is a bit jarring (even though we don't allow mustaches in attributes anymore).

we can always safely assume "a directive takes a single expression."

What do you mean by that? Wouldn't the directive just get an object?

@yyx990803
Copy link
Member Author

@JosephSilber an object literal is a valid JavaScript expression.

@Pandahisham
Copy link

@yyx990803 the latest syntax is wonderful , it reads really well and intuitively, respect 👍

@acasar
Copy link

acasar commented Sep 20, 2015

@yyx990803 Thanks for the latest alpha release! I really like the syntax.

Do you think we could find even more concise wording for .literal modifier? Like .raw for example, since we are essentially passing a raw value that shouldn't be parsed:

<div v-dirname.raw="abc">

@JosephSilber
Copy link

@acasar I like that!

Since literals will be used all over the place (99% of v-links will probably be literal), it makes sense to shorten the name.

Not sure about raw though. VM variables are just as "raw" as literal strings...

@simplesmiler
Copy link
Member

Maybe .just? Because it is just a string.

@yyx990803
Copy link
Member Author

I thought about shortening it to .lit, but it's much less self-explanatory.

@acasar
Copy link

acasar commented Sep 20, 2015

@JosephSilber I like .raw because it's really short. And since you are passing a "raw value", it is somehow appropriate. There are other options like .plain or just .lit but they're not so expressive.

In the end I'd be fine with just using quotes, which will probably work anyway since it's a valid expression:

<div v-dirname="'abc'">

@simplesmiler
Copy link
Member

Another viable option is .str.

@young-steveo
Copy link

I think .literal is the most precise, semantic name. "Use this value literally; do not interpolate it."

As an aside, I didn't even know what the v-link directive was for most of this binding debate. It's not mentioned in the official Vue docs anywhere. I finally found it in vue-router (I use Grapnel for routing in my apps).

Most of my props/bindings are not literal.

@azamat-sharapov
Copy link

@simplesmiler I laughed at .just :D but .str would open more possibilities, like validation, in the future I mean.

@thelinuxlich
Copy link

.str is shorter and explains the intent even better.

// silly example
v-link.str="1"

There would be no mistake about knowing if the value would be string or number.

@OEvgeny
Copy link

OEvgeny commented Sep 21, 2015

I prefer raw or lit because there is no reference to type. If there will be validation or anything else related to the type of value (for example parsing), I will prefer str, int, float, bool, obj, array, date etc.

@yyx990803
Copy link
Member Author

Are a few extra letters really that hard to deal with? Imo being semantically accurate is more important than being concise, especially when it's just a few letters' difference.

@OEvgeny
Copy link

OEvgeny commented Sep 21, 2015

@yyx990803 it looks a bit long compared to others for me. It makes literal different among them,

@fullfs
Copy link

fullfs commented Sep 22, 2015

@yyx990803 Noticed v-component -> is change in 1.0.0. beta. Is it going to be that way? I mean, previously its was a directive option. But now it's like directive itself. No special syntax (.lit or something?), simple name potentially overlapping with almost anything.

@yyx990803
Copy link
Member Author

@fullfs this is because

  1. Vue components are already modeled after Web Components (custom elements, content/slot API), and Web Components use is on normal elements to indicate "turn this into another element".
  2. We are already using is on dynamic components.

@Morgul
Copy link

Morgul commented Sep 22, 2015

@yyx990803 Regarding the .literal vs something shorter, I personally don't mind the extra characters. However, @thelinuxlich brings up a good point:

<!-- Will `v-link` get a string or a number? Both are technically literals... -->
<my-component v-link.literal="5280"></my-component>

<!-- Ah, it will get a string. -->
<my-component v-link.str="5280"></my-component>

The same case can be made using true or false as well; while literal explains the intention, "We will pass this value to the directive without parsing it as an expression", there can be some confusion about if there will be any parsing at all.

Now, this problem is solved by sufficient documentation, but I felt it was worth drawing attention to what I felt was a really good point that might have been missed.

@yyx990803
Copy link
Member Author

@Morgul I don't think we should evaluate the syntax based on "does it make sense without the docs", because that's impractical. Rather, I think we should evaluate the syntax by "does it make sense enough so that you only need to read the docs once".

The .str may better reflect the end result - the value is bound as a string. But .literal better reflects the intent. A directive is either reactive or literal. Reactive directives take expressions, literal directives take strings. A directive is reactive by default, and the .literal modifier makes it literal. .str does not reflect this underlying difference.

@thelinuxlich
Copy link

That said, I agree now with @yyx990803

@marktinsley
Copy link

+1 for .literal

@Morgul
Copy link

Morgul commented Sep 23, 2015

@yyx990803 Alright, I agree with that. I'm completely onboard for .literal.

@fullfs
Copy link

fullfs commented Sep 23, 2015

@yyx990803 if it's about reactive/non-reactive, maybe .static will do or something?

@onyxblade
Copy link

@yyx990803
v-bind:attr might be just little messier than v-bind-attr.
: and @ are nice shorthands of v-bind and v-on, but why must use colon in v-directive?

Besides, though not very important, Rails users would feel a bit inconvenient to handle attributes which contains colons when they use Rails code to generate HTML. So at least I hope that v-bind="{}" syntax would remain in 1.0 release.

By the way, can directives have alias? Only for that basic directives like v-bind to bind are ok, not needed to include attr name. Doing that because Vue would be the only framework in project XD

@yyx990803
Copy link
Member Author

@CicholGricenchos the colon is necessary because directive names can contain hyphens. How do you know v-bind-attr is not a directive called bind-attr?

@yyx990803
Copy link
Member Author

Closing this issue now - if there are any specific concerns, please post at the forum instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests