Repeating string n times inside Angular HTML without using controller - angularjs

I have the following code...
<div> {{line.tab}} </div>
This prints out the number of tabs I want (1, 2, 3) and works fine. Now I need to know how to print the tab char (\t) as many times. I would prefer not to use the controller.
In other word line.tab can be 1, 2, or 3 if it is 1 it should have 0 tabs 2 should have 1, etc.
The only way I have been able to accomplish this so far is using the controller like this (this uses 4 spaces instead of a tab but same idea)...
span(ng-repeat="i in ctrl.getNumber(line.tab) track by $index")
span {{line.n.properties.indicator}}
span {{line.n.properties.e}}
span {{line.n.properties.value}}
controller : function(){
this.getNumber = function(num) {
return new Array(num-1);
}
}
I was hoping to do this without having to use the controller function and without multiple spans.

You could try
<div ng-repeat="n in [0, 1, 2]">
&#9
</div>
Here's the doc for ngRepeat https://docs.angularjs.org/api/ng/directive/ngRepeat

Related

How to exclude empty spaces when counting length in Angular?

I am checking the length of a title and setting a class, based on that length. I noticed though, that the length is also checking the empty spaces, so when you have a space, it would count as +1.
I want to get rid of that. So I want to exclude the count of empty spaces in the length. How can I do it, if it's possible?
<h1 ng-class="{'specific-class': title.name.length >= 10}">{{title.name}}</h1>
You could check this by replacing the spaces with empty strings:
title.name.replace(' ','').length >= 10
The full line would be:
<h1 ng-class="{'specific-class': title.name.replace(' ','').length >= 10}">{{title.name}}</h1>
Say if title.name was 'Hello World!', title.name.length would be 12, but title.name.replace(' ','').length is 11.
EDIT
Turns out you can't use slashes inside HTML or Angular will convert them to html-safe characters. I'd suggest therefore to separate the checker out into its own module. I've attached a snippet so you can see how it's done:
angular
.module("app", [])
.controller("test", function($scope) {
// Example title object. Just load title objects as you would normally.
$scope.title = {name: 'The World of McNamara'};
// The actual magic happens here:
$scope.checker = function(word) {
return word.replace(/\s/g, '').length >= 10;
}
})
.specific-class {
background-color: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!-- snippet checks whatever is passed to it as title.name -->
<div ng-app="app" ng-controller="test">
<h1 ng-class="{'specific-class': checker(title.name)}">{{title.name}}</h1>
</div>
You could do in multiple ways to remove empty spaces from the string like using split and then join.
title.name.split(' ').join('')
Or could be done by regex approach:
title.name.replace(/ /g, '')
It is up to you how you want to implement.
You would have to use .trim :
title.name.trim().length
Or you could do :
title.name.replace(" ", "").length
EDIT :
In your code it would be like so :
<h1 ng-class="{'specific-class': title.name.replace(' ', '').length >= 10}">{{title.name}}</h1>
Using ternary logic :
<h1 ng-class="(title.name.replace(' ', '').length >= 10)? 'specific-class' : 'another-class' ">{{title.name}}</h1>

Compiling a String in Angular Before Displaying

I have the following html:
<div ng-repeat="string in myStrings">
<p>{{string}}</p>
</div>
And a string like this that gets added to $scope.myStrings:
$scope.stringIwantToBeCompiled = 'I want to count to 4 via angular: {{2+2}}';
I would like the string to show 4 instead of the {{2+2}} angular expression.
Am I barking up the wrong tree here by trying to do this via $compile? If not, how is it done? Just putting it in compile fails. Do I absolutely HAVE to do this in a directive?
PLNKR FOR REFERENCE
Not sure what your exact goal is, but I can think of two approaches to accomplish this without compiling:
1) Split up the values like so:
<div ng-repeat="string in myStrings">
<p>{{string}}{{mathValue}}</p>
</div>
in controller:
$scope.mathValue = 2+2;
2) Use a function to return the string (I like using this anytime I'm doing anything binding that is non-trivial):
<div ng-repeat="string in myStrings">
<p>{{stringFunction()}}</p>
</div>
in controller:
$scope.mathValue = 2+2;
$scope.stringFunction = function() {
return 'I want to count to 4 via angular: '+$scope.mathValue;
};
I'm not 100% sure whether you are just wanting to count the number of strings in the myStrings array, or just have the ability to add a count, but given your Plunker, you could do the following:
To simply add two variables, update the following line:
$scope.stringIwantToBeCompiled = 'I want to count to 4 via angular: ' + (2+2);
If you wanted to show the count of the number of strings, swap the order of your scope variable declarations and show the myStrings length
$scope.myStrings = ['I am a string', 'I am another string', 'more strings!'];
$scope.stringIwantToBeCompiled = 'I want to count to 4 via angular: ' + $scope.myStrings.length;
Counting the strings will only give you 3, of course, because there are only 3 strings in the array.
Does that solve it for you?
UPDATE
OK - So I think what you want is the count in the string with an ng-click to correspond to the count correct?
If so, then the following on your ng-repeat would do it...
<p>{{string}} {{$index}} </p>
Using $index gives you the index of the repeating item. You can always add 1 to the $index to make it 1-based instead of zero based:
<p>{{string}} {{$index + 1}} </p>
You can append the angular expresion {{}} to the string like:
$scope.stringIwantToBeCompiled = 'I want to count to 4 via angular: ' + {{stuff or 2 + 2}};
Or use $compile Fiddle example
I really needed to use a directive with $compile like shown here:
app.directive('dynamicAlert', function ($compile) {
return {
restrict: 'A',
replace: true,
link: function (scope, ele, attrs) {
scope.$watch(attrs.dynamicAlert, function(html) {
ele.html(html);
$compile(ele.contents())(scope);
});
}
};
});
http://plnkr.co/edit/bJPEyfkKsertulTN7rWp?p=preview

Ng-Repeat array to rows and columns

Thanks for taking the time to read this, I was wondering how I might be able to use ng-repeat to create a grid like box of options. I would like to take an array repeat nth number of items and then move to the next row or column until all items are listed. e.g.
assuming I had an array like [opt1,opt2,opt3,opt4,opt5,opt6,opt7] I would like to display it like this:
opt1 opt2 opt3
opt4 opt5 opt6
opt7
This is more a styling/markup problem than an AngularJS one. If you really want to, you can do:
<span ng:repeat="(index, value) in array">
{{value}}<br ng:show="(index+1)%3==0" />
</span>
http://jsfiddle.net/JG3A5/
Sorry for my HAML and Bootstrap3:
.row
.col-lg-4
%div{'ng:repeat' => "item in array.slice(0, array.length / 3)"}
{{item}}
.col-lg-4
%div{'ng:repeat' => "item in array.slice(array.length / 3, array.length * 2/3)"}
{{item}}
.col-lg-4
%div{'ng:repeat' => "item in array.slice(array.length * 2/3, array.length)"}
{{item}}
There is another version, with possibility to use filters:
<div class="row">
<div class="col-md-4" ng-repeat="remainder in [0,1,2]">
<span ng-repeat="item in array" ng-if="$index % 3 == remainder">{{item}}</span>
</div>
</div>
If all of your items are in one single array, your best bet is to make a grid in CSS. This article should be helpful: http://css-tricks.com/dont-overthink-it-grids/
You can use $index from ng-repeat to apply the correct class for your column (in this case a 4 column grid):
<div class="col-{{ $index % 4 }}"></div>
If you have a 2 dimensional array (split into rows and columns) that opens up more possibilities like actually using an HTML table.
I find it easier to simply use ng-repeat combined with ng-if and offsetting any indexes using $index. Mind the jade below:
div(ng-repeat="product in products")
div.row(ng-if="$index % 2 === 0")
div.col(ng-init="p1 = products[$index]")
span p1.Title
div.col(ng-if="products.length > $index + 1", ng-init="p2 = products[$index + 1]")
span p2.Title
div.col(ng-if="products.length <= $index + 1")
Between Performance, Dynamics and Readability
It seems putting the logic in your JavaScript is the best method. I would just bite-the-bullet and look into:
function listToMatrix(list, n) {
var grid = [], i = 0, x = list.length, col, row = -1;
for (var i = 0; i < x; i++) {
col = i % n;
if (col === 0) {
grid[++row] = [];
}
grid[row][col] = list[i];
}
return grid;
}
var matrix = listToMatrix(lists, 3);
console.log('#RedPill', matrix);
# Params: (list, n)
Where list is any array and n is an arbitrary number of columns desired per row
# Return: A matroid
# Note: This function is designed to orient a matroid based upon an arbitrary number of columns with variance in its number of rows. In other words, x = desired-columns, y = n.
You can then create an angular filter to handle this:
Filter:
angular.module('lists', []).filter('matrical', function() {
return function(list, columns) {
return listToMatrix(list, columns);
};
});
Controller:
function listOfListsController($scope) {
$scope.lists = $http.get('/lists');
}
View:
<div class="row" ng-repeat="row in (lists | matrical:3)">
<div class="col col-33" ng-repeat="list in row">{{list.name}}</div>
</div>
With this, you can see you get n number of rows -- each containing "3" columns. When you change the number of desired columns, you'll notice the number of rows changes accordingly (assuming the list-length is always the same ;)).
Here's a fiddle.
Note, that you get the ol' Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!. This is because Angular is recalling the matrical function upon every iteration. Allegedly, you can use the as results alias to prevent Angular from reevaluating the collection, but I had no luck. For this, it may be better to filter the grid inside of your controller and use that value for your repeater: $filter('matrical')(items) -- but please post back if you come across an elegant way of filtering it in the ng-repeat.
I would stress, again, you're probably heading down a dark alley by trying to write the logic in your view -- but I encourage you to try it in your view if you haven't already.
Edit
The use of this algorithm should be combined with a Matrical Data-Structure to provide methods of push, pop, splice, and additional methods -- in tandem with appropriate logic to complement Bi-Directional Data-Binding if desired. In other words, data-binding will not work out of the box (of course) as when a new item is added to your list, a reevaluation of the entire list must take place to keep the matrix's structural integrity.
Suggestion: Use the $filter('matrical')($scope.list) syntax in combination with $scope.$watch and recompile/calculate item-positions for the matrix.
Cheers!

AngularJS: ng-repeat an array with keys

I have an array with unordered keys, and I want to display them. The problem is that angular repeats it for all the keys, even when they are not set.
this is the code:
<div ng-controller="MyCtrl">
Hello, {{a[10]}}!
<p ng-repeat="b in a">
1. {{b}}
</p>
</div>
<script>
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.a = [];
$scope.a[10] = "aaa";
}
</script>
and this is the output:
Hello, aaa!
1.
1.
1.
1.
1.
1.
1.
1.
1.
1.
1. aaa
i want only the array keys that are set to output. no empty b's please...
here is a jsfiddle
In essence your problem is not AngularJS related but rather how JavaScript works.
If you have an empty array and you assign an element to position 10, then JavaScript will automatically resize the array to 11 elements (since an array index starts at zero) so the index is large enough to hold your element.
You could write extra code to filter the empty elements, but based on what you write, I think you would be better off with using an object to store your data.
I have created a plnkr for your convenience: http://plnkr.co/edit/apRLuJr4zqS2zbMz322Q?p=preview
// Array
$scope.sampleArray = [];
$scope.sampleArray[10] = 'test';
// Object
$scope.sampleObject = {};
$scope.sampleObject[10] = 'test';
As you can see the syntax is very similar, but the output is completely different.
By using an object, you will automatically eliminate the empty lines.
It will also keep your code simpler since you won't have to deal with the empty array elements.
Hope that helps!
There's plenty of ways to do a cleanup on your array inside the controller (e.g. using $watchcallback on a that would remove the empty elements from it whenever it changes).
Here's a solution that uses a simple custom filter, defined in a controller:
function MyCtrl($scope) {
$scope.namea = 'Superhero';
$scope.a = [];
$scope.a[10] = "aaa";
$scope.myFilter = function(item){
return item;
}
}
<p ng-repeat="b in a | filter:myFilter">
1. {{b}}
</p>
As stated in filter docs, the 'filter' filter can take a function:
function: A predicate function can be used to write arbitrary filters.
The function is called for each element of array. The final result is
an array of those elements that the predicate returned true for.

