Vue - визуализация компонентов в зависимости от состояния родительских данных - PullRequest
0 голосов
/ 27 ноября 2018

Я пытаюсь визуализировать компоненты в зависимости от состояния массива в родительском (App.vue).Я совсем не уверен, что это правильный подход для этого варианта использования (новичок в Vue и не опытный программист), поэтому я с удовольствием приму совет, если вы считаете, что это неправильный способ думать об этом.

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

data: function() {
    return {
        id: 2,
        question: "Has it worked before?",
        answer: undefined,
        requires: [
            {
                id: 1,
                answer: "Yes"
            }
        ]
    }
}

Предполагается, что этот вопрос будет отображаться, если ответ на вопрос 1 был положительным.

Моя проблема в том, что я не уверен, как сделать мои компоненты условно.Текущий подход заключается в отправке события от компонента, когда на него был получен ответ, и прослушивании этого события в родительском элементе.Когда событие срабатывает, родитель обновляет массив, который содержит «состояние» всех ответов на вопросы.Теперь мне нужно проверить этот массив из каждого компонента, чтобы увидеть, есть ли там вопросы, на которые дан ответ, и если соблюдаются правильные условия, покажите вопрос.

Мой вопрос: как я могу проверить данные в родительском элементе и показать / скрыть свой компонент в зависимости от него?А также - это хорошая идея или я должен сделать что-то другое?

Вот еще немного кода для справки:

App.vue

<template>
    <div id="app">
        <div class="c-troubleshooter">
            <one @changeAnswer="updateActiveQuestions"/>
            <two @changeAnswer="updateActiveQuestions"/>
        </div>
    </div>
</template>

<script>
import one from './components/one.vue'
import two from './components/two.vue'

export default {
    name: 'app',
    components: {
        one,
        two
    },
    data: function() {
        return {
            activeQuestions: []
        }
    },
    methods: {
        updateActiveQuestions(event) {
            let index = this.activeQuestions.findIndex( ({ id }) => id === event.id );
            if ( index === -1 ) {
                this.activeQuestions.push(event);
            } else {
                this.activeQuestions[index] = event;
            }
        }
    }
}
</script>

two.vue

<template>
    <div v-if="show">
        <h3>{{ question }}</h3>
        <div class="c-troubleshooter__section"> 
            <div class="c-troubleshooter__input">
                <input type="radio" id="question-2-a" name="question-2" value="ja" v-model="answer">
                <label for="question-2-a">Ja</label>
            </div>
            <div class="c-troubleshooter__input">
                <input type="radio" id="question-2-b" name="question-2" value="nej" v-model="answer">
                <label for="question-2-b">Nej</label>
            </div>
        </div>
    </div>
</template>

<script>
export default {
    data: function() {
        return {
            id: 2,
            question: "Bla bla bla?",
            answer: undefined,
            requires: [
                {
                    id: 1,
                    answer: "Ja"
                }
            ]
        }
    },
    computed: {
        show: function() {
            // Check in parent to see if requirements are there, if so return true
            return true;
        }
    },
    watch: {
        answer: function() {
            this.$emit('changeAnswer', {
                id: this.id,
                question: this.question,
                answer: this.answer
            })
        }
    }
}
</script>

Ответы [ 2 ]

0 голосов
/ 27 ноября 2018

Это довольно широкий вопрос, но я постараюсь дать несколько полезных советов.

Первый data должен использоваться для внутреннего состояния.Очень часто компонент должен использовать props для вещей, которые, как вы думаете, будут data, которыми он владеет.В данном случае это так: вопросы должны координироваться родителем, поэтому родитель должен владеть данными.Это позволяет вам создать разумную функцию для управления отображением компонента вопроса.

Имея данные, принадлежащие родителю, также позволяет вам создать один компонент вопроса, который настраивается в соответствии с его параметрами.Или у вас может быть несколько разных типов компонентов вопроса (вы можете использовать :is, чтобы выбрать правильный), но почти наверняка некоторые из них можно использовать повторно, если вы передадите их вопрос / ответ / другую информацию.

Чтобы обновить ответы, вы отправите изменения из компонентов вопроса и позволите родителю фактически изменить значение.Я использую settable, вычисляемый, чтобы разрешить использование v-model в компоненте.

new Vue({
  el: '#app',
  data() {
    return {
      questions: [{
          id: 1,
          question: 'blah 1?',
          answer: null
        },
        {
          id: 2,
          question: 'blah 2?',
          answer: null,
          // this is bound because data is a function 
          show: () => {
            const q1 = this.questions.find((q) => q.id === 1);

            return Boolean(q1.answer);
          }
        },
        {
          id: 3,
          question: 'Shows anyway?',
          answer: null
        }
      ]
    };
  },
  components: {
    questionComponent: {
      template: '#question-template',
      props: ['props'],
      computed: {
        answerProxy: {
          get() {
            return this.answer;
          },
          set(newValue) {
            this.$emit('change', newValue);
          }
        }
      }
    }
  }
});
<script src="https://unpkg.com/vue@latest/dist/vue.js"></script>
<div id="app">
  <div class="c-troubleshooter">
    <question-component v-for="q in questions" v-if="!q.show || q.show()" :props="q" @change="(v) => q.answer = v" :key="q.id">
    </question-component>
  </div>
  <h2>State</h2>
  <div v-for="q in questions" :key="q.id">
    {{q.question}} {{q.answer}}
  </div>
