Here is my plunker and the code I can't get to work starts on line 32
I am trying to apply an equivalent to markdown filter onto a directive... I created the filter and tested with manually applying the filter and it works that way,, but I should only use the filter conditionally when the type of content on directive is set to markdown.
I am trying to acplish this by updating ng-model >>> ngModel.$setViewValue(html)
but I am getting an error
ngModel.$setViewValue is not a function.. which makes me thing that the controller is not recognized although it is required by the directive.
Here is a working controller:
var app = angular.module('testOne', ["ngResource", "ngSanitize"]);
app.controller('testOneCtrl', function ($scope) {
$scope.product = {
id:12,
name:'Cotton T-Shirt, 2000',
description:'### markdown\n - list item 1\n - list item 2',
price:29.99
};
});
app.directive("myText", function ($parse) {
return {
restrict: "E",
require: "?ngModel",
scope:{
css: "@class", type: "@type"
},
controller: function ($scope, $element, $attrs) {},
templateUrl: "template.html",
pile: function(elm, attrs, ngModel){
var expFn = $parse(attrs.contentType + '.' + attrs.value);
return function(scope,elm,attrs){
scope.$parent.$watch(expFn, function(val){
scope.exp = { val: val };
if ( attrs.type == 'markdown'){
var converter = new Showdown.converter();
var html = converter.makeHtml(val);
//scope.exp.val = html;
ngModel.$setViewValue(html);
ngModel.$render();
}
})
scope.$watch('exp.val', function(val){
expFn.assign(scope.$parent, val)
})
}
}
}
})
This is a filter for markdown which works when applied.. (I would consider using the filter if I could figure out the way to conditionally apply it to existing directive but I'd rather do it with ng-model)
/*
app.filter('markdown', function ($sce) {
var converter = new Showdown.converter();
return function (value) {
var html = converter.makeHtml(value || '');
return $sce.trustAsHtml(html);
};
});
*/
Here is the directive template
<div ng-class="{{css}}"
ng-click="view = !view"
ng-bind-html="exp.val">
</div>
<div>
<textarea rows="4" cols="30" ng-model="exp.val"></textarea>
</div>
This is the directive in use:
<mb-text ng-cloak
type="markdown"
content-type="product"
value="description"
class="test-one-text-2">
</mb-text>
Here is my plunker and the code I can't get to work starts on line 32 http://plnkr.co/edit/pmCjQL39BWWowIAgj9hP?p=preview
I am trying to apply an equivalent to markdown filter onto a directive... I created the filter and tested with manually applying the filter and it works that way,, but I should only use the filter conditionally when the type of content on directive is set to markdown.
I am trying to acplish this by updating ng-model >>> ngModel.$setViewValue(html)
but I am getting an error
ngModel.$setViewValue is not a function.. which makes me thing that the controller is not recognized although it is required by the directive.
Here is a working controller:
var app = angular.module('testOne', ["ngResource", "ngSanitize"]);
app.controller('testOneCtrl', function ($scope) {
$scope.product = {
id:12,
name:'Cotton T-Shirt, 2000',
description:'### markdown\n - list item 1\n - list item 2',
price:29.99
};
});
app.directive("myText", function ($parse) {
return {
restrict: "E",
require: "?ngModel",
scope:{
css: "@class", type: "@type"
},
controller: function ($scope, $element, $attrs) {},
templateUrl: "template.html",
pile: function(elm, attrs, ngModel){
var expFn = $parse(attrs.contentType + '.' + attrs.value);
return function(scope,elm,attrs){
scope.$parent.$watch(expFn, function(val){
scope.exp = { val: val };
if ( attrs.type == 'markdown'){
var converter = new Showdown.converter();
var html = converter.makeHtml(val);
//scope.exp.val = html;
ngModel.$setViewValue(html);
ngModel.$render();
}
})
scope.$watch('exp.val', function(val){
expFn.assign(scope.$parent, val)
})
}
}
}
})
This is a filter for markdown which works when applied.. (I would consider using the filter if I could figure out the way to conditionally apply it to existing directive but I'd rather do it with ng-model)
/*
app.filter('markdown', function ($sce) {
var converter = new Showdown.converter();
return function (value) {
var html = converter.makeHtml(value || '');
return $sce.trustAsHtml(html);
};
});
*/
Here is the directive template
<div ng-class="{{css}}"
ng-click="view = !view"
ng-bind-html="exp.val">
</div>
<div>
<textarea rows="4" cols="30" ng-model="exp.val"></textarea>
</div>
This is the directive in use:
<mb-text ng-cloak
type="markdown"
content-type="product"
value="description"
class="test-one-text-2">
</mb-text>
Share
Improve this question
asked Feb 4, 2014 at 23:10
GRowingGRowing
4,72713 gold badges55 silver badges75 bronze badges
1 Answer
Reset to default 4Why ngModel
is empty?
When using
require
on a directive the controller is passed as the 4th argument to the linking function. In you code you try to reference it as an argument of the pile function. The controller is only instantiated before the linking phase so it could never be passed into the pile function anyway.The bigger issue is that
require
can only get a controller of the same element ({ require: 'ngModel' }
), or parent elements ({ require: '^ngmodel' }
). But you need to reference a controller from a child element (within the template).
How to get ngModel
?
Do not use require at all as you cannot get child element's controller with it.
From angular.element docs:
jQuery/jqLite Extras
controller(name) - retrieves the controller of the current element or its parent. By default retrieves controller associated with the ngController directive. If name is provided as camelCase directive name, then the controller for this directive will be retrieved (e.g. 'ngModel').
Inside the linking function you can get the hold of the controller like so:
var ngModel = elm.find('textarea').controller('ngModel');
I fixed your directive:
here is a plunker: http://plnkr.co/edit/xFpK7yIYZtdgGNU5K2UR?p=preview
template:
<div ng-class="{{css}}" ng-bind-html="exp.preview"> </div>
<div>
<textarea rows="4" cols="30" ng-model="exp.val"></textarea>
</div>
Directive:
app.directive("myText", function($parse) {
return {
restrict: "E",
templateUrl: "template.html",
scope: {
css: "@class",
type: "@type"
},
pile: function(elm, attrs) {
var expFn = $parse(attrs.contentType + '.' + attrs.value);
return function(scope, elm, attrs) {
scope.exp = {
val: '',
preview: null
};
if (attrs.type == 'markdown') {
var converter = new Showdown.converter();
var updatePreview = function(val) {
scope.exp.preview = converter.makeHtml(val);
return val;
};
var ngModel = elm.find('textarea').controller('ngModel');
ngModel.$formatters.push(updatePreview);
ngModel.$parsers.push(updatePreview);
}
scope.$parent.$watch(expFn, function(val) {
scope.exp.val = val;
});
scope.$watch('exp.val', function(val) {
expFn.assign(scope.$parent, val);
});
};
}
};
});