AngularJS - ngRepeat and ngModel

I have spent the better part of a few hours writing and re-writing this, and am probably just going to write my own directive here if there isn't an answer.
I have a columnized display of inputs, 10 in each of the 6 columns. I am using 2 ngRepeat directives to display them. I am placing 6 fiddles below with my varied attempts at getting them to work right. The problem is, when I use an array of objects, all the data are updated simultaneously. View Fiddle #1 below to see the example.
Here is a quick snippet of the code, which you can also see on the fiddle page. If anyone has some pointers or a way to get #1, #2, or #6 to work, please let me know!
HTML:
<div ng-controller='EntryCtrl'>
<div class="span2" ng-repeat="a in [0,1,2,3,4,5]">
<div ng-repeat="i in entry.slice($index*10, ($index*10)+10)" ng-class="{'control-group': true, error: !entry[($parent.$index*10)+$index].correct}">{{($parent.$index*10)+$index}}<input type="text" class="span12" id="entry-{{($parent.$index*10)+$index}}" ng-model="entry[($parent.$index*10)+$index].input" /></div>
</div>
</div>
Javascript:
var myApp = angular.module('myApp', []);
myApp.controller('EntryCtrl', ['$scope', function($scope) {
$scope.entry=filledArray(60,{input:'',correct:true});
}]);
function filledArray(len, val) {
var rv = new Array(len);
while (--len >= 0) {
rv[len] = val;
}
return rv;
}
Fiddle #1: Array of objects using ng-model pointing to entry using $index and $parent.$index: http://jsfiddle.net/DFEkG/ -- All models update simultaneously
Fiddle #2: Array of objects using ng-model pointing to i instead of $index: http://jsfiddle.net/DFEkG/1/ -- All models update simultaneously
Fiddle #3: Array using ng-model pointing to entry using $index and $parent.$index: http://jsfiddle.net/DFEkG/2/ -- Weird behavior
Fiddle #4 Array using ng-model pointed to i instead of $index: http://jsfiddle.net/DFEkG/3/ -- Broken
Fiddle #5 Array using only $index and $parent.$index and array only in ng-repeat directive: http://jsfiddle.net/DFEkG/4/ -- WORKS! But not as object
Fiddle #6 Same as technique as 5, but with an object: http://jsfiddle.net/DFEkG/5/ -- Same as fiddles 1 and 2
The problem is in your filledArray function. It assigns the same exact object to each array item. The same object {input: '', correct: true} is referenced in all 60 instances.
So, to fix it you can simply make a new copy of it on each iteration:
function filledArray(len, val) {
var rv = new Array(len);
while (--len >= 0) {
rv[len] = angular.copy(val);
}
return rv;
}
Fiddle.

Resources