R
const LabeledEdit = {
template: `
<div class="labeled-edit">
<label>
<span class="label">{{ label }}</span>
<input autofocus ref="input" v-model="value"
@input="$emit('input', $event)" @keyup.enter="applyInput"
/>
</label>
</div>
`,
data: () => ({
value: ''
}),
props: {
data: Object,
valueKey: String,
label: String
},
methods: {
applyInput(e) {
const newData = { ...this.data, [this.valueKey]: this.value };
this.$emit('save', newData);
}
},
mounted() {
this.value = this.data[this.valueKey];
this.$refs.input.focus();
},
name: 'LabeledEdit'
};
const MyList = {
template: <div class="my-list"> <div class="row" v-for="person in persons" :key="person.id"> <span class="pid">{{ person.id + '.' }}</span> <labeled-edit v-if="person === personForEdit" :data="person" valueKey="name" @save="changePersonData(person, $event)" ></labeled-edit> <template v-else> <span class="name">{{ person.name }}</span> <button @click="personForEdit = person">Edit</button> </template> </div> </div> ,
data: () => ({
personForEdit: null
}),
props: {
persons: { type: Array }
},
methods: {
changePersonData(person, changedPerson) {
const personIdx = this.persons.findIndex(p => p.id === person.id);
if (personIdx < 0)
throw new Error('No person found for changePersonName');
this.persons.splice(personIdx, 1, { ...changedPerson });
this.personForEdit = null;
},
},
components: { LabeledEdit },
name: 'MyList'
};
const App = {
template: <div class="app"> <div class="row create"> <labeled-edit v-if="showEdit" :data="newPerson" valueKey="name" label="Создать:" @save="addPerson" ></labeled-edit> <button v-else class="btn-create" @click="createPerson">Create</button> </div> <hr> <my-list :persons="persons"></my-list> </div> ,
data: () => ({
showEdit: false,
newPerson: null,
persons: [
{ id: 0, name: 'Ivan' },
{ id: 1, name: 'Alex' },
{ id: 2, name: 'John' }
]
}),
methods: {
createPerson() {
const lastId = this.persons.reduce((r, p) => r = Math.max(r, p.id), -1);
this.newPerson = { id: lastId + 1, name: '' };
this.showEdit = true;
},
addPerson(person) {
this.persons.push({ ...person });
this.showEdit = false;
}
},
components: { MyList, LabeledEdit },
name: 'App'
};
new Vue({
el: '#app',
components: { App },
template: <app></app>
});body, input { font: 1rem sans-serif; }
hr { padding: 0; border: none; border-bottom: 1px solid #ccc; }
.app { max-width: 300px; }
.app * { box-sizing: border-box; }
.labeled-edit,
.labeled-edit input { width: 100%; }
.row.create .labeled-edit input { width: auto; }
.btn-create { margin: 0.1rem 0 0.1rem 1rem; }
.my-list { width: 100%; }
.my-list .row { display: flex; align-content: center; padding: 0.4rem 1rem; }
.my-list .row:nth-child(even) { background: #eee7; }
.my-list .labeled-edit { margin-left: -2px; }
.my-list button { flex: 0 0 auto; }
.my-list .pid { flex: 0 0 auto; margin: 3px 0.5em 3px 0; color: #aaa; }
.my-list .name { flex: 1 0 auto; margin: 3px 0; }<div id="app" v-cloak></div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>Using one input component, for both creation and editing: const LabeledEdit = {
template: <div class="labeled-edit"> <label> <span class="label">{{ label }}</span> <input autofocus ref="input" v-model="value" @input="$emit('input', $event)" @keyup.enter="applyInput" /> </label> </div> ,
data: () => ({
value: ''
}),
props: {
data: Object,
valueKey: String,
label: String
},
methods: {
applyInput(e) {
const newData = { ...this.data, [this.valueKey]: this.value };
this.$emit('save', newData);
}
},
mounted() {
this.value = this.data[this.valueKey];
this.$refs.input.focus();
},
name: 'LabeledEdit'
};
const MyList = {
template: <div class="my-list"> <div class="row" v-for="person in persons" :key="person.id"> <span class="pid">{{ person.id + '.' }}</span> <span class="name">{{ person.name }}</span> <button @click="$emit('edit', person)">Edit</button> </div> </div> ,
props: {
persons: { type: Array }
},
components: { LabeledEdit },
name: 'MyList'
};
const App = {
template: <div class="app"> <div class="row create"> <labeled-edit v-if="showEdit" :data="personData" valueKey="name" :label="creatingPerson ? 'Создать:' : 'Изменить:'" @save="applyChanges" ></labeled-edit> <button v-else class="btn-create" @click="createPerson">Create</button> </div> <hr> <my-list :persons="persons" @edit="editPerson"></my-list> </div> ,
data: () => ({
showEdit: false,
personData: null,
creatingPerson: false,
persons: [
{ id: 0, name: 'Ivan' },
{ id: 1, name: 'Alex' },
{ id: 2, name: 'John' }
]
}),
methods: {
createPerson() {
this.creatingPerson = true;
const lastId = this.persons.reduce((r, p) => r = Math.max(r, p.id), -1);
this.personData = { id: lastId + 1, name: '' };
this.showEdit = true;
},
editPerson(person) {
this.creatingPerson = false;
this.personData = person;
this.showEdit = true;
},
applyChanges(person) {
if (!this.creatingPerson) {
const idx = this.persons.findIndex(p => p.id === person.id);
this.$set(this.persons, idx, { ...person });
} else {
this.persons.push({ ...person });
}
this.showEdit = false;
}
},
components: { MyList, LabeledEdit },
name: 'App'
};
new Vue({
el: '#app',
components: { App },
template: <app></app>
});body, input { font: 1rem sans-serif; }
hr { padding: 0; border: none; border-bottom: 1px solid #ccc; }
.app { max-width: 300px; }
.app * { box-sizing: border-box; }
.labeled-edit { width: 100%; }
.labeled-edit label { display: flex; flex-flow: row nowrap; align-content: center; }
.labeled-edit span { line-height: 1.6em; }
.labeled-edit input { flex: 1 1 auto; min-width: 0; margin-left: 0.2em; }
.btn-create { margin: 0.1rem 0 0.1rem 1rem; }
.my-list { width: 100%; }
.my-list .row { display: flex; align-content: center; padding: 0.4rem 1rem; }
.my-list .row:nth-child(even) { background: #eee7; }
.my-list button { flex: 0 0 auto; }
.my-list .pid { flex: 0 0 auto; margin: 3px 0.5em 3px 0; color: #aaa; }
.my-list .name { flex: 1 0 auto; margin: 3px 0; }<div id="app" v-cloak></div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>