Auto growing textarea in ionic - javascript

I am trying to add an autogrowing textarea to my app but for some reason it is not working. The module that I am using is https://github.com/tagged/autogrow (it was recommneded on the ionic forum)

The answer above does not shrink - here is an improved version:
https://codepen.io/benshope/pen/xOPvpm
angular.module('app').directive('expandingTextarea', function () {
return {
restrict: 'A',
controller: function ($scope, $element, $attrs, $timeout) {
$element.css('min-height', '0');
$element.css('resize', 'none');
$element.css('overflow-y', 'hidden');
setHeight(0);
$timeout(setHeightToScrollHeight);
function setHeight(height) {
$element.css('height', height + 'px');
$element.css('max-height', height + 'px');
}
function setHeightToScrollHeight() {
setHeight(0);
var scrollHeight = angular.element($element)[0]
.scrollHeight;
if (scrollHeight !== undefined) {
setHeight(scrollHeight);
}
}
$scope.$watch(function () {
return angular.element($element)[0].value;
}, setHeightToScrollHeight);
}
};
});
This will transform all your textareas to grow/shrink.
Hope that helps!

I wrote a very simple directive that works with Ionic 2 and ion-textarea. Here it is:
import { Directive, HostListener, ElementRef } from "#angular/core";
#Directive({
selector: "ion-textarea[autoresize]" // Attribute selector
})
export class Autoresize {
#HostListener("input", ["$event.target"])
onInput(textArea: HTMLTextAreaElement): void {
this.adjust();
}
constructor(public element: ElementRef) {
}
ngOnInit(): void {
this.adjust();
}
adjust(): void {
let ta = this.element.nativeElement.querySelector("textarea");
ta.style.overflow = "hidden";
ta.style.height = "auto";
ta.style.height = ta.scrollHeight + "px";
}
}
Here is a gist: https://gist.github.com/maxt3r/2485356e91a1969bdb6cf54902e61165
EDIT: Look at the gist for other suggestions from other people.

I found a much more better way to do this without using any other third party library or directive.
$scope.updateEditor = function() {
var element = document.getElementById("page_content");
element.style.height = element.scrollHeight + "px";
};
Then simply adding ng-keypress="updateEditor()" to the textarea would do the job.
<textarea ng-keypress="updateEditor()" ng-model="bar"> </textarea>
I Hope this helps others who might face this problem in the future.
Update: Here is a codepen for this: http://codepen.io/kpourdeilami/pen/KDepk
Update 2: Use the snippet provided by #benshope
Update 3: If you're on Ionic/Angular 2, use the answer provided by "Max Al Farakh"

Try Angular-Elastic. It is an angular directive built to auto-expand a textarea. Use bower to install it.
bower install angular-elastic
add it to your project, then you can use it as an attribute
<textarea msd-elastic ng-model="foo"> </textarea>
or as class
<textarea class="msd-elastic" ng-model="bar"> </textarea>

From Ionic 4.4 it's built-in, see the autoGrow property:
TextArea#Properties
<ion-textarea auto-grow="true" rows="1"></ion-textarea>

Do you mean vertically auto-growing? I tried this:
<textarea ng-model='doc.description'
rows='{{doc.description.length/50 + 1}}'
cols='50'></textarea>
Kinda hackish, but after having determined an expected column length, lets define the row length based on the length of the inputed text. It starts growing vertically when I start typing! (no scrolling/out of view text).

With ionic-5 , there is an option called auto-grow, set it to true in your view.
In css, set min-height, max-height, to control the text grow.
ion-textarea {
min-height: 100px;
max-height: 200px;
}
Also, after the above fix, if you get some odd behaviour with placeholder text, add below inside the ion-textarea
::ng-deep textarea {
min-height: 100px;
}

