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

hope to have a way to set scope of variable #6913

Closed
caikan opened this issue Oct 25, 2017 · 24 comments
Closed

hope to have a way to set scope of variable #6913

caikan opened this issue Oct 25, 2017 · 24 comments

Comments

@caikan
Copy link

caikan commented Oct 25, 2017

Sorry, Chinease only. I'll try to translate later.

What problem does this feature solve?

有时我希望在组件某区域渲染数据,它们都来自于data上比较深层的某一个属性,当数据量比较多时,直接使用文本插值的写法会比较繁琐,比如:
(以下为了精简代码,采用了pug、es6语法,并忽略Vue组件data必须为函数的要求)
The mustache interpolations of data in some deep props make the code cumbersome:

data.js

export default {
  some: {
    deep: {
      props: {
        a: 1,
        b: 2,
      },
    },
  },
};

my-page.vue

<template lang="pug">
  div
    p {{some.deep.props.a}}
    p {{some.deep.props.b}}
</template>
<script>
  import data from './data.js'
  export default {
    data,
  }
</script>

我想到几种变通的做法:
I tried some solutions:

  1. 使用计算属性:Use computed properties:

    my-page.vue

    <template lang="pug">
      div
        p {{props.a}}
        p {{props.b}}
    </template>
    <script>
      import data from './data.js'
      export default {
        data,
        computed: {
          props() {
            return this.some.deep.props;
          },
        },
      }
    </script>

    这样做相对简单,但是总要为此去定义很多计算属性似乎也有些繁琐。
    It is easy to use, but the computed properties become cumbersome.

  2. 利用v-for指令来缩小作用域:Use v-for directive to define a scoped variable:

    my-page.vue

    <template lang="pug">
      div
        template(v-for="props in [some.deep.props]")
          p(ref="a") {{props.a}}
          p(ref="b") {{props.b}}
    </template>
    <script>
      import data from './data.js'
      export default {
        data,
      }
    </script>

    这是一个取巧的做法,但也有副作用:用$refs访问v-for范围内的ref引用会变成数组。
    It almost works well, but have side-effect: $refs in the v-for scope will become an array.

  3. 将数据渲染区域定义为子组件,子组件接受简化后的数据:Use child component:

    data-view.vue

    <template lang="pug">
      div
        p {{props.a}}
        p {{props.b}}
    </template>
    <script>
      export default {
        props: ['props'],
      }
    </script>

    my-page.vue

    <template lang="pug">
      div
        data-view(:props="some.deep.props")
    </template>
    <script>
      import data from './data.js'
      import DataView from './data-view.vue'
      export default {
        components: {DataView},
        data,
      }
    </script>

    子组件内部的写法能简化许多,但是整体花销仍然不小。一个额外的问题是,子组件多引入了一个可能原本并不必要的包装根元素。
    Interpolations in child component become simple, but more children is also cumbersome. In addition, the child need a wrapper element as the component root, which was not needed before.

  4. 利用子组件作用域插槽来封装一个通用的辅助组件:Use the scoped slot to make a reusable component:

    sub-scope.vue

    <template lang="pug">
      div
        slot(:sub="data")
    </template>
    <script>
      export default {
        props: ['data'],
      }
    </script>

    my-page.vue

    <template lang="pug">
      div
        sub-scope(:data="some.deep.props")
          template(slot-scope="scope")
            p {{scope.sub.a}}
            p {{scope.sub.b}}
        // or
        sub-scope(:data="some.deep.props")
          template(slot-scope="{sub}")
            p {{sub.a}}
            p {{sub.b}}
    </template>
    <script>
      import data from './data.js'
      import SubScope from './sub-scope.vue'
      export default {
        components: {SubScope},
        data,
      }
    </script>

    这也是取巧利用了子组件来改变访问作用域。可以复用看上去好像是一个优点,但实际使用还是比较繁琐,甚至代码看上去有些更乱了。与上一个方法同样存在的一个问题是,子组件引入了可能原本并不必要的包装根元素。
    Only the interpolations look like simpler, but other codes become more complex.

What does the proposed API look like?

