angularjs ckeditor directive sometimes fails to load data from a service - angularjs

I used Vojta's angularJS directive but sometimes ckeditor would fail to display the data from a service. This almost never happened on a refresh, but often happened when navigating back to the page. I was able to verify that the $render function was always calling ck.setData with the correct data, but sometimes it would not display.

It appears that the $render method was sometimes called before ckeditor was ready. I was able to resolve this by adding a listener to the instanceReady event to make sure that it was called at least once after ckeditor was ready.
ck.on('instanceReady', function() {
ck.setData(ngModel.$viewValue);
});
In the interest of completeness, here is the complete directive that I used.
//Directive to work with the ckeditor
//See http://stackoverflow.com/questions/11997246/bind-ckeditor-value-to-model-text-in-angularjs-and-rails
app.directive('ckEditor', function() {
return {
require: '?ngModel',
link: function(scope, elm, attr, ngModel) {
var ck = CKEDITOR.replace(elm[0],
{
toolbar_Full:
[
{ name: 'document', items : [] },
{ name: 'clipboard', items : [ 'Cut','Copy','Paste','PasteText','PasteFromWord','-','Undo','Redo' ] },
{ name: 'editing', items : [ 'Find','Replace','-','SpellChecker', 'Scayt' ] },
{ name: 'forms', items : [] },
{ name: 'basicstyles', items : [ 'Bold','Italic','Underline','Strike','Subscript','Superscript' ] },
{ name: 'paragraph', items : [
'NumberedList','BulletedList','-','JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock' ] },
{ name: 'links', items : [] },
{ name: 'insert', items : [ 'SpecialChar' ] },
'/',
{ name: 'styles', items : [ 'Styles','Format','Font','FontSize' ] },
{ name: 'colors', items : [] },
{ name: 'tools', items : [ 'Maximize' ] }
]
,
height: '290px',
width: '99%'
}
);
if (!ngModel) return;
//loaded didn't seem to work, but instanceReady did
//I added this because sometimes $render would call setData before the ckeditor was ready
ck.on('instanceReady', function() {
ck.setData(ngModel.$viewValue);
});
ck.on('pasteState', function() {
scope.$apply(function() {
ngModel.$setViewValue(ck.getData());
});
});
ngModel.$render = function(value) {
ck.setData(ngModel.$viewValue);
};
}
};
});

function updateModel() {
scope.$apply(function() {
if ( ck.getData().length ) {
ngModel.$setViewValue(ck.getData());
}
});
}
see full code:
return {
require: '?ngModel',
scope: true,
link: function (scope, element, attr, ngModel) {
if (!ngModel) return;
var ck = CKEDITOR.replace(element[0]);
ck.on('instanceReady', function() {
ck.setData(ngModel.$viewValue);
});
function updateModel() {
scope.$apply(function() {
if ( ck.getData().length ) {
ngModel.$setViewValue(ck.getData());
}
});
}
ck.on('pasteState', updateModel);
ck.on('change', updateModel);
ck.on('key', updateModel);
ck.on('dataReady', updateModel);
ngModel.$render = function() {
ck.setData(ngModel.$modelValue);
};
}
}

I also meet this issue
And when i found a new directive. It works well for me!!!
Please try this:
return {
require: '?ngModel',
scope: true,
link: function (scope, element, attr, ngModel) {
if (!ngModel) return;
var ck = CKEDITOR.replace(element[0]);
ck.on('instanceReady', function() {
ck.setData(ngModel.$viewValue);
});
function updateModel() {
scope.$apply(function() {
if ( ck.getData().length ) {
ngModel.$setViewValue(ck.getData());
}
});
}
ck.on('pasteState', updateModel);
ck.on('change', updateModel);
ck.on('key', updateModel);
ck.on('dataReady', updateModel);
ngModel.$render = function() {
ck.setData(ngModel.$modelValue);
};
}
};

Related

