Angularjs multiple ckEditors have same ngModel - angularjs

I have ckeditor with wiris plug in ,and I have ck-editors instances in ng-repeate ,when user posts any wiris question at any instance of ck-editor ,that post is posted to fist instance of ck-editor.I want if 3 instances of ck-editors in ng-repeat,users post 2nd instance of ck-editor with wiris equation then that post should show at only 2nd instance of ck-editor only ,Presently it shows at only 1st instance .
My html :
<div ng-repeat="item in items">
<form role="form" name="DiscussionForm" novalidate>
<input type="text" name="followup" ng-model="followupDis"
ck-editor placeholder="Compose" required>
<div><button type="submit"ng-click="start()">Post</button>
</div>
</form>
</div>
in js
app.directive('ckEditor', function() {
return {
require: '?ngModel',
link: function(scope, elm, attr, ngModel) {
var ck = CKEDITOR.replace(elm[0]);
if (!ngModel) return;
ck.on('instanceReady', function() {
ck.setData(ngModel.$viewValue);
});
function updateModel() {
scope.$apply(function() {
ngModel.$setViewValue(ck.getData());
});
}
ck.on('change', updateModel);
ck.on('focus', updateModel);
ck.on('key', updateModel);
ck.on('dataReady', updateModel);
ngModel.$render = function(value) {
ck.setData(ngModel.$viewValue);
};
}
};
});
I am expecting in directive I have create the instance for the first element var ck = CKEDITOR.replace(elm[0]); so it should change.
So any one can share the solution for multiple instances of ckeditor with wiris .

