【问题标题】:Aurelia binding - back to view model on button clickAurelia 绑定 - 单击按钮返回查看模型
【发布时间】:2017-04-10 19:25:51
【问题描述】:

在 Aurelia 应用程序中,我有一个带有单个输入的“重命名”表单

<input ... value.bind="something.name" />

还有两个按钮:Save 和 Cancel。

相同的something 对象已在其他控件中使用。所以,我不希望 name 在单击 Save 按钮之前更改。

有没有一种很好的声明方式来实现这一点,还是我必须将name 复制到另一个属性,然后将其复制回Save 触发器?

【问题讨论】:

  • AFIK 不支持开箱即用的需求。但如果是重复性需求,创建自定义资源也不是什么难事。
  • @qtuan 你能给我一些指导吗?
  • 使用自定义资源,我的意思是避免重复的逻辑。你的用例是什么?如果它始终是具有单个输入的表单,一个简单的解决方案是将表单封装在自定义元素中。
  • @qtuan 不,形式不同(目前有两种)。用例是:我们有一个列表,我们添加一个弹出框来编辑列表中的一项,它绑定到保存列表中的项目。在用户按下保存之前,列表不应更改。
  • 其实我也有类似的需求,只是尝试了一个使用自定义绑定行为的解决方案。我会发布我的答案。

标签: javascript aurelia


【解决方案1】:

您可以为您的编辑弹出窗口创建一个模型对象,并且仅在单击Save 时复制对列表中的项目所做的编辑。这是一个简化的示例:https://gist.run/?id=af3af031c5acc4c46407679f5ab1376b

查看

<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>

视图模型

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;
    }
}

【讨论】:

  • 是的,这或多或少是我们这样做的方式,但它看起来对我的口味来说有点过于冗长,所以我要求替代......
  • 您可能能够使用绑定行为或信号来实现类似的东西,我会听从 Jeremy Danyow 的意见。但即使你可以,我也不确定这样做是否真的是一个好主意。我会 ping Jeremy。
【解决方案2】:

我认为 Ashley Grant 的回答是一种非常清晰/直截了当的方式。我会选择类似的东西。既然您要求使用更少代码的替代方法,那么您就去吧....不确定它是否更好,这只是另一种方法,可能不太清楚...

https://gist.run?id=e931202307361d472c3e0ee4f523a833

视图模型有一个名为editPerson 的属性,它代表当前正在编辑的人。

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;
  }
}

视图使用one-way 绑定将视图模型数据推送到输入中。对输入的编辑不会更新模型,因为绑定是一种方式。当表单提交时,视图模型的save() 方法将被调用,该方法具有将输入值复制到模型中的逻辑。

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>

【讨论】:

  • 如果这是对绑定行为的正确使用,你能看看我下面的答案吗?提前致谢!
  • @qtuan 看起来很棒!这是对绑定行为的极好使用。有很多方法可以处理保存事件部分,我认为你所拥有的可以实现非常简洁的绑定表达式。
【解决方案3】:

这是使用自定义“可取消”绑定行为的替代方法。绑定会做这些事情:

  1. 拦截 updateSource 以将写入重定向到隐藏存储
  2. 侦听保存事件以将隐藏存储写回原始源。

"2" 是按照在绑定上下文中观察 saved 属性的约定完成的。如果需要更大的灵活性,可以将属性名称作为参数传递给自定义绑定。

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;
  }
}

要点跑:https://gist.run/?id=2c7e40e88d1d3c18e9d2bca6be438b47

参考:内置throttle binding behavior

【讨论】:

    【解决方案4】:

    我所做的是复制我要编辑的对象,然后将其绑定到视图,当我单击保存时,我然后将已编辑对象与我的 originalObject “合并”,以便更改反映在其他控件中.

    所以我的编辑 ViewModel 看起来像这样:

    activate(existingUser: User) {
        let userCopy: User = JSON.parse(JSON.stringify(existingUser));
    }
    

    在我看来,然后我使用userCopy 对象来绑定属性。当我点击保存时,我会执行以下操作:

    let indexOfUser = this.users.indexOf(existingUser);
    Object.assign(this.users[indexOfUser], userCopy);
    

    我已经稍微简化了代码,因为在我的用例中,我正在使用一个对话框,该对话框将已编辑的用户返回到不同的 ViewModel,这就是保存用户列表等的 ViewModel。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-13
      • 2022-12-11
      • 2013-06-29
      • 2021-07-19
      • 2016-08-20
      相关资源
      最近更新 更多