2013-07-13 21 views
9

Trong AngularJS, nó là đơn giản làm thế nào để $ xem một biến phạm vi $ và sử dụng để cập nhật khác. Nhưng thực hành tốt nhất là gì nếu hai biến phạm vi cần xem nhau?Fahrenheit và Celsius chuyển đổi hai chiều trong AngularJS

Tôi có ví dụ như một công cụ chuyển đổi hai chiều sẽ chuyển đổi Fahrenheit thành Celsius và ngược lại. Nó hoạt động ổn nếu bạn gõ "1" vào Fahrenheit, nhưng hãy thử "1.1" và góc sẽ trả lại xung quanh một chút trước khi ghi đè Fahrenheit mà bạn vừa nhập vào là một giá trị hơi khác nhau (1,1000000000000014):

function TemperatureConverterCtrl($scope) { 
    $scope.$watch('fahrenheit', function(value) { 
    $scope.celsius = (value - 32) * 5.0/9.0; 
    }); 
    $scope.$watch('celsius', function(value) { 
    $scope.fahrenheit = value * 9.0/5.0 + 32; 
    }); 
} 

Dưới đây là a plunker: http://plnkr.co/edit/1fULXjx7MyAHjvjHfV1j?p=preview

Các cách có thể khác nhau để ngăn chặn góc từ "nảy" xung quanh và buộc nó sử dụng giá trị bạn đã nhập như là, ví dụ: bằng cách sử dụng trình định dạng hoặc phân tích cú pháp (hoặc bất kỳ mẹo nào khác)?

+1

Lý do đây là câu hỏi hay là vì tình huống này có vòng lặp vô hạn trên nó - nếu các tính toán không phải là nghịch đảo của nhau ... +1. Và tôi sẽ ngạc nhiên khi thấy bất kỳ câu trả lời khác nào khác. – rGil

+0

Do một nền tảng trong DSP, thật khó cho tôi để chống lại đi vào toán học này, vì vậy tôi sẽ chỉ cho nó lên: Có thể cho điều này để đi vào một vòng lặp vô hạn (ngoại trừ Angular có tích hợp sẵn giới hạn lặp lại tối đa). Đây là một giải thích tuyệt vời về lý do tại sao các giá trị "trả lại" ở tất cả (do kiểm tra bẩn): http://stackoverflow.com/a/9693933/885163. Và khi kết hợp với độ chính xác giới hạn của điểm nổi, có thể cho các giá trị dao động 0, 1 hoặc nhiều lần, thậm chí vô thời hạn, một hiệu ứng được gọi là "chu kỳ giới hạn" trong bộ lọc DSP IIR. * phew * như thế nào cho một bình luận? –

Trả lời

9

Tôi nghĩ giải pháp đơn giản nhất, nhanh nhất và chính xác nhất là có cờ để theo dõi trường nào đang được chỉnh sửa và chỉ cho phép cập nhật từ trường đó.

Tất cả những gì bạn cần là sử dụng chỉ thị ng-change để đặt cờ trên trường đang được chỉnh sửa.

Working Plunk

Mã thay đổi cần thiết:

Sửa bộ điều khiển để trông như thế này:

function TemperatureConverterCtrl($scope) { 
    // Keep track of who was last edited 
    $scope.edited = null; 
    $scope.markEdited = function(which) { 
    $scope.edited = which; 
    }; 

    // Only edit if the correct field is being modified 
    $scope.$watch('fahrenheit', function(value) { 
    if($scope.edited == 'F') { 
     $scope.celsius = (value - 32) * 5.0/9.0; 
    } 
    }); 
    $scope.$watch('celsius', function(value) { 
    if($scope.edited == 'C') { 
     $scope.fahrenheit = value * 9.0/5.0 + 32; 
    } 
    }); 
} 

Và sau đó thêm chỉ thị đơn giản này để các lĩnh vực đầu vào (sử dụng F hoặc C nếu thích hợp) :

<input ... ng-change="markEdited('F')/> 

Bây giờ chỉ có trường được nhập vào mới có thể thay đổi trường khác.

Nếu bạn cần có khả năng sửa đổi các lĩnh vực ngoài một đầu vào, bạn có thể thêm một phạm vi hoặc điều khiển chức năng mà trông giống như sau:

$scope.setFahrenheit = function(val) { 
    $scope.edited = 'F'; 
    $scope.fahrenheit = val; 
} 

Sau đó, lĩnh vực C sẽ được cập nhật trên tiếp theo $ digest cycle.

Giải pháp này có tối thiểu mã bổ sung, loại bỏ mọi cơ hội cập nhật nhiều lần trên mỗi chu kỳ và không gây ra bất kỳ sự cố hiệu suất nào.

