Buộc chạy một hàm thuộc tính được tính toán


80

Đưa ra một thuộc tính được tính toán

vm.checkedValueCount = ko.computed(function(){
  var observables = getCurrentValues();  //an array of ko.observable[]
  return _.filter(observables, function(v) { return v() }).length;
});

giả sử getCurrentValues ​​() có thể trả về các tập hợp có thể quan sát khác nhau được sửa đổi ở những nơi khác trong mã (và đến từ một cấu trúc phức tạp hơn một mô hình quan sát được).

Tôi cần checkedValueCountcập nhật bất cứ khi nào

  • một trong những phụ thuộc của nó thay đổi
  • getCurrentValues ​​() trả về một tập hợp các có thể quan sát khác.

Vấn đề là ko.computeddường như ghi nhớ giá trị trả về cuối cùng và chỉ cập nhật khi một phần phụ thuộc cập nhật. Điều này xử lý trường hợp đầu tiên nhưng không xử lý trường hợp sau.

Những gì tôi đang tìm kiếm là một cách để buộc kiểm tra lại số tiền đã kiểm tra. Một cái gì đó mà tôi có thể sử dụng như:

changeCurrentValues();
vm.checkeValueCount.recalculate();

Nói một cách đơn giản nhất, cho rằng tôi có

a = ko.computed(function() { return Math.random() })

làm thế nào tôi có thể buộc gọi a()hai lần để trả về các giá trị khác nhau.


Xem câu trả lời được cập nhật "viết lại" của tôi.
Josh

Câu trả lời:


116

Tôi nhận ra rằng câu trả lời đầu tiên của tôi đã bỏ sót ý của bạn và sẽ không giải quyết được vấn đề của bạn.

Vấn đề là một máy tính sẽ chỉ đánh giá lại nếu có một số quan sát được buộc nó phải đánh giá lại. Không có cách nào để buộc máy tính phải đánh giá lại.

Tuy nhiên, bạn có thể giải quyết vấn đề này với một số hackery bằng cách tạo ra một giá trị giả có thể quan sát được và sau đó cho người đăng ký biết rằng nó đã thay đổi.

(function() {

    var vm = function() {
        var $this = this;

        $this.dummy = ko.observable();

        $this.curDate = ko.computed(function() {
            $this.dummy();
            return new Date();
        });

        $this.recalcCurDate = function() {
            $this.dummy.notifySubscribers();
        };        
    };

    ko.applyBindings(new vm());

}());​

Đây là một Fiddle cho thấy cách tiếp cận này


