Я пытаюсь использовать VueJS 2 для визуализации простого v-for, где я хотел бы, чтобы переменная цикла представляла свойство, к которому я хотел бы получить доступ. Это минимальный пример, показывающий, чего я хотел бы достичь:
var app = new Vue({
el: '#app',
data() {
return {
properties: ["a", "b", "c"],
a: "Value Of A",
b: "Value Of B",
c: "Value Of C"
};
}
})
<div id = "app">
<div> this renders fine: a = {{ this['a'] }} </div>
<div>
but this doesn't:
<div v-for = "name in properties" :key = "name"> {{name}} = {{ this[name] }}</div>
</div>
</div>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
По сути, я хотел бы отображать поля данных выборочно и в определенном порядке, определяемом полем свойств. Но пример ничего не может отобразить для {{ this[name] }}
. На самом деле он даже не отображается, если я изменил его на {{ this['a'] }}
для быстрого тестирования. {{ this['a'] }}
если поместить за пределы блока v-for, он будет отображаться нормально.
В чем может быть проблема с блоком v-for?
да, вы можете обернуть a, b, c в другой объект, чтобы избежать использования «этого», но я не всегда контролирую модель. Было бы хорошо, если бы я мог назначить это переменной внутри моего шаблона.
{{ $data[name] }} вместо {{ this[name] }}
круто, а как насчет свойств и вычисляемых свойств?
На самом деле вам не нужно будет вызывать this
в скобках, {{ a }}
выведет свойство a
, либо вычисленные свойства
правильно, но как насчет цикла v-for?
На самом деле я не могу найти, почему this
не будет работать в v-for
, поэтому просто могу предложить вызвать метод и вернуть желаемый результат в этом случае.
Я предполагаю, что более важный вопрос заключается в том, следует ли мне использовать «это» в шаблоне? Пробовал Vue3, и «это» отлично работает в цикле for.
Для меня я использовал this
в v-on
, когда ключ объекта был динамической переменной в корне свойства данных компонента, однако вы можете предотвратить это, используя вычисляемое свойство или метод
@Mohsen Мохсен, ты не против добавить свое решение $data в ответ?
По соглашению все в шаблоне vue this
автоматически. Фактически, вы не можете получить доступ ни к чему, кроме свойств, данных, методов, вычислений, фильтров и т. д., за пределами экземпляра компонента this
в шаблоне.
Итак, this
недоступен, потому что все, что вы заявляете в шаблоне, подразумевает его.
Насколько я понимаю, вы не можете заставить this
исчезнуть в функции javascript, это только вопрос того, к чему он относится. Я знаю, что могу напрямую обращаться к полям this
в шаблоне без явного указания this
. Но мне нужно будет знать имя поля статически. Здесь я просто хочу использовать скобки.
this
подразумевается в шаблоне как экземпляр компонента. и все остальное, на что вы хотите сослаться, просто добавьте к данным или вычислите.
для вашего примера создайте метод get(prop)
и просто верните его, используя this
в методе
Покопавшись в скомпилированном шаблоне (через Vue Template Explorer), я понял, что этот (упрощенный, чтобы просто содержать проблемный v-for
) шаблон:
<div id = "app">
<div v-for = "name in properties" :key = "name"> {{name}} = {{ this[name] }}</div>
</div>
компилируется в:
function render() {
with(this) {
return _c('div', {
attrs: {
"id": "app"
}
}, _l((properties), function (name) {
return _c('div', {
key: name
}, [_v(" " + _s(name) + " = " + _s(this[name]))])
}), 0)
}
}
где _c
— это сокращение от createElement
, _l
— это сокращение от renderList
, а _v
— это сокращение от createTextVNode
.
Теперь ясно, что причина, по которой я могу писать {{ a }}
, не упоминая, где разрешать a
, связана с выражением with(this)
, где this
является прокси-объектом.
Однако проблема с v-for заключается в том, что фактический рендерер элемента передается как функция обратного вызова в _l
:
function (name) {
return _c('div',
{key: name},
[_v(" " + _s(name) + " = " + _s(this[name]))])
}
Способ вызова обратного вызова, применяется правило привязки по умолчанию, а this
привязан к глобальному объекту окна.
С VueJS 3 (обозреватель шаблонов) тот же шаблон компилируется немного по-другому:
import { renderList as _renderList, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock, toDisplayString as _toDisplayString, createVNode as _createVNode } from "vue"
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", { id: "app" }, [
(_openBlock(true), _createBlock(_Fragment, null, _renderList(_ctx.properties, (name) => {
return (_openBlock(), _createBlock("div", { key: name }, _toDisplayString(name) + " = " + _toDisplayString(this[name]), 1 /* TEXT */))
}), 128 /* KEYED_FRAGMENT */))
]))
}
Обратите внимание, что функция рендеринга списка теперь определена как лямбда-выражение, поэтому this
захвачено лексически.
Итак, чтобы заставить все это работать, я остановился на методе, предложенном другими в комментариях:
let app = new Vue({
el: '#app',
data() {
return {
properties: ["a", "b", "c", "d"],
a: "Value Of A",
b: "Value Of B",
c: "Value Of C"
};
},
computed: {
d() {
return "Value of D";
}
},
methods: {
get(prop) {
return this[prop];
}
},
})
<script src = "https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id = "app">
<div>
from $data:
<div v-for = "name in properties" :key = "name"> {{name}} = {{ $data[name] }}</div>
</div>
<div>
from get:
<div v-for = "name in properties" :key = "name"> {{name}} = {{ get(name) }}</div>
</div>
</div>