Vue.js Изменение реквизита

Я немного запутался в том, как изменять свойства внутри компонентов, допустим, у меня есть следующий компонент:

{
    props: {
        visible: {
            type: Boolean,
            default: true
        }
    },
    methods: {
         hide() {
              this.visible = false;
         }
    }
} 

Хотя это работает, это даст следующее предупреждение:

Старайтесь не изменять свойство напрямую, так как значение будет перезаписано при повторном рендеринге родительского компонента. Вместо этого используйте данные или вычисленное свойство на основе значения свойства. Изменяемая опора: "visible" (находится в компоненте)

Теперь мне интересно, как лучше всего справиться с этим, очевидно, что свойство visible передается при создании компонента в DOM: <Foo :visible="false"></Foo>


person woutr_be    schedule 24.11.2016    source источник
comment
Почему вы редактируете опору? Он должен (с точки зрения кода) управляться из одного места, либо из компонента, либо из родительского компонента? (Вы можете выполнить комбинацию, передав метод обновления в качестве опоры, в вашем случае hide() может быть на родительском элементе, который затем отправляет ссылку на него как опору).   -  person ArneHugo    schedule 24.11.2016
comment
@ArneHugo Как видите, у самого компонента есть метод, позволяющий отображать / скрывать элемент. Родитель также может обновить это свойство. Думайте об этом как о предупреждающем сообщении, родитель может контролировать, видно ли оно, сам компонент может удалить себя.   -  person woutr_be    schedule 24.11.2016
comment
Да я вижу, что. Я предлагаю взять реквизиты visible (логическое значение) и hide (функция). Затем hide определяется в родительском элементе, который также владеет состоянием visible. Таким образом, вы не редактируете реквизиты, а редактируете родительское состояние, что разрешено.   -  person ArneHugo    schedule 24.11.2016
comment
Возможно, вы можете создать скрипку, чтобы показать, что именно вы делаете, и я могу изменить ее, чтобы показать вам, что Я имею в виду.   -  person ArneHugo    schedule 24.11.2016
comment
@ArneHugo я скоро соберу пример   -  person woutr_be    schedule 24.11.2016
comment
@ArneHugo Вот пример: jsfiddle.net/4qj3e4bc/1 - я понимаю, что вы говоря, но я не думаю, что родитель должен нести ответственность за видимость компонента, каждый родитель, который хочет отображать предупреждение, должен будет реализовать этот метод. И я мог бы захотеть скрыть предупреждение через x секунд, что означает, что каждый родитель также должен будет реализовать это. Я бы предпочел, чтобы компонент обрабатывал это.   -  person woutr_be    schedule 24.11.2016
comment
Хорошо, я отправил ответ, дайте мне знать, сделал ли он то, что вы хотите.   -  person ArneHugo    schedule 24.11.2016


Ответы (7)


Ссылка на код в скрипке

Так или иначе, вы должны выбрать одно место для проживания государства, а не два. Я не знаю, более уместно иметь его только в Alert или просто в родительском для вашего варианта использования, но вы должны выбрать один.

Как решить, где живет государство

Зависит ли родительский или какой-либо родственный компонент от состояния?

  • Да: тогда он должен быть в родительском (или в каком-либо внешнем управлении состоянием)
  • Нет: Тогда проще иметь в состоянии самого компонента
  • Вроде оба: см. Ниже

В некоторых редких случаях вам может понадобиться комбинация. Возможно, вы хотите дать возможность родителю и ребенку скрывать ребенка. Тогда у вас должно быть состояние как в родительском, так и в дочернем (поэтому вам не нужно редактировать дочерние реквизиты внутри дочернего элемента).

Например, дочерний элемент может быть видимым, если: visible && state_visible, где visible исходит из реквизита и отражает значение в родительском состоянии, а state_visible - из дочернего состояния.

Я не уверен, что вам нужно именно такое поведение, но вот фрагмент. Я бы предположил, что вы действительно хотите просто вызвать toggleAlert родительского компонента, когда вы щелкаете по дочернему.

var Alert = Vue.component('alert', {
  template: `
        <div class="alert" v-if="visible && state_visible">
        Alert<br> 
        <span v-on:click="close">Close me</span>
      </div>`,
  props: {
    visible: {
      required: true,
      type: Boolean,
      default: false
    }
  },
  data: function() {
    return {
      state_visible: true
    };
  },
  methods: {
    close() {
      console.log('Clock this');
      this.state_visible = false;
    }
  }
});