我感觉利用v-for指令的办法很巧妙,就是多了一些副作用,虽然也不是非常严重的影响。
因此我希望能有一个指令,能够如同v-for一样定义一个局部作用域变量,但又不会影响$refs引用的表现。
I hope there is a directive can define scoped variables like v-for, but will not affect the performance of $refs.

div(v-scope="props of some.deep") {{props.a}}
// or
div(v-scope="(a, b) of some.deep.props") {{a}}, {{b}}

或者可以提供两种ref元素访问接口:
Or the ref API should be optimized:

  • $refs仅用于访问指定引用名的第一项,类似于querySelector()$refs should only be used for reference the first of elements with the same ref value, like querySelector().
  • $groupedRefs(我想也许还有更好一点的名字)则用于访问包含相同引用名的全部元素数组,类似于querySelectorAll()$groupedRefs is used for reference the array of the elements with the same ref value, like querySelectorAll().
@caikan
Copy link
Author

caikan commented Oct 27, 2017

其实我很希望是这样的API:

div(v-scope="some.deep.props") {{a}}, {{b}}

如果不指定要导出的变量,那么就把v-scope指定的表达式值作为优先级最高的作用域变量,相当于导出了全部属性变量。
If no exporting variable is specified, all properties should be exported.

@Solander
Copy link

Solander commented Dec 1, 2017

You should really translate this one. I think, but I'm not sure, that this is exactly what I want in Vue as well. A way to declare a scope on a container.

@caikan
Copy link
Author

caikan commented Dec 5, 2017

Translation updated. @Solander

@flip111
Copy link

flip111 commented Dec 21, 2017

What about changing

<script>
  import data from './data.js'
  export default {
    data,
  }
</script>

into

<script>
  import data from './data.js'
  export default {
    data.some.deep.props,
  }
</script>

@Kingwl
Copy link
Member

Kingwl commented Dec 29, 2017

@atinux Thanks for your comments

  1. I'm also cautious about this feature, I did not provide a shortcut ( such as $v="" ) and export all props in the implementation

  2. This feature is sometimes useful:

<div v-for="item in items" v-local:result="doSomeComplicated(item)">
    <span>{{result.a}}</span>
    <span>{{result.b}}</span>
    <input :value="item.some">
</div>
  1. this is seems not a heavy or complicated implementation

hope to get more feedback about this feature :XD

@jkzing
Copy link
Member

jkzing commented Dec 29, 2017

@atinux Thanks for your comments

@Austio, just help to @ the right person. 😁

@ortega-io
Copy link

I also would like to be able to use the mentioned syntax:

<div v-for="item in items" v-local:result="doSomeComplicated(item)">
    <span>{{result.a}}</span>
    <span>{{result.b}}</span>
    <input :value="item.some">
</div>

To be able to access deep properties without having to use long names like:

object.child.property

@sirlancelot
Copy link

sirlancelot commented Feb 6, 2018

How about some sort of v-let style notation to be more like JavaScript? My thoughts:

<template>
    <div>
        <!-- Object-style syntax, similar to `v-bind` -->
        <template v-let="{ props: some.deep.props }">
            <span>{{ props.a }}</span>
            <span>{{ props.a }}</span>
        </template>
    </div>
</template>
<template>
    <div>
        <!-- Single-value syntax, similar to `v-bind:arg` -->
        <template v-let:props="some.deep.props">
            <span>{{ props.a }}</span>
            <span>{{ props.a }}</span>
        </template>
    </div>
</template>

JavaScript's let keyword is block-scoped, v-let should be consistent with that, so the leted values should only be accessible in the child nodes.

@AlexandreBonneau
Copy link
Contributor

If you declare a v-let, then shouldn't there be a v-const?

@sirlancelot
Copy link

@AlexandreBonneau No, that doesn't make sense.

@Austio
Copy link

Austio commented Feb 7, 2018

My thoughts are that you would still need a computed property or method in most cases for this where the data is coming from some api b/c there will be possibility of null/undefined. Keeping a separation between the template and presenting the data will be marginally more verbose but the gains are clarity and also reduction in size of vue core.

@OEvgeny
Copy link

