Chupachar
@Chupachar
front-end dev

Как решить проблему с логикой валидации после перезагрузки страницы в Vue.js?

Есть форма с двумя полями. В компоненте TheEducation.vue находится бизнес-логика и логика валидации полей, а в компоненте BaseForm.vue происходит отрисовка полей и сохранение в локальное хранилище. Суть в том, что изначально шаблон работает с массивом propList, который я передаю из TheEducation в BaseForm, и поля "errorMessages":"error" получают ошибки по необходимости. Всё работает. После перезагрузки страницы в компоненте BaseForm отрабатывает уже ветка else:
if (!storedList) {
      list.value = propList;
      listType.value = 'propList: ' + JSON.stringify(list.value); 
    } else {
      list.value = storedList;
      listType.value = 'storedList: ' + JSON.stringify(list.value);
    }

Соответственно, шаблон работает уже с данными из локального хранилища, и функция getValidatePanels уже не присваивает массиву значение свойства errorMessages. В чем может быть проблема? Я полагаю, что возможно функция getValidatePanels после перезагрузки страницы работает не с тем массивом. Во всяком случае, буду очень благодарен, если поможете разобраться. Это мой давний висяк, и я не могу решить его.

Песочницы:
https://replit.com/@teplandrey41/ValidationForm#sr...
https://codesandbox.io/p/sandbox/polished-morning-...
https://playcode.io/1843620
Код:
Родитель
<template> 
  <BaseForm 
     @blurEvent="handlePanelBlurEvent" 
    :personalDetailsPanelArr="personalDetailsPanelArr" 
    :educationPanelInputsArr="educationPanelInputsArr"/> 
</template>
<script setup>
import { ref, onMounted, watch } from 'vue';
import BaseForm from './BaseForm.vue';

const educationPanelInputsArr = ref([
  { id: Math.floor(Math.random() * 100), name: 'InstituteName', errorMessages: '', validationData: 'EducationInstituteName', label: 'Name of educational institution', type: 'text', value: '', isEducationName: true },
  { id: Math.floor(Math.random() * 100), name: 'YearOfEntry', errorMessages: '', validationData: 'EducationYearOfEntry', label: 'Year of entry', type: 'number', value: ''}
]);

const personalDetailsPanelArr = ref([  
  {
    id: Math.floor(Math.random() * 100),
    type: 'Education',
    title: 'Education',
    isOpen: false,
    inputs: educationPanelInputsArr.value,
  }
]);

  //"запрос на сервер" для получения валидации по полям
  const getValidatePanels = () => {

    let storedDataPanels = ref(JSON.parse(localStorage.getItem('personalDetailsEducationPanels')) || []);

    //формирую объект с полями, value которых нужно будет провалидировать (отправить на сервер)
    const newInitialPersonalDetailsListArray = storedDataPanels.value.map(item => {
        const details = {};
        item.inputs.forEach(input => {
            details[input.validationData] = input.value;
        });
        return details;
    });
    
    //функция для присваивания возвращенных ошибок валидации с сервера 
    const extractDataFromPanels = (panel) => {
      panel.forEach(inputField => {
        const fieldName = inputField.validationData;
        const foundInput = panel.find(input => input.validationData === fieldName);

        if (foundInput.value.trim() === '') {
          foundInput.errorMessages = 'error';
        } else {
          foundInput.errorMessages = '';
        }
      });
    }

    extractDataFromPanels(educationPanelInputsArr.value); 
    
  }

  const handlePanelBlurEvent = () => {
    getValidatePanels();
  }

onMounted(() => {
  getValidatePanels()
});
</script>


Потомок
<template>
  <h1>{{ computedURLTitle }}</h1>
  <div
      v-for="(input, index) in allPanels" :key="index">
    <hr>
    <div
        v-for="(inputField, inputIndex) in input.inputs"
        :key="inputIndex">
      <BaseInput
          v-model="inputField.value"
          :type="inputField.type"
          :label="inputField.label"
          @blur="$emit('blurEvent')"
      />
      
       <div style="color: red;"> {{inputField.errorMessages}} </div>
      
    </div>  
  </div>
  <button @click="handleAddPanel">add</button>
  
  <div style="color: blue;">{{ listType }}</div>
  
</template>
<script setup>
import { ref, defineProps, defineEmits, computed, watch } from 'vue';
import BaseInput from './BaseInput.vue';
import { useRouter } from 'vue-router';

  const emits = defineEmits(['blurEvent']);
  
const router = useRouter();
const currentURL = ref(router.currentRoute.value.path);

const educationURL = '/education';

let listType = ref(null)

const props = defineProps({
  personalDetailsPanelArr: {
    type: Array,
  },
  educationPanelInputsArr: {
    type: Array,
  },
});
  const pathTitles = {
    '/education': 'Education',
  };

  const computedURLTitle = computed(() => {
    const currentPath = currentURL.value;
    return pathTitles[currentPath];
  });

console.log('personalDetailsPanelArr: ', props.personalDetailsPanelArr)

let personalDetailsList = ref([]);

const storedPersonalDetailsList =
    JSON.parse(localStorage.getItem('personalDetailsEducationPanels'));
if (storedPersonalDetailsList) {
  personalDetailsList.value = storedPersonalDetailsList;
}


  const updateLocalStorage = () => {
    localStorage.setItem('personalDetailsEducationPanels', JSON.stringify(personalDetailsList.value));
  }

const watchInput = (panel) => {
  panel.inputs.forEach((input) => {
    watch(input, (newValue) => {
      if (newValue.isEducationName) {
        panel.title = newValue.value || 'Education';
      }
      updateLocalStorage();
    });
  });
};

const panelLists = {
  '/education': {
    list: personalDetailsList,
    storedList: storedPersonalDetailsList,
    propList: props.personalDetailsPanelArr,
  },
};

const allPanels = computed(() => {
  const currentList = panelLists[currentURL.value];

  if (currentList) {

    const { list, storedList, propList } = currentList;

    if (!storedList) {
      list.value = propList;
      listType.value = 'propList: ' + JSON.stringify(list.value); 
    } else {
      list.value = storedList;
      listType.value = 'storedList: ' + JSON.stringify(list.value);
    }

    list.value.forEach((panel) => {
      watchInput(panel);
    });

    return list.value;
  }

  return [];
});
</script>
  • Вопрос задан
  • 53 просмотра
Пригласить эксперта
Ответы на вопрос 1
Chupachar
@Chupachar Автор вопроса
front-end dev
Задача решена, переделал логику компонентов. Сделал дочерний компонент dummy для рендера данных (убрал лишний код), а родителю предоставил логику работы с локальным хранилищем:
const personalDetailsData = ref([]);
onMounted(async () => {
  const localPersonalDetailsData = JSON.parse(localStorage.getItem('personalDetailsEducationPanels')) || [];
  if (localPersonalDetailsData.length) {
    personalDetailsPanelArr.value = localPersonalDetailsData;
  }
});
watch(personalDetailsPanelArr, (newValue) => {
  personalDetailsData.value = newValue;
  localStorage.setItem('personalDetailsEducationPanels', JSON.stringify(newValue));
}, {
  deep: true
});
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы