AngularJS: Custom directive is not correctly computing data requirements - angularjs

I am building a custom directive to allow for a glyphicon to serve as an indicator (color), a notice (tooltip), a simple action (single click) and an ability for customized action in a modal (double click).
Here is a plunker of my progress.
On first load the colors are correct -- gray if value = 0, green for value of 1 and red for value of 2. The click also is correct -- a 0 clicked becomes 1, a 1 clicked becomes 2 and a 2 clicked becomes 1.
However, the colors are not responding correctly to the clicks. The first click seems to be ignored because the color stays the same and the second click does trigger a color change but now 1 = red and 2 = green (backwards).
Can anyone see what I am doing wrong that would make the color scheme fail?
Here is the code from the plunker --
app.js
(function() {
angular.module('app', ['ui.bootstrap'])
.directive('sglclick', SingleClickDirective)
.directive('loanProgressIcon', LoanProgressIconDirective)
.controller('MainController', MainController);
function SingleClickDirective($parse) {
return {
restrict: 'A',
link: function(scope, element, attr) {
var fn = $parse(attr['sglclick']);
var delay = 300,
clicks = 0,
timer = null;
element.on('click', function(event) {
clicks++; //count clicks
if (clicks === 1) {
timer = setTimeout(function() {
fn(scope, {
$event: event
});
clicks = 0; //after action performed, reset counter
}, delay);
} else {
clearTimeout(timer); //prevent single-click action
clicks = 0; //after action performed, reset counter
}
});
}
};
}
function LoanProgressIconDirective($compile) {
var progressMarkers = [{
'id': 1,
'cat': 'its_list',
'glyph': 'list-alt',
'tip': 'ITS List Verfified'
}, {
'id': 2,
'cat': 'fsa_compliant',
'glyph': 'home',
'tip': 'FSA Eligibility'
}, {
'id': 3,
'cat': 'has_liens',
'glyph': 'star',
'tip': 'Prior Lien Verfied'
}, {
'id': 4,
'cat': 'valid_leases',
'glyph': 'leaf',
'tip': 'Leases Valid'
}];
var statusColors = [
{ val: 0, color: '#CCC', class: 'pending'},
{ val: 1, color: '#006837', class: 'completed'},
{ val: 2, color: '#900', class: 'overdue'}
];
return {
restrict: 'A',
require : 'ngModel',
link: linker,
templateUrl: 'loanProgress.html',
scope: {
cat: '#',
ngModel: '='
}
};
function linker(scope, element, attrs, ctrl) {
scope.loan = {
id: progressMarkers[scope.cat]['id'],
glyphicon: progressMarkers[scope.cat]['glyph'],
tooltip: progressMarkers[scope.cat]['tip']
};
var styleChange = function () {
scope.loan.style = statusColors[scope.ngModel]['color'];
};
styleChange();
var setter = ctrl.$setViewValue;
ctrl.$setViewValue = function() {
setter.apply(this, arguments);
styleChange();
};
scope.progClicked = function() {
if(parseInt(scope.ngModel) === 0){
ctrl.$setViewValue(1);
} else if(parseInt(scope.ngModel) === 1){
ctrl.$setViewValue(2);
} else if(parseInt(scope.ngModel) === 2){
ctrl.$setViewValue(1);
}
};
scope.progDblClicked = function() {
alert('Icon ' + scope.ngModel + ' was double clicked.');
};
}
}
function MainController($scope) {
$scope.loan = {
its_list: 1,
fsa_compliant: 2,
has_liens: 1,
valid_leases: 0
};
}
})();
index.html
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="jquery#*" data-semver="2.0.3" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script data-require="bootstrap#3.0.2" data-semver="3.0.2" src="//netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min.js"></script>
<script data-require="angular.js#1.2.4" data-semver="1.2.4" src="http://code.angularjs.org/1.2.4/angular.js"></script>
<script data-require="ui-bootstrap#*" data-semver="0.11.0" src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.11.0.min.js"></script>
<link data-require="bootstrap-css#3.0.2" data-semver="3.0.2" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css" />
<link rel="stylesheet" href="app.css" />
<script src="app.js"></script>
</head>
<body ng-controller="MainController">
<div class="container">
<div class="row">
<div class="col-xs-12">
<table class="table table-striped">
<thead>
<tr>
<th colspan="4" class="text-left">LOAN PROGRESS ICONS</th>
</tr>
</thead>
<tbody>
<tr>
<th>ITS</th>
<th>FSA</th>
<th>LIEN</th>
<th>LEASES</th>
</tr>
<tr>
<td>
<span loan-progress-icon cat="0" ng-model="loan.its_list"></span>
</td>
<td>
<span loan-progress-icon cat="1" ng-model="loan.fsa_compliant"></span>
</td>
<td>
<span loan-progress-icon cat="2" ng-model="loan.has_liens"></span>
</td>
<td>
<span loan-progress-icon cat="3" ng-model="loan.valid_leases"></span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<p>{{loan | json}}</p>
</div>
</div>
</div>
</body>
</html>
Directive Template:
<span sglclick="progClicked()" ng-dblclick="progDblClicked()" class="glyphicon glyphicon-{{loan.glyphicon}}" tooltip="{{loan.tooltip}}" style="font-size:18px;color:{{loan.style}};cursor:pointer;"></span>
and app.css
th, td{
text-align:center;
}
.row{
margin: 15px 0;
}
.completed{
color: #006837;
}
.pending{
color: #CCCCCC;
}
.overdue{
color: #990000;
}
Thanks in advance!