OEvgeny commented Feb 7, 2018

Userland solution which works, but I highly recommend not to use it "as is".

(May be useful when you can move some logic into separate component which will make template more cleaner)

@AlexandreBonneau
Copy link
Contributor

@OEvgeny This is a nice workaround, thanks.

However, a simpler v-let directive could prevent cluttering the template with those additional tags.
I think this feature request is still valid.

@FranckFreiburger
Copy link

note that xsl has the concept of const variable.
Splitting templates or using computed or method is not always possible without breaking logical blocks or reducing readability.

@daqin0525
Copy link

个人习惯和风格可能都不太一样,清晰明朗,简洁易用,就是好框架,感觉用法还是不错的,很符合现在的代码编程习惯

@vpiskunov
Copy link

Any updates on this? Just ran into this as well...

@zippaaa
Copy link

zippaaa commented Nov 22, 2018

I was looking for a solution too... While I found only this:

<template>
<div v-for="item in items" v-myvar="myvar = doSomeComplicated(item)">
    <span>{{myvar.a}}</span>
    <span>{{myvar.b}}</span>
    <input :value="item.some">
</div>
</template>

<script>
export default {
	//...
	//fake
	directives: {
		myvar: {}
	}
};
</script>

@AlexandreBonneau
Copy link
Contributor

Using the directives object is counterintuitive ; if you would want to declare those local variables, then it ought to be better to use:

export default {
	//...
	//fake
	localVariables: {
		myvar: {}
	}
};

However, having to name very local var in the component seems a bit too verbose.
If you have multiple <v-for> in your component, you'll have to name each temporary variable differently, while perhaps they would manipulate the same data.

v-let is still my preferred way to go, and would be scoped in the template to the very html element it is declared on.

@zippaaa
Copy link

zippaaa commented Nov 23, 2018

if you would want to declare those local variables, then it ought to be better to use:
export default {
localVariables: {
myvar: {}
}
};

Is it works now? Can you show a complete example with local variable in template?

@AlexandreBonneau
Copy link
Contributor

No, it does not work now, as we are discussing options to implement (or not) local variables!

The point of my previous comment was to explain how I think having to declare the local variables in the <script> part is a bad idea, and how v-let could solve this elegantly and concisely.

@zippaaa
Copy link

zippaaa commented Nov 23, 2018

I agree with you.
I wrote an example of how I temporarily solved the problem.

@Vovencia
Copy link

Vovencia commented Jan 17, 2019

I was looking for a solution too... While I found only this:

<template>
<div v-for="item in items" v-myvar="myvar = doSomeComplicated(item)">
    <span>{{myvar.a}}</span>
    <span>{{myvar.b}}</span>
    <input :value="item.some">
</div>
</template>

<script>
export default {
	//...
	//fake
	directives: {
		myvar: {}
	}
};
</script>

Another way:
be careful, values are assigned to the instance
(this.bar = my.very.deeply.nested.bar), (this.secondBar = my.very.deeply.nested.secondBar), (this.foo = my.deeply.nested.foo)

<template>
  <div
        :bar="((bar = my.very.deeply.nested.bar), (secondBar = my.very.deeply.nested.secondBar), undefined)"
        v-bind="((foo = my.deeply.nested.foo), {})"
  >
    I am static! <span v-text="foo + ' '"></span> <span v-text="bar"></span>{{ secondBar }}
  </div>
</template>

<script>
import * as Vue from "vue";

export default {
  data: () => ({
    my: {
      deeply: { nested: { foo: "hello" } },
      very: { deeply: { nested: { bar: "world", secondBar: "!!!" } } }
    }
  })
};
</script>

https://codesandbox.io/s/l55551j0xq

@doncatnip
Copy link

A v-let or v-local or something similar would be pretty awesome. Some constellations force me to write a myriad of components with only a very small template and maybe one computed getter, introducing lots of boilerplate that could be avoided with a v-let.

@HerringtonDarkholme
Copy link
Member

Our new RFC repo https://github.com/vuejs/rfcs is dedicated to such feature request.

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

Successfully merging a pull request may close this issue.