If it can serve someone, I changed a little bit benshope's solution since I needed the textarea to grow even when user do a carriage return.
So instead of listening to the changes on the input value (which didn't always fire when doing a carriage return) I listent the input event on the textarea.
(function () {
'use strict';
angular
.module('app')
.directive('expandingTextarea', expandingTextarea);
function expandingTextarea() {
return {
restrict: 'A',
controller: function ($scope, $element, $attrs, $timeout) {
$element.css('min-height', '0');
$element.css('resize', 'none');
$element.css('overflow-y', 'hidden');
setHeight(0);
$timeout(setHeightToScrollHeight);
function setHeight(height) {
$element.css('height', height + 'px');
$element.css('max-height', height + 'px');
}
function setHeightToScrollHeight() {
console.log('set height');
setHeight(0);
var scrollHeight = angular.element($element)[0]
.scrollHeight;
if (scrollHeight !== undefined) {
setHeight(scrollHeight);
}
}
angular.element($element)[0].addEventListener("input", setHeightToScrollHeight);
}
};
}})();

juste install :
bower install angular-elastic or
npm install angular-elastic;
then import the elastic.js file in your index.html like this
<script src="js/elastic.js" type="text/javascript"></script>
the after that inject it in you angular module like this:
angular.module('yourApp', ['monospaced.elastic']);
the after that in your html file, in your footer-bar do like this:
<ion-footer-bar style="height: auto; overflow: visible !important"><textarea rows="1" msd-elastic ng-model="myMsg">
</textarea>

Related

Validate dynamically generated inputs inside a div

I'm trying to validate dynamically generated inputs, but I do not know how to do this.
When I add the div that triggers the directive and inserts the inputs dynamically the div adds the 'has-error' class but does not apply the input style, anyone knows the best way to do this that I'm trying to do?
Here is the markup:
<div ng-if="conditionItem.field.id"
ng-class="{true: 'has-error'}[conditionItem.field.hasError]"
dynamic
input-router
source="conditionItem.field"
ng-click="FieldConditionsCtrl.valueTest(conditionItem.field.hasError)"
ng-required="true"
ng-model="conditionItem.situation[$index]">
</div>
Here is the directive how generate the inputs:
(function() {
'use strict';
angular
.module('applicationInputs', ['rzModule', 'focus-if', 'ui.utils.masks'])
.directive('inputRouter', inputRouter);
/** #ngInject */
function inputRouter($compile){
return {
restrict: 'EA',
scope: {
ngModel: '=',
source: '=',
placeholder: '#',
tabIndex: '='
},
link: function(scope, element, attrs) {
var canvas = angular.element(element[0]);
scope.source.editable = angular.isUndefined(scope.source.editable) ? true : scope.source.editable === true;
//used by setting to override selected field
if (angular.isDefined(attrs.dynamic)) {
scope.$watch('source', function () {
var html = '<' + scope.source.type + 'input></' + scope.source.type + 'input>';
canvas.children().detach();
canvas.append($compile(html)(scope));
});
} else {
var html = '<' + scope.source.type + 'input></' + scope.source.type + 'input>';
canvas.append($compile(html)(scope));
}
}
}
}
})();
Here is my style:
.has-error {
border-color: red
}
Try border: 1px solid red; instead of just setting border-color. Border width is 0 by default, so just setting a color isn't enough
Just a couple nitpicky-subjective style comments also:
element is already a jqLite/jquery element, so no need to call angular.element
the scope.source.editable === true assignment can be shortened to !!scope.source.editable if you really want it to be a boolean.
While clever, this sort of jquery style element building is generally a code smell in angular js. If you really want to go this route, I would build self-contained directives for your inputs and use inputRouter's template to choose. It's easier to understand, so your future self will thank you
{true: 'has-error'}[conditionItem.field.hasError] took me a minute. Just write it as {conditionItem.field.hasError: 'has-error'}
I'm a big fan of these style guides for AngularJS:
John Papa - ES5, but good advice
Todd Motto - ES6+
There's overlap, but take what you like from each.

Update background image without refresh angularjs

Hey everyone I am currently building a mobile app for my company and I have a angularjs function that is currently adding a css class based on whether it's day or night. It does what it's supposed to do perfectly by putting a day or night image based on whether it's day or night.
The issue I am running into is that when testing the app right before the clock let's say goes from 1:59 to 2 it doesn't change the background image until you refresh the page. I want it to automatically change the background image without having to refresh and can't seem to find a way to do it. I will link the code here!
Any help is appreciated as it has me completely stumped...
Here is what I have in my html.
<div class="dashboard-welcome" ng-class="{'hide-homescreen-image-landscape'
: isLandscape, 'homescreenImageDay' : isDay, 'homescreenImageNight' :
isNight }">
Here is where the function is being called
angular.module('starter').controller('homeCtrl', ['$scope', '$interval',
'jsBridge', 'authService', 'tierService', '$window', fnc]);
function fnc($scope, $interval, jsBridge, authService, tierService, $window)
{
$scope.$on('$ionicView.afterEnter', function (event, viewData) {
setOrientation();
$scope.isDay = !isDayTime(1000);
$scope.isNight = isDayTime(1000);
});
Here is where the function is instantiated. Basically checking what time it is.
var isDayTime = function () {
var h = new Date().getHours();
if (h >= 14) {
return true;
} else {
return false;
}
}
I can't supply all the code since this application is thousands of lines long but this is the working function as of now. Just need it to switch background images without refreshing using angularjs...
Assuming that inside your div you are using an background-image: url("...")
I suggest you set up an object to hold a single $scope.isDay value instead of doing the calculation twice.
Also use the $interval service to check every nth millisecond to update your $scope.isDay value.
The code below works fine in dynamically changing a background image on a page using its CSS class.
HTML:
<div ng-class="data.isDay ? 'back1' : 'back2'"></div>
JS:
var exampleApp = angular.module('exampleApp', []);
exampleApp.controller('exampleController', function($scope, $interval) {
$scope.data = {
isDay: true,
classVal: {
one: 'text-danger',
two: 'text-success'
}
};
$interval(function() {
$scope.toggleImage();
}, 600);
$scope.toggleImage = function() {
$scope.data.isDay = ($scope.data.isDay ? false : true)
};
$scope.toggleImage();
});
Here is also a plnkr demo

Don't scroll on element resize

I'm making an angular directive that hides element a when the user scrolls on element b. It works fine, but I can't figure out this behaviour:
It might be hard to tell, but essentially when you scroll to the bottom the scroll bar expands because element a sits above element b, so essentially the thing I'm scrolling on has more space available. After that, I'm not sure why it scrolls up. Here's a gif of the full page if that makes it any clearer:
My directive is written in typescript (angular version 1.5.7 NOT 2.x), I'll work on translating it to javascript but in the interest of getting this question out there as quickly as possible here's the ts:
interface IScope extends ng.IScope {
showHeader: boolean;
}
export class IncodeHideHeaderDirective implements ng.IDirective {
restrict = "AE";
require: "ngModel";
scope: Object;
replace = true;
link: ng.IDirectiveLinkFn | ng.IDirectivePrePost;
oldy: number;
justScrolled = false;
constructor() {
const self = this;
this.link = (scope: IScope, element: ng.IAugmentedJQuery) =>
{
element.bind("scroll",
() => {
if (element[0].scrollTop > self.oldy) {
console.log("collapsing");
scope.$eval("showHeader=false");
}
else if (element[0].scrollTop < self.oldy)
{
console.log("expanding");
scope.$eval("showHeader=true");
}
self.oldy = element[0].scrollTop;
}
);
element.bind("load",
() => {
console.log(scope);
this.oldy = element[0].scrollTop;
});
};
}
public static factory(): ng.IDirectiveFactory {
const directive = () => new IncodeHideHeaderDirective();
return directive;
}
}
angular.module("incode.module")
.directive("ixHeader", incode.directives.label.IncodeHideHeaderDirective.factory());
pretty basic stuff. How do I get the scrollbar to stop doing this weird stuff?
Here's a fiddle demonstrating the problem.
That's not an Angular answer, but why not just remove your header bar from the flow by making it position: fixed (or absolute) ? It would not cause the reflow of the main contents, and you would have none of the issues you are experiencing.
.slideUp {
/* ... */
position: fixed;
}
https://jsfiddle.net/e8j938g8/3/

How to use angular-dotdotdot?

I am using dotdotdot seems to bea cool plug-in... however, I need to use it an angular, is not working if new elements are loaded (via http.get <> ng-repeat)...
So I find that there is an angular-dotdotdot... How to use it? is not very clear...
Suppose I have the classical usage of dotdotdot like this:
// ellipsis body to 4 lines height
var ellipsis = $(".ellipsis-case-body");
var nrLines = 4;
var ellHeight = parseInt(ellipsis.css('line-height'), 10) * nrLines;
ellipsis.dotdotdot({ height: ellHeight });
// ellipsis title to 1 line height
ellipsis = $(".ellipsis-case-title");
nrLines = 1;
ellHeight = parseInt(ellipsis.css('line-height'), 10) * nrLines;
ellipsis.dotdotdot({ height: ellHeight });
How to use it with angular?
In their documentation
$scope.myText = 'some really long text';
Template:
<p dotdotdot='myText'>{{myText}}</p>
But how to set options?
It doesn't look like you can. This is the directive source code, from GitHub:
angular.module('dotdotdot-angular', [])
.directive('dotdotdot', ['$timeout', function($timeout) {
return {
restrict: 'A',
link: function(scope, element, attributes) {
// We must pass in the scope binding, e.g. dotdotdot='myText' for scope.myText
scope.$watch(attributes.dotdotdot, function() {
// Wait for DOM to render
// NB. Don't use { watch: true } option in dotdotdot as it needlessly polls
$timeout(function() {
element.dotdotdot();
}, 400);
});
}
}
}]);
Note that there is nothing being passed to .dotdotdot().
You will probably want to fork this or just write your own.

How to manipulate styles of directive in AngularJS?

I'm writing a component using AngularJS and AngularJS directives.
I'm doing something like this:
var MyApp = angular.module('MyApp', []);
MyApp.directive('myTag', function() {
return { /* Some logic here*/ }
});
I want to be able to change style of my component (using CSS), something like this:
<my-tag class="MyClass"></my-tag>
Besides this I want to be able to manipulate all elements style inside my
component (HTML markup inside of my-tag).
Do you have any advice or useful examples how to manipulate the style properties of custom tags using AngularJS?
This should do the trick.
var MyApp = angular.module('MyApp', []);
MyApp.directive('myTag', function() {
return {
link: function(scope, element, attributes){
element.addClass('MyClass');
}
}
});
This is how AngularJS adds core CSS styles:
angular.element(document).find('head').prepend('<style type="text/css">#charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide{display:none !important;}ng\\:form{display:block;}</style>');
You can find this code in angular.js v1.2.0-rc.2.
EDIT
In a custom directive, I use this solution to bundle CSS stylesheets in the directive:
var outputColorCSS = {
selector: 'span.ouput-color',
rules: [
'display: inline-block',
'height: 1em',
'width: 5em',
'background: transparent',
'border: 3px solid black',
'text-align: center',
'font-weight: bold',
'font-size: 0.8em'
]
};
var outputColorStyleSheet = outputColorCSS.selector + outputColorCSS.rules.join(';');
angular.element(document).find('head').prepend('<style type="text/css">' + outputColorStyleSheet + '</style>');
Then you can use class="ouput-color" in your directive templates.
I found it very clean and useful.
I'm a little late to the party, but why aren't you all using the built in .css() method?
just use:
link: function(scope, elem, attr, ctrl)
{
elem.css({'display': 'block', 'height': '100%', 'width': '100%'});
}
or whatever css you desire.
You can put custom styles in a directive's declaration with a parameter, just like you exemplified.
In order to declare a style like that, you have to define a variable to hold the custom styles:
scope: {
myClass: '#myClass'
},
And then set that parameter in the directive's template, like this:
<my-tag my-class="CustomClass"></my-tag>
Finally, in the template of the directive itself, reference that class:
<h1 class="{{myClass}}">{{myContent}}</h1>
I made a plunker that shows how you can customize styles in a directive, check it out here .
Plunker
To manipulate the css style through an attribute directive, you could do something like this:
var app = angular.module('colorSwap', []);
app.directive('styleChanger', function() {
return {
'scope': false,
'link': function(scope, element, attrs) {
var someFunc = function(data)
{
/* does some logic */
return 'background-color:' + data;
}
var newStyle = attrs.styleChanger;
scope.$watch(newStyle, function (style) {
if (!style) {
return ;
}
attrs.$set('style', someFunc(style));
});
}
};
});
Some html:
<div ng-app="colorSwap">
<input type="txt" ng-init="colorName= 'yellow'" ng-model="colorName" />
<div style-changer="colorName">this is the div content</div>
</div>
To make an element directive, change it's own style, something like this:
app.directive('elementWithStyle', function() {
return {
'restrict' : 'E',
'scope': {},
'controller': function($scope) {
$scope.someStyle = 'Cyan';
$scope.someFunc = function() { $scope.someStyle = 'purple' };
},
'template': '<div style="background: {{someStyle}}" ng-click="someFunc()"> click me to change colors </div>'
}
});
And the html:
<div ng-app="colorSwap">
<element-with-style>123</element-with-style>
</div>
I hope this helps. The rest of the answers cover class manipulation more or less.
For css manipulation inside of the childs of your directive try this:
var MyApp = angular.module('MyApp', []);
MyApp.directive('myTag', function() {
return {
link: function(scope, element, attr){
// For your tag
element.addClass('MyClass');
// For elements inside your directive tag
var tag_childs = element[0].childNodes;
for(var i = 0; i < element[0].childElementCount; i++){
tag_childs[i].style.height = '70px';
}
}
}
});
Here is an example, please note that this is probably not the best use of AngularJS, being declarative, you would likely want to just put the classes on the markup. However, just so you understand what's going on, let me demonstrate a simple directive to do what you first asked.
var MyApp = angular.module('MyApp', []);
MyApp.directive('myTag', function($compile) {
return {
restrict: 'E', // this means it will be an element
link: function(scope, element, attrs, ctrl) {
// First, I included the $compile service because it will be needed
// to compile any markup you want to return to the element.
// 1. Add the class, as you wanted
element.addClass('MyClass');
// 2. Add markup
var html = '<div>Hello World</div>';
//Compile it and add it back
$compile(html)(scope);
element.html(html);
}
};
});
Finally, on your markup, you just put this in:
<my-tag />
app.directive('bookslist', function() {
return {
scope: true,
templateUrl: 'templates/bookslist.html',
restrict: "E",
controller: function($scope){
},
link: function(scope, element, attributes){
element.addClass('customClass');
}
}
});
.customClass table{
background: tan;
}
.customClass td{
border: 1px solid #ddd;
}
<!DOCTYPE html>
<html>
<head>
<link href="app.css" rel="stylesheet">
<script type="text/javascript" src="angular.min.js"></script>
<script type="text/javascript" src="app.js"></script>
<title>Task</title>
</head>
<body ng-app="app">
<div ng-controller="myCtrl">
<bookslist></bookslist>
</div>
</body>
</html>
Angular
app.directive("time",function(){
var directive={};
directive.restrict="A";
directive.link=function(scope,element,attr,ctrl){
element.css({
backgroundColor:'#ead333'
});
}
var time=new Date().toTimeString();
directive.template=time;
return directive;
});
HTML
The times is <span time></span>
I didn't found the perfect solution just yet, but I'm following John Papa's styling of controllers even with directives:
the directive is a folder (directiveName.directive)
3 files inside: directiveName.directive.js, directiveName.template.html, directiveName.styles.css
use templateUrl when declaring the directive. The template has the link to the css file, as usual
I found it to be very clean and follows a pattern. The bad side of it is that you create several <link> tags near the directives in the rendered HTML (not seem to be a issue still, though). Check out this comment too.
That being said, take a look at Angular 1.5 component's. It's relatively new and has a much better approach. Now I use directives only for DOM manipulation (not reusability as components).

Categories