Webix 4.3 module not loading in Angularjs

I have an issue with the new Webix 4.3 release:
angular.js:66 Uncaught Error: [$injector:modulerr] Failed to instantiate
module monitorApp due to:
Error: [$injector:modulerr] Failed to instantiate module webix due to:
Error: [$injector:nomod] Module 'webix' is not available! You either
misspelled the module name or forgot to load it. If registering a module
ensure that you specify the dependencies as the second argument.
I get this error just updating the version from 4.2, here is how do i inject the module:
var app=angular.module("monitorApp", ['webix', 'ngRoute','ui.router']);
html:
<!--Includes-->
<script src="Scripts/angular.js"></script>
<script src="Scripts/webix.js"></script>
Any ideas about what is it going?
Starting from Webix 4.3, AngularJs integration is a separate module
https://docs.webix.com/desktop__whats_new_4_3.html
You can grab it from github
https://github.com/webix-hub/webix-angular
Or install through npm
npm install webix-angular
You can add an extra file which is mandatory for the Webix Angular Integration part.
// Code goes here
if (window.angular)
(function(){
function id_helper($element){
//we need uniq id as reference
var id = $element.attr("id");
if (!id){
id = webix.uid();
$element.attr("id", id);
}
return id;
}
function locate_view_id($element){
if (typeof $element.attr("webix-ui") != "undefined")
return $element.attr("id");
return locate_view_id($element.parent());
}
//creates webix ui components
angular.module("webix", [])
.directive('webixUi', [ "$parse", function($parse) {
return {
restrict: 'A',
scope: false,
link:function ($scope, $element, $attrs, $controller){
var dataname = $attrs["webixUi"];
var callback = $attrs["webixReady"];
var watch = $attrs["webixWatch"];
var wxRoot = null;
var id = id_helper($element);
$element.ready(function(){
if (wxRoot) return;
if (callback)
callback = $parse(callback);
//destruct components
$element.bind('$destroy', function() {
if (wxRoot && !wxRoot.$destructed && wxRoot.destructor)
wxRoot.destructor();
});
//ensure that ui is destroyed on scope destruction
$scope.$on('$destroy', function(){
if (wxRoot && !wxRoot.$destructed && wxRoot.destructor)
wxRoot.destructor();
});
//webix-ui attribute has some value - will try to use it as configuration
if (dataname){
//configuration
var watcher = function(data){
if (wxRoot) wxRoot.destructor();
if ($scope[dataname]){
var config = webix.copy($scope[dataname]);
config.$scope =$scope;
$element[0].innerHTML = "";
wxRoot = webix.ui(config, $element[0]);
if (callback)
callback($scope, { root: wxRoot });
}
};
if (watch !== "false")
$scope.$watch(dataname, watcher);
watcher();
} else {
//if webix-ui is empty - init inner content as webix markup
if (!$attrs["view"])
$element.attr("view", "rows");
var ui = webix.markup;
var tmp_a = ui.attribute; ui.attribute = "";
//FIXME - memory leaking, need to detect the moment of dom element removing and destroy UI
if (typeof $attrs["webixRefresh"] != "undefined")
wxRoot = ui.init($element[0], $element[0], $scope);
else
wxRoot = ui.init($element[0], null, $scope);
ui.attribute = tmp_a;
if (callback)
callback($scope, { root: wxRoot });
}
//size of ui
$scope.$watch(function() {
return $element[0].offsetWidth + "." + $element[0].offsetHeight;
}, function() {
if (wxRoot) wxRoot.adjust();
});
});
}
};
}])
.directive('webixShow', [ "$parse", function($parse) {
return {
restrict: 'A',
scope: false,
link:function ($scope, $element, $attrs, $controller){
var attr = $parse($attrs["webixShow"]);
var id = id_helper($element);
if (!attr($scope))
$element.attr("hidden", "true");
$scope.$watch($attrs["webixShow"], function(){
var view = webix.$$(id);
if (view){
if (attr($scope)){
webix.$$(id).show();
$element[0].removeAttribute("hidden");
} else
webix.$$(id).hide();
}
});
}
};
}])
.directive('webixEvent', [ "$parse", function($parse) {
var wrap_helper = function($scope, view, eventobj){
var ev = eventobj.split("=");
var action = $parse(ev[1]);
var name = ev[0].trim();
view.attachEvent(name, function(){
return action($scope, { id:arguments[0], details:arguments });
});
};
return {
restrict: 'A',
scope: false,
link:function ($scope, $element, $attrs, $controller){
var events = $attrs["webixEvent"].split(";");
var id = id_helper($element);
setTimeout(function(){
var first = $element[0].firstChild;
if (first && first.nodeType == 1)
id = first.getAttribute("view_id") || id;
var view = webix.$$(id);
for (var i = 0; i < events.length; i++) {
wrap_helper($scope, view, events[i]);
}
});
}
};
}])
.directive('webixElements', [ "$parse", function($parse) {
return {
restrict: 'A',
scope: false,
link:function ($scope, $element, $attrs, $controller){
var data = $attrs["webixElements"];
var id = id_helper($element);
if ($scope.$watchCollection)
$scope.$watchCollection(data, function(collection){
setTimeout(function(){
var view = webix.$$(id);
if (view){
view.define("elements", collection);
view.refresh();
}
},1);
});
}
};
}])
.directive('webixData', [ "$parse", function($parse) {
return {
restrict: 'A',
scope: false,
link:function ($scope, $element, $attrs, $controller){
var data = $attrs["webixData"];
var id = id_helper($element);
if ($scope.$watchCollection)
$scope.$watchCollection(data, function(collection){
if (collection){
setTimeout(function(){
loadData($element, id, collection, 0);
},1);
}
});
}
};
}]);
function loadData($element, id, collection, num){
if (num > 10) return;
var first = $element[0].firstChild;
if (first && first.nodeType == 1)
id = first.getAttribute("view_id") || id;
var view = webix.$$(id);
if (view){
if (view.options_setter){
view.define("options", collection);
view.refresh();
}else{
if (view.clearAll)
view.clearAll();
view.parse(collection);
}
} else {
webix.delay(loadData, this, [$element, id, collection], 100, num+1);
}
}
})();
<!doctype html>
<html lang="en" ng-app="webixApp">
<head>
<meta charset="utf-8">
<title>Webix - Angular : Layouts</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.4/angular.js"></script>
<script src="https://angular-ui.github.io/ui-router/release/angular-ui-router.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.webix.com/edge/webix.css">
<script type="text/javascript" src="https://cdn.webix.com/edge/webix.js"></script>
<script type="text/javascript" src="script.js"></script>
<script type="text/javascript">
var app = angular.module('webixApp', ["webix", 'ui.router']);
app.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/add");
$stateProvider
.state('/add', {
url: '/add',
template: 'This is the Routed Addition File'
})
.state('/sub', {
url: '/sub',
template: 'This is the Routed Subtraction File'
})
.state('/div', {
url: '/div',
template: 'This is the Routed Division File'
})
.state('/multi', {
url: '/multi',
template: 'This is the Routed Multiplication File'
})
})
app.controller('jsonCtrl', function($scope) {
var treeData = {
data: [{
id: "root",
value: "Super Micro Flow",
open: true,
link: "add",
data: [{
id: "1",
open: true,
value: "AP1",
link: "multi",
data: [{
id: "1.1",
value: "2G09#M10000761",
link: "div",
data: [{
id: "1.11",
value: "2G09#M10000761",
link: "add",
data: [{
id: "1.11",
value: "2G09#M10000761",
link: "sub"
}, {
id: "1.12",
value: "2G09#M10855757",
link: "multi"
}, {
id: "1.13",
value: "2G09#D10PP0761",
link: "div"
}]
}, {
id: "1.12",
value: "2G09#M10855757",
link: "multi"
}, {
id: "1.13",
value: "2G09#D10PP0761",
link: "div"
}]
}, {
id: "1.2",
value: "2G09#M10855757",
link: "multi"
}, {
id: "1.3",
value: "2G09#D10PP0761",
link: "div"
}]
}, {
id: "2",
open: false,
value: "AP12",
link: "multi",
data: [{
id: "2.1",
value: "2G09#M10000761",
link: "sub"
}, {
id: "2.2",
value: "2G09#M10855757",
link: "multi"
}, {
id: "2.3",
value: "2G09#D10PP0761",
link: "div"
}]
}, {
id: "3",
open: false,
value: "EP45",
link: "div",
data: [{
id: "3.1",
value: "2G09#M10000761",
link: "sub"
}, {
id: "3.2",
value: "2G09#M10855757",
link: "multi"
}, {
id: "3.3",
value: "2G09#D10PP0761",
link: "div"
}]
}, {
id: "4",
open: false,
value: "CR7767",
link: "sub",
data: [{
id: "4.1",
value: "2G09#M10000761",
link: "sub"
}, {
id: "4.2",
value: "2G09#M10855757",
link: "multi"
}, {
id: "4.3",
value: "2G09#D10PP0761",
link: "div"
}]
}]
}]
}
$scope.myTree = {
view: "tree",
data: treeData,
template: "{common.icon()}{common.folder()}<a ui-sref='#link#' href='##link#'>#value#</a>",
}
})
</script>
</head>
<body ng-controller="jsonCtrl">
<div webix-ui type="space">
<div height="35">Welcome to Angular Webix App </div>
<div view="cols" type="wide" margin="10">
<div width="300">
<input type="text" placeholder="Type something here" ng-model="app"> Hello {{app}}!
<div webix-ui="myTree"></div>
</div>
<div view="resizer"></div>
<div view="tabview">
<div header="Angular UI Routing Example">
<div ng-controller="jsonCtrl">
<div ui-view></div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
You can copy the script file and put into your application as an independent file. Don't write anything in this file.

