13

私はknockout.jsを使用して従業員のリストを表示しています。ページに1つの非表示のモーダルマークアップがあります。1人の従業員の[詳細]ボタンをクリックしたときに、その従業員をモーダルポップアップにデータバインドしたいと思います。ko.applyBindings(employee、element)を使用していますが、問題は、ページが読み込まれるときに、モーダルが何かにバインドされた状態で開始することを期待していることです。

だから私は疑問に思っています、遅い/延期されたデータバインディングを行うためのトリック/戦略はありますか?仮想バインディングを調べましたが、ドキュメントは十分に役に立ちませんでした。

ありがとう!

4

3 に答える 3

37

MVVVMでモーダルを操作する別の方法を提案したいと思います。MVVMでは、ViewModelはビューのデータであり、ビューはUIを担当します。この提案を検討すると、次のようになります。

this.detailedEmployee = ko.observable({}),

var self = this;
this.showDetails = function(employee){
    self.detailedEmployee(employee);
    $("#dialog").dialog("show"); //or however your dialog works
}

私は強く同意しthis.detailedEmployee = ko.observable({})ますが、私はこの行に強く反対しています:$("#dialog").dialog("show");。このコードはViewModelに配置され、モーダルウィンドウを表示します。実際、これはViewの責任であるため、MVVMアプローチを台無しにします。このコードは現在のタスクを解決すると思いますが、将来的には多くの問題を引き起こす可能性があります。

  • ポップアップを閉じるときは、メインのViewModelが一貫した状態になるように設定detailedEmployeeする必要があります。undefined
  • ポップアップを閉じるときに、アプリケーションで別のモーダルのコンポーネントを使用するときに、検証と閉じる操作を破棄する可能性が必要になる場合があります

私の場合、これらの点は非常に重要なので、別の方法を提案したいと思います。ポップアップにデータを表示する必要があることを「忘れた」場合、バインディングwithによって問題が解決する可能性があります。

this.detailedEmployee = ko.observable(undefined);
var self = this;
this.showDetails = function(employee){
    self.detailedEmployee(employee);
}

<div data-bind="with: detailedEmployee">
Data to show
</div>

ご覧のとおり、ViewModelはデータの表示方法について何も知りませ表示する必要のあるデータについてのみ知っています。withバインディングは、が定義されている場合にのみコンテンツを表示しますdetailedEmployeewith次に、ポップアップにコンテンツを表示するのと同様のバインディングを見つける必要があります。名前を付けましょうmodal。そのコードは次のようになります。

ko.bindingHandlers['modal'] = {
    init: function(element) {
        $(element).modal('init');
        return ko.bindingHandlers['with'].init.apply(this, arguments);
    },
    update: function(element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        var returnValue = ko.bindingHandlers['with'].update.apply(this, arguments);

        if (value) {
            $(element).modal('show');
        } else {
            $(element).modal('hide');
        }

        return returnValue;
    }
};

ご覧のとおり、withプラグインを内部で使用し、バインディングに渡された値に応じてポップアップを表示または非表示にします。定義されている場合-'show'。そうでない場合-'非表示'。その使用法は次のようになります。

<div data-bind="modal: detailedEmployee">
    Data to show
</div>

あなたがする必要がある唯一のことはあなたの好きなモーダルプラグインを使うことです。Twitter Bootstrapポップアップコンポーネントを使用して例を準備しました:http://jsfiddle.net/euvNr/embedded/result/

この例では、カスタムバインディングはもう少し強力です。onBeforeCloseイベントをサブスクライブし、必要に応じてこのイベントをキャンセルできます。お役に立てれば。

于 2012-05-17T06:34:30.687 に答える
3

@Romanychによって提供された回答でリンクされているJSFiddleは、もう機能していないようです。

そこで、ブートストラップ3とブートストラップモーダルライブラリを使用して、完全なCRUDサポートと基本的な検証を備えた独自の例(彼の元のフィドルに基づく)を作成しました: https ://jsfiddle.net/BitWiseGuy/4u5egybp/

カスタムバインディングハンドラー

ko.bindingHandlers['modal'] = {
  init: function(element, valueAccessor, allBindingsAccessor) {
    var allBindings = allBindingsAccessor();
    var $element = $(element);
    $element.addClass('hide modal');

    if (allBindings.modalOptions && allBindings.modalOptions.beforeClose) {
      $element.on('hide', function() {
        var value = ko.utils.unwrapObservable(valueAccessor());
        return allBindings.modalOptions.beforeClose(value);
      });
    }
  },
  update: function(element, valueAccessor) {
    var value = ko.utils.unwrapObservable(valueAccessor());
    if (value) {
      $(element).removeClass('hide').modal('show');
    } else {
      $(element).modal('hide');
    }
  }
};

使用例

景色

