Trigger directive on ng-click - angularjs

I using elastic directive for resizing textarea from this answer.
But i using ng-show for textarea, and on click height of textarea is 0.
So i need to use $watch somehow to trigger directive on click, but don't know how.
Html:
<textarea ng-show="showOnClick" elastic ng-model="someProperty"></textarea>
<a ng-click="showOnClick = true"> Show text area </a>
Directive:
.directive('elastic', [
'$timeout',
function($timeout) {
return {
restrict: 'A',
link: function($scope, element) {
$scope.initialHeight = $scope.initialHeight || element[0].style.height;
var resize = function() {
element[0].style.height = $scope.initialHeight;
element[0].style.height = "" + element[0].scrollHeight + "px";
};
element.on("input change", resize);
$timeout(resize, 0);
}
};
}
]);
Here is JSFIDDLE

as requested, the solution is to $watch the ngShow attr and run some sort of init function when the value is true.
user produced jsfiddle
example code:
.directive('elastic', [
'$timeout',
function($timeout) {
return {
restrict: 'A',
scope: {
ngShow: "="
},
link: function($scope, element, attr) {
$scope.initialHeight = $scope.initialHeight || element[0].style.height;
var resize = function() {
element[0].style.height = $scope.initialHeight;
element[0].style.height = "" + element[0].scrollHeight + "px";
};
if (attr.hasOwnProperty("ngShow")) {
function ngShow() {
if ($scope.ngShow === true) {
$timeout(resize, 0);
}
}
$scope.$watch("ngShow", ngShow);
setTimeout(ngShow, 0);
}
element.on("input change", resize);
$timeout(resize, 0);
}
};
}
]);

Related

Cannot set ng-model value to null from directive

In the example below I am trying to set controller value to null, when user closes popup window with error. But angularjs somehow takes old value, despite I am updating $viewValue and $modelValue. If you type in input any value, for example 1, and press Tab, then after 1 second you will see 1.00, however I am trying to set it to null (and view value should be empty string).
(function () {
var module = angular.module('TestApp', []);
module.directive('decimalTextbox', ['$timeout', function ($timeout) {
return {
restrict: 'A',
replace: false,
require: '?ngModel',
scope: {
onValidate: '='
},
link: function (scope, element, attrs, ctrl) {
ctrl.$formatters.unshift(function (value) {
return value != null ? parseFloat(value).toFixed(2) : '';
});
ctrl.$parsers.unshift(function (value) {
return value == '' ? null : parseFloat(value);
});
$(element).on('blur', function () {
$timeout(function () {
var result = { isValid: true, value: ctrl.$modelValue };
scope.onValidate(result);
if (!result.isValid) {
// show popup with close button here
// to simplify example I use setTimeout
setTimeout(function () {
$(element).val('');
ctrl.$viewValue = '';
ctrl.$modelValue = null;
scope.$apply();
console.log(ctrl.$modelValue);
}, 1000);
}
});
});
}
};
}]);
module.controller('TestController', ['$scope', function (scope) {
scope.someValue = 3;
scope.validate = function (result) {
result.isValid = false;
};
}]);
})();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.min.js"></script>
<div ng-app="TestApp" ng-controller="TestController">
<input
type="text"
ng-model="someValue"
decimal-textbox=""
on-validate="validate">
<h3>{{ someValue }}</h3>
</div>
To manipulate $viewValue, use $setViewValue function, it will take care update $modelValue and call intermediate $$parsers, $$formatters and $$validators pipeline behind the scenes.
//ctrl.$viewValue = ''; //instead of this
ctrl.$setViewValue(''); //use this.
//ctrl.$modelValue = null; //no need to set $modelValue

Angularjs, Range.insertNode, ngClick : After $compile Angular isn't aware of element - ngClick not working

ngClick is still not responding even after $compile. The new element is being applied to the DOM and is accessible via jQuery and JS. I assume that the issue is with the range.insertNode function. What am I missing here?
Here's my directive:
.directive('selectText', [
'$rootScope',
'$compile',
'$window',
function ($rootScope, $compile, $window) {
return {
restrict: 'A',
scope: {
hlid: "=",
tu: "="
},
link: function (scope, element, attrs) {
element.on('mouseup', function () {
//console.log("Attrs: "+JSON.stringify(attrs));
if ($window.getSelection().toString()) {
var text = $window.getSelection().toString();
if(text == '') {
console.log("No selection");
return;
}
var selection = $window.getSelection();
var range = selection.getRangeAt(0);
var selectionContents = range.extractContents();
var clk = "edSel('hl_"+scope.hlid+"','"+attrs.id+"');";
// var span = $compile(angular.element('<hlight id="hl_'+scope.hlid+'" class="cr-pr noselect clickable" title="Text Selection" ng-click="'+clk+'">'+text+'</hlight>'))(scope);
var span = angular.element($compile('<hlight id="hl_'+scope.hlid+'" class="cr-pr noselect clickable" title="Text Selection" ng-click="'+clk+'">'+text+'</hlight>')(scope));
console.log(span);
range.insertNode(span[0]);
scope.tu.target = element.html();
//selection.removeAllRanges();
var arr = {};
arr.action = 'add';
arr.tuid = attrs.id;
arr.hlid = 'hl_'+scope.hlid;
arr.content = element.html();
scope.$emit('hlChange', arr);
scope.hlid++;
console.log(element.html());
var modal = UIkit.modal("#hl_modal");
modal.show();
}
});
scope.edSel = function(id,tuid) {
console.log('ID: '+id+" - tuID: "+tuid);
}
}
};
}])
Thanks for any help