</div>

<template id="question-template">
  <div>
    {{props.question}}
    <div class="c-troubleshooter__input">
        <input type="radio" :id="`question-${props.id}-a`" :name="`question-${props.id}`" value="ja" v-model="answerProxy">
        <label :for="`question-${props.id}-a`">Ja</label>
    </div>
    <div class="c-troubleshooter__input">
        <input type="radio" :id="`question-${props.id}-b`" :name="`question-${props.id}`" value="nej" v-model="answerProxy">
        <label :for="`question-${props.id}-b`">Nej</label>
    </div>
  </div>
</template>
0 голосов
/ 27 ноября 2018

Условное отображение вопросов

Как @Roy J предлагает в комментариях, данные вопросов, вероятно, принадлежат родителю.Именно родитель обрабатывает все данные и решает, какие вопросы следует задавать.Однако существует множество стратегий для этого:

  1. Отображать вопросы условно с v-if или v-show непосредственно в родительском шаблоне:

Возможно, логика для отображениянекоторые вопросы не являются общими.Это может зависеть от большего количества вещей, пользовательских настроек ... Я не знаю.Если это так, просто визуализируйте вопросы условно непосредственно в родительском элементе, чтобы вам не требовался доступ ко всем данным вопросов в любом вопросе.Код должен выглядеть примерно так:

<template>
    <div id="app">
        <div class="c-troubleshooter">
            <one @changeAnswer="updateActiveQuestions" v-if="displayQuestion(1)"/>
            <two @changeAnswer="updateActiveQuestions" v-if="displayQuestion(2)"/>
        </div>
    </div>
</template>

<script>
import one from './components/one.vue'
import two from './components/two.vue'

export default {
    name: 'app',
    components: {
        one,
        two
    },
    data: function() {
        return {
            activeQuestions: [],
        }
    },
    methods: {
        updateActiveQuestions(event) {
            let index = this.activeQuestions.findIndex( ({ id }) => id === event.id );
            if ( index === -1 ) {
                this.activeQuestions.push(event);
            } else {
                this.activeQuestions[index] = event;
            }
        },
        displayQuestion(index){
          // logic...
        }
    },
}
</script>
Передавайте ссылку на предыдущий вопрос на каждый вопрос:

Если какой-либо вопрос должен быть виден только после того, как на предыдущий вопрос был дан ответ или просмотрен, или что-то в этом роде, вы можете передать его какПодпишите на каждый вопрос, чтобы они знали, должны ли они сделать или нет:

<template>
    <div id="app">
        <div class="c-troubleshooter">
            <one @changeAnswer="updateActiveQuestions"/>
            <two @changeAnswer="updateActiveQuestions" prev="activeQuestions[0]"/>
        </div>
    </div>
</template>

И в two.vue:

props: ['prev'],
computed: {
    show: function() {
        return this.prev && this.prev.status === 'ANSWERED';
        // or some logic related to this, idk

    }
},
просто передайте все данные детям:

Когда вы закодируете их, вы можете просто передать все данные вопросов в качестве опоры каждому компоненту вопроса, а затем использовать их в вычисляемом свойстве.Это не то, что я бы сделал, а просто работает, и поскольку объекты являются ссылками, это не обязательно неэффективно.

Использование универсального компонента:

Кажется странным иметь one.vue,two.vue для каждого вопроса, и, конечно, он плохо масштабируется.

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

Если шаблон действительно отличается от каждого вопроса к другому, это может усложниться.Однако, если, как я подозреваю, они имеют общую структуру HTML, с определенным заголовком или общей кнопкой «спросить» внизу и тому подобным образом, то вы сможете решить эту проблему с помощью слотов Vue .

Помимо проблем с шаблонами, я полагаю, что каждый вопрос в вашем приложении может получить произвольное количество «подвопросов» (например, two.vue с question-2-a и question-2-b).Это потребует сложной и гибкой структуры данных для данных вопросов (которая станет более сложной, когда вы начнете добавлять несколько вариантов, несколько возможных ответов и т. Д. И т. Д.).Это может быть очень сложным, но вам, вероятно, придется работать над этим, пока вы не сможете использовать один question.vue компонент, это наверняка окупится.

Совет: избегайте наблюдателей

Вы используете *От 1053 * до answer в шаблоне two.vue, затем с помощью наблюдателя для отслеживания изменений в переменной answer и отправки события.Это сложное и трудное для чтения, вы можете использовать @input или @change события для элемента <input> вместо:

<input type="radio" id="question-2-a" name="question-2" value="ja" v-model="answer" @input="emitAnswer">

И затем вместо наблюдателя есть метод:

emitAnswer() {
this.$emit('changeAnswer', {
    id: this.id,
    question: this.question,
    answer: this.answer
})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...