Why does my directive fail without errors? - angularjs

This directive:
angular.module('WizmoApp', [])
.directive('confirmClick', function() {
return {
priority: -1,
restrict: 'A',
link: function(scope, element, attrs){
element.bind('click', function(e){
var message = attrs.ngConfirmClick;
// confirm() requires jQuery
if(message && !confirm(message)){
//e.stopImmediatePropagation();
//e.preventDefault();
}
});
}
};
});
, which I'm trying to transcribe from here:
Confirmation dialog on ng-click - AngularJS
is enough to bring my app to a screeching halt.
No display on-screen, no js errors, no nothing, just a blank sacreen.
This is how I'm using it:
<tr class=""
ng-repeat="package in adminManifestVm.Manifest | orderBy:'Id' track by $index"
ng-click="adminManifestVm.debinPackage(package.Id);"
ng-confirm-click="Are you sure you want to debin this?">
No idea how to debug a directive, let alone write one.
[ EDIT ]
I just noticed that, in the example, the directive is actually called ngConfirmClick. Changed it, but makes no difference.

you are using it wrong
<tr class=""
ng-repeat="package in adminManifestVm.Manifest | orderBy:'Id' track by $index"
ng-click="adminManifestVm.debinPackage(package.Id);"
confirm-click val="Are you sure you want to debin this?">
and in your directive add scope
angular.module('WizmoApp', [])
.directive('confirmClick', function() {
return {
scope:{
val: '='
},
priority: -1,
restrict: 'A',
link: function(scope, element, attrs){
element.bind('click', function(e){
var message = val;
// confirm() requires jQuery
if(message && !confirm(message)){
//e.stopImmediatePropagation();
//e.preventDefault();
}
});
}
};
});

That solution is not working. This error makes no sense:
Syntax Error: Token 'Package' is an unexpected token at column 6 of
the expression [Move Package back to Pile?] starting at [Package back
to Pile?].
This is how I've implemented it:
Directive:
(function(){
angular
.module('WizmoApp')
.directive('confirmClick', function () {
return {
priority: -1,
restrict: 'A',
link: function(scope, element, attrs){
element.bind('click', function(e){
var message = attrs.val;
// confirm() requires jQuery
if(message && !confirm(message)){
e.stopImmediatePropagation();
e.preventDefault();
}
});
}
}
});
})();
View:
<tr class=""
ng-repeat="package in adminManifestVm.Manifest | orderBy:'Id' track by $index"
ng-click="adminManifestVm.debinPackage(package.Id);"
confirm-click
val="Move Package back to Pile?">

Related

possible for directive to run each time when scope changes?

I want to write a directive to for a table TBODY to show some text when it is empty. I want to achieve this by writing a directive that detects if the table's TBODY has any child TR, if not then show some text.
I do not wish to use ng-if="model.entries.length == 0" because I might have a TR in there for creating new entry that won't belong to entries.
The directive I wrote currently only works one time because it only runs once. When entries changes the directive won't run again and therefore the empty text is still showing
baseModule.directive('emptyTbody', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
if (element.find('tr').length == 0) {
element.addClass('empty');
} else {
element.removeClass('empty');
}
}
}
});
Is it possible to write a directive that runs when scope changes like regular angular behavior? Or if this cannot be achieved through a directive, is there any other ways to achieve this?
Here is the Html
<tbody empty-tbody>
<tr ng-if="isCreating()">
<td>
<input ng-model="creatingItem.Name"/>
</td>
</tr>
<tr ng-repeat="item in model.entries" >
<td>
<input ng-model="item.Name"/>
</td>
</tr>
</tbody>
Create a isolate scope and put watch there
Like this
baseModule.directive('emptyTbody', function() {
return {
restrict: 'A',
scope: {
source: '='
},
link: function(scope, element, attrs) {
scope.$watch("source", function(nv) {
if (nv) {
if (nv.length == 0)
element.addClass('empty');
else
element.removeClass('empty');
} else
element.addClass('empty');
});
}
}
});
HTML
<tbody empty-tbody source="model.leps">
EDIT
If you wanna to use only from element .
You can use anonymous function to watch.
Try like this
baseModule.directive('emptyTbody', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
scope.$watch(function() {
return element.find('tr').length;
}, function(nv) {
if(nv){
console.log("Table has data")
}
else
console.log("Table has no data");
});
}
}
});
JSFIDDLE

Find scope of clicked element in ng-repeat