Angularjs responsive directive live updating issue (possibly due to ng-repeating the directive)

I am creating a post feed by ng-repeating JSON files from the cloud. I tried to make the posts responsive by using angular directives that update the template url with the screen size.
The problem is that only the last post in the ng-repeat responds and changes templates (with or without the reverse filter) when I resize the page. The other posts just remain the template that it was when originally loaded.
Here's the ng-repeat in the page
<div ng-show="post_loaded" ng-repeat="post in posts | reverse | filter:searchText ">
<feed-post>
</feed-post>
</div>
Here's the directive javascript file
app.directive('feedPost', function ($window) {
return {
restrict: 'E',
template: '<div ng-include="templateUrl"></div>',
link: function(scope) {
$window.onresize = function() {
changeTemplate();
scope.$apply();
};
changeTemplate();
function changeTemplate() {
var screenWidth = $window.innerWidth;
if (screenWidth < 768) {
scope.templateUrl = 'directives/post_mobile.html';
} else if (screenWidth >= 768) {
scope.templateUrl = 'directives/post_desktop.html';
}
}
}
};});
This happens because you re-assigning the .onresize in each directive and it stays effective only for the last linked directive.
I'd suggest to use it in a more angular way. You don't actually need a custom directive
In the controller that manages list of posts add reference to $window in $scope
$scope.window = $window;
Then in template make use of it
<div ng-include="directives/post_mobile.html" ng-if="window.innerWidth < 768"></div>
<div ng-include="directives/post_desktop.html" ng-if="window.innerWidth >= 768"></div>
To avoid extra wrappers for posts feed you might want to use ng-repeat-start, ng-repeat-end directives
this is a directive i wrote based on bootstrap sizes and ngIf directive :
mainApp.directive("responsive", function($window, $animate) {
return {
restrict: "A",
transclude: 'element',
terminal: true,
link: function($scope, $element, $attr, ctrl, $transclude) {
//var val = $attr["responsive"];
var block, childScope;
$scope.$watch(function(){ return $window.innerWidth; }, function (width) {
if (width < 768) {
var s = "xs";
} else if (width < 992) {
var s = "sm";
} else if (width < 1200) {
var s = "md";
} else {
var s = "lg";
}
console.log("responsive ok?", $attr.responsive == s);
if ($attr.responsive == s) {
if (!childScope) {
$transclude(function(clone, newScope) {
childScope = newScope;
clone[clone.length++] = document.createComment(' end responsive: ' + $attr.responsive + ' ');
block = {
clone: clone
};
$animate.enter(clone, $element.parent(), $element);
});
}
} else {
if (childScope) {
childScope.$destroy();
childScope = null;
}
if (block) {
block.clone.remove();
block.clone = null;
block = null;
}
}
});
}
};
});

How to get ng-model value from directive to controller in Angular?

