Using angular ng-repeat for multi-dimentional array - javascript

I'm currently working on a game and am trying to use NG repeat to dynamically add divs depending on how many latter and words there are. so for example if the answer to a queston in the game was "Clean Sheet" then i would want NG-Repeat to make the correct amount of columns so that it would say __ __ __ __ __ space __ __ __ __ __ then the user can fill them. Ive currently wrote this :
try{
//in this example presume $stateParams.answer is "clean sheet"
var answerArr = $stateParams.answer.toString().split(' ');
var finalLines = "";
$scope.mainWordHolder = [];
angular.forEach(answerArr, function(value, key) {
var amt = value.length;
$scope.amtofLetters=[];
for (var i=0; i<amt; i++) {
$scope.amtofLetters.push(i);
}
$scope.mainWordHolder.push($scope.amtofLetters);
$scope.amtofLetters = [];
});
console.log($scope.mainWordHolder);
}catch(e){console.log("error : "+ e);}
at this point my $scope.mainWordHolder is :
[ [ 0, 1, 2, 3, 4 ], [ 0, 1, 2, 3, 4 ] ]
which is exactly what i want so i know how many letters are needed for each word. How can i use ng-repeat to show this as divs so that i can create a keyboard and the user can enter keys like other popular games.
Ive tried this :
<div class="row">
<div ng-repeat="content in answerArr" class="col">
<div ng-repeat="contentt in mainWordHolder" class="col">
</div>
</div>
</div>
but i get nothing for some reason. Any help appreciated.

It looks like you're trying to reference answerArr in your ng-repeat, but answerArr is not stored on the scope.
When you use ng-repeat="content in answerArr", answerArr needs to be stored on the scope to be accessible in the HTML.
Try storing answerArr on your scope to be able to access it's contents via ng-repeat.

You need to iterate over the 'content' which represents the array in the whole array (the outer). Try this :
<div class="row">
<div ng-repeat="content in answerArr" class="col">
<div ng-repeat="contentt in content" class="col">
</div>
</div>
</div>

Related

Print an array from a function inside Angular HTML

