Use 2 ui-codemirrors in 1 controller - angularjs

I am coding a very very basic playground by using AngularJS and ui-codemirror. Here is the code (JSBin).
<html>
<head>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/angular-ui/0.4.0/angular-ui.css">
<link rel="stylesheet" href="https://codemirror.net/lib/codemirror.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.min.js"></script>
<script src="https://codemirror.net/lib/codemirror.js"></script>
<script src="https://codemirror.net/addon/edit/matchbrackets.js"></script>
<script src="https://codemirror.net/mode/htmlmixed/htmlmixed.js"></script>
<script src="https://codemirror.net/mode/xml/xml.js"></script>
<script src="https://codemirror.net/mode/javascript/javascript.js"></script>
<script src="https://codemirror.net/mode/css/css.js"></script>
<script src="https://codemirror.net/mode/clike/clike.js"></script>
<script src="https://codemirror.net/mode/php/php.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui/0.4.0/angular-ui.js"></script>
</head>
<body>
<div ng-app="myApp">
<div ng-controller="codeCtrl">
HTML:<br>
<textarea ui-codemirror ng-model="html"></textarea>
<br>CSS:<br>
<textarea ui-codemirror ng-model="css"></textarea>
</div>
Output:
<section id="output">
<iframe></iframe>
</section>
</div>
</body>
</html>
JavaScript:
var myApp = angular.module('myApp', ['ui']);
myApp.value('ui.config', {
codemirror: {
mode: 'text/x-php',
lineNumbers: true,
matchBrackets: true,
}
});
function codeCtrl($scope, codeService) {
$scope.html = '<body>default</body>';
$scope.css = "body {color: red}";
$scope.$watch('html', function () { codeService.render($scope.html, $scope.css); }, true);
$scope.$watch('css', function () { codeService.render($scope.html, $scope.css); }, true);
}
myApp.service('codeService', function () {
this.render = function (html, css) {
source = "<html><head><style>" + css + "</style></head>" + html +"</html>";
var iframe = document.querySelector('#output iframe'),
iframe_doc = iframe.contentDocument;
iframe_doc.open();
iframe_doc.write(source);
iframe_doc.close();
}
})
The above code works, but the problem is it applies one same ui.config to 2 ui-codemirror. Does anyone know how to apply mode html to the first ui-codemirror and mode css to the second ui-codemirror?
Additionally, how could I set the height (or rows) and width (or cols) of a ui-codemirror?

Since you're dealing with two separate text areas that have rather different roles (or imagine if they were more), it makes sense to define separate directives for them, each one accepting a different config object. I've created a JSBin which shows one possible approach, via directive factory that can be used to generated different "mirrors".
angular.module('codeMirrorApp')
.factory('CodeMirrorFactory', ['$parse',
function($parse) {
return {
createDirective: function(config) {
var configString = JSON.stringify(config);
return {
scope: true,
restrict: 'E',
template: '<textarea ui-codemirror=' + configString + ' ng-model="content"></textarea>',
controller: ['$scope', '$attrs', function($scope, $attrs) {
var handler = $parse($attrs.handler);
$scope.$watch('content', function(value) {
handler($scope, { content: value });
});
}]
};
}
};
}
]);
I'm intentionally using handlers provided by the parent controller instead of bindings to the parent scope as this makes things look more understandable even while looking at the HTML markup.
The controller:
angular.module('codeMirrorApp')
.controller('MirrorsController', ['RenderMirrors',
function(RenderMirrors) {
var ctrl = this,
html,
css;
ctrl.handleHtml = function(htmlString) {
html = htmlString;
RenderMirrors.render(html, css);
};
ctrl.handleCss = function(cssString) {
css = cssString;
RenderMirrors.render(html, css);
};
}
]);
Markup:
<div ng-app="codeMirrorApp">
<div ng-controller="MirrorsController as ctrl">
HTML:<br>
<html-code-mirror handler="ctrl.handleHtml(content)"></html-code-mirror>
<br>CSS:<br>
<css-code-mirror handler="ctrl.handleCss(content)"></css-code-mirror>
</div>
Output:
<section id="output">
<iframe></iframe>
</section>
</div>
Hope this helps.

Controller:
function codeCtrl($scope, codeService) {
$scope.editorOptions1 = {mode: 'text/html',
lineNumbers: false,
matchBrackets: true};
$scope.editorOptions2 = {mode: 'text/css',
lineNumbers: true,
matchBrackets: true};
$scope.html = '<body>default</body>';
$scope.css = "body {color: red}";
$scope.$watch('html', function () { codeService.render($scope.html, $scope.css); }, true);
$scope.$watch('css', function () { codeService.render($scope.html, $scope.css); }, true);
}
Html :
<div ng-controller="codeCtrl">
HTML:<br>
<textarea ui-codemirror="editorOptions1" ng-model="html"></textarea>
<br>CSS:<br>
<textarea ui-codemirror="editorOptions2" ng-model="css"></textarea>
</div>

Related

tinymce is not working inside ng-view?

I am working in a MEAN project.
At the back end I need tinymce for cms editing.
I am using ng-view for each page content. but tiny mce is not working inside ng-view
here is my index.html file here it is working fine
<html lang="en" ng-app="AdminApp" >
<head>
<script type="text/javascript" src="/asset/tiny/tiny_mce/tiny_mce.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<script type="text/javascript" src="includes/tinymce.js"></script>
<script type="text/javascript">
tinyMCE.init({
// General options
width : "505",
height : "150",
mode : "textareas",
theme : "advanced",
extended_valid_elements : "iframe[src|width|height|name|align|type|class|frameborder]",
plugins : "autolink,lists,pagebreak,style,layer,table,save,advhr,advimage,advlink,emotions,iespell,inlinepopups,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras,template,wordcount,advlist,autosave,imagemanager",
// Theme options
theme_advanced_buttons1 : "bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,search,replace,|,media,|,bullist,numlist,|,blockquote,|,undo,redo,|,link,unlink,|,",
theme_advanced_buttons2 : "fontsizeselect,forecolor,backcolor,|,preview,fullscreen,code,insertimage",
theme_advanced_buttons3 : "",
theme_advanced_buttons4 : "",
theme_advanced_toolbar_location : "top",
theme_advanced_toolbar_align : "left",
theme_advanced_statusbar_location : "bottom",
theme_advanced_resizing : true,
relative_urls : false,
remove_script_host : false,
document_base_url : "",
// Example content CSS (should be your site CSS)
content_css : "css/content.css",
// Style formats
style_formats : [
{title : 'Bold text', inline : 'b'},
{title : 'Red text', inline : 'span', styles : {color : '#ff0000'}},
{title : 'Red header', block : 'h1', styles : {color : '#ff0000'}},
{title : 'Example 1', inline : 'span', classes : 'example1'},
{title : 'Example 2', inline : 'span', classes : 'example2'},
{title : 'Table styles'},
{title : 'Table row 1', selector : 'tr', classes : 'tablerow1'}
],
// Replace values for the template plugin
template_replace_values : {
username : "Some User",
staffid : "991234"
}
});
</script>
<base href="/admin/" />
</head>
<body>
<!--div ng-view></div-->
<textarea>hello</textarea>
</body>
</html>
inside ng-view code
<textarea>home</textarea>
I am using node.js server
Please help me to solve this
Thank you
You can't use tinymce as is in angularjs applications. You should create first directive for that.
However there is already directive for that which you can use: https://github.com/angular-ui/ui-tinymce
Here is the steps to get started with it once you have downloaded ui-tinymce:
index.html
<!DOCTYPE html>
<head>
<script type="text/javascript" src="bower_components/tinymce-dist/tinymce.js"></script>
<script type="text/javascript" src="bower_components/angular/angular.js"></script>
<script type="text/javascript" src="bower_components/angular-ui-tinymce/src/tinymce.js"></script>
<script type="text/javascript" src="app.js"></script>
</head>
<body ng-app="myApp">
<form method="post" ng-controller="TinyMceController">
<textarea ui-tinymce="tinymceOptions" ng-model="tinymceModel"></textarea>
<button ng-click="getContent()">Get content</button>
<button ng-click="setContent()">Set content</button>
</form>
</body>
app.js
var myAppModule = angular.module('myApp', ['ui.tinymce']);
myAppModule.controller('TinyMceController', function($scope) {
$scope.tinymceModel = 'Initial content';
$scope.getContent = function() {
console.log('Editor content:', $scope.tinymceModel);
};
$scope.setContent = function() {
$scope.tinymceModel = 'Time: ' + (new Date());
};
$scope.tinymceOptions = {
plugins: 'link image code',
toolbar: 'undo redo | bold italic | alignleft aligncenter alignright | code'
};
});
Hope, it works for you. Reference Link - http://embed.plnkr.co/vL7MqL/
Index.html
<script src="//tinymce.cachefly.net/4.0/tinymce.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.2/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.2/angular-route.min.js"></script>
<script type="text/javascript" src="tinymce.js"></script>
<script src="https://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.6.0.js"></script>
<script src="example.js"></script>
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet">
<div ng-app="plunker">
<ng-view></ng-view>
</div>
view.html
<textarea data-ui-tinymce data-ng-model="modal.one"></textarea>
change in the input doesn't change the textarea<br>
<input ng-model="modal.one">
tinymce.js
/**
* Binds a TinyMCE widget to <textarea> elements.
*/
angular.module('ui.tinymce', [])
.value('uiTinymceConfig', {})
.directive('uiTinymce', ['uiTinymceConfig', function(uiTinymceConfig) {
uiTinymceConfig = uiTinymceConfig || {};
var generatedIds = 0;
return {
require: '?ngModel',
link: function(scope, elm, attrs, ngModel) {
var expression, options, tinyInstance;
// generate an ID if not present
if (!attrs.id) {
attrs.$set('id', 'uiTinymce' + generatedIds++);
}
options = {
// Update model when calling setContent (such as from the source editor popup)
setup: function(ed) {
ed.on('init', function(args) {
ngModel.$render();
});
// Update model on button click
ed.on('ExecCommand', function(e) {
ed.save();
ngModel.$setViewValue(elm.val());
if (!scope.$$phase) {
scope.$apply();
}
});
// Update model on keypress
ed.on('KeyUp', function(e) {
console.log(ed.isDirty());
ed.save();
ngModel.$setViewValue(elm.val());
if (!scope.$$phase) {
scope.$apply();
}
});
},
mode: 'exact',
elements: attrs.id
};
if (attrs.uiTinymce) {
expression = scope.$eval(attrs.uiTinymce);
} else {
expression = {};
}
angular.extend(options, uiTinymceConfig, expression);
setTimeout(function() {
tinymce.init(options);
});
ngModel.$render = function() {
console.log("render")
if (!tinyInstance) {
tinyInstance = tinymce.get(attrs.id);
}
if (tinyInstance) {
tinyInstance.setContent(ngModel.$viewValue || '');
}
};
}
};
}]);
example.js
var myApp = angular.module('plunker', ['ngRoute','ui.tinymce','ui.bootstrap']).
config(['$routeProvider', function($routeProvider) {
console.log("init angular");
$routeProvider.when('/', {templateUrl: 'view.html', controller: 'View'});
$routeProvider.otherwise({redirectTo: '/'});
}]);
myApp.controller('View', ['$scope', function ($scope) {
console.log("View Controller");
$scope.modal = {one:"hello"};
}])

Can't get RubaXa/angular-legacy-sortablejs to move items between lists

Can someone tell me why this plunk will not allow me to move items between the two lists?
I am able to get shared lists to work using the plain RubaXa Sortable library and plain Javascript, but I have not been able to get them to work with Angular and RubaXa/angular-legacy-sortablejs library.
I have read and re-read the docs on the configuration options here and I swear I am doing it correctly.
I have also reviewed the example from the docs (not allowed to link it here due to low rep points) with no success.
I have created two lists and connected them using identical config info:
var ctrl = this;
ctrl.sortableConf = {
group: {
name: 'tags',
pull: true,
put: true
},
sort: true,
animation: 150,
draggable: '.list-group-item',
filter: '.js-remove',
chosenClass: ".sortable-chosen"
};
They both sort just fine internally, I just can't drag an item from one to the other or vice versa.
The documentation is wrong, or I don't know how to properly reference it when not using a partial page instead of an embedded template.
After debugging the options-loading code in sortable.js, I realized that it was not loading the group: block from the ctrl.sortableConf and I was getting stuck with the default values:
After trying a ton of different ways to do this, I stumbled on this example and was able to work it out form there.
Here is a plunk and a copy of the code just in case that goes away:
// create angular app
var tagsApp = angular.module('tagsApp', ['ng-sortable']);
tagsApp.controller('bugTagController', ['$scope', function($scope) {
$scope.tags = [
'Beans',
'Potatoes'
];
$scope.bugTagControllerConfig = {
group: 'tags',
pull: true,
put: true,
sort: true,
animation: 150,
draggable: '.list-group-item',
filter: '.js-remove',
chosenClass: ".sortable-chosen",
accept: function(sourceItemHandleScope, destSortableScope) {
console.log('masterTagController:accept');
return true;
},
onStart: function(evt) {
console.log('masterTagController:onStart');
},
onEnd: function(evt) {
console.log('masterTagController:onEnd');
},
onAdd: function(evt) {
console.log('masterTagController:onAdd');
},
onRemove: function(evt) {
console.log('masterTagController:onAdd');
},
onFilter: function(evt) {
var el = masterTags.closest(evt.item); // get dragged item
el && el.parentNode.removeChild(el);
console.log('masterTagController:onFilter');
},
onSort: function(evt) {
console.log('masterTagController:onSort');
var $item = $(evt.item);
var id = $item.data('id');
if (evt.action === 'add') {
console.log('masterTagController:add');
// put a check to make sure it's unique
// check to see if this node has already been added and prevent it it has
var itemCount = evt.item.parentNode.children.length;
for (var i = 0; i < itemCount; i++) {
var $child = $(evt.item.parentNode.children[i]);
var childId = $child.data('id');
if (childId === id && i !== evt.newIndex) {
console.log('masterTagController:rejectedNewItem');
evt.preventDefault();
return;
}
}
if (evt.newIndex === itemCount - 1) {
Sortable.utils.swap(evt.item.parentNode, evt.newIndex, evt.newIndex - 1);
}
}
}
};
}]);
tagsApp.controller('masterTagController', ['$scope', function($scope) {
$scope.tags = [
'Apples',
'Oranges',
'Comquats',
'Bannanas',
'Pineapples'
];
$scope.masterTagControllerConfig = {
group: 'tags',
pull: true,
put: true,
sort: true,
animation: 150,
draggable: '.list-group-item',
filter: '.js-remove',
chosenClass: ".sortable-chosen",
accept: function(sourceItemHandleScope, destSortableScope) {
console.log('masterTagController:accept');
return true
},
onStart: function(evt) {
console.log('masterTagController:onStart');
},
onEnd: function(evt) {
console.log('masterTagController:onEnd');
},
onAdd: function(evt) {
console.log('masterTagController:onAdd');
},
onRemove: function(evt) {
console.log('masterTagController:onAdd');
},
onFilter: function(evt) {
var el = masterTags.closest(evt.item); // get dragged item
el && el.parentNode.removeChild(el);
console.log('masterTagController:onFilter');
},
onSort: function(evt) {
console.log('masterTagController:onSort');
var $item = $(evt.item);
var id = $item.data('id');
if (evt.action === 'add') {
console.log('masterTagController:add');
// put a check to make sure it's unique
// check to see if this node has already been added and prevent it it has
var itemCount = evt.item.parentNode.children.length;
for (var i = 0; i < itemCount; i++) {
var $child = $(evt.item.parentNode.children[i]);
var childId = $child.data('id');
if (childId === id && i !== evt.newIndex) {
console.log('masterTagController:rejectedNewItem');
evt.preventDefault();
return;
}
}
if (evt.newIndex === itemCount - 1) {
Sortable.utils.swap(evt.item.parentNode, evt.newIndex, evt.newIndex - 1);
}
}
}
};
}]);
And here is the html:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div ng-app="tagsApp">
<!-- Starting Secondary Tags -->
<div class="col-md-2">
<h2>Tags on this list</h2>
<div class="well" ng-controller="bugTagController">
<ul id="bugTags" class="list-group" ng-sortable="bugTagControllerConfig" ng-model="tags" style="well-lg">
<li class="list-group-item" ng-repeat="tag in tags" ng-sortable-item style="well-lg">
<div ng-sortable-item-handle>{{ tag }}</div>
</li>
</ul>
</div>
</div>
<!-- Ending Secondary Tags -->
<!-- Starting Master Tags -->
<div class="col-md-2">
<h2>Master Tag List</h2>
<div class="well" ng-controller="masterTagController">
<ul id="masterTags" class="list-group" ng-sortable="masterTagControllerConfig" ng-model="tags" style="well-lg">
<li class="list-group-item" ng-repeat="tag in tags" ng-sortable-item style="well-lg">
<div ng-sortable-item-handle>{{ tag }}</div>
</li>
</ul>
</div>
<!-- Ending Master Tags -->
</div>
</div>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.1.0.js" integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk=" crossorigin="anonymous"></script>
<script type="text/javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular-route.js"></script>
<script type="text/javascript" src="https://rubaxa.github.io/Sortable/Sortable.js"></script>
<script type="text/javascript" src="ng-sortable.js"></script>
<script src="script.js"></script>
</body>
</html>

Merge two examples in Angularjs

I am trying to build #TagFriends and comment box feature from FACEBOOK.
I found two examples that might help, I am having library issue when I import one examples library in second one, whole code fails to work, if they are executed individually they work correctly.
1.Simple comment box :
http://devzone.co.in/angularjs-example-simple-user-comment-box/
2.#Tag friend code Below:
<!DOCTYPE html>
<html>
<head>
<link data-require="bootstrap#*" data-semver="3.3.2" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
<link rel="stylesheet" href="http://urbanoalvarez.es/smart-area/dist/smart-area.css">
<script data-require="jquery#2.1.3" data-semver="2.1.3" src="http://code.jquery.com/jquery-2.1.3.min.js"></script>
<script data-require="bootstrap#*" data-semver="3.3.2" src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
<script data-require="angular.js#1.3.8" data-semver="1.3.8" src="https://code.angularjs.org/1.3.8/angular.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular-sanitize.js"></script>
<script src="http://urbanoalvarez.es/smart-area/dist/smart-area.js"></script>
<script src="elastic.js"></script>
<script src="app.js"></script>
<style>
.user{
color: #0074D9;
}
</style>
</head>
<body ng-app="myApp">
<div class="container" ng-controller="DemoController">
<h3>MOC 12</h3>
<!--<h4>#user mentions</h4>-->
<textarea class="form-control code" rows="5" ng-model="text" ng-trim="false" msd-elastic smart-area="config"></textarea>
<hr>
<small class="text-muted">
<b>Available users:</b><br> Bret, Antonette, Samantha, Karianne, Kamren, Leopoldo_Corkery, Elwyn.Skiles, Delphine, Maxime_Nienow, Moriah.Stanton <br>
Type for example "Hey #Antonette"
</div>
<script>
/*
* angular-elastic v2.4.2
* (c) 2014 Monospaced http://monospaced.com
* License: MIT
*/
angular.module('myApp', ['smartArea', 'monospaced.elastic'])
.controller('DemoController', ['$scope', '$http', function($scope, $http) {
$scope.text = '';
$scope.config = {
autocomplete: [
{
words: [/#([A-Za-z]+[_A-Za-z0-9]+)/gi],
cssClass: 'user'
}
],
dropdown: [
{
trigger: /#([A-Za-z]+[_A-Za-z0-9]+)/gi,
list: function(match, callback){
// match is the regexp return, in this case it returns
// [0] the full match, [1] the first capture group => username
$http.get('http://jsonplaceholder.typicode.com/users')
.success(function(data){
// Prepare the fake data
var listData = data.filter(function(element){
return element.username.substr(0,match[1].length).toLowerCase() === match[1].toLowerCase()
&& element.username.length > match[1].length;
}).map(function(element){
return {
display: element.username, // This gets displayed in the dropdown
item: element // This will get passed to onSelect
};
});
callback(listData);
}).error(function(err){
console.error(err);
});
},
onSelect: function(item){
return item.display;
},
mode: 'replace'
}
]
};
}]);
angular.module('monospaced.elastic', [])
.constant('msdElasticConfig', {
append: ''
})
.directive('msdElastic', [
'$timeout', '$window', 'msdElasticConfig',
function($timeout, $window, config) {
'use strict';
return {
require: 'ngModel',
restrict: 'A, C',
link: function(scope, element, attrs, ngModel) {
// cache a reference to the DOM element
var ta = element[0],
$ta = element;
// ensure the element is a textarea, and browser is capable
if (ta.nodeName !== 'TEXTAREA' || !$window.getComputedStyle) {
return;
}
// set these properties before measuring dimensions
$ta.css({
'overflow': 'hidden',
'overflow-y': 'hidden',
'word-wrap': 'break-word'
});
// force text reflow
var text = ta.value;
ta.value = '';
ta.value = text;
// exit if elastic already applied (or is the mirror element)
if ($ta.data('elastic')) {
return;
}
// Opera returns max-height of -1 if not set
maxHeight = maxHeight && maxHeight > 0 ? maxHeight : 9e4;
// append mirror to the DOM
if (mirror.parentNode !== document.body) {
angular.element(document.body).append(mirror);
}
// set resize and apply elastic
$ta.css({
'resize': (resize === 'none' || resize === 'vertical') ? 'none' : 'horizontal'
}).data('elastic', true);
/*
* methods
*/
/*
* initialise
*/
// listen
if ('onpropertychange' in ta && 'oninput' in ta) {
// IE9
ta['oninput'] = ta.onkeyup = adjust;
} else {
ta['oninput'] = adjust;
}
$win.bind('resize', forceAdjust);
scope.$watch(function() {
return ngModel.$modelValue;
}, function(newValue) {
forceAdjust();
});
scope.$on('elastic:adjust', function() {
initMirror();
forceAdjust();
});
$timeout(adjust);
/*
* destroy
*/
scope.$on('$destroy', function() {
$mirror.remove();
$win.unbind('resize', forceAdjust);
});
}
};
}
]);
</script>
</body>
</html>
I have implemented this feature. Find the GitHub: github.com/noob93/Smart-Comment-Box
The Devzone example is using a deprecated feature of AngularJs, I modified the code and feature started working fine.

handsontable in an angularjs directive - render an anchor that has an ng-click

So I'm using Handsontable to render a grid. (Yes, I am NOT using the ngHandsontable. I started out with that but ran into problems and so I went with just rendering a Handsontable from an angularjs directive.)
I want one column to hold an anchor tag.
I want the anchor tag to have the angularjs ng-click directive.
Everything renders correctly but the ng-click is not called.
Here is my example.
var APP = angular.module('APP', ['controllers']);
angular.module('controllers',[])
.controller('testController', function ($scope) {
$scope.doNgClick = function() {
alert('ng-click');
// console.log('ng-click');
};
$scope.simple = [
{
test: "<a href='javascript:void(0);' ng-click='doNgClick()'>Test</a>"
// test: "<a ng-click='doNgClick()'>Test</a>"
}
];
});
APP.directive('htable',function($compile) {
var directive = {};
directive.restrict = 'A';
directive.scope = {
data : '='
};
directive.link = function(scope,element,attrs) {
var container = $(element);
// var safeHtmlRenderer = function (instance, td, row, col, prop, value, cellProperties) {
// var escaped = Handsontable.helper.stringify(value);
// td.innerHTML = escaped;
// return td;
// };
var settings = {
data: scope.data,
readOnly: true,
colHeaders: ['Link'],
columns: [
{
data: "test",
renderer: "html",
// renderer: safeHtmlRenderer,
readyOnly: true
}
]
};
var hot = new Handsontable( container[0], settings );
hot.render();
// console.log(element.html());
// $compile(element.contents())(scope);
};//--end of link function
return directive;
});
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="//handsontable.com/dist/handsontable.full.css">
</head>
<body>
<div ng-app="APP">
<div ng-controller="testController">
<div htable data="simple"></div>
</div
</div>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.24/angular.min.js"></script>
<script src="//handsontable.com/dist/handsontable.full.js"></script>
</body>
</html>
After much reading and digging here is my own answer.
//-- With help from the following:
//--
//-- http://stackoverflow.com/questions/18364208/dynamic-binding-of-ng-click
//-- http://weblogs.asp.net/dwahlin/creating-custom-angularjs-directives-part-3-isolate-scope-and-function-parameters
//--
var APP = angular.module('APP', ['controllers']);
angular.module('controllers',[])
.controller('testController', function ($scope) {
$scope.click = function(msg) {
console.log('ctrl_doNgClick: ng-click: msg: '+msg);
};
$scope.simple = [
{
test: "<a href='javascript:void(0);' ng-click='dir_ctrl_click(\"blah1,blah1\")'>Test 1</a>"
},
{
test: "<a href='javascript:void(0);' ng-click='doClick(\"blah2,blah2\")'>Test 2</a>"
},
{
test: "<a href='javascript:void(0);' ng-click='doClick(\"blah3,blah3\")'>Test 3</a>"
}
];
});
APP.directive('htable',function($compile) {
var directive = {};
directive.restrict = 'A';
directive.scope = {
data : '=',
click : '&'
};
directive.controller = function($scope) {
$scope.dir_ctrl_click = function( msg ) {
console.log('controller: dir_ctrl_click: click via the directive controller method');
$scope.click()(msg);
};
};
directive.link = function(scope,element,attrs) {
var container = $(element);
scope.doClick = function(msg) {
console.log('link: doClick: click via the directive link method');
scope.click()(msg);
};
var linkHtmlRenderer = function (instance, td, row, col, prop, value, cellProperties) {
//-- here is the magic that works
//-- the method, in ng-click, must either be defined here in the link method or in the controller method (the example data contains both)
var el = angular.element(td);
el.html($compile(value)(scope));
return el;
};
var settings = {
data: scope.data,
readOnly: true,
colHeaders: ['Link'],
columns: [
{
data : "test",
renderer : linkHtmlRenderer,
readyOnly : true
}
]
};
var hot = new Handsontable( container[0], settings );
// hot.render();
};//--end of link function
return directive;
});
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="http://handsontable.com/dist/handsontable.full.css">
</head>
<body>
<div ng-app="APP">
<div ng-controller="testController">
<div htable data="simple" click="click"></div>
</div
</div>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.24/angular.min.js"></script>
<script src="http://handsontable.com/dist/handsontable.full.js"></script>
</body>
</html>

Mithril with angularjs

I am a newbie to Mithril JS framework and trying to integrate Mitril view with angularJS. Has anyone tried this before?
I want to check how can we bind the angular controller methods to click events of elements created in Mitril.
I got this working by having this code
var e = document.getElementById('elementId');
var scope = angular.element(e).scope();
m("a[href='javascript:;']", {
onclick : scope.someMethod
}, "Test");
But I am not sure if this is right way to do this.
I'd say that is not idiomatic angular code.
A more idiomatic way might be to use a directive on the Angular side, and pass in an event dispatcher controller to the view on the mithril side:
//mithril code
var testWidget = function(ctrl) {
return m("a[href='javascript:;']", {onclick: ctrl.onclick}, "Test")
}
//angular code
angular.module("foo").directive("testWidget", function() {
return {
restrict: "E",
link: function($scope, element, attrs) {
var template = testWidget({
onclick: function() {
$scope.$apply(function() {
$scope.$eval(attrs.onclick)
})
}
})
m.render(element, template)
}
}
})
angular.module("foo").controller("MyCtrl", function() {
this.doStuff = function() {
console.log("called doStuff")
}
})
<div ng-controller="MyCtrl as c">
<test-widget onclick="c.doStuff()"></test-widget>
</div>
// Code goes here
(function() {
'use strict';
angular
.module('app', [])
.directive('testMithrilScope', testMithrilScope)
.controller('MyCtrl', MyCtrl);
var testMithrilWidgetScope = function(ctrl) {
return m("a[href='javascript:;']", {onclick: ctrl.directiveclick}, ctrl.text)
}
var htmllinks = [
{text: "Link 1 "},
{text: "Link 2 "},
{text: "Link 3 "},
{text: "Link 4 "},
{text: "Link 5 "},
{text: "Link 6 "}
];
function testMithrilScope() {
return {
restrict: "E",
scope : {
htmlclick: '&'
},
link: function($scope, element, attrs) {
function makeList1() {
return m('ul', htmllinks.map(function(a, index){
return m('li', testMithrilWidgetScope({
directiveclick : function() {
var data = {
arg1: a.text
}
$scope.htmlclick(data);
},
text : a.text
})
);
}));
}
var template1 = makeList1();
m.render(element[0], template1)
}
}
}
function MyCtrl() {
this.doStuff = function(text) {
console.log("You clicked: " + text)
}
}
})();
<!DOCTYPE html>
<html>
<head>
<script data-require="angularjs#1.5.8" data-semver="1.5.8" src="https://opensource.keycdn.com/angularjs/1.5.8/angular.min.js"></script>
<script data-require="mithril#0.2.4" data-semver="0.2.4" src="https://cdn.jsdelivr.net/mithril/0.2.4/mithril.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="app">
<div ng-controller="MyCtrl as ctrl">
<test-mithril-scope htmlclick="ctrl.doStuff(arg1)"></test-mithril-scope>
</div>
</body>
</html>

Resources