+0

Nếu có ai nghĩ rằng ghi chú không liên quan của tôi ở phía dưới là không phù hợp, tôi sẽ xóa nó. Hãy cho tôi biết. Cảm ơn. – OverZealous

+1

câu trả lời của bạn là siêu. Tôi đã ghi chú thêm của bạn trong bước tiến tốt và đánh giá cao nó. Tôi sẽ chỉnh sửa câu hỏi của tôi để đưa ra (hoặc reword) thêm nhu cầu. –

+0

Tuyệt vời, vui vì bạn không bị xúc phạm! :-) Bây giờ tôi sẽ dải ra rằng bit, bản thân mình. – OverZealous

7

Đây là câu hỏi khá hay và tôi trả lời câu hỏi mặc dù nó đã được chấp nhận :)

Trong góc, $ scope là mô hình. Mô hình này là nơi lưu trữ dữ liệu bạn có thể muốn tồn tại hoặc sử dụng trong các phần khác của ứng dụng, và như vậy, nó phải được thiết kế với lược đồ tốt giống như bạn làm trong cơ sở dữ liệu chẳng hạn.

Mô hình của bạn có hai trường dư thừa về nhiệt độ, đây không phải là ý tưởng hay. Cái nào là nhiệt độ "thực"? Có những lúc bạn muốn chuẩn hóa một mô hình, chỉ cho hiệu quả truy cập, nhưng đó thực sự chỉ là một lựa chọn khi các giá trị là không cần thiết, như bạn thấy, đây không phải do độ chính xác của dấu phẩy động.

Nếu bạn muốn tiếp tục sử dụng mô hình, nó sẽ trông giống như thế này. Bạn sẽ chọn một hoặc cái khác là nhiệt độ "nguồn của sự thật", sau đó có đầu vào khác như một hộp nhập thuận tiện với trình định dạng và phân tích cú pháp. Hãy nói rằng chúng ta muốn Fahrenheit trong mô hình:

<input type="number" ng-model="temperatureF"> 
<input type="number" ng-model="temperatureF" fahrenheit-to-celcius> 

Và chỉ thị chuyển đổi:

someModule.directive('fahrenheitToCelcius', function() { 
    return { 
    require: 'ngModel', 
    link: function(scope, element, attrs, ngModel) { 
     ngModel.$formatters.push(function(f) { 
     return (value - 32) * 5.0/9.0; 
     }); 
     ngModel.$parsers.push(function(c) { 
     return c * 9.0/5.0 + 32; 
     }); 
    } 
    }; 
}); 

Với điều này, bạn sẽ tránh được những "nảy xung quanh" vì $ parsers chỉ chạy trên hành động của người dùng, không về thay đổi mô hình. Bạn vẫn còn có số thập phân dài nhưng điều đó có thể được khắc phục bằng cách làm tròn.

Tuy nhiên, có vẻ như tôi không nên sử dụng mô hình cho việc này. Nếu tất cả các bạn muốn là "mỗi hộp cập nhật các hộp khác", bạn có thể làm chính xác điều đó và thậm chí không có một mô hình. Điều này giả định các hộp nhập liệu bắt đầu trống và nó chỉ là một công cụ đơn giản để người dùng chuyển đổi nhiệt độ. Nếu đó là trường hợp, bạn không có mô hình, không có bộ điều khiển, và thậm chí không khó sử dụng Angular cả. Đó là một widget hoàn toàn dựa trên khung nhìn tại thời điểm đó, và về cơ bản nó chỉ là jQuery hoặc jqlite. Đó là tính hữu dụng hạn chế mặc dù, vì không có mô hình nó không thể có hiệu lực bất cứ điều gì khác trong Angular.

Để làm điều đó, bạn chỉ có thể thực hiện một chỉ thị temperatureConverter có mẫu với một vài hộp nhập và xem cả hai hộp và đặt giá trị của chúng. Một cái gì đó như:

fahrenheitBox.bind('change', function() { 
    celciusBox.val((Number(fahrenheitBox.val()) - 32) * 5.0/9.0); 
}); 
+1

Câu trả lời này đưa ra một điểm tuyệt vời về việc thiết kế giản đồ chuẩn hóa đúng cho phạm vi $; Cho dù đánh dấu và bảo vệ (@OverZealous 'câu trả lời) hoặc thiết kế lược đồ tốt hơn là tốt nhất, có thể khác nhau trên cơ sở từng ứng dụng, và cả hai câu trả lời là tuyệt vời. –

Các vấn đề liên quan