I'm doing an app where I print some cars (I receive them from the database) and in one column I have one string with multiple tags that are divided by commas, I made a function to receive that as an array of words and then print each word for make tags, but I don't really know how to call the function or how to do it because it's repeating multiple times when it shouldn't do that.
<!-- Category Card -->
<div *ngFor="let car of cars" class="col-md-4">
<div class="card-image-overlay m-auto" id="tags">
{{separar(car?.tags)}}
</div>
</div>
Then the code of the function "separar":
public separar(string) {
var palabras = [];
palabras = string.split(",");
for (var i = 0 ; i < palabras.length ; i++) {
document.getElementById("tags").innerHTML +=
("<span class='card-detail-badge'>" + palabras[i] + "</span>");
}
}
I'm getting this:
and it should only print 3 times those tags.
Probably is a mistake very easy but Im new to angular and my teacher doesn't know why it doesn't work. :(
Use ngFor to do it "the Angular way" 😉
getTags(tags: sring): string[] {
return tags.split(',');
}
<!-- Category Card -->
<div *ngFor="let car of cars" class="col-md-4">
<div class="card-image-overlay m-auto" id="tags">
<span class='card-detail-badge' *ngFor="let tag of getTags(car?.tags)">
{{tag}}
</span>
</div>
</div>
Try returning the HTML string from the template function instead.
Also, avoid common (sometimes "reserved") words for variables i.e use tagListStr instead of string.
public separar(tagListStr) {
return tagListStr
.split(",")
.map(tag => `<span class='card-detail-badge'>${tag}</span>`)
.join('');
}

Use angularjs nested ng-repeat to construct complex table

I'm having trouble making proper table with nested ng-repeat.
What I wanted is this https://jsbin.com/razamagabo/1/edit?output
but I'm stuck at here https://plnkr.co/edit/d5voXIpzYL81sSl9BSY2?p=preview
I don't mind my markup is not table but I'm still stuck with div
<div class="row">
<div class="col-xs-6" ng-repeat="obj in data">
{{obj.date}}
<div ng-repeat="user in obj.users">
<br>
{{user.name}}
<br>
{{user.mark}}
</div>
</div>
</div>
In order for you to be able to display your data in the desired way, it will probably be easiest if you restructure your data in the JS before trying to render it.
It will be very complicated to try and match on the user names when they are in separate objects in the data array.
I would suggest processing your scope.data in the controller. (I'm assuming that you don't have much control on how you are receiving the data).
For example after you get your data...
$scope.data = [
{
date:'1-1-2016',
users:[
{
'name':'james',
'mark':18
},
{
'name':'alice',
'mark':20
}
]
},
{
date:'2-1-2016',
users:[
{
'name':'james',
'mark':60
},
{
'name':'alice',
'mark':55
}
]
}
]
var userData = {};
var possibleDates = [];
for (dataObj of Object.entries($scope.data)) {
for (userObj of dataObj) {
if ( !userData[userObj.name] ) {
userData[userObj.name] = {};
}
userData[userObj.name][dataObj.date] = userObj.mark;
if (dates.indexOf(dataObj.date) < 0) {
dates.push(dataObj.date);
}
}
}
$scope.users = userData;
$scope.dates = possibleDates;
this will give you an object like this on your scope
$scope.users = {
'james': {
'1-1-2016': 18,
'2-1-2016': 60
},
'alice': {
'1-1-2016': 20,
'2-1-2016': 55
}
};
$scope.dates = ['1-1-2016', '2-1-2016'];
This to me seems easier to structure for your template. Though this assumes each user has an entry for each date.
<div>
<div id='header-row'>
<div id='empty-corner></div>
<div class='date-header' ng-repeat='date in $scope.dates></div>
</div>
<div class='table-row' ng-repeat='{key, value} in $scope.users'>
<div class='user-name'>{{ key }}</div>
<div class='user-data' ng-repeat='date in $scope.dates>
{{ value[date] }}
</div>
</div>
</div>
As long as you apply inline-block styles to the rows/elements this should give you what you are looking for.
Though you can also think of ways to simplify your data even further. You could instead of having each user have an object where the dates are keys, you could just push the values into an array.
With your current data structure it is not possible to display it like you want. You are trying to loop over date-users objects in data array but then you want to display user from inside users array in separate rows. With ng-repeat you can loop through rows tr but not through columns. First you would need to map your data array to group elements that are supposed to be visible in 1 row into 1 object in array. Currently you have them in 2 separate objects:
James mark: 18 and James mark: 60.

AngularJS: displaying an array of objects in separate rows

Let's say, I have an array of objects and I want to display it in several rows. Each row should consist of a specific number of objects. Basically, it should look like this:
<div class="row">
<div class="col-md-4">item</div>
<div class="col-md-4">item</div>
<div class="col-md-4">item</div>
</div>
<div class="row">
<div class="col-md-4">item</div>
<div class="col-md-4">item</div>
<div class="col-md-4">item</div>
</div>
...
I've implemented it with a very dirty trick, generating an additional array of numbers and iterating through it (4 is a number of objects in a row):
<div class="row titles-row" ng-repeat="row in outRange(series.length, 4)">
<project-preview class="col-md-3" ng-repeat="project in series" ng-if="$index < (row + 1)*4&& $index >= (row)*4"></project-preview>
</div>
and outRange function:
$scope.outRange = function(items_num, row_width) {
items_num = items_num > 0 ? items_num : 0;
var rows_num = Math.ceil(items_num / row_width);
return Array.apply(null, Array(rows_num)).map(function (_, i) {return i;});
};
It works, but I feel like there should be a better way to do it.
If this is just a matter of presentation, bootstrap (which it seems you might be using) will automatically put the other objects on a separate row when the sum of columns is more than 12 (it uses floats). If however the objects have significantly different sizes, this might not look so good indeed. Still, I would tend to leave this under control of CSS, rather than in javascript.
One approach would be to use a display: flexbox on the container, which should have the effect you want automatically. Lookup this CSS property to discover the true strength of flexbox.
If you really want to do it in javascript, you could have a template like:
<div ng-class='{row: $index % 4 == 0}' ng-repeat='...'>
<div class='col-md-4'>
..
</div>
</div>
This will generate extra divs, but that is likely acceptable.
Instead of outRange, use a filter to create chunks out of the series array. Lodash has a chunk method. Or you can implement one yourself.
Thanks for your ideas. I came up with this solution:
mainApp.filter('slice', function() {
return function(array, row_width, scope) {
if(array == undefined)
return;
if(scope.sliceResult != undefined)
return scope.sliceResult;
scope.sliceResult = [];
var rows_num = Math.ceil(array.length / row_width);
for(var i = 0; i < rows_num; i++) {
scope.sliceResult.push(array.slice(i * row_width, i * row_width + row_width));
}
return scope.sliceResult;
};
});
And here how I use it:
<div class="row titles-row" ng-repeat="row in series | slice: 4 : this">
<project-preview class="col-md-3" ng-repeat="project in row"></project-preview>
</div>
Still I don't like that I need to pass the scope inside the filter.

Combine divs within a for loop

Is it possible to use a JavaScript for loop to combine a number of div's? I have 16 sets of these I am wanting to put into a for loop. The problem I am having is that its HTML not JavaScript I am trying to do this with. I haven't seen anything so far on how to go about this. Thanks for any help or suggestions.
What the following code does is catch the data pre-defined from a 16X17 table and inserts it into one cell in my document. I then have have other code using the div id's that makes visible just the one I need.
<div id="101" class="hidden"><script>document.write(tab1a1)</script></div>
<div id="102" class="hidden"><script>document.write(tab1a2)</script></div>
<div id="103" class="hidden"><script>document.write(tab1a3)</script></div>
<div id="104" class="hidden"><script>document.write(tab1a4)</script></div>
<div id="105" class="hidden"><script>document.write(tab1a5)</script></div>
<div id="106" class="hidden"><script>document.write(tab1a6)</script></div>
<div id="107" class="hidden"><script>document.write(tab1a7)</script></div>
<div id="108" class="hidden"><script>document.write(tab1a8)</script></div>
<div id="109" class="hidden"><script>document.write(tab1a9)</script></div>
<div id="110" class="hidden"><script>document.write(tab1a10)</script></div>
<div id="111" class="hidden"><script>document.write(tab1a11)</script></div>
<div id="112" class="hidden"><script>document.write(tab1a12)</script></div>
<div id="113" class="hidden"><script>document.write(tab1a13)</script></div>
<div id="114" class="hidden"><script>document.write(tab1a14)</script></div>
<div id="115" class="hidden"><script>document.write(tab1a15)</script></div>
<div id="116" class="hidden"><script>document.write(tab1a16)</script></div>
<div id="117" class="hidden"><script>document.write(tab1a17)</script></div>
Update: Pulling data from table
<!--Start- Takes Assembly number from Data Table--> <!--Change "<Col1;" equala columns-->
for (var x = 1; x<Col1; x++){window["aa"+x] = document.getElementById("part1Table").rows[0].cells[x+1].innerHTML;}
<!--End--- Takes Assembly number from Data Table-->
<!--Start- Takes Assembly Rows from Data Table--> <!--Change "<Row1;" equals rows-->
for (var y = 1; y<Row1+1; y++){window["rows"+y] = document.getElementById("part1Table").rows[y].cells[1].innerHTML;}
<!--End- Takes Assembly Rows from Data Table-->
<!--Start- Takes Part number from Data Table--> <!-- "<Col1;" equals columns----> <!-- If a Column is added to main table add a new line below---->
for (var z1 = 1; z1 <Col1; z1++) {window["tab1a"+z1] = document.getElementById("part1Table").rows[1].cells[z1 +1].innerHTML;}
for (var z2 = 1; z2 <Col1; z2++) {window["tab2a"+z2] = document.getElementById("part1Table").rows[2].cells[z2 +1].innerHTML;}
for (var z3 = 1; z3 <Col1; z3++) {window["tab3a"+z3] = document.getElementById("part1Table").rows[3].cells[z3 +1].innerHTML;}
Try using AngularJS (which is a javascript framework that you can easily add to your html page (see AngularJS.com for a quick intro))
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
</head>
<body ng-app='myApp'>
<div ng-repeat='entry in entries'>
<div id={{entry.id}} class="hidden"><script>document.write({{entry.table}})</script></div>
</div>
<script src='app.js'></script>
</body>
</html>
Then within a controller in a file called app.js you would make a array of objects 1...100 or whatever number you want and pass it to a scope like this
var myApp = angular.module('myApp', []);
myApp.controller('MainCtrl', function($scope) {
$scope.entries = [{
id: '101', table: 'tab1a1'}, {id: '102', table: 'tab1a2'}]
// extend to your own range
});
having a bunch of sequential variables like tab1a1 is difficult to work with. Can you put those in an array? Then...
var b=document.getElementsByTagName('body')[0];
for(var i=1; i<18; i++){
var d=document.createElement('div'); //make a div element
d.id='10'+i; //assign sequential id
d.innerHTML=tab1a[i]; //put the the array element content corresponding to that number into the new div element
b.appendChild(d); //place new element into the DOM
}
Another option would be to use excel and basically make a column with on the rows 101,102,103... And a similar same column for the tables. Then just add the code lines in string format and bind the colums together with &. Copy everything and paste this in notepad and then in your code!
Ex:
="<div id='" & A1 & "' etc etc
Thank you to everyone who gave suggestions. I spent a few more hours with the code and came up with the following. It works great in testing. I can now add and remove rows and columns from my table without having to change the code.
for (var w = 1; w <Row1; w++)
for (var z1 = 1; z1 <Col1; z1++) {window["tab" + w + "a" + z1] = document.getElementById("part1Table").rows[w].cells[z1 +1].innerHTML;}
This replaces my 17 lines of the following:
for (var z17 = 1; z17 <Col1; z17++) {window["tab17a"+z17] = document.getElementById("part1Table").rows[17].cells[z17 +1].innerHTML;}
This is very good because my new table has 50+ rows.

how to show array element one by one on page while array is property of an object which inside of services

i have this a testPaper object in Services :
this.testPapers=[{
courseName: 'Languages' , moduleName: 'Arabic',
paperName: 'Arabic-V',
paperDate: '15/08/2014',
// paperStartTime:
//papertEndTime:
// PaperDuration: '00:10:00',
marksForEachQuestion: 5,
totalMarks: 15,
Question: ['Mirwaha meaning in English?','Jwwaz meaning in English?','Hafila meaning in English?']
}
while the controller:
.controller('questionStart',function($scope, $routeParams,$rootScope,crudService){
// $scope.allCourses= crudService.courses;
// $scope.allModules=crudService.modules;
$scope.allTestPapers=crudService.testPapers;
}
and UI
<ol>
<li data-ng-repeat="p in allTestPapers | filter : mSelectedCourse
| filter: mSelectedModule
| filter : mSelectedPaper">
<hr>
<div class="row">
<div class="col-xs-12 col-sm-3">
Paper Name :<b>{{p.paperName}}</b>
</div>
<div class="col-xs-12 col-sm-3">
Total Marks:<b>{{p.totalMarks}}</b>
</div>
<div class="col-xs-12 col-sm-3">
Marks For Each Question: <b>{{p.marksForEachQuestion}}</b>
</div>
<div class="col-xs-12 col-sm-3">
Total Question: <b>{{p.Question.length}}</b>
</div>
</div>
<hr>
<h4>{{p.Question}}</h4>
<!--Which logic need to put in above line to not show all question at once instead one at a time
and when user click a button then next question will show??? how to do this-->
<button data-ng-click="">next</button>
</ol>
now from this i want to show the Question array on the page but one by one (mean from 0 to last item but one question show at page then on button click i want to change the question).
How can i do this;
So here is how you should go on about it, starting from the UI, which will have a heading for question, an input box for answer and a next button:
<h3 id='q'>Questions will appear here<h3>
Answer: <input type:'text' id='a'><br>
<button onclick="showQuestion()">Next</button>
Then your tesPapers, for this example i used two papers:
var testPapers = [{
courseName: 'Languages' , moduleName: 'Arabic',
paperName: 'Arabic-V',
paperDate: '15/08/2014',
// paperStartTime:
//papertEndTime:
// PaperDuration: '00:10:00',
marksForEachQuestion: 5,
totalMarks: 15,
Question: ['Mirwaha meaning in English?','Jwwaz meaning in English?','Hafila meaning in English?']
},{
courseName: 'Languages' , moduleName: 'Arabic',
paperName: 'Arabic-V',
paperDate: '15/08/2014',
// paperStartTime:
//papertEndTime:
// PaperDuration: '00:10:00',
marksForEachQuestion: 5,
totalMarks: 15,
Question: ['Mirwaha meaning in English?','Jwwaz meaning in English?','Hafila meaning in English?']
}];
Now all the question logic, its simple.. you need to have two counters, one for paperNumber other for Question number.. when current paper's questions end, you need to increment the paperNumber and when all papers end, simply display End of all papers.
var paperNumber = 0;
var questionNumber = 0;
function showQuestion(){
document.getElementById('q').innerHTML = testPapers[paperNumber].Question[questionNumber];
questionNumber++;
if(questionNumber > testPapers[paperNumber].Question.length){
document.getElementById('q').innerHTML = "End of this Paper";
questionNumber = 0;
paperNumber++;
if(paperNumber >= testPapers.length){
document.getElementById('q').innerHTML = "End of All Papers";
}
}
}
OfCourse you can change the UI however you want but the core logic should remain the same, specially the question and papers looping mechanism.
See the DEMO here
You need to inject the service into the controller for the required view. Then you should be able to use the ng-repeat directive:
<p ng-repeat="quest in serviceName.testPaper[0].Question">{{quest}}</p>
edit as per comments:
in controller:
var index = 0;
$scope.currentQuestion = serviceName.testPaper[0].Question[index];
$scope.nextQuestion = function() {
$scope.currentQuestion = serviceName.testPaper[0].Question[index+1];
};
in html
<p>{{currentQuestion}}</p>
<button ng-click="nextQuestion()">Next Question</button>

Categories

Resources