<div data-bind="modal: UserBeingEdited" class="fade" role="dialog" tabindex="-1">
  <form data-bind="submit: $root.SaveUser">
    <div class="modal-header">
      <a class="close" data-dismiss="modal">×</a>
      <h3>User Details</h3>
    </div>
    <div class="modal-body">
      <div class="form-group">
        <label for="NameInput">Name</label>
        <input type="text" class="form-control" id="NameInput" placeholder="User's name"
           data-bind="value: UserBeingEdited() && UserBeingEdited().Name, valueUpdate: 'afterkeydown'">
      </div>
      <div class="form-group">
        <label for="AgeInput">Age</label>
        <input type="text" class="form-control" id="AgeInput" placeholder="User's age"
           data-bind="value: UserBeingEdited() && UserBeingEdited().Age, valueUpdate: 'afterkeydown'">
      </div>
      <!-- ko if: ValidationErrors() && ValidationErrors().length > 0 -->
      <div class="alert alert-danger" style="margin: 20px 0 0">
        Please correct the following errors:
        <ul data-bind="foreach: { data: ValidationErrors, as: 'errorMessage'     }">
          <li data-bind="text: errorMessage"></li>
        </ul>
      </div>
      <!-- /ko -->
    </div>
    <div class="modal-footer">
      <button type="button" data-dismiss="modal" class="btn btn-default">Cancel</button>
      <button type="submit" class="btn btn-primary">Save Changes</button>
    </div>
  </form>
</div>

ViewModel

/* ViewModel for the individual records in our collection. */
var User = function(name, age) {
  var self = this;
  self.Name = ko.observable(ko.utils.unwrapObservable(name));
  self.Age = ko.observable(ko.utils.unwrapObservable(age));
}

/* The page's main ViewModel. */
var ViewModel = function() {
  var self = this;
  self.Users = ko.observableArray();

  self.ValidationErrors = ko.observableArray([]);

  // Logic to ensure that user being edited is in a valid state
  self.ValidateUser = function(user) {
    if (!user) {
      return false;
    }

    var currentUser = ko.utils.unwrapObservable(user);
    var currentName = ko.utils.unwrapObservable(currentUser.Name);
    var currentAge = ko.utils.unwrapObservable(currentUser.Age);

    self.ValidationErrors.removeAll(); // Clear out any previous errors

    if (!currentName)
      self.ValidationErrors.push("The user's name is required.");

    if (!currentAge) {
      self.ValidationErrors.push("Please enter the user's age.");
    } else { // Just some arbitrary checks here...
      if (Number(currentAge) == currentAge && currentAge % 1 === 0) { // is a whole number
        if (currentAge < 2) {
          self.ValidationErrors.push("The user's age must be 2 or greater.");
        } else if (currentAge > 99) {
          self.ValidationErrors.push("The user's age must be 99 or less.");
        }
      } else {
        self.ValidationErrors.push("Please enter a valid whole number for the user's age.");
      }
    }

    return self.ValidationErrors().length <= 0;
  };

  // The instance of the user currently being edited.
  self.UserBeingEdited = ko.observable();

  // Used to keep a reference back to the original user record being edited
  self.OriginalUserInstance = ko.observable();

  self.AddNewUser = function() {
    // Load up a new user instance to be edited
    self.UserBeingEdited(new User());
    self.OriginalUserInstance(undefined);
  };

  self.EditUser = function(user) {
    // Keep a copy of the original instance so we don't modify it's values in the editor
    self.OriginalUserInstance(user);

    // Copy the user data into a new instance for editing
    self.UserBeingEdited(new User(user.Name, user.Age));
  };

  // Save the changes back to the original instance in the collection.
  self.SaveUser = function() {
    var updatedUser = ko.utils.unwrapObservable(self.UserBeingEdited);

    if (!self.ValidateUser(updatedUser)) {
      // Don't allow users to save users that aren't valid
      return false;
    }

    var userName = ko.utils.unwrapObservable(updatedUser.Name);
    var userAge = ko.utils.unwrapObservable(updatedUser.Age);

    if (self.OriginalUserInstance() === undefined) {
      // Adding a new user
      self.Users.push(new User(userName, userAge));
    } else {
      // Updating an existing user
      self.OriginalUserInstance().Name(userName);
      self.OriginalUserInstance().Age(userAge);
    }

    // Clear out any reference to a user being edited
    self.UserBeingEdited(undefined);
    self.OriginalUserInstance(undefined);
  }

  // Remove the selected user from the collection
  self.DeleteUser = function(user) {
    if (!user) {
      return falase;
    }

    var userName = ko.utils.unwrapObservable(ko.utils.unwrapObservable(user).Name);

    // We could use another modal here to display a prettier dialog, but for the
    // sake of simplicity, we're just using the browser's built-in functionality.
    if (confirm('Are you sure that you want to delete ' + userName + '?')) {
      // Find the index of the current user and remove them from the array
      var index = self.Users.indexOf(user);
      if (index > -1) {
        self.Users.splice(index, 1);
      }
    }
  };
}

ViewとViewModelを使用したノックアウトの初期化

var viewModel = new ViewModel();

// Populate the ViewModel with some dummy data
for (var i = 1; i <= 10; i++) {
  var letter = String.fromCharCode(i + 64);
  var userName = 'User ' + letter;
  var userAge = i * 2;
  viewModel.Users.push(new User(userName, userAge));
}

// Let Knockout do its magic!
ko.applyBindings(viewModel);
于 2016-05-05T01:46:36.197 に答える
1

従業員をラップする別のオブザーバブルを作成します。

this.detailedEmployee = ko.observable({}),

var self = this;
this.showDetails = function(employee){
    self.detailedEmployee(employee);
    $("#dialog").dialog("show"); //or however your dialog works
}

クリックをに添付しますshowDetailsapplyBindings次に、ページの読み込み時に呼び出すことができます。

于 2012-05-16T21:18:32.423 に答える