Angular lazy tree

I'm studying angular and trying to write treeview with dynamic load. It has some initial pre-loaded nodes, but many of them have to be loaded by demand.
I write initial tree view here jsfiddle but cannot understand how to implement lazy load.
when I click NodeLazy I want to update treeService.Nodes and this update have to refresh view
when I click any Node I want select this node by bold font and be able get selected from treeService
Sure, I could use one of existing angular tree, but I want to understand how to do this.
var app = angular.module('demo', []);
app.directive("treeView", function() {
return {
restrict: "E",
replace: true,
scope: {
t: "=src"
},
template: "<ul class='ul-node'><tree-branch ng-repeat='c in t.children' src='c'></tree-branch></ul>"
};
});
app.directive("treeBranch", function($compile) {
return {
restrict: "E",
replace: true,
scope: {
b: "=src"
},
template: "<li class='{{b.class_li}}'>{{b.name}}</li>",
controller: function($scope) {
var has_children = angular.isArray($scope.b.children);
$scope.b.class_li = has_children ? "li-node" : "li-leaf";
},
link: function(scope, element, attrs) {
var has_children = angular.isArray(scope.b.children);
if (has_children) {
element.append('<tree-view src="b"></tree-view>');
$compile(element.contents())(scope);
}
element.on("click", function(event) {
console.log("click|" + scope.b.name);
event.stopPropagation();
if (has_children) {
element.toggleClass("ul-collapsed");
}
});
}
};
});
app.service("treeService", function() {
var nodes = {
children: [
{ name: "Node_1",
children: [
{ name: "Node_1.1",
children: [{ name: "Leaf_1" }, { name: "Leaf_2" }] },
{ name: "NodeLazy_1.2", }] },
{ name: "NodeLazy_2", }]
};
return {
Nodes : nodes
};
});
app.controller("ctrl", function($scope, treeService) {
$scope.nodes = treeService.Nodes;
});