is it possible to get the clicked row (scope) of a element in ng-repeat without nowing the name?
For example: scope.person works, but when I change persons into another name I can't use my directive global.
I need the data to do a edit like this: http://vitalets.github.io/x-editable/
I will write a directive which changes the text into a input field. To save it I need the name of the field, "lastname" in the example and the ID from the row.
HTML:
<tr ng-repeat="person in persons | orderBy:lastname">
<td class="editable">{{person.lastname}}</td>
<td>{{person.surname}}</td>
</tr>
Directive:
app.directive('editable', [function () {
return {
restrict: 'C',
link: function (scope, elem, attrs) {
elem.bind('click', function(event) {
console.log(scope.person)
});
}
};
}])
You can use Angular's tracking ng-repeats by $index and pass it to your directive:
HTML:
<tr ng-repeat="person in persons | orderBy:lastname">
<td editable="$index" class="editable">{{person.lastname}}</td>
<td>{{person.surname}}</td>
</tr>
Directive:
app.directive('editable', [function () {
return {
restrict: 'A',
link: function (scope, elem, attrs) {
elem.bind('click', function(event) {
console.log(attrs.Editable);
});
}
};
}])
You are already catching the current scope and it is working fine you just need to manipulate the data and apply scope.$apply();
app.directive('editable', [function () {
return {
restrict: 'C',
link: function (scope, elem, attrs) {
elem.bind('click', function(event) {
console.log(scope.person)
scope.person.surname="hello"
console.log(scope.person)
scope.$apply();
});
}
};
}])
In the first console output you can see the original clicked element and in second console you can see the maupilated data.
Now why apply is required:- It is required to run the digest cycle of angular to adapt the change happen outside the scope of angular js (bind is jquery event not angular's).
Plunker
Add a Scope to your directive so you can pass any Model to be edited.
And display the ID with the Attrs variable.
app.directive('editable', [function () {
return {
scope: {
model : '='
},
restrict: 'C',
link: function (scope, elem, attrs) {
elem.bind('click', function(event) {
alert(scope.model +" "+ attrs.id )
});
}
};}]);
the html:
<span class="editable" model="person.lastname" id="{{$index}}">{{person.lastname}}</span>
The Fiddle
Browse how works the custom directives in angular. You can pass like parameter the lastname through 'attrs' in your link. Something like this:
html code:
<my-first-directive lastname="{{person.lastname}}"></my-first-directive>
custom directive code:
link: function (scope, elem, attrs) {
elem.bind('click', function(event) {
console.log(attrs['lastname'])
});
}
This approach not is good in my opinion. But i hope it works you.
If you read about scopes in directives you will can try other approachs.
You can pass the same parameter to directive scope so.
In your directive:
return {
restrict: 'C',
scope: {
lastname: '=' // If you want know more about this, read about scopes in directives
}
link: function (scope, elem, attrs) {
elem.bind('click', function(event) {
console.log(scope.lastname) // now, scope.lastname is in your directive scope
});
}
};
Be careful whit names when you work with directives. If your variable is 'myVar', when you write the directive html code you need write:
<my-first-directive my-var="myVar"></my-first-directive>
For the same reason that you need write 'my-first-directive' when you name directive is 'myFirstDirective'.
Good luck!

onFinishRender not being fired

Plunker
I'm loading data into a table using ng-repeat.
There is an onFinishRender, which emits ngRepeatFinished. However it's not being fired.
Any ideas why it's not working?
logsmgr.directive('onFinishRender', function ($timeout) {
showLog("onFinishRender");
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
$timeout(function () {
scope.$emit('ngRepeatFinished');
});
}
}
}
});
You need to add the directive to one of your repeat elements. For example, from your Plunker add on-finish-render.
<tr ng-repeat="data in model.DataList" on-finish-render>

Angular - How to "bind" when "oninput" with "contenteditable span"?

"<span contenteditable>{{ line.col2 }}</span>"
Hello,
This code is good at initialisation but if I edit the span, no bing is send and my array model never updated...
So, I have tried this :
<span contenteditable ng-model="line.col2" ng-blur="line.col2=element.text()"></span>
But "this.innerHTML" does not exist.
What can I do ?
Thank at all ;-)
you can remove the ng-blur and you will have to add this directive:
<span contenteditable ng-model="myModel"></span>
Here is the directive taken from the documentation:
.directive('contenteditable', function() {
return {
restrict: 'A', // only activate on element attribute
require: '?ngModel', // get a hold of NgModelController
link: function(scope, element, attrs, ngModel) {
if(!ngModel) return; // do nothing if no ng-model
// Specify how UI should be updated
ngModel.$render = function() {
element.html(ngModel.$viewValue || '');
};
// Listen for change events to enable binding
element.on('blur keyup change', function() {
scope.$apply(read);
});
read(); // initialize
// Write data to the model
function read() {
var html = element.html();
// When we clear the content editable the browser leaves a <br> behind
// If strip-br attribute is provided then we strip this out
if( attrs.stripBr && html == '<br>' ) {
html = '';
}
ngModel.$setViewValue(html);
}
}
}
});
I only will point you to possible solution, then you need to parse/clean HTML better.
<span contenteditable data-ng-blur="bar = $event.target.innerHTML">
{{bar}}
</span>
// upd.
Angular events such as click, blur, focus, ... - fired with scope context, e.g. this will be current scope.
Use $event, be happy.
Solution with Mirrage and gab help :
<span contenteditable="true" ng-model="ligne.col2">{{ ligne.col2 }}</span>
app.directive('contenteditable', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, ctrl) {
// view -> model
element.bind('blur', function() {
scope.$apply(function() {
ctrl.$setViewValue(element.html());
});
});
// model -> view
ctrl.$render = function() {
element.html(ctrl.$viewValue);
};
// load init value from DOM
ctrl.$render();
}
};
});
Thank at all ;-)

