0

I'm currently writing a Vue application where I allow the editing of some components by the end user. The rendered structure is currently generated from a tree-like data structure and passed down through component props, and I have written the code that updates the saved state.

I have confirmed from the highest level component that the saved state is being appropriately updated, but the rendered state does not update as well (as in below newlines should be removed). I've looked up this issue and seen recommendations to use Vue.set() and forceUpdate(), but I haven't gotten either to work, so I'm manually setting the innerText of the element for the time being.

I'm assuming I'm missing something simple to enforce updating the rendering?

Incorrect Rendering

    <template v-for="(element, index) in contents">
        ....
        <div
            class="element__ordered__list"
            :class="isEditMode && 'can-drag'"
            v-else-if="element.type === 2"
            :key="index + '-' + element.type"
        >
            <div
                class="element__ordered__list__item"
                v-for="(step, index) in element.steps"
                :key="'ol' + index"
            >
                <span>{{ index + 1 }}</span>
                <span
                    class="element__edit--lists"
                    :contenteditable="isEditMode ? 'true': 'false'"
                    @blur="onOrderedListEdit($event, index, element)"
                >{{step}}</span>
            </div>
        </div>
        ....
    </template>

    <script>
    import draggable from 'vuedraggable';
    
    export default {
        name: 'InstructionContainer',
        props: {
            title: String,
            contents: Array,
            top_level: Boolean,
            isEditMode: {
                type: Boolean,
                default: false
            }
        },
        components: {
            draggable
        },
        methods: {
            onOrderedListEdit(evt, index, listState) {
                let updatedContent = evt.target.textContent.replace('\xa0', ' '); // Remove &nbsp
                evt.target.textContent = updatedContent; // Ensure updated contents
                listState.steps[index] = updatedContent; // Store updated state
            }
        }
    };
    </script>
Mark Benningfield
  • 2,800
  • 9
  • 31
  • 31
  • 1
    Don't use `contenteditable` expecting two way data binding; use `` instead; use css to format the `input` tag matching your UI requirement; it is very much possible. – Ashwin Bande Jan 19 '21 at 05:28
  • Not a bad idea. I may switch over to using an **** element instead of what i have done as I move forward. I don't expect this to solve the issue though, as the problem is the element in question is not updating the contents when the state of the prop updates - which I need as the input's value still needs to be cleaned of garbage (newlines and &nbsp) before saving to state - which I was hoping would update the rendering to remove newlines. – Jordan Austin Jan 20 '21 at 04:33
  • Following up again. @ashwinbande pointed me in the correct direction with using inputs/textareas. Using a v-model="stateVar" instead of {{stateVar}} properly 2-way binds the state. – Jordan Austin Jan 26 '21 at 06:27

1 Answers1

0

Use a key with v-for, <template v-for="(element, index) in contents" :key="index">

Yash Maheshwari
  • 1,842
  • 2
  • 7
  • 16