Skip to content
This repository was archived by the owner on May 25, 2019. It is now read-only.

Model not linked propperly #115

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 114 additions & 120 deletions src/ui-codemirror.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,139 +12,133 @@ angular.module('ui.codemirror', [])
*/
function uiCodemirrorDirective($timeout, uiCodemirrorConfig) {

return {
restrict: 'EA',
require: '?ngModel',
compile: function compile() {
return {
restrict: 'EA',
require: '?ngModel',
scope: {
model: '=ngModel'
},
link: postLink
};

// Require CodeMirror
if (angular.isUndefined(window.CodeMirror)) {
throw new Error('ui-codemirror needs CodeMirror to work... (o rly?)');
}
function postLink(scope, iElement, iAttrs, ngModel) {

return postLink;
}
};

function postLink(scope, iElement, iAttrs, ngModel) {

var codemirrorOptions = angular.extend(
{ value: iElement.text() },
uiCodemirrorConfig.codemirror || {},
scope.$eval(iAttrs.uiCodemirror),
scope.$eval(iAttrs.uiCodemirrorOpts)
);

var codemirror = newCodemirrorEditor(iElement, codemirrorOptions);

configOptionsWatcher(
codemirror,
iAttrs.uiCodemirror || iAttrs.uiCodemirrorOpts,
scope
);

configNgModelLink(codemirror, ngModel, scope);

configUiRefreshAttribute(codemirror, iAttrs.uiRefresh, scope);

// Allow access to the CodeMirror instance through a broadcasted event
// eg: $broadcast('CodeMirror', function(cm){...});
scope.$on('CodeMirror', function(event, callback) {
if (angular.isFunction(callback)) {
callback(codemirror);
} else {
throw new Error('the CodeMirror event requires a callback function');
}
});

// onLoad callback
if (angular.isFunction(codemirrorOptions.onLoad)) {
codemirrorOptions.onLoad(codemirror);
}
}

function newCodemirrorEditor(iElement, codemirrorOptions) {
var codemirrot;

if (iElement[0].tagName === 'TEXTAREA') {
// Might bug but still ...
codemirrot = window.CodeMirror.fromTextArea(iElement[0], codemirrorOptions);
} else {
iElement.html('');
codemirrot = new window.CodeMirror(function(cm_el) {
iElement.append(cm_el);
}, codemirrorOptions);
// Require CodeMirror
if (angular.isUndefined(window.CodeMirror)) {
throw new Error('ui-codemirror needs CodeMirror to work... (o rly?)');
}

var codemirrorOptions = angular.extend(
{ value: iElement.text() },
uiCodemirrorConfig.codemirror || {},
scope.$eval(iAttrs.uiCodemirror),
scope.$eval(iAttrs.uiCodemirrorOpts)
);

var codemirror = newCodemirrorEditor(iElement, codemirrorOptions);

configOptionsWatcher(
codemirror,
iAttrs.uiCodemirror || iAttrs.uiCodemirrorOpts,
scope
);

configNgModelLink(codemirror, ngModel, scope);

configUiRefreshAttribute(codemirror, iAttrs.uiRefresh, scope);

// Allow access to the CodeMirror instance through a broadcasted event
// eg: $broadcast('CodeMirror', function(cm){...});
scope.$on('CodeMirror', function (event, callback) {
if (angular.isFunction(callback)) {
callback(codemirror);
} else {
throw new Error('the CodeMirror event requires a callback function');
}
});

// onLoad callback
if (angular.isFunction(codemirrorOptions.onLoad)) {
codemirrorOptions.onLoad(codemirror);
}
}

return codemirrot;
}
function newCodemirrorEditor(iElement, codemirrorOptions) {
var codemirrot;

if (iElement[0].tagName === 'TEXTAREA') {
// Might bug but still ...
codemirrot = window.CodeMirror.fromTextArea(iElement[0], codemirrorOptions);
} else {
iElement.html('');
codemirrot = new window.CodeMirror(function (cm_el) {
iElement.append(cm_el);
}, codemirrorOptions);
}

function configOptionsWatcher(codemirrot, uiCodemirrorAttr, scope) {
if (!uiCodemirrorAttr) { return; }
return codemirrot;
}

var codemirrorDefaultsKeys = Object.keys(window.CodeMirror.defaults);
scope.$watch(uiCodemirrorAttr, updateOptions, true);
function updateOptions(newValues, oldValue) {
if (!angular.isObject(newValues)) { return; }
codemirrorDefaultsKeys.forEach(function(key) {
if (newValues.hasOwnProperty(key)) {
function configOptionsWatcher(codemirrot, uiCodemirrorAttr, scope) {
if (!uiCodemirrorAttr) { return; }

if (oldValue && newValues[key] === oldValue[key]) {
return;
}
var codemirrorDefaultsKeys = Object.keys(window.CodeMirror.defaults);
scope.$watch(uiCodemirrorAttr, updateOptions, true);
function updateOptions(newValues, oldValue) {
if (!angular.isObject(newValues)) { return; }
codemirrorDefaultsKeys.forEach(function (key) {
if (newValues.hasOwnProperty(key)) {

codemirrot.setOption(key, newValues[key]);
if (oldValue && newValues[key] === oldValue[key]) {
return;
}

codemirrot.setOption(key, newValues[key]);
}
});
}
});
}
}

function configNgModelLink(codemirror, ngModel, scope) {
if (!ngModel) { return; }
// CodeMirror expects a string, so make sure it gets one.
// This does not change the model.
ngModel.$formatters.push(function(value) {
if (angular.isUndefined(value) || value === null) {
return '';
} else if (angular.isObject(value) || angular.isArray(value)) {
throw new Error('ui-codemirror cannot use an object or an array as a model');
}
return value;
});


// Override the ngModelController $render method, which is what gets called when the model is updated.
// This takes care of the synchronizing the codeMirror element with the underlying model, in the case that it is changed by something else.
ngModel.$render = function() {
//Code mirror expects a string so make sure it gets one
//Although the formatter have already done this, it can be possible that another formatter returns undefined (for example the required directive)
var safeViewValue = ngModel.$viewValue || '';
codemirror.setValue(safeViewValue);
};

function configNgModelLink(codemirror, ngModel, scope) {
if (!ngModel) { return; }
// CodeMirror expects a string, so make sure it gets one.
// This does not change the model.
ngModel.$formatters.push(function (value) {
if (angular.isUndefined(value) || value === null) {
return '';
} else if (angular.isObject(value) || angular.isArray(value)) {
throw new Error('ui-codemirror cannot use an object or an array as a model');
}
return value;
});

// Keep the ngModel in sync with changes from CodeMirror
codemirror.on('change', function(instance) {
var newValue = instance.getValue();
if (newValue !== ngModel.$viewValue) {
scope.$evalAsync(function() {
ngModel.$setViewValue(newValue);
scope.$watch('model', function () {
//Code mirror expects a string so make sure it gets one
//Although the formatter have already done this, it can be possible that another formatter returns undefined (for example the required directive)
var safeValue = scope.model || '';
codemirror.setValue(safeValue);
});
}
});
}

function configUiRefreshAttribute(codeMirror, uiRefreshAttr, scope) {
if (!uiRefreshAttr) { return; }

scope.$watch(uiRefreshAttr, function(newVal, oldVal) {
// Skip the initial watch firing
if (newVal !== oldVal) {
$timeout(function() {
codeMirror.refresh();

// Keep the ngModel in sync with changes from CodeMirror
codemirror.on('change', function (instance) {
var newValue = instance.getValue();
if (newValue !== ngModel.$viewValue) {
scope.model = newValue;
}
});
}
});
}
}

function configUiRefreshAttribute(codeMirror, uiRefreshAttr, scope) {
if (!uiRefreshAttr) { return; }

scope.$watch(uiRefreshAttr, function (newVal, oldVal) {
// Skip the initial watch firing
if (newVal !== oldVal) {
$timeout(function () {
codeMirror.refresh();
});
}
});
}

}