AngularJs how to call prettyprint?

I'm trying to use prettyprint plugin for my angularjs app.
But cannot make it works. I create a simple directive and call method prettyPrint(), but the code is not formatted.
FIDDLE: http://jsfiddle.net/Tropicalista/yAv4f/2/
App.directive('test', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
$(element).prettyPrint()
}
};
});
I modified your code and i'll update here:
http://jsfiddle.net/yAv4f/6/
html:
<div ng-app="Knob" ng-controller="myCtrl">
<pre class="prettyprint linemus"></pre>
<pre class="prettyprint linemus"><!DOCTYPE html><html lang="en"></html></pre>
</div>
javascript:
var App = angular.module('Knob', []);
App.controller('myCtrl', function($scope) {
$scope.dom = '<!DOCTYPE html><html lang="en"></html>'
})
App.directive('prettyprint', function() {
return {
restrict: 'C',
link: function postLink(scope, element, attrs) {
element.html(prettyPrintOne(scope.dom));
}
};
});
Basically, you need to use the file prettify.js to control the execution of the prettify() function, with prettyPrintOne() you can execute it in a specific html text.
And to simplify the use of the directive, like prettify stlyle, i'll suggest restric to 'C' a class and change the the directive name to 'prettyprint'
I've expanded on the previous answers and created a jsfiddle with a working directive that responds in realtime to model changes:
http://jsfiddle.net/smithkl42/cwrgLd0L/27/
HTML:
<div ng-app="prettifyTest" ng-controller="myCtrl">
<div>
<input type="text" ng-model="organization.message" />
</div>
<prettify target="organization"><pre><code class="prettyprint">console.log('{{target.message}}');
</code>
</pre>
</prettify>
</div>
JS:
var App = angular.module('prettifyTest', []);
App.controller('myCtrl', function ($scope) {
$scope.organization = {
message: 'Hello, world!'
};
});
App.directive('prettify', ['$compile', '$timeout', function ($compile, $timeout) {
return {
restrict: 'E',
scope: {
target: '='
},
link: function (scope, element, attrs) {
var template = element.html();
var templateFn = $compile(template);
var update = function(){
$timeout(function () {
var compiled = templateFn(scope).html();
var prettified = prettyPrintOne(compiled);
element.html(prettified);
}, 0);
}
scope.$watch('target', function () {
update();
}, true);
update();
}
};
}]);
h/t to #DanielSchaffer (see Template always compiles with old scope value in directive).
Angular already has this filter built-in for JSON:
<pre>
{{data | json}}
</pre>
If you want to make your own directive, you can use the JSON object directly:
app.filter('prettyJSON', function () {
function syntaxHighlight(json) {
return JSON ? JSON.stringify(json, null, ' ') : 'your browser doesnt support JSON so cant pretty print';
}
return syntaxHighlight;
});
With markup
<pre>
{{data | prettyJSON}}
</pre>
I would like to make a small addition to the directive by #carlosmantilla
You can achieve the same thing without creating the scope variable. I have added this correction on github
This should work properly I assume.
http://jsfiddle.net/yAv4f/143/
var App = angular.module('Knob', []);
App.controller('myCtrl', function($scope) {
$scope.text = "function f1(){int a;}";
})
function replaceText(str)
{
var str1 = String(str);
return str1.replace(/\n/g,"<br/>");
}
app.directive('prettyprint', function() {
return {
restrict: 'C',
link: function postLink(scope, element, attrs) {
element.html(prettyPrintOne(replaceText(element.html()),'',true));
}
};
});
I struggled with this issue for quite a while and wanted to chime in here, albeit much later than everyone else (for real though, who's still using AngularJS in late 2017? This guy.) My specific use-case was where I have code (xml) being dynamically loaded on the page which needed to be pretty printed over and over again.
This directive will take in your code as an attribute, remove the prettyprinted class that's added to the element right after you run prettyPrint(). It will watch for changes on the inputted code from the parent's scope and run the code again when changes occur.
Only dependency is that you have Google's code-prettify. I had it self-hosted, hence the PR.prettyPrint() (as instructed in the docs as of sept 2017).
The directive fully encapsulates the needed Google code-prettify functionality for dynamic content.
angular.module('acrSelect.portal.directives')
.directive('prettyPrint', ['$timeout', function($timeout) {
return {
restrict: 'E',
scope: {
'code': '=',
},
template: '<pre ng-class="{prettyprint: code}">{{ code }}</pre>',
link: function (scope, element, attr) {
scope.$watch('code',function(){
$timeout(function() {
//DOM has finished rendering
PR.prettyPrint();
element.find(".prettyprint").removeClass("prettyprinted");
});
});
}
}
}
]);
The html in the parent template might look
<pretty-print code="selectedCode" ng-show="codeIsSelected"></pretty-print>
Hope this helps another poor soul!

Resources