Angular Directive not working when DOM updated - angularjs

I have following directive
var app = angular.module('TestApp', []);
app.directive('selectMultiple', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
$(element).find('select').material_select();
$(element).find("ul li").first().click(function() {
$(element).find("ul li:not(:first-child)").each(function(index) {
$(this).find("input[type=checkbox]").prop("checked", $(element).find("ul li").first().find("input[type=checkbox]").prop("checked"));
});
});
}
};
});
And I am also using dirpagination on same page, Its working , but when page changed, means DOM gets new elements, whatever written in directive is completely not working.
AM I missing any thing, I am new to angular so might be I'll be missing some things
Thanks

Related

Typeerror - Not a function in angular directive

I have defined a custom click directive as below:
(function() {
'use strict';
angular.module('myApp.core')
.directive('customClick', customClick);
customClick.$inject = ['$rootScope'];
function customClick() {
return {
restrict: 'A',
/*scope: {
customClick: '&'
},*/
link: function(scope, element, attrs){
$(element).on('click', function(e) {
scope.$apply(function() {
console.log("Hello..customClick..");
scope.customClick();
});
});
}
};
}
})();
And I get the following error on this;
Error logged by WDPR Angular Error handler service {xx.."stacktrace":"TypeError: a.customClick is not a function","cause":"unknown cause"}(anonymous function)bowerComponents.js:5745
How can I resolve this? If I add scope with '&' I get demanding isolated scope. Hence how to resolve it?
If I remove - scope.customClick();, it does not show anything on second html for custom-click, it has impact on only 1 html, and its controller. I want to use it in multiple controller + html.
customClick is a function on the directive itself. It is not a function on the scope. That's why the error has occurred.
link is used to manipulate dom/add event handlers on elements, which you have rightly done with element.bind('click', function() {
Whenever click occurs, the function binded to click automatically gets invoked. There is no need of watch and the invoking statement.
Your link can just be
link: function(scope, element){
element.bind('click', function() {
console.log("Hello..customClick..");
});
}
As you have used camel case in naming the directive, exercise caution in its usage in template.
You can use it as <p custom-click>hi</p>
I would recommend you to avoid using jQuery in angular apps. Try following
angular.module('newEngagementUI.core')
.directive('customClick', customClick);
customClick.$inject = ['$rootScope'];
function customClick() {
return {
restrict: 'A',
scope: {
customClick: '&'
},
link: function(scope, element, attrs){
element.bind('click', function () {
scope.customClick();
})
}
};
}
In your template:
<div custom-click="clickFunction"></div>
And your template controller should be like:
angular.module('myApp', []).controller(['$scope', function ($scope) {
$scope.clickFunction = function () {
alert('function passed');
}
}])
Working fiddle here: https://jsfiddle.net/xSaber/sbqavcnb/1/

ionic hide-tabs not work on nested view

Followed by the article How do I hide the tabs in Ionic Framework, the hide-tabs directive works fine.
But it has some problem when nested view.
As you can see in the codepen http://codepen.io/WilsonFpz/pen/MaxaQR, when the user navigate from home->level1->level2,
the level2`s tabs show again, but the $root.hideTabs is false as printing out.
var module = angular.module('app.directives', []);
module.directive('hideTabs', function($rootScope) {
return {
restrict: 'A',
link: function(scope, element, attributes) {
scope.$watch(attributes.hideTabs, function(value){
$rootScope.hideTabs = value;
});
scope.$on('$destroy', function() {
$rootScope.hideTabs = false;
});
}
};
});
Please give me some hints. Thanks.

Why does the angular expression in the directive template execute twice?

Its a simple directive:
app.directive('ngFoo', function($parse){
var controller = ['$scope', function ngNestCtrl($scope) {
$scope.getCanShow = function() {
console.log('show');
return true;
};
}];
var fnPostLink = function(scope, element, attrs) {
console.log('postlink');
};
var fnPreLink = function(scope, element, attrs) {
console.log('prelink');
};
var api = {
template: '<div ng-if="getCanShow()">foo</div>',
link: {
post: fnPostLink,
pre: fnPreLink
},
restrict: 'E',
controller:controller
};
return api;
});
My goal was to find when "show" gets output to console. At this moment I figure it happens after linking (pre & post).
This makes sense. Since the template is rendered after those phases. (Correct me if I am wrong).
But then again, why would the template be rendered twice?
http://plnkr.co/edit/JNhON2lY9El00dzdL39J?p=preview
Angular has multiple digest cycles and you're seeing two of them. This is totally normal and perfectly ok.

TinyMCE <textarea> two way bound with AngularJS

Is it possible to apply two way binding to a <textarea></textarea> that has had TinyMCE applied to it for Rich Text Formatting.
I can't get it to work! I can get TinyMCE to load the content of my model, but when I update the text in TinyMCE, my model does not auto update!
Is there a way?
You can do this by creating your own directive.
What you need to do is to let your directive sync your model when something in the TinyMCE editor changes. I have not used TinyMCE, but Wysihtml5. I think you can remake this to use TinyMCE instead.
angular.module('directives').directive('wysihtml5', ['$timeout',
function ($timeout) {
return {
restrict: 'E',
require: 'ngModel',
template: "<textarea></textarea>", // A template you create as a HTML file (use templateURL) or something else...
link: function ($scope, $element, attrs, ngModel) {
// Find the textarea defined in your Template
var textarea = $element.find("textarea");
// When your model changes from the outside, use ngModel.$render to update the value in the textarea
ngModel.$render = function () {
textarea.val(ngModel.$viewValue);
};
// Create the editor itself, use TinyMCE in your case
var editor = new wysihtml5.Editor(textarea[0],
{
stylesheets: ["/style.css"],
parserRules: wysihtml5ParserRules,
toolbar: true,
autoLink: true,
useLineBreaks: false,
});
// Ensure editor is rendered before binding to the change event
$timeout(function () {
// On every change in the editor, get the value from the editor (textarea in case of Wysihtml5)
// and set your model
editor.on('change', function () {
var newValue = textarea.val();
if (!$scope.$$phase) {
$scope.$apply(function () {
ngModel.$setViewValue(newValue);
});
}
});
}, 500);
}
};
}]);
Then you can use the directive in your html page like this:
<wysihtml5 ng-model="model.text" />
Here's a link if you need more info on creating your own directive: http://docs.angularjs.org/guide/directive
Also compare the render function from the directive above to this render function from angular-ui-tinymce ( https://github.com/angular-ui/ui-tinymce )
ngModel.$render = function() {
if (!tinyInstance) {
tinyInstance = tinymce.get(attrs.id);
}
if (tinyInstance) {
tinyInstance.setContent(ngModel.$viewValue || '');
}
Plnkr: http://plnkr.co/edit/04AFkp?p=preview
However depending on the timing of the loading of your DOM you may need to set the priority on your directive upwards. :-)
Here is my solution using a custom angular directive.
You'll need to use jQuery with angularJS, TinyMCE 4 and their jQuery plugin.
myApp.directive('tinymce', function() {
return {
restrict: 'C',
require: 'ngModel',
link: function(scope, element, attrs, modelCtrl) {
element.tinymce({
setup: function (e) {
e.on("change", function() {
modelCtrl.$setViewValue(element.val());
scope.$apply();
}
}
});
}
}
}
Then in your HTML:
<textarea class="tinymce" ng-model="data"></textarea>
That's it, have fun.

bind event in a directive doesn't work if the directive is in a ng-repeat loop

It seems everyone is asleep on the angularjs google group :)
Here's my problem:
I have a select in a directive, and I want to bind a function to the 'change' event of that select.
My problem is that when I use this directive in an ng-repeat loop, the bind to the event doesn't work anymore (why ??).
EDIT:
In my real case, there are three or more <select>, created and populated with data from a json file.
Here is a simplified version of the directive, and I made a plunker as well.
angular.module('test', [])
.directive('mySelect', function() {
var baseElt = angular.element('<select><option>1</option><option>2</option></select>');
return {
restrict: 'E',
compile: function(topElement) {
var elt = baseElt.clone();
topElement.append(elt);
return function(scope, element, attributes, ngModelCtrl) {
elt.bind('change', function() {
alert("change !");
});
};
}
};
});
you need
app.directive('mySelect', function() {
return {
restrict : 'E',
template : '<select><option>1</option><option>2</option></select>',
link : function(scope, element, attributes, ngModelCtrl) {
element.bind('change', function() {
console.log("change !");
});
}
}
});
Demo: Fiddle

Resources