Bind array to highcharts data part using Angular Directives

I need to bind an array of data to "data" part of highcharts which is embedded in
Angular directives but I don't know how exactly to do that.
This is my controller part which fetches data from repository:
vm.data = [];
function getVmAll(forceRefresh) {
return datacontext.esxservers.getAllWithoutPaging(forceRefresh, vm.vmAllSearchTemp)
.then(function (data) {
vm.data= data;
});
return data;
}
);
}
and here is the template which I use the controller in it:
<section class="mainbar" data-ng-controller="mbVirtualizationOverviewCtrl as vm">
<mb-virtualization-overview-donut-chart value="vm.data"></mb-virtualization-overview-donut-chart>
</section>
and the last but not least one is my directive which I use highcharts settings:
'use strict';
angular.module('app').directive('mbVirtualizationOverviewDonutChart', [
function () {
return {
restrict: "E",
templateUrl: '/app/directives/virtualizationOverview/mbVirtualizationOverviewDonutChartTemplate.html',
scope: {
value : "="
},
link: function (scope, el, attrs) {
scope.chart = Highcharts.chart(el[0], {
chart: {
type: 'pie',
},
title: {
text: scope.innerTitle
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: false
},
showInLegend: true
}
},
series: [{
data: undefined
}]
});
var init = true;
scope.$watch(
function () {
var chart = scope.chart;
if (init && scope.value.length > 0) {
init = false;
scope.value.forEach(function (item, index) {
chart.series[0].data.push(item);
});
}
});
}
};
}
]);