The first problem is due to the fact that on is a jqLite/jQuery method and does not trigger the digest loop for you. This means that the UI will not be updated to reflect the changes to the model.
You either need to wrap the code that affects the model in a call to $apply or use $timeout instead of setTimeout.
With $apply:
timer = setTimeout(function() {
scope.$apply(fn(scope, {
$event: event
}));
clicks = 0;
}, delay);
With $timeout:
element.on('click', function(event) {
clicks++; //count clicks
if (clicks === 1) {
timer = $timeout(function() {
fn(scope, {
$event: event
});
clicks = 0;
}, delay);
} else {
$timeout.cancel(timer); //prevent single-click action
clicks = 0; //after action performed, reset counter
}
});
The second issue is the following code:
var setter = ctrl.$setViewValue;
ctrl.$setViewValue = function() {
setter.apply(this, arguments);
styleChange();
};
After calling $setViewValue the internal ngModelWatch will not detect the change and update the ngModel in your directive until later when the digest loop runs. Currently styleChange runs before this happens.
To run styleChange after the digest loop has finished you can use $timeout:
ctrl.$setViewValue = function() {
setter.apply(this, arguments);
$timeout(styleChange);
};
Demo: http://plnkr.co/edit/5MfQk49NKWFiEOqS2r5V?p=preview

Related

Dynamically update ion.rangeSlider ngModel in AngularJS directive