var demo = new Vue({
  el: '#demo',
  components: {
    'alert': Alert
  },
  data: {
    hasAlerts: false
  },
  methods: {
    toggleAlert() {
      this.hasAlerts = !this.hasAlerts
    }
  }
})
.alert {
  background-color: #ff0000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo" v-cloak>
  <alert :visible="hasAlerts"></alert>

  <span v-on:click="toggleAlert">Toggle alerts</span>
</div>

person ArneHugo    schedule 24.11.2016
comment
Я полностью понимаю вашу точку зрения, но я считаю, что это довольно обычное поведение, не так ли? Наличие всей логики в моем родительском компоненте означает необходимость копировать / вставлять эту логику во множество различных компонентов, которые все используют Alert. Играть на скрипке имеет смысл, и на данный момент кажется, что это лучший вариант. - person woutr_be; 24.11.2016
comment
А как насчет дочерних компонентов, которые являются формами? Может быть, вы хотите передать данные от родителя к потомку, иметь возможность редактировать их, а затем, если есть успешная публикация, передать обновленные данные обратно родителю? - person Allen Wang; 12.03.2018
comment
@AllenWang в этом случае данные (и сеттеры) могут жить в родительском элементе, в то время как дочерний элемент реализует пользовательский интерфейс, с которым взаимодействуют пользователи. Ребенку не обязательно владеть копией данных. - person ArneHugo; 13.03.2018
comment
Даже если вы хотите, чтобы у родителя была окончательная копия, которая не изменилась бы, если в дочернем элементе не было успешного обновления? Ваша точка зрения имеет смысл, поскольку, если существует сложная структура данных, и вы хотите обновить только одну ее часть, трудно понять логику замены правильной части, если вам нужно передать данные. - person Allen Wang; 13.03.2018
comment
@AllenWang Если вам нужна копия данных в том виде, в котором они были до редактирования, я бы сделал это вне компонентов, в магазине. Таким образом, логика хранения копии будет жить в магазине, а компоненты будут легче понять. Считайте оригинал источником истины, а копию - черновиком. Затем вы можете показать черновик в режиме редактирования и правду в режиме презентации. - person ArneHugo; 14.03.2018
comment
@AllenWang Но если вы не хотите использовать хранилище, сохраните как исходные данные, так и копию в родительском компоненте, так легко понять, где находятся данные. - person ArneHugo; 14.03.2018
comment
Хотя я понимаю это, мне кажется, что это неправильно. Мне нужны компоненты, чтобы можно было изолировать мой код. Не кажется хорошей идеей иметь данные вне компонента. Что касается хранилищ, если это длинный список дочерних компонентов, каждый со своими дочерними компонентами в глубоком дереве, как координировать, какой дочерний элемент получает какую часть данных хранилища? Опять же, в этом есть некоторый смысл, но все это кажется неправильным для банального вложения компонентов. - person Bernardo Dal Corno; 22.05.2019
comment
@ Z.Khullah, иногда вам нужно хранить данные вне компонента. Вот почему у нас есть концепция контролируемых компонентов. Управляемый компонент может быть чисто презентационным или содержать поведенческую логику без сохранения своего состояния. У него даже может быть какое-то собственное состояние. Где вы должны держать государство, зависит от того, что вы пытаетесь сделать. - person ArneHugo; 22.05.2019
comment
Я не понимаю и согласен. Тогда я предпочитаю магазины, а не делегировать родительскому компоненту эту ответственность ИМХО. - person Bernardo Dal Corno; 22.05.2019

Если свойство полезно только для этого дочернего компонента, дайте дочернему элементу prop, как initialVisible, и data, как mutableVisible, а в ловушке created (которая вызывается при сборке структуры данных компонента), просто this.mutableVisible = this.initialVisible.

Если свойство используется другими дочерними элементами родительского компонента, вам необходимо сделать его data родительским, чтобы сделать его доступным для всех дочерних компонентов. Затем в дочернем this.$emit('visibleChanged', currentVisible), чтобы уведомить родителя об изменении visible. В родительском шаблоне используйте <ThatChild ... :visibleChanged="setVisible" ...>. Взгляните на руководство: http://vuejs.org/v2/guide/components.html

person PanJunjie潘俊杰    schedule 24.11.2016

Согласно компоненту Vue.js:

Когда родительское свойство обновляется, оно переходит к дочернему, но не наоборот. Итак, как мы сообщаем родителям, когда что-то происходит? Здесь на помощь приходит настраиваемая система событий Vue.

Используйте $emit('my-event) из дочернего элемента, чтобы отправить событие родителю. Получите событие в дочернем объявлении внутри родительского с помощью v-on:my-event (или @my-event).

Рабочий пример:

// child

Vue.component('child', {
  template: '<div><p>Child</p> <button @click="hide">Hide</button></div>',
  methods: {
    hide () {
      this.$emit('child-hide-event')
    }
  },
})

// parent

new Vue({
  el: '#app',
  data: {
    childVisible: true
  },
  methods: {
    childHide () {
      this.childVisible = false
    },
    childShow () {
      this.childVisible = true
    }
  }
})
.box {
  border: solid 1px grey;
  padding: 16px;
}
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<div id="app" class="box">
  <p>Parent | childVisible: {{ childVisible }}</p>
  <button @click="childHide">Hide</button>
  <button @click="childShow">Show</button>
  <p> </p>
  <child @child-hide-event="childHide" v-if="childVisible" class="box"></child>
</div>

person François Romain    schedule 07.11.2017

После прочтения ваших последних комментариев кажется, что вы обеспокоены наличием логики для отображения / скрытия предупреждений для родительского элемента. Поэтому я бы предложил следующее:

родитель

# template
<alert :alert-visible="alertVisible"></alert>

# script
data () {
  alertVisible: false,
  ...
},
...

Затем в дочернем предупреждении вы должны $ смотреть значение опоры и переместить всю логику в предупреждение:

ребенок (предупреждение)

# script
data: {
  visible: false,
  ...
},
methods: {
  hide () {
    this.visible = false
  },
  show () {
    this.visible = true
  },
  ...
},
props: [
  'alertVisible',
],
watch: {
  alertVisible () {
    if (this.alertVisible && !this.visible) this.show()
    else if (!this.alertVisible && this.visible) this.hide()
  },
  ...
},
...
person GuyC    schedule 24.11.2016
comment
обновлен, чтобы вы могли установить параметр для родительского элемента для управления любыми дочерними предупреждениями. - person GuyC; 24.11.2016
comment
да, нужно иметь в виду, что вы будете использовать param (alertVisible) только для родительского элемента, чтобы установить состояние, вы не сможете надежно прочитать его. Если вы хотите это сделать, вы начинаете требовать события или передачу других свойств метода (показанных / скрытых и т. Д.), Которые вызывает предупреждение, но для цели предупреждения маловероятно, что родитель действительно должен знать свое состояние, поэтому он должен работать нормально большую часть времени без дополнительной логики. Также позволяет легко контролировать несколько предупреждений от родителя, что является бонусом. - person GuyC; 24.11.2016
comment
Я ожидаю, что часы будут работать только в первый раз. поэтому в случае многократного отображения предупреждения (что является обычным случаем) это решение не сработает. В противном случае родителю по-прежнему нужен способ сбросить до исходного значения (и знать, когда) - person Yauhen.F; 14.08.2019

Чтобы помочь кому-нибудь, я столкнулся с той же проблемой. Я просто изменил свою переменную, которая находилась внутри v-model = "", с массива props на data. Помните разницу между реквизитом и данными, в моем случае это не было проблемой, изменить его, вы должны взвесить свое решение.

E.g.:

<v-dialog v-model="dialog" fullscreen hide-overlay transition="dialog-bottom-transition">

До:

export default {
    data: function () {
        return {
            any-vars: false
        }
    },
    props: {
            dialog: false,
            notifications: false,
            sound: false,
            widgets: false
        },
    methods: {
        open: function () {
            var vm = this;

            vm.dialog = true;
        }
    }
}

После:

export default {
    data: function () {
        return {
            dialog: false
        }
    },
    props: {
            notifications: false,
            sound: false,
            widgets: false
        },
    methods: {
        open: function () {
            var vm = this;

            vm.dialog = true;
        }
    }
}
person Marco    schedule 22.05.2018

Возможно, это похоже на взлом и нарушает концепцию единого источника данных, но его работа) Это решение создает локальную прокси-переменную и наследует данные от props. Далее работаем с прокси-переменной.

Vue.component("vote", {
    data: function() {
        return {
            like_: this.like,
            dislike_: this.dislike,
        }
    },

    props: {
        like: {
            type: [String, Number],
            default: 0
        },
        dislike: {
            type: [String, Number],
            default: 0
        },
        item: {
            type: Object
        }
    },

    template: '<div class="tm-voteing"><span class="tm-vote tm-vote-like" @click="onVote(item, \'like\')"><span class="fa tm-icon"></span><span class="tm-vote-count">{{like_}}</span></span><span class="tm-vote tm-vote-dislike" @click="onVote(item, \'dislike\')"><span class="fa tm-icon"></span><span class="tm-vote-count">{{dislike_}}</span></span></div>',

    methods: {
        onVote: function(data, action) {
            var $this = this;
            // instead of jquery ajax can be axios or vue-resource
            $.ajax({
                method: "POST",
                url: "/api/vote/vote",
                data: {id: data.id, action: action},
                success: function(response) {
                    if(response.status === "insert") {
                        $this[action + "_"] = Number($this[action + "_"]) + 1;
                    } else {
                        $this[action + "_"] = Number($this[action + "_"]) - 1;
                    }
                },
                error: function(response) {
                    console.error(response);
                }
            });
        }
    }
});

использовать компонент и передать реквизиты

<vote :like="item.vote_like" :dislike="item.vote_dislike" :item="item"></vote>
person Vitaliy    schedule 15.07.2017

Интересно, почему другие пропускают его, когда в предупреждении есть подсказка

Старайтесь не изменять свойство напрямую, так как значение будет перезаписано при повторном рендеринге родительского компонента. Вместо этого используйте данные или вычисленное свойство на основе значения свойства. Изменяемая опора: видимая (находится в компоненте)

Попробуйте создать вычисляемое свойство из свойства, полученного в дочернем компоненте как

computed: {
  isVisible => this.visible
}

И используйте это вычисленное в вашем дочернем компоненте, а также для передачи изменений вашему родительскому компоненту.

person Saksham    schedule 13.04.2020