Link function of Directive is not being called

I have a scenario where I need to show a pie-chart in a popup modal (used ui-bootstrap modal). I used c3.js for my pie-chart requirement (inside a directive).
The pie-chart is not loading inside the pop up. But to my surprise when I tried to debug the issue when I opened the console it is loading. When I re-size the window it is loading.
How can i fix this issue?
'use strict';
angular.module('App')
.directive('pieChartDirective', function() {
return {
restrict: 'A',
scope: {
chartdata: '=',
},
link: function(scope, elem, attrs) {
var chart = c3.generate({
bindto: '#chart',
data: {
columns: [
['Javascript', scope.chartdata.Javascript],
['HTML', scope.chartdata.HTML],
['Css', scope.chartdata.Css],
['Angular', scope.chartdata.Angular],
['Bootstrap', scope.chartdata.Bootstrap],
['Jquery', scope.chartdata.Jquery],
['Communication', scope.chartdata.Communication]
],
type: 'pie',
},
legend: {
show: false
},
tooltip: {
format: {
value: function(value, ratio, id, index) {
return value;
}
}
}
});
}
};
});
Html:
<div pie-chart-directive chartdata="oChartData">
<div id="chart"></div>
</div>
Are you shure, that you include your directive properly in your html code as
<div pie-chart-directive></div>
Maybe you have to change your restriction to 'E' to use your directive as a tag elment
<pie-chart-directive chartdata="myData"></pie-chart-directive>
The reason could be, that at the moment you try to generate the chart, your '#chart' div isn't yet present in the dom tree. Therefore you have to resize to trigger a new draw. Try to wait until the dom is loaded
$('#chart').ready(function() {
var chart = c3.generate({
bindto: '#chart',
data: {
columns: [
['Javascript', scope.chartdata.Javascript],
['HTML', scope.chartdata.HTML],
['Css', scope.chartdata.Css],
['Angular', scope.chartdata.Angular],
['Bootstrap', scope.chartdata.Bootstrap],
['Jquery', scope.chartdata.Jquery],
['Communication', scope.chartdata.Communication]
],
type: 'pie',
},
legend: {
show: false
},
tooltip: {
format: {
value: function(value, ratio, id, index) {
return value;
}
}
}
});
});
I got it by giving size property:
link: function(scope, elem, attrs) {
var chart = c3.generate({
bindto: '#chart',
size: {
width:400,
height:350
},
data: {
columns: [
['Javascript', scope.chartdata.Javascript],
['HTML', scope.chartdata.HTML],
['Css', scope.chartdata.Css],
['Angular', scope.chartdata.Angular],
['Bootstrap', scope.chartdata.Bootstrap],
['Jquery', scope.chartdata.Jquery],
['Communication', scope.chartdata.Communication]
],
type: 'pie',
},
legend: {
show: false
},
tooltip: {
format: {
value: function(value, ratio, id, index) {
return value;
}
}
}
});
}