I have found by changing the id of the attribute we can solve this one .
in js:
app.directive('ckEditor', function() {
var counter = 0, prefix = "followupDiscussionPostReply-";
return {
require: '?ngModel',
link: function(scope, elm, attr, ngModel) {
if (!attr.id) {
attr.$set('id', prefix + (++counter));
}
// rest of the code
these changes fixed the multiple ckeditors have same ngModel issue for me .

Related

How to get instances of ckeditor with ng-repeat?

I am trying to use ckeditor with angularjs I have added a directive for the same. It is working fine. The problem is when I try to get the instances list of the ckeditor.
// directive
app.directive('ckeditor', function () {
return {
require: '?ngModel',
link: function (scope, element, attr, ngModel) {
var ck = CKEDITOR.replace(element[0]);
if(!ngModel)return;
ck.on('pasteState', function () {
scope.$apply(function () {
ngModel.$setViewValue(ck.getData());
});
});
ngModel.$render = function (value) {
ck.setData(ngModel.$viewValue);
};
}
};
});
// ng-repeat
<div ng-repeat="key in []| range:0:(vm.listCount-1)">
<textarea ckeditor id="content_{{key + 1}}"
ng-model="vm.contentList[key].content">
</textarea>
</div>
In controller I am trying to get instances list. There instead of
content_0,content_1 etc. I am getting content_{{key + 1}} only one instance
console.log(CKEDITOR.instances);
I want to get the proper instance of the ckeditor but I am getting only one value that is content_{{key + 1}} Please someone suggest.
My guess is that the directive needs to set the id attribute before invoking CKEDITOR.replace:
app.directive('ckeditor', function () {
return {
require: '?ngModel',
link: function (scope, element, attr, ngModel) {
//COMPUTE id attribute
if (attr.key) {
var keyValue = scope.$eval(attr.key);
element[0].id += "_"+keyValue;
};
var ck = CKEDITOR.replace(element[0]);
if(!ngModel)return;
ck.on('pasteState', function () {
scope.$apply(function () {
ngModel.$setViewValue(ck.getData());
});
});
ngModel.$render = function (value) {
ck.setData(ngModel.$viewValue);
};
}
};
});
Usage:
<div ng-repeat="key in [0,1]">
<textarea ckeditor key="$index+1" id="content"
ng-model="vm.contentList[key].content">
</textarea>
</div>
The CKEDITOR is likely instantiating the editor before the AngularJS framework computes id="content_{{key + 1}}".

angular directive was only called once

I am using a directive to build a custom validator and it works fine. But, it was called only once! If my "roleItems" are updated, this directive was not called again! How can it be called every time when "roleItems" are updated?
Here are the markups. And "Not-empty" is my directive.
<form name="projectEditor">
<ul name="roles" ng-model="project.roleItems" not-empty>
<li ng-repeat="role in project.roleItems"><span>{{role.label}}</span> </li>
<span ng-show="projectEditor.roles.$error.notEmpty">At least one role!</span>
</ul>
</form>
This is my directive. It should check if the ng-model "roleItems" are empty.
angular.module("myApp", []).
directive('notEmpty', function () {
return {
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
ctrl.$validators.notEmpty = function (modelValue, viewValue) {
if(!modelValue.length){
return false;
}
return true;
};
}
};
});
Main purpose of validator is validate ngModel value of user input or model change, so it should be uset to checkbox/textara/input and etc. You cant validate ng-model of everything. Angular is enough intelligent to knows that ng-model makes no sens so he is just ignoring it .
I you wanna change only error message you can check it via .length property. If you wanna make whole form invalid , i suggest you to make custom directive , put it on , and then in validator of this directive check scope.number.length > 0
Basically just adjust your directive code to input element and hide it .... via css or type=hidden, but dont make ngModel="value" its not make sense because ng-model is expecting value which can be binded and overwriteen but project.roleItems is not bindable! so put ng-model="dummyModel" and actual items to another param ...
<input type="hidden" ng-model="dummyIgnoredModel" number="project.roleItems" check-empty>
angular.module("myApp", []).
directive('checkEmpty', function () {
return {
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
ctrl.$validators.push(function (modelValue, viewValue) {
if(!scope.number.length){
return false;
}
return true;
});
//now we must "touch" ngModel
scope.$watch(function()
{
return scope.number
}, function()
{
ctrl.$setViewValue(scope.number.length);
});
}
};
});

AngularJS: Model not updating when using directive OR model-options

I have the current section of html that is used to check a users password,
<div class="form-group">
<label for="auditName" class="col-lg-4 control-label">Current Password </label>
<div class="col-lg-8">
<input type="password" placeholder="Current Password"
name="currentPassword"
class="form-control"
ng-model="currentPassword"
required=""
password-new
ng-model-options="{ updateOn: 'blur' }">
</div>
<div class="col-lg-offset-4" ng-if="form.$pending.oldPassword">checking....</div>
<div class="col-lg-offset-4" ng-if="form.$error.oldPassword">Please create a NEW password</div>
</div>
{{currentPassword}}
My issue is that the currentPassword is not being updated, so nothing is being displayed on the screen. If I remove the model-options AND I remove the reference to the new-password directive it will display as you type - so both of these are for some reason stopping the model from updating the value.
The directive new-password looks like this, and is still in a basic format I found elsewhere until I get this working properly,
app.directive('passwordNew', function ($timeout, $q) {
return {
restrict: 'AE',
require: 'ngModel',
link: function (scope, elm, attr, model) {
model.$asyncValidators.oldPassword = function () {
//here you should access the backend, to check if username exists
//and return a promise
var defer = $q.defer();
$timeout(function () {
model.$setValidity('oldPassword', true);
defer.resolve;
}, 1000);
return defer.promise;
};
}
}
});
Any ideas?
The {{currentPassword}} in your HTML is outside the scope of the directive. You need to link the two scopes. Checkout "Isolating the Scope of a Directive" in https://docs.angularjs.org/guide/directive.
Put something like this on the directive
scope: {
currentPassword: '='
},
EXAMPLE
This is how I solve a similar problem
app.directive('availableEmail', [
'dataSvc', (data:otolane.direct.IDataService) => {
return {
require: 'ngModel',
restrict: 'A',
link: function (scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function (viewValue) {
ctrl.$setValidity('availableEmail', true);
//only check the db if value is an email
if (viewValue.length > 3 && !ctrl.$error.email) {
data.account.checkEmail(viewValue)
.then(() => {
// data method resolves if email is available
ctrl.$setValidity('availableEmail', true);
})
.catch(() => {
//returns error if email is in use
ctrl.$setValidity('availableEmail', false);
});
}
return viewValue;
});
}
};
}
]);

How can I setValidate on an element in the form from a directive?

I have a directive that links to a textbox on the form, and I would like for this directive to set the 'required' error.
Here's a fiddle that shows what I'm trying to do
http://jsfiddle.net/scottieslg/7qLsj3rr/3/
Html:
<div ng-app='myApp' ng-controller='TestCtrl'>
<ng-form name='testForm'>
<input type='text' name='myInput' />
<div ng-messages="testForm.myInput.$error">
<div ng-message="required">Required</div>
</div>
<test-directive ng-model='testModel'></test-directive>
</ng-form>
</div>
Javascript:
var app = angular.module('myApp', ['ngMessages']);
app.controller('TestCtrl', function($scope) {
$scope.testModel = {}
});
app.directive('testDirective', function() {
return {
restrict: 'E',
require: 'ngModel',
template: '<div><button ng-click="setError()">Set Error</button></div>',
link: function(scope, element, attrs, ngModelCtrl) {
scope.setError = function() {
// How can I set .setValidate('require', true) on myInput from here??
}
}
}
});
If you want the test-directive to be able to control the ngModelController instance on a separate named input in a form, then using the ng-model directive again isn't the right thing to do, as that would create a new ngModelController instance on test-directive.
What the test-directive actually needs to know is the name of the input which has the controller:
<test-directive name='myInput'></test-directive>
Then it can access the form controller, using
require: '^form',
and use the name attribute value to find the ngModelController instance on the form:
link: function(scope, element, attrs, formController) {
scope.setError = function() {
var ngModelCtrl = formController[attrs.name];
ngModelCtrl.$setValidity('required', false);
}
}
You can see this at http://jsfiddle.net/7qLsj3rr/6/ .
Note: if you're using required as the key, then as soon as you type in the input again again, angular's own required validation will kick in an remove the error.

bootstrap selectpicker not working with angularjs dynamic data load for dropdown

It is working for Static dropdown list, but when its applying for dynamic data load with angularjs the selectpicker has been applied, but data's are not loaded.
if I removed this directive from dropdown then datas are loaded perfectly, what is the issue? I have tried more...
Note : the method created using with class so, no issue in that
bootselectpicker: function() {
return {
restrict: "A",
require: "ngModel",
link: function(scope, element, attrs, ctrl) {
element.selectpicker();
}
}
}
<select id="special-size" bootselectpicker ng-model="items.selectSize" ng-options="size.key as size.value for size in products.sizes" ng-change="sizeChange('size',items.selectSize)" class="selectpicker size-droplist">
<option value="">Select Size</option>
</select>
You have to wait until the DOM is loaded since the SelectPicker is constructed based on the <option> -elements inside your <select> -element. If the ng-options has not been constructed yet there is no <option> -elements and thus the SelectPicker is empty.
You init the SelectPicker after DOM is ready by using Angular's $timeout with no delay. Without delay the $timeout just waits until the DOM is ready and then runs the callback.
Like this:
link: function(scope, element, attrs, ctrl) {
$timeout(function() {
element.selectpicker();
});
}
Also, if your products.sizes is updated you have to manually run element.selectpicker('refresh') since the SelectPicker does not listen for changes in <option>s.
a solution to this is the following:
Defining a select like this:
<select class="selectpicker" data-live-search="true" ng-model="id">
<option class="small-font" selected value='any'>Anyone</option>
<option class="small-font" ng-repeat="member in List" data-select-watcher data-last="{{$last}}" value="{{member.id}}">{{member.name}}</option>
</select>
and the selectWatcher directive:
app.directive('selectWatcher', function ($timeout) {
return {
link: function (scope, element, attr) {
var last = attr.last;
if (last === "true") {
$timeout(function () {
$(element).parent().selectpicker('val', 'any');
$(element).parent().selectpicker('refresh');
});
}
}
};
});
What it does is detect when the last option has been added in the select, and then choose the default option and refresh.
setTimeout(function () {
$('.selectpicker').selectpicker('refresh');
},1000)
Call this function where you are making your array for ng-repeat or ng-options
try this directive
.directive('selectPicker', ['$parse', function ($parse) {
return {
restrict: 'A',
require: '?ngModel',
priority: 10,
compile: function (tElement, tAttrs, transclude) {
tElement.selectpicker($parse(tAttrs.selectpicker)());
tElement.selectpicker('refresh');
return function (scope, element, attrs, ngModel) {
if (!ngModel) return;
scope.$watch(attrs.ngModel, function (newVal, oldVal) {
scope.$evalAsync(function () {
if (!attrs.ngOptions || /track by/.test(attrs.ngOptions)) element.val(newVal);
element.selectpicker('refresh');
});
});
ngModel.$render = function () {
scope.$evalAsync(function () {
element.selectpicker('refresh');
});
}
};
}
};
}])
this is the html
<select class="form-control with-search " select-picker data-live-search="true" title="Select Consumer" ng-model="selectedConsumer" ng-options="c.id as c.firstName + ' ' + c.lastName for c in consumers">
</select>

Resources