À, tôi đã thử cái này nhưng chắc là tôi đã nối nhầm. Điều này trông có vẻ khó hiểu nhưng tốt (và đủ tốt để tôi có thể mở rộng ko.computed để cho phép nó hoạt động
George Mauer

Luôn luôn đơn giản là tốt nhất.
Henry Rodriguez

1
Điều này hữu ích khi bạn thay đổi một mảng lớn dữ liệu không thể quan sát được và sau khi thay đổi, một số phần dữ liệu phải được hiển thị.
Marek Bar

1
Việc bạn sử dụngtifySubscriber () là một điều tốt. Tốt hơn so với những gì tôi đã làm trong việc xây dựng một số ngẫu nhiên và thiết lập đó để giá trị của hình nộm ()
efreed

9

một phương pháp để buộc tính toán lại tất cả các vật thể quan sát tùy thuộc vào bạn:

getCurrentValues.valueHasMutated()

9
Computeds không có phương pháp này
George Mauer

getCurrentValues(); //an array of ko.observable[]
Rustam

Ồ, tôi hiểu rồi. Bạn đang nói rằng hãy tìm một thứ có thể quan sát được là một phụ thuộc của máy tính và gọi nó là valueHasMutatedphương thức. Về cơ bản, câu trả lời này không khác gì câu trả lời của Josh, phải không? Trên thực tế, nó buộc bạn phải biết những gì được tham chiếu và biết rằng những tham chiếu đó có thể quan sát được (và không được tính toán).
George Mauer

2
Tuy nhiên, thật tốt khi đề cập đến chức năng này vì nó vẫn là một tùy chọn cho người đọc, mặc dù có lẽ ai đó có thể cập nhật câu trả lời được chấp nhận để lưu ý các trường hợp mà chức năng này có thể được hoán đổi vớitifySubscriber () và nếu có bất kỳ hình phạt hoặc lợi thế nào.
Shaun Wilson

4

Câu trả lời này về mặt khái niệm giống với câu mà @josh đã đưa ra, nhưng được trình bày dưới dạng trình bao bọc chung chung hơn. Lưu ý: phiên bản này dành cho máy tính 'có thể ghi'.

Tôi đang sử dụng Typecript nên tôi đã đưa vào định nghĩa ts.d trước. Vì vậy, hãy bỏ qua phần đầu tiên này nếu không liên quan đến bạn.

interface KnockoutStatic
{
    notifyingWritableComputed<T>(options: KnockoutComputedDefine<T>, context ?: any): KnockoutComputed<T>;
}

Thông báo-có thể ghi-tính

Một wrapper cho một ghi observablerằng luôn gây thuê bao để được thông báo - ngay cả khi không quan sát được cập nhật như là kết quả của các writecuộc gọi

Chỉ cần thay thế function<T> (options: KnockoutComputedDefine<T>, context)bằng function(options, context)nếu bạn không sử dụng Typecript.

ko.notifyingWritableComputed = function<T> (options: KnockoutComputedDefine<T>, context)
{
    var _notifyTrigger = ko.observable(0);
    var originalRead = options.read;
    var originalWrite = options.write;

    // intercept 'read' function provided in options
    options.read = () =>
    {
        // read the dummy observable, which if updated will 
        // force subscribers to receive the new value
        _notifyTrigger();   
        return originalRead();
    };

    // intercept 'write' function
    options.write = (v) =>
    {
        // run logic provided by user
        originalWrite(v);

        // force reevaluation of the notifyingWritableComputed
        // after we have called the original write logic
        _notifyTrigger(_notifyTrigger() + 1);
    };

    // just create computed as normal with all the standard parameters
    return ko.computed(options, context);
}

Trường hợp sử dụng chính cho trường hợp này là khi bạn đang cập nhật thứ gì đó sẽ không kích hoạt thay đổi trong một đối tượng có thể quan sát được hàm 'truy cập' read.

Ví dụ: tôi đang sử dụng LocalStorage để đặt một số giá trị, nhưng không có thay đổi nào đối với bất kỳ giá trị nào có thể quan sát được để kích hoạt đánh giá lại.

hasUserClickedFooButton = ko.notifyingWritableComputed(
{
    read: () => 
    {
        return LocalStorageHelper.getBoolValue('hasUserClickedFooButton');
    },
    write: (v) => 
    {
        LocalStorageHelper.setBoolValue('hasUserClickedFooButton', v);        
    }
});

Lưu ý rằng tất cả những gì cần thiết để thay đổi là ko.computedđể ko.notifyingWritableComputedrồi mọi thứ sẽ chăm sóc của chính nó.

Khi tôi gọi hasUserClickedFooButton(true)thì 'dummy' có thể quan sát được tăng dần buộc bất kỳ người đăng ký nào (và người đăng ký của họ) phải nhận giá trị mới khi giá trị trong LocalStorage được cập nhật.

(Lưu ý: bạn có thể nghĩ rằng bộ notify: 'always'mở rộng là một tùy chọn ở đây - nhưng đó là một cái gì đó khác).


Có một giải pháp bổ sung cho một thiết bị quan sát được tính toán chỉ có thể đọc được:

ko.forcibleComputed = function(readFunc, context, options) {
    var trigger = ko.observable().extend({notify:'always'}),
        target = ko.computed(function() {
            trigger();
            return readFunc.call(context);
        }, null, options);
    target.evaluateImmediate = function() {
        trigger.valueHasMutated();
    };
    return target;
};


myValue.evaluateImmediate();

Từ nhận xét @mbest https://github.com/knockout/knockout/issues/1019 .


Ý bạn là gì không có giá trị? Nó đã hoạt động trước khi chuyển sang cái này. Bạn có đang sử dụng các bản cập nhật hoãn lại không (cài đặt chung). Chỉ cần nhận ra rằng có thể có xung đột nếu bạn mong đợi có thể cập nhật giá trị có thể quan sát và sau đó ngay lập tức sử dụng giá trị. Nếu vậy, hãy thử thêm ko.tasks.runEarly () vào phương thức của tôi.
Simon_Weaver

1

giả sử getCurrentValues ​​() có thể trả về các tập hợp có thể quan sát khác nhau được sửa đổi ở nơi khác trong mã

Tôi giả sử getCurrentValues ​​() là một hàm. Nếu bạn có thể biến nó thành một máy tính, thì Số tiền đã kiểm tra của bạn sẽ bắt đầu hoạt động một cách kỳ diệu.

Bạn có thể biến getCurrentValues ​​thành một hàm được tính toán thay vì một hàm không?


Tôi đang đơn giản hóa kịch bản thực tế của mình rất nhiều nhưng chắc chắn, giả sử nó có thể xảy ra (sẽ không đúng nếu nó cần lấy tham số chẳng hạn) - Tôi không thấy điều đó sẽ tạo ra bất kỳ sự khác biệt nào. Bên trong getCurrentValues ​​(), tôi đang cắt và dò tìm dữ liệu khác để xác định nút nào của chế độ xem dạng cây cần được trả về. vấn đề là hàm có thể trả về các giá trị có thể quan sát được khác nhau và tôi cần máy tính để xử lý nó. Về cơ bản chính xác những gì các tài liệu nói về với sự phụ thuộc năng động nhưng với các biến chứng của họ được bổ sung một cách nhanh chóng
George Mauer

Nó sẽ tạo ra sự khác biệt nếu "cắt và cắt hạt" dựa trên các vật có thể quan sát được. Ví dụ: giả sử cắt và cắt của bạn là các nút có .IsChecked () == true. Sau đó, bạn sẽ có một máy tính được gọi là .currentValues ​​() sẽ đánh giá tất cả các nút và trả về các nút bằng .IsChecked (). Có thể thực hiện cắt và cắt hạt trên các đặc tính có thể quan sát được không?
Judah Gabriel Himango

Tôi thực sự không chắc bạn đang nói gì Judah, mọi lời kêu gọi đều không thể quan sát được trong trường hợp của tôi - nhưng quan trọng hơn, tôi không thấy điều đó có thể quan trọng như thế nào, loại trực tiếp không phản ánh bất kỳ điều gì. những gì bạn đã gọi (Tôi chưa kiểm tra nhưng trừ khi javascript đã hoàn toàn đi vào lisp-land, điều này là không thể). Tất cả những gì máy tính có thể làm là theo dõi tốt nhất bất kỳ vật thể quan sát nào được gọi.
George Mauer

-1

vì không có cách nào để buộc cập nhật một máy tính, tôi đã tạo một tệp có tên toForceComputedUpdate có thể quan sát được và tôi đã gọi nó trong hàm được tính toán để máy tính sẽ lắng nghe cập nhật của nó, sau đó để buộc cập nhật, tôi gọi nó như thế này toForceComputedUpdate (Toán .random)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.