Reputation: 47417
I've searched here on SO and tried the answers I found, but I can't seem to get the model value out of the ngModel of my custom directive.
Here's the directive
/*
*usage: <markdown ng:model="someModel.content"></markdown>
*/
breathingRoom.directive('markdown', function () {
var nextId = 0;
return {
require: 'ngModel',
replace: true,
restrict: 'E',
template: '<div class="pagedown-bootstrap-editor"></div>',
link:function (scope, element, attrs, ngModel) {
var editorUniqueId = nextId++;
element.html($('<div>' +
'<div class="wmd-panel">' +
'<div id="wmd-button-bar-' + editorUniqueId + '"></div>' +
'<textarea class="wmd-input" id="wmd-input-' + editorUniqueId + '">{{modelValue()}}' +
'</textarea>' +
'</div>' +
'<div id="wmd-preview-' + editorUniqueId + '" class="wmd-panel wmd-preview"></div>' +
'</div>'));
var converter = new Markdown.Converter();
var help = function () {
// 2DO: add nice modal dialog
alert("Do you need help?");
};
var editor = new Markdown.Editor(converter, "-" + editorUniqueId, {
handler: help
});
editor.run();
// local -> parent scope change (model)
jQuery("#wmd-input-" + editorUniqueId).on('change', function () {
var rawContent = $(this).val();
ngModel.$setViewValue(rawContent);
scope.$apply();
});
// parent scope -> local change
scope.modelValue = function () {
console.log('modelvalue - ', ngModel.$viewValue);
return ngModel.$viewValue;
};
}
};
});
And here's the HTML
<markdown ng-class="{error: (moduleForm.Description.$dirty && moduleForm.Description.$invalid) || (moduleForm.Description.$invalid && submitted)}"
id="Description"
name="Description"
placeholder="Description"
ng-model="module.description"
required></markdown>
The problem here is that the output is simply
{{modelValue()}}
I also tried creating a private method
function getModelValue() {
console.log(ngModel.$viewValue);
return ngModel.$viewValue;
}
and then change the one template line to
'<textarea class="wmd-input" id="wmd-input-' + editorUniqueId + '">' + getModelValue() +
but then the output is
NaN
Where am I going wrong?
if it matters, here's the order of my scripts (not including vendor scripts)
<script src="app.js"></script>
<script src="directives/backButtonDirective.js"></script>
<script src="directives/bootstrapSwitchDirective.js"></script>
<script src="directives/markdownDirective.js"></script>
<script src="directives/trackActiveDirective.js"></script>
<script src="services/alertService.js"></script>
<script src="services/roleService.js"></script>
<script src="services/moduleService.js"></script>
<script src="services/changePasswordService.js"></script>
<script src="services/userService.js"></script>
<script src="controllers/usersController.js"></script>
<script src="controllers/userController.js"></script>
<script src="controllers/moduleController.js"></script>
<script src="controllers/modulesController.js"></script>
Upvotes: 18
Views: 49383
Reputation: 2141
The HTML your inserting isn't getting compiled. It's easiest just to move it into your template, or investigate using ng-transclude. Here's an example of moving it into your template.
breathingRoom.directive('markdown', function () {
var nextId = 0;
return {
require: 'ngModel',
replace: true,
restrict: 'E',
template: '<div class="pagedown-bootstrap-editor"><div class="wmd-panel">' +
'<div id="wmd-button-bar-{{editorUniqueId}}"></div>' +
'<textarea class="wmd-input" id="wmd-input-{{editorUniqueId}}">{{modelValue()}}' +
'</textarea>' +
'</div>' +
'<div id="wmd-preview-{{editorUniqueId}}" class="wmd-panel wmd-preview"></div>' +
'</div></div>',
link:function (scope, element, attrs, ngModel) {
scope.editorUniqueId = nextId++;
// parent scope -> local change
scope.modelValue = function () {
console.log('modelvalue - ' + ngModel.$viewValue);
return ngModel.$viewValue;
};
}
};
});
Upvotes: 14
Reputation: 5413
Look at angular's parse service. It enables you get and set the value of property referenced in ng-model.
link: function(scope, elem, attrs) {
var getter = $parse(attrs.ngModel);
var setter = getter.assign;
var value = getter(scope);
setter(scope, 'newValue');
}
Upvotes: 3
Reputation: 3256
The simplest way is:
If ngModel variable is on the top level of the scope
link: function(scope, elem, attrs) {
if (attrs.ngModel) {
var myModelReference = scope[attrs.ngModel];
}
}
If ngModel refers to a property deeply nested on the scope (best way)
(so, for scope.prop1.prop2
, attrs.ngModel
will be "prop1.prop2
"), and since you can't just look up scope['prop1.prop2']
, you need to dig in by converting the string key to actual nested keys.
For this I recommend Lodash _.get()
function
link: function(scope, elem, attrs) {
if (attrs.ngModel) {
var myModelReference = _.get(scope, attrs.ngModel);
}
}
Upvotes: 2
Reputation: 34830
You weren't actually resolving your model as the {{modelValue()}}
expression was just part of the HTML string your were building in the link function.
You should move the editor markup to the template
so that you can bind to ng-model
.
Assuming the goal is to create the necessary HTML markup for the Markdown Editor and then show the preview of the converted markdown I would split this into two roles:
Markup:
<div ng-app="app" ng-controller="DemoCtrl">
<h3>Markdown editor</h3>
<markdown-editor ng-model="markdown"></markdown-editor>
</div>
JavaScript:
var app = angular.module('app', []);
app.filter('markdown', function () {
return function (input) {
return input.toUpperCase(); // this is where you'd convert your markdown
};
});
app.directive('markdownEditor', function () {
return {
restrict: 'E',
scope: {
ngModel: "="
},
template:
'<div>' +
'<textarea ng-model="ngModel"></textarea>' +
'<div class="preview">{{ ngModel | markdown }}</div>' +
'</div>'
};
});
app.controller('DemoCtrl', function ($scope) {
$scope.markdown = "**hello world**";
});
The =
scope sets up a two way binding on the property passed to ng-model
and {{ ngModel | markdown }}
pipes the value of ngModel
to the markdown
filter.
http://jsfiddle.net/benfosterdev/jY3ZK/
Upvotes: 10