admin管理员组文章数量:1391987
In Aurelia application I have a "Rename" form with a single input
<input ... value.bind="something.name" />
and two buttons: Save
and Cancel
.
The same something
object is already used in other controls. So, I don't want the name
to change until Save
button is clicked.
Is there a nice declarative way to acplish that or do I have to copy name
to another property and then copy it back on Save
trigger?
In Aurelia application I have a "Rename" form with a single input
<input ... value.bind="something.name" />
and two buttons: Save
and Cancel
.
The same something
object is already used in other controls. So, I don't want the name
to change until Save
button is clicked.
Is there a nice declarative way to acplish that or do I have to copy name
to another property and then copy it back on Save
trigger?
- AFIK there is no support for your demand out of the box. But if it is a repetitive demand, it would not be difficult to create a custom resource for it though. – qtuan Commented May 12, 2016 at 9:41
- @qtuan Could you give me some guidance on how I could do it myself? – Mikhail Shilkov Commented May 12, 2016 at 9:46
- With a custom resource, I mean to avoid the repetitive logic. What are your use cases? If it is always a form with a single input, an easy solution is to encapsulate the form in a custom element. – qtuan Commented May 12, 2016 at 10:00
- @qtuan No, the forms are different (two so far). Use case is: we have a list, we add a popup to edit one item of the list, which is bound to save item that the list has. The list should not change until user presses Save. – Mikhail Shilkov Commented May 12, 2016 at 10:46
- In fact I also have a similar demand and just tried a solution using custom binding behavior. I'll post my answer. – qtuan Commented May 13, 2016 at 3:39
4 Answers
Reset to default 2You can create a model object for your edit popup and only copy the edits made to the item in the list when Save
is clicked. Here's a simplified example: https://gist.run/?id=af3af031c5acc4c46407679f5ab1376b
View
<template>
<ul>
<li repeat.for="person of people">${person.firstName} ${person.lastName} <button click.delegate="editPerson(person)">Edit</button></li>
</ul>
<div if.bind="editing">
First Name: <input type="name" value.bind="editModel.firstName" />
Last Name: <input type="name" value.bind="editModel.lastName" />
<button click.delegate="savePerson()">Save</button>
<button click.delegate="cancelEdit()">Cancel</button>
</div>
</template>
ViewModel
export class App {
editing = false;
people = [
{ firstName: 'John', lastName: 'Doe' },
{ firstName: 'Jane', lastName: 'Smith' },
{ firstName: 'Bob', lastName: 'Smith' }
];
editPerson(person) {
this.editing = true;
this.editObject = person;
this.editModel = Object.assign({},person);
}
savePerson() {
this.editing = false;
Object.assign(this.editObject, this.editModel);
this.editObject = null;
this.editModel = null;
}
cancelEdit() {
this.personBeingEdited = null;
this.editing = false;
}
}
I think Ashley Grant's answer is a very clear/straightforward way of doing this. I would go with something like that. Since you asked for an alternate approach, that uses less code, here you go.... not sure if it's any better, it's just another way of doing it, probably less clear...
https://gist.run?id=e931202307361d472c3e0ee4f523a833
The view model has a property called editPerson
which represents the person that is currently being edited.
app.js
export class App {
people = [
{ firstName: 'John', lastName: 'Doe' },
{ firstName: 'Jane', lastName: 'Smith' },
{ firstName: 'Bob', lastName: 'Smith' }
];
editPerson = null;
save() {
this.editPerson.firstName = this.firstNameInput.value;
this.editPerson.lastName = this.lastNameInput.value;
this.editPerson = null;
}
}
The view uses one-way
bindings to push the view-model data into the inputs. Edits to the inputs will not update the model because the binding is one way. When the form is submitted the view-model's save()
method will be called, which has logic to copy the input values into the model.
app.html
<template>
<ul>
<li repeat.for="person of people">
${person.firstName} ${person.lastName}
<button click.delegate="editPerson = person">Edit</button>
</li>
</ul>
<form if.bind="editPerson" submit.delegate="save()">
<label>
First Name:
<input ref="firstNameInput" value.one-way="editPerson.firstName">
</label>
<label>
Last Name:
<input ref="lastNameInput" value.one-way="editPerson.lastName">
</label>
<button type="submit">Save</button>
<button type="button" click.delegate="editPerson = null">Cancel</button>
</form>
</template>
Here is a alternative using a custom "cancellable" binding behavior. The binding would do these things:
- Intercept
updateSource
to redirect writing to a hidden storage - Listen for save event to write back the hidden storage to original source.
"2" is done by convention that observe a saved
property on the binding context. If need more flexibility, the property name can be passed as an argument to the custom binding.
app.html
<template>
<require from='./cancellable'></require>
<div>
Name: ${name}
Age: ${age}
<button click.delegate="edit()">Edit</button>
</div>
<div if.bind="editing">
<h3>Cancellable edit</h3>
Name: <input value.bind="name & cancellable">
Age: <input value.bind="age & cancellable">
<div><button click.delegate="save()">Save</button>
<button click.delegate="cancel()">Cancel</button></div>
</div>
<div if.bind="editing">
<h3>Instant edit</h3>
Name: <input value.bind="name">
Age: <input value.bind="age">
</div>
</template>
app.js
export class App {
constructor() {
this.name = 'John';
this.age = 20;
this.editing = false;
this.saved = false;
}
edit() {
this.saved = false;
this.editing = true;
}
save() {
this.saved = true;
this.editing = false;
}
cancel() {
this.saved = false;
this.editing = false;
}
}
cancellable.js
import {inject} from 'aurelia-dependency-injection';
import {BindingEngine} from 'aurelia-binding';
@inject(BindingEngine)
export class CancellableBindingBehavior {
constructor(bindingEngine) {
this.bindingEngine = bindingEngine;
}
bind(binding, scope) {
let value;
let modified = false;
let cancellable = {
originalUpdateSource: binding.updateSource,
originalUpdateTarget: binding.updateTarget,
};
// 1. Intercept "updateSource" to redirect write to a hidden value storage
binding.updateSource = (val) => {
value = val;
modified = true;
};
// 2. Intercept updateTarget" so that can observe change from original source
binding.updateTarget = (val) => {
value = val;
modified = false;
cancellable.originalUpdateTarget.call(binding, val);
}
// 3. Observe the "saved" event to copy back to original source
let bindingContext = scope.bindingContext;
cancellable.subscription = this.bindingEngine.propertyObserver(bindingContext, 'saved')
.subscribe((newValue, oldValue) => {
if (newValue && modified) {
cancellable.originalUpdateSource.call(binding, value);
}
});
binding.cancellable = cancellable;
}
unbind(binding, scope) {
binding.updateSource = binding.cancellable.originalUpdateSource;
binding.updateTarget = binding.cancellable.originalUpdateTarget;
binding.cancellable.subscription.dispose();
binding.cancellable = null;
}
}
Gist run: https://gist.run/?id=2c7e40e88d1d3c18e9d2bca6be438b47
Reference: the built-in throttle binding behavior
What I've done is made a copy of the object I want to edit and then bind that to the view, when I click save I then 'merge' the editedObject with my originalObject so the changes are reflected in the other controls.
So my edit ViewModel looks like this:
activate(existingUser: User) {
let userCopy: User = JSON.parse(JSON.stringify(existingUser));
}
In my view I then user the userCopy
object to bind properties. When I click save I do the following:
let indexOfUser = this.users.indexOf(existingUser);
Object.assign(this.users[indexOfUser], userCopy);
I've simplified the code a bit because in my use case I'm working with a dialog that returns the editedUser to a different ViewModel and that's the ViewModel that holds the list of users etc.
本文标签: javascriptAurelia bindingback to view model on button clickStack Overflow
版权声明:本文标题:javascript - Aurelia binding - back to view model on button click - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744654167a2617863.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论