I created a custom directive in which I have given text box and I want to take that model value in my controller.
I tried to find it, but didn't success.
here is my directive code
app.directive("bhAddCategory", ["$rootScope", "$timeout", "CategoryFactory", "ArticleFactory", "RecentArticleFactory", "focus", function ($rootScope, $timeout, CategoryFactory, ArticleFactory, RecentArticleFactory, focus) {
return {
scope: {
display: '=bhCategoryToggle',
imageOverflow: '=bhImageOverflow',
textBoxCss: '#bhTextBoxCss',
rmText: '=bhRmText'
},
replace: true,
template: '<div>' +
'<div class="pull-left forDrop"><input type="text" focus-on="focusMe" ng-class="myColonyList" class="effect1" placeholder="Add a colony" data-ng-model="newCategoryName" data-ng-trim="true" ng-keypress="pressEnter($event)"></div>' +
'<div class="pull-right"><img src="/images/greyplus.png" ng-class="{imageoverflow: imageOverflow}" ng-show="loadplus" data-ng-click="addCategory()" alt="add category"><img src="/images/loader.gif" ng-class="{imageoverflow: imageOverflow}" alt="" ng-show="loadgif" class="colonyloder"></div >' +
'</div>',
link: function (scope, element, attrs) {
scope.loadplus = true;
scope.pressEnter = function (keyEvent) {
if (keyEvent.which === 13)
scope.addCategory();
};
scope.resetNewCategoryName = function () {
if (scope.rmText) {
scope.newCategoryName = '';
}
};
scope.addCategory = function () {
scope.display = false;
var addNewCat = scope.newCategoryName;
commonNotification($rootScope, true, false, '', '');
var categoryData = {
category_name: addNewCat,
category_type: 0
};
if (addNewCat !== undefined && addNewCat !== '') {
scope.loadgif = true;
var category_details = CategoryFactory.nameExists(addNewCat);
if (!category_details.exist) {
CategoryFactory.addAtPostion(categoryData, category_details.mid)
.then(function (category) {
scope.loadgif = false;
scope.resetNewCategoryName();
commonNotification($rootScope, false, true, true, category.success);
$timeout(function () {
$rootScope.newStatus = false;
}, 3000);
}, function (error) {
commonNotification($rootScope, false, true, true, error.message);
$timeout(function () {
$rootScope.newStatus = false;
}, 3000);
});
} else {
scope.loadgif = false;
commonNotification($rootScope, false, true, true, 'Category name already exists!');
$timeout(function () {
$rootScope.newStatus = false;
}, 2000);
}
} else {
commonNotification($rootScope, false, true, true, 'Category name is required!');
$timeout(function () {
$rootScope.newStatus = false;
}, 2000);
}
};
}
};
}]);
And here is my controller
app.controller('bookmarkCtrl', ["$scope", "$http", "$rootScope", "$timeout", "RecentArticleFactory", "CategoryFactory", "ArticleFactory", "focus", "debounce", "userFactory", function ($scope, $http, $rootScope, $timeout, RecentArticleFactory, CategoryFactory, ArticleFactory, focus, debounce, userFactory) {
}]);
Can I get any working demo, so that I can understand and implement in my code
Thanks in Advance
Use a parent scope excecution parameter on your directive.This is a scope function which is declared on your parent controller and is invoked from within your directive whenever you want to pass something to your controller.
To accomplish this we declare a scope parameter in our directive with the '&' prefix
See this example which i created for another answer
http://jsfiddle.net/jwd3gywz/26/
JS
angular.module('components', []).controller('Composer', function Composer($scope, $http) {
// adding snippet to composed text
$scope.composed_text = '';
$scope.updateCaretPosition=function(pos){
$scope.caret_position=pos;
console.log('called'+pos);
}
$scope.$watch('caret_position',function(){
console.log($scope.caret_position);
})
}).directive('caretPosition', function() {
return {
scope:{updateCaretPosition:'&'},
link: function(scope, element, attrs) {
element.bind('keyup click', function(e){
var caret_position = element[0].selectionStart;
scope.updateCaretPosition({pos:caret_position});
console.log('my current position: ' + caret_position);
});
}
}
});
angular.module('myApp', ['components'])
HTML
<!doctype html>
<html ng-app="myApp">
<body>
<div ng-controller="Composer">
<textarea class="form-control composed_text" ng-model="composed_text" update-caret-position="updateCaretPosition(pos)" caret-position="" rows="20"></textarea>
</div>
</body>
</html>

How to find when a specific character is typed in textarea

We have requirement to show a drop down when user enters a "#".
I am planning to have a directive as following:
app.controller('MainCtrl', function($scope) {
$scope.values = ['#'];
$scope.valuesEntered = false;
});
app.directive('identifier', function ($parse) {
return {
scope: {
values: '=values'
},
link: function (scope, elm, attrs) {
elm.bind('keypress', function(e){
var char = String.fromCharCode(e.which||e.charCode||e.keyCode), matches = [];
angular.forEach(scope.values, function(value, key){
if(char === value) matches.push(char);
}, matches);
if(matches.length !== 0){
$scope.valuesEntered = true;
}
});
}
}
});
Will this be ok ?
Here is a simple directive I made that will allow you to specify an expression to evaluate when a given key is pressed or one of an array of keys is pressed.
Note that this is a one-way street. There is currently no going back once you have detected that keypress, even if the user pressed backspace.
var app = angular.module('sample', []);
app.controller('mainCtrl', function($scope) {
$scope.values = ['#', '!'];
$scope.valuesEntered = false;
$scope.valuesEntered2 = false;
});
app.directive('whenKeyPressed', function($parse) {
return {
restrict: 'A',
scope: {
action: '&do'
},
link: function(scope, elm, attrs) {
var charCodesToMatch = [];
attrs.$observe('whenKeyPressed', function(keys) {
if (angular.isArray(keys))
charCodesToMatch = keys.map(function(key) {
if (angular.isString(key))
return key.charCodeAt(0);
});
else if (angular.isString(keys))
charCodesToMatch = keys.split('').map(function(ch) {
return ch.charCodeAt(0);
});
});
elm.bind('keypress', function(e) {
var charCode = e.which || e.charCode || e.keyCode;
if (charCodesToMatch.indexOf(charCode) > -1)
scope.action();
});
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="sample">
<div ng-controller="mainCtrl">
<p>Values "#" entered? {{valuesEntered}}</p>
<textarea ng-model="str" when-key-pressed="#" do="valuesEntered = true"></textarea>
<p>Values {{values}} entered 2: {{valuesEntered2}}</p>
<textarea ng-model="str2" when-key-pressed="{{values}}" do="valuesEntered2 = true"></textarea>
</div>
</div>
Plunkr demo

Resources