Angular material Treeview collapse/expand check box not working

Angular Material Tree view Checkbox is working fine when i try to collapse/expand first time and checked whether on Parent Node or Child Node, but it is not working when i try to Checked the different node (i.e.Child Node) on second time. I use custom directive to achieve Tree View functionality. Below is my Code.
Custom Directive
NFSModule.directive('nfsTree', function () {
return {
restrict: 'E',
replace: true,
scope: {
tree: '=source',
},
template: '<nfs-node ng-repeat="c in tree.children" cnode="c"></nfs-node>'
};
});
NFSModule.directive('nfsNode', function ($compile) {
return {
restrict: 'E',
replace: true,
scope:{
node: '=cnode'
},
template: '<li><span ng-click="toggleVisibility(node)"> {{ ( node.childrenVisibility && node.children.length ) ? "+" : "-" }}</span>' +
'<nfs-tree-checkbox ng-model="node.checked" ng-click="checkNode(node)"><span ng-bind="node.name"></span></nfs-tree-checkbox></li>', // HTML for a single node.
link: function (scope, element) {
/*
* Here we are checking that if current node has children then compiling/rendering children.
* */
if (scope.node && scope.node.children && scope.node.children.length > 0) {
scope.node.childrenVisibility = true;
var childNode = $compile('<ul ng-if="!node.childrenVisibility"><nfs-tree source="node" ></nfs-tree></ul>')(scope);
element.append(childNode);
} else {
scope.node.childrenVisibility = false;
}
},
controller: ["$scope", function ($scope) {
// This function is for just toggle the visibility of children
$scope.toggleVisibility = function (node) {
if (node.children) {
node.childrenVisibility = !node.childrenVisibility;
}
};
// Here We are marking check/un-check all the nodes.
$scope.checkNode = function (selectednode) {
if (selectednode.checked != undefined) {
var has_child = angular.isArray(selectednode.children);
if (has_child = true)
{
checkChildren(selectednode);
}
}
function checkChildren(c) {
angular.forEach(c.children, function (c) {
c.checked = selectednode.checked;
});
}
};
}]
};
});
NFSModule.directive('nfsTreeCheckbox', function () {
return {
restrict: 'E',
replace: true,
transclude: true,
scope: {
name: '#',
ngModel: '=',
ngClick: '=',
ngDisabled: '=',
label: '#'
},
template: function (elem, attr) {
return '<md-input-container class="md-inline" style="height:10px !important; margin-top:-5px; !important; margin-left:5px !important;">' +
'<md-checkbox ng-model="ngModel" ng-click="ngClick" ng-disabled="ngDisabled" aria-label="{{label}}" class="md-primary">' +
'<ng-transclude></ng-transclude>' +
' </md-checkbox>' +
'</md-input-container>'
}
};
});
$SCOPE
$scope.location = {
children:
[
{
name: 'Europe',
children: [
{
name: 'Italy',
children: [
{
name: 'Rome'
},
{ name: 'Milan' }
]
},
{ name: 'Spain' }
]
},
{
name: 'South America',
children: [
{ name: 'Brasil' },
{ name: 'Peru' }
]
}
]
};
HTML
<ul >
<nfs-tree source="location"></nfs-tree>
</ul>

Resources