I'm trying to update the value of an Ion.RangeSlider when its bound ng-model scope variable changes. The model updates when the Ion.RangeSlider is used, but not vice-versa. Other inputs with the same ng-model update when the model value changes, so this must be some special case.
Edit: Woo! Here's a snippet #lin :) Also jsfiddle.
var app = angular.module('ngModelIonRangeSliderDemo', []);
app.controller('MainCtrl', function ($scope, $rootScope, $timeout) {
$scope.someNumber = 10;
}).directive('ionRangeSlider', function ionRangeSlider() {
return {
restrict: 'A',
scope: {
rangeOptions: '=',
model: '=ngModel'
},
link: function (scope, elem, attrs) {
scope.$watch('model',function () {
elem.ionRangeSlider(scope.rangeOptions);
});
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.10/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/1.1.2/ui-bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.1.6/js/ion.rangeSlider.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.1.6/css/ion.rangeSlider.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.1.6/css/ion.rangeSlider.skinFlat.min.css" />
<div ng-app="ngModelIonRangeSliderDemo">
<div ng-controller="MainCtrl" class="wrapper">
<h3>Text input updates slider, but not vice-versa.</h3>
<input ion-range-slider ng-model="someNumber" range-options="{min: -100, max: 100, step: .001}">
<br/>
<input type="text" ng-model="someNumber" class="form-control">
</div>
</div>
I have tried all kinds of suggestions in over ten somewhat-related stack overflow posts (which is how I have set up the current scope.$watch scheme on the ngModel), but none have worked. There aren't any errors in my console. What's wrong? Also, why doesn't it work without any mention of the model in my directive? Please let me know if there's anything important I have failed to include in this post.
Just use slider.update() inside your directive and you will be fine:
var app = angular.module('myApp', []);
app.controller('MainCtrl', function ($scope, $rootScope, $timeout) {
$scope.someNumber = 15;
$scope.apply = false;
}).directive('ionRangeSlider', function ionRangeSlider() {
return {
restrict: 'A',
scope: {
rangeOptions: '=',
model: '=ngModel',
apply: '=apply'
},
link: function (scope, elem, attrs) {
elem.ionRangeSlider(scope.rangeOptions);
scope.$watch('apply',function () {
if (scope.apply) {
scope.apply = false;
var slider = elem.data("ionRangeSlider");
slider.update({
from: scope.model
});
}
});
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.10/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/1.1.2/ui-bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.1.6/js/ion.rangeSlider.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.1.6/css/ion.rangeSlider.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.1.6/css/ion.rangeSlider.skinFlat.min.css" />
<div ng-app="myApp" ng-controller="MainCtrl" class="wrapper">
<h3>Text input updates slider and vice-versa.</h3>
<input ion-range-slider ng-model="someNumber" apply="apply" range-options="{min: -100, max: 100, step: .001}">
<br/>
<input type="text" ng-model="someNumber" class="form-control" ng-change="apply = true">
</div>
Extra demo, how binding ion.rangeSlider to input works:
http://jsfiddle.net/IonDen/r5aox84v/
var $range = $(".js-range-slider"),
$inputFrom = $(".js-input-from"),
$inputTo = $(".js-input-to"),
instance,
min = 0,
max = 1000,
from = 0,
to = 0;
$range.ionRangeSlider({
type: "double",
min: min,
max: max,
from: 200,
to: 800,
onStart: updateInputs,
onChange: updateInputs
});
instance = $range.data("ionRangeSlider");
function updateInputs (data) {
from = data.from;
to = data.to;
$inputFrom.prop("value", from);
$inputTo.prop("value", to);
}
$inputFrom.on("input", function () {
var val = $(this).prop("value");
// validate
if (val < min) {
val = min;
} else if (val > to) {
val = to;
}
instance.update({
from: val
});
});
$inputTo.on("input", function () {
var val = $(this).prop("value");
// validate
if (val < from) {
val = from;
} else if (val > max) {
val = max;
}
instance.update({
to: val
});
});

Ng-Click not working on mobile devices. How to change to ngTouch

I have a website which uses angular.js. The ng-click is working fine on laptop/desktop but not on mobile devices. From my research, I learned that i need to use ngTouch and I undertand that. My problem is that I am not a programmer and does not know how to do it.
I am hoping that there is someone who can help me or provide me with the right step or code. this is my code:
<div class="container" ng-controller="MessageBoardCtrl">
<div class="span6">
<div class="row-fluid item" ng-repeat="item in items" ui-animate>
<div class="span2"><img src="../images/post.png" width="48px" height="48px"/></div>
<div class=" well well-small">
<p>{{item.message}}</p>
</div>
</div>
</div>
<div class="span6">
<div class='well'>
<button class="btn btn-primary" ng-click="sendMessage()">Share</button>
Here is the javascript:
<script src="../templates/js/jquery.js"></script>
<script src="../templates/js/angular.js"></script>
<script src="../templates/js/angular-ui.js"></script>
<script src="../templates/js/angular-touch.js"></script>
<script src="../templates/js/angular-touch.min.js"></script>
<script>
function MessageBoardCtrl($scope, $http, $timeout) {
$scope.items = [];
$scope.message = '';
$scope.email = '';
$scope.lastTime = 0;
$scope.refreshMessages = function() {
$http.get('../templates/faucet.php/messages?time=' + $scope.lastTime).success(function(data) {
for(id in data) {
item = data[id];
$scope.items.unshift(item);
if($scope.lastTime<item.time)
$scope.lastTime = item.time;
}
});
}
$scope.sendMessage = function() {
if(!$scope.message)
return;
$http.post('../templates/faucet.php/add_message', {message: $scope.message, email: $scope.email}).success(function() {
$scope.message = '';
});
}
$scope.periodicRefresh = function() {
$scope.refreshMessages();
$timeout($scope.periodicRefresh, 5000, true);
}
$scope.refreshMessages();
}
</script>
Can someone give me a clean code based on the above that will work for ngtouch and instruction as well. Thanks in advance.
You can write your own directive for touch event. Below is an example directive to handle touch events. The directive below only fire the event in case of touch/long touch. scope.isMoved will prevent firing event when user tap on screen and move they finger around.
function directive($timeout) {
var dir = {
link: link,
restrict: 'A',
scope: {
onTouch: '&'
}
};
return dir;
function link(scope, element) {
scope.isMoved = false;
$timeout(function () {
// user start tap on screen
element.bind('touchstart', function () {
scope.isMoved = false;
});
element.bind('touchend click', function (evt) {
if (!scope.isMoved) {
scope.onTouch(evt);
}
});
//
element.bind('touchmove', function () {
scope.isMoved = true;
});
});
}
}
In HTML:
<a on-touch="someFunction()"> Touch</a>

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.

Issue with Popover AngularJS

I have a bunch of table rows which include inputs and buttons, namely. I would like to have a Popover display to the right of an input for a row if the value isn't matching the requirements defined. The button will also be disabled until the value of the input is correct.
Relevant HTML:
<div class="row col-md-4">
<table ng-controller="TestController" style="width: 100%">
<tr ng-repeat="element in model.InvoiceNumbers">
<td><input ng-model="element.id"
popover="Invoice must match ##-####!"
popover-placement="right"
popover-trigger="{{ { false: 'manual', true: 'blur'}[!isValidInvoice(element.id)] }}"
popover-title="{{element.id}}"/></td>
<td>{{element.id}}</td>
<td><button ng-disabled="!isValidInvoice(element.id)">Approve</button></td>
</tr>
</table>
</div>
Relevant JavaScript:
app.controller("TestController", function ($scope) {
$scope.model = {
InvoiceNumbers : [
{ id: '12-1234' },
{ id: '12-1235' },
{ id: '1234567' },
{ id: '1' },
{ id: '' }],
};
$scope.isValidInvoice = function (invoice) {
if (invoice == null) return false;
if (invoice.length != 7) return false;
if (invoice.search('[0-9]{2}-[0-9]{4}') == -1) return false;
return true;
};
});
The button gets disabled correctly on my local solution. However, I can't get the Popover to work; it behaves as if the model in its scope isn't getting updated. So, I looked through several links here (though most were from 2013 so I'd imagine a bit has changed) and their problems seemed to be solved by removing primitive binding. That didn't fix anything here. I added some console.log() lines in the function getting called from the Popover, and it was getting the correct value from the model each time. I also added a title to the Popover to show that its seeing the right value from the model.After seeing the log showing that it should be working correctly, I've run out of ideas.
The issue is element.id isn't updating dynamically within the trigger (it keeps its initial value, unlike popover-title which updates with the model). Is there something I did wrong?
Also, I've only been working with angular for a day so if you all have any suggestions on better ways to accomplish this, I'm open to suggestions.
Plunker: http://plnkr.co/edit/tiooSxSDgzXhbmIty3Kc?p=preview
Thanks
Found a solution on the angular-ui github page that involved adding these directives:
.directive( 'popPopup', function () {
return {
restrict: 'EA',
replace: true,
scope: { title: '#', content: '#', placement: '#', animation: '&', isOpen: '&' },
templateUrl: 'template/popover/popover.html'
};
})
.directive('pop', function($tooltip, $timeout) {
var tooltip = $tooltip('pop', 'pop', 'event');
var compile = angular.copy(tooltip.compile);
tooltip.compile = function (element, attrs) {
var parentCompile = compile(element, attrs);
return function(scope, element, attrs ) {
var first = true;
attrs.$observe('popShow', function (val) {
if (JSON.parse(!first || val || false)) {
$timeout(function () {
element.triggerHandler('event');
});
}
first = false;
});
parentCompile(scope, element, attrs);
}
};
return tooltip;
});
And here's the changes I made to the controller and view to make it work like I wanted in the original question:
<div class="row col-md-4">
<table ng-controller="TestController" style="width: 100%">
<tr ng-repeat="element in model.InvoiceNumbers">
<td><input ng-model="element.id"
pop="Invoice must match ##-####!"
pop-placement="right"
pop-show="{{element.showPop}}"
ng-blur="isValidInvoice($index, $event)" /></td>
<td>{{element.id}}</td>
<td><button ng-disabled="!isValidInvoice($index)">Approve</button></td>
</tr>
</table>
</div>
JavaScript:
app.controller("TestController", function ($scope) {
$scope.model = {
InvoiceNumbers: [
{ id: '12-1234', showPop: false },
{ id: '12-1235', showPop: false },
{ id: '1234567', showPop: false },
{ id: '1', showPop: false },
{ id: '', showPop: false }]
};
$scope.isValidInvoice = function ($index, $event) {
var obj = $scope.model.InvoiceNumbers[$index];
var isValid = function () {
if (obj.id === null) return false;
if (obj.id.length != 7) return false;
if (obj.id.search('[0-9]{2}-[0-9]{4}') == -1) return false;
return true;
};
if ($event != null && $event.type == "blur") obj.showPop = !isValid();
return isValid();
};
});
Plunker: http://plnkr.co/edit/5m6LHbapxp5jqk8jANR2?p=preview

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