可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
See this jsfiddle: http://jsfiddle.net/8bENp/66/
If you look at the JavaScript console, you'll see something like this:
TypeError: Object NaN has no method 'replace' at makeHtml (https://raw.github.com/coreyti/showdown/master/compressed/showdown.js:62:705) at render (http://fiddle.jshell.net/_display/:50:42) at link (http://fiddle.jshell.net/_display/:54:13) at k (https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js:42:321) at e (https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js:38:198) at k (https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js:42:261) at e (https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js:38:198) at https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js:37:332 at https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js:15:440 at Object.e.$eval (https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js:85:416) angular.min.js:60
The problem is that model.$modelValue
is NaN
when its type shouldn't even be a number. Nevertheless, the markdown renders. I could add a typeof model.$modelValue == 'string'
check, but I'd rather treat the underlying cause. Any idea?
回答1:
The problem in your directive is that angular triggers the watch once before the expression has been evaluated. So the very first time the value is undefined
. I don't believe that can be prevented, but is how AngularJS works.
I added a val
parameter to your render function to show the actual watched value (logged to the console--see the fiddle at the bottom). The ngModelController
initializes $modelValue
to NaN, that's why NaN
is passed to the function instead of undefined
.
But since it seems as if the makeHtml
function expects a string, an easy fix is to pass it an empty string if the value is falsy (might be even better to convert it to a string).
var htmlText = converter.makeHtml(model.$modelValue || '');
Updated fiddle.
回答2:
I was not aware that $modelValue was initialized to NaN and ran into a similar issue. If the $modelValue is really needed at initialization, a solution may be to watch it until it has been assigned a new value:
.directive('contentEditor', function(){ return { restrict: 'A', require: 'ngModel', link: function($scope, $element, $attrs, ngModel){ var unregister = $scope.$watch(function(){ return ngModel.$modelValue; }, initialize); function initialize(value){ ngModel.$setViewValue(value); unregister(); } //... } }; });
The $watch returns a deregistration function, so it can be unregistered as soon as a new value has been assigned to $modelValue.
回答3:
I think you can also wrap it in a ngModel.$render
function.
Like so:
.directive('classOnValue', function($timeout) { return { restrict: 'A', require: 'ngModel', link: function(scope, element, attrs, ngModel) { ngModel.$render = function(){ //Do something with your model var actualValue = ngModel.$modelValue; } }} })
回答4:
Another variant (just if somebody will find that question here): just wrap execution of directive in $timeout function. As example, my directive which use it:
.directive('classOnValue', function($timeout) { return { restrict: 'A', require: 'ngModel', link: function(scope, element, attrs, ngModel) { $timeout(function() { var value = (attrs.value || ngModel.$modelValue || ngModel.$viewValue ); if (value) { element.addClass(attrs.classOnValue); } }); }} })