Pass Multidimensional Array Elements to Div in Js - javascript

I have looked and haven't found any solutions to my issue.
I have a grid like a puzzle.
<div id="grid-container">
<div class="item" id="item1"></div>
<div class="item" id="item2"></div>
<div class="item" id="item3"></div>
<div class="item" id="item4"></div>
<div class="item" id="item5"></div>
<div class="item" id="item6"></div>
<div class="item" id="item7"></div>
<div class="item" id="item8"></div>
<div class="item" id="item9"></div>
<div class="item" id="item10"></div>
<div class="item" id="item11"></div>
<div class="item" id="item12"></div>
<div class="item" id="item13"></div>
<div class="item" id="item14"></div>
<div class="item" id="item15"></div>
<div class="item" id="item16"></div>
<div class="item" id="item17"></div>
<div class="item" id="item18"></div>
<div class="item" id="item19"></div>
<div class="item" id="item20"></div>
<div class="item" id="item21"></div>
<div class="item" id="item22"></div>
<div class="item" id="item23"></div>
<div class="item" id="item24"></div>
<div class="item" id="item25"></div>
<div class="item" id="item26"></div>
<div class="item" id="item27"></div>
<div class="item" id="item28"></div>
<div class="item" id="item29"></div>
<div class="item" id="item30"></div>
<div class="item" id="item31"></div>
<div class="item" id="item32"></div>
<div class="item" id="item33"></div>
<div class="item" id="item34"></div>
<div class="item" id="item35"></div>
<div class="item" id="item36"></div>
</div>
and I have an array of what I want to fill the puzzle with:
var ws = [
['A', 'P', 'P', 'L', 'E', 'P'],
['A', 'G', 'C', 'A', 'A', 'I'],
['A', 'R', 'A', 'A', 'A', 'Z'],
['A', 'A', 'A', 'A', 'T', 'Z'],
['P', 'P', 'A', 'A', 'A', 'A'],
['A', 'E', 'P', 'R', 'A', 'Y']
];
I have also created class to print out the array.
class Puzzle {
constructor(width, height) {
this.width = (width)? width : 6;
this.height = (height)? height : 6;
if(this.width != this.height)
throw "height and width must be the same"
}
print(){
console.log(ws)
}
}
puz = new Puzzle(6, 6);
puz.print();
What I want to do is to pass the output of the array into each div with IDs: item1..36.
I have used
document.getElementById('myArray').innerHTML = ws;
to try to get it to work but its not working. Please I need a better approach to it.

You may try this inside your print function. (my guess is your print will update the UI)
print() {
for(var i =1 ; i<= this.height; i++){
for(var j = 1; j <= this.width; j++) {
document.getElementById(`item${(i-1) * this.height + j}`).innerHTML = ws[i-1][j-1];
}
}
}
Hope this will help!

you need to create a matrix on the contructor,
class Puzzle {
constructor(width, height) {
var ws = [
['A', 'P', 'P', 'L', 'E', 'P'],
['A', 'G', 'C', 'A', 'A', 'I'],
['A', 'R', 'A', 'A', 'A', 'Z'],
['A', 'A', 'A', 'A', 'T', 'Z'],
['P', 'P', 'A', 'A', 'A', 'A'],
['A', 'E', 'P', 'R', 'A', 'Y']
];
if(this.width != this.height)
throw "height and width must be the same"
}
print(){
console.log(this)
}
}
puz = new Puzzle(6, 6);
now you can create a function that update the page and put values on elements:
you put all items on an 1D array
and then iterate items with 2 for cycle:
function updatePage(var x){
var elements = document.getElementsByClassName("item");
var k = 0;
for(var i = 0; i < puzzle.height; i++){
for(var j = 0; j < puzzle.width; j++){
elements[k].innerHTML = x.grid[i][j];
k++;
}
}
}
and finally you can call your function to view grid elements on the page:
updatePage(puzz);

Related

slice an array based on indeces

I have the current HTML:
<div id="1-1">TITLE1</div>
<div id="1-2">TITLE2</div>
<div id="1-3">TITLE3</div>
<div id="1-4">TITLE4</div>
<div id="1-5">TITLE5</div>
<div class="topic">A1</div>
<div class="topic">B2</div>
<div class="topic">C3</div>
<div class="topic">D4</div>
<div class="topic">E5</div>
<div class="topic">F6</div>
<div class="topic">G7</div>
<div class="topic">H8</div>
<div class="topic">I9</div>
<div class="topic">J10</div>
<div class="topic">K11</div>
<div class="topic">L12</div>
<div class="topic">M13</div>
<div class="topic">N14</div>
<div class="topic">O15</div>
And I want to use JavaScript to create a for loop that will append the correct .topic nodes into the correct TITLE div based on this object:
all = {'1-1': ['A', 'B', 'C'],
'1-2': ['D'],
'1-3': ['E', 'F', 'G', 'H'],
'1-4': ['I', 'J'],
'1-5': ['K', 'L', 'M', 'N', 'O']
};
Where the loop will ideally result in:
<div id="1-1">TITLE1
<div class="topic">A1</div>
<div class="topic">B2</div>
<div class="topic">C3</div>
</div>
<div id="1-2">TITLE2
<div class="topic">D4</div>
</div>
<div id="1-3">TITLE3
<div class="topic">E5</div>
<div class="topic">F6</div>
<div class="topic">G7</div>
<div class="topic">H8</div>
</div>
<div id="1-4">TITLE4
<div class="topic">I9</div>
<div class="topic">J10</div>
</div>
<div id="1-5">TITLE5
<div class="topic">K11</div>
<div class="topic">L12</div>
<div class="topic">M13</div>
<div class="topic">N14</div>
<div class="topic">O15</div>
</div>
I've taken a stab at this by creating two arrays -- one of the "TITLE*" locations in all and another of how many "topics" should be in each slice
let header_position = [0,4,6,11,14]
let topics_between = [3,1,4,2,5]
I was trying to leverage this and find a pattern for the for loop so that I can use these array numbers in order to put things in the right place but I can't seem to figure out the proper loop. Any help would be appreciated:
Attempted Solution
for (i = 0; i < header_position.length; i++) {
// i = 0 topics.slice(0,2)
// i = 2 topics.slice(3,3)
// i = 3 topics.slice(4,7)
// i = 4 topics.slice(8,9)
// i = 5 topics.slice(10,14)
let currentslice = topics.slice(header_position[i] + 1, header_position[i + 1] - 2)
if (i === 0) {
currentslice = topics.slice(0, topics_between[i] - 1)
}
console.log(currentslice)
currentslice.appendTo("#1-" + [i + 1])
}
This loop does not work as it stands, but I would appreciate any help or considerations in trying to get this logic functional!
Your question seems pretty theorical... And looks like an XY problem to me.
I assume your titles certainly will not be TITLE1, TITLE2 and so on with some real content.
I suppose you managed to define the all array to have a start of organisation of your page... But from what's posted, I can't suggest any better way.
So if I just take the all and the header_position arrays as-is, the loop would use two indexes, one for the headers and one for the topics.
Have a look below and tell me if that answers your theorical question as well as your concrete need.
let all = [ "TITLE1", "A", "B", "C",
"TITLE2", "D",
"TITLE3", "E", "F", "G", "H",
"TITLE4", "I", "J",
"TITLE5", "K", "L", "M", "N", "O"]
let header_position = [0,4,6,11,14]
//let topics_between = [3,1,4,2,5] // not used
let headerIndex = -1
let topicIndex = 0
for(i=0;i<all.length;i++){
if(header_position.indexOf(i)>-1){
headerIndex++
continue
}
$(".header").eq(headerIndex).append($(".topic").eq(topicIndex))
topicIndex++
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="header" id="1-1">TITLE1</div>
<div class="header" id="1-2">TITLE2</div>
<div class="header" id="1-3">TITLE3</div>
<div class="header" id="1-4">TITLE4</div>
<div class="header" id="1-5">TITLE5</div>
<div class="topic">A1</div>
<div class="topic">B2</div>
<div class="topic">C3</div>
<div class="topic">D4</div>
<div class="topic">E5</div>
<div class="topic">F6</div>
<div class="topic">G7</div>
<div class="topic">H8</div>
<div class="topic">I9</div>
<div class="topic">J10</div>
<div class="topic">K11</div>
<div class="topic">L12</div>
<div class="topic">M13</div>
<div class="topic">N14</div>
<div class="topic">O15</div>
From what I have understood, you want it to be split up by title. You can do this:
let count = 0;
for (let i = 0; i < all.length; i++) {
if (all[i].includes("TITLE")) {
let element = document.createElement("div");
element.innerHTML = all[i];
element.id = `1-${count + 1}`;
document.body.appendChild(element);
count++;
} else {
let element = document.createElement("div");
element.className = "topic";
element.innerHTML = `${all[i]}${i - count + 1}`;
document.getElementById(`1-${count}`).appendChild(element);
}
}
Let me know if it works.

Showing even indexes in one column and odd indexes in another column using v-for

I want to show the even indexes of my array myArray:
myArray: [{'label': 'hasan', 'value': 'hosein'},
{'label': '1', 'value': '2'},
{'label': 'gholi', 'value': 'gholam'},
{'label': '3', 'value': '4'},
{'label': 'an', 'value': 'goh'},
{'label': '5', 'value': '6'},
{'label': 'pashm', 'value': 'khar'},
{'label': '7', 'value': '8'}]
in the right and odd indexes on the left column using v-for.
This is my HTML code:
<div class="row" v-for="objData in myArray" :key="objData.label">
// right column
<div class="line col-2"></div>
<div class="line col-3 "></div>
// left column
<div class="line col-2"></div>
<div class="line col-3 "></div>
</div>
I tried to separate the odd and even indexes into two new arrays and added a <div> above <div class="row> with a new v-for to loop through both arrays in my separate <div>s but it scrambled my array elements. So how can I show even indexes of myArray on the right and odd indexes on the left column?
Spoiler: I like the last one better
You could either use this trick with i and v-if:
new Vue({
el: '#app',
data() {
return {
myArray: [{label:"hasan",value:"hosein"},{label:"1",value:"2"},{label:"gholi",value:"gholam"},{label:"3",value:"4"},{label:"an",value:"goh"},{label:"5",value:"6"},{label:"pashm",value:"khar"},{label:"7",value:"8"}]
};
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.21/vue.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap-grid.min.css" />
<div id="app" class="container">
<div class="row">
<div class="col-sm">
<template v-for="(objData, i) in myArray" :key="objData.label">
<div v-if="i%2">{{objData.label}}</div>
</template>
</div>
<div class="col-sm">
<template v-for="(objData, i) in myArray" :key="objData.label">
<div v-if="!(i%2)">{{objData.label}}</div>
</template>
</div>
</div>
</div>
Or separate your array in two in a computed prop, using reduce:
new Vue({
el: '#app',
data() {
return {
myArray: [{label:"hasan",value:"hosein"},{label:"1",value:"2"},{label:"gholi",value:"gholam"},{label:"3",value:"4"},{label:"an",value:"goh"},{label:"5",value:"6"},{label:"pashm",value:"khar"},{label:"7",value:"8"}]
};
},
computed: {
splitArray() {
const [ evens, odds ] = this.myArray.reduce((res, item, i) => {
res[i % 2].push(item);
return res;
}, [[], []]);
return { evens, odds };
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.21/vue.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap-grid.min.css" />
<div id="app" class="container">
<div class="row">
<div class="col-sm">
<div v-for="objData in splitArray.odds" :key="objData.label">
{{objData.label}}
</div>
</div>
<div class="col-sm">
<div v-for="objData in splitArray.evens" :key="objData.label">
{{objData.label}}
</div>
</div>
</div>
</div>
... And if you want to go further, and not have to repeat yourself in the template:
new Vue({
el: '#app',
data() {
return {
myArray: [{label:"hasan",value:"hosein"},{label:"1",value:"2"},{label:"gholi",value:"gholam"},{label:"3",value:"4"},{label:"an",value:"goh"},{label:"5",value:"6"},{label:"pashm",value:"khar"},{label:"7",value:"8"}]
};
},
computed: {
splitArray() {
return this.myArray.reduce((res, item, i) => {
res[1 - i % 2].push(item);
return res;
}, [[], []]);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.21/vue.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap-grid.min.css" />
<div id="app" class="container">
<div class="row">
<div v-for="(col, i) in splitArray" :key="`col-${i}`" class="col-sm">
<div v-for="objData in splitArray[i]" :key="objData.label">
{{objData.label}}
</div>
</div>
</div>
</div>
You should make your data like this
var myArray = [
{
left: {'label': 'hasan', 'value': 'hosein'},
right: {'label': '1', 'value': '2'}
}...
];
OR
use display:grid without div.row
<div v-for="(objData, index) in myArray" :key="objData.label" :class="['line', {col-2: index%2 === 1, col-3: index%2 === 0}"></div>
What about this? Managed to separate the array in two columns but resorted to two v-for, maybe you won't like that. It uses less javascript than the other solutions proposed but with the trade-off of a more complicated HTML.
HTML:
<div class="row">
<!-- Odd column -->
<div style="border: 1px solid red;">
<div v-for="(value, index) in myArray">
<div v-if="index % 2 != 0">{{ value }}</div>
</div>
</div>
<!-- Even column -->
<div style="border: 1px solid blue;">
<div v-for="(value, index) in myArray">
<div v-if="index % 2 == 0">{{ value }}</div>
</div>
</div>
</div>

fire directive after ng-repeat is finished

I am using owl carousel,for which data is coming from a ajax call. after ajax ng-repeat with populated the html content.
So i want to fire the directive which makes the owl carousel, after this all is done. How to do that.
One way i can think of using onFinishedRender directive, but this is not working.
directive :
directives.directive('onFinishRender',['$timeout', '$parse', function ($timeout, $parse) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
$timeout(function () {
scope.$emit('ngRepeatFinished');
if(!!attr.onFinishRender){
$parse(attr.onFinishRender);
}
});
}
}
}
}]);
controller:-
pastWorkService.getAllPastWork()
.success(function(data) {
var len = data.length;
$scope.pastWorkData1 = data.slice(0, len/2);
$scope.pastWorkData2 = data.slice(len/2, len - 1);
$scope.owlCarouselPastWork1 = function(){
$('.owl-carousel2').owlCarousel({
items: 4,
loop:true,
slideSpeed : 1000,
autoPlay: 3000,
itemsDesktop: [1199, 4],
itemsDesktopSmall: [980, 3],
itemsTablet: [768, 3],
itemsTabletSmall: false,
itemsMobile: [479, 1],
navigation: false
});
};
$scope.owlCarouselPastWork = function(){
$('.owl-carousel1').owlCarousel({
items: 4,
loop:true,
slideSpeed : 1000,
autoPlay: 3000,
itemsDesktop: [1199, 4],
itemsDesktopSmall: [980, 3],
itemsTablet: [768, 3],
itemsTabletSmall: false,
itemsMobile: [479, 1],
navigation: false
});
};
});
html:-
<div class="owl-carousel1" role="listbox" >
<div class="item" ng-repeat="p in pastWorkData1" on-finish-render="owlCarouselPastWork1()">
<img src="{{p.mainImage}}" alt="completed-projects" />
<div class="panel-footer">{{p.apartmentName}}</div>
</div>
</div>
<!-- second row goes here as owl carousel can contain 1 row -->
<div class="owl-carousel2" role="listbox" >
<div class="item" ng-repeat="p in pastWorkData2" on-finish-render="owlCarouselPastWork1()">
<img src="{{p.mainImage}}" alt="completed-projects" />
<div class="panel-footer">{{p.apartmentName}}</div>
</div>
</div>
Since you simply want to call a function after the last repeated item is rendered, here is one approach you could use:
html
<div class="owl-carousel1" role="listbox" >
<div class="item" ng-repeat="p in pastWorkData1">
<img src="{{p.mainImage}}" alt="completed-projects" />
<div class="panel-footer">{{p.apartmentName}}</div>
<span ng-if="$last" on-finish-render function-to-call="owlCarouselPastWork()"></span>
</div>
</div>
<!-- second row goes here as owl carousel can contain 1 row -->
<div class="owl-carousel2" role="listbox" >
<div class="item" ng-repeat="p in pastWorkData2">
<img src="{{p.mainImage}}" alt="completed-projects" />
<div class="panel-footer">{{p.apartmentName}}</div>
<span ng-if="$last" on-finish-render function-to-call="owlCarouselPastWork1()"></span>
</div>
</div>
directive
.directive('onFinishRender', function($timeout) {
return {
restrict: 'A',
scope: {
functionToCall: '&'
},
link: function(scope, elem, attrs) {
$timeout(function() {
scope.functionToCall();
});
}
}
})
Here is an extremely simple and contrived example to illustrate this idea. You could probably even rework this to have the function in the directive and you would simply pass the class identifier you want to call it on.

AngularJS: How to display an element on another div when clicked

I have an array of letters and I want to log whatever letter is clicked.
HTML:
<div class="col-md-2 col-sm-10 col-lg-1 letters"
ng-repeat="choice in images[num].letters track by $index">
<ul>
<li ng-click="displayLetter()"class="wordList">
{{choice}}
</li>
</ul>
</div>
JS:
$scope.displayLetter = function() {
console.log($scope.choice);
};
letters is stored inside an array of chars assigned to an object. The object is in an array of objects.
$scope.images =
[
{ pics: ["img/carrion.png", "img/telefrag.png", "img/thunder-skull.png", "img/skull-ring.png"], word: 'skull', length: 5,
letters: ['u', randLet(), randLet(), randLet(), 's', randLet(), 'k', 'l', randLet(), randLet(), randLet(), 'l' ]}
I keep getting undefined. How do I solve this?
I even tried adding an ng-model
<li ng-model="choice" ng-click="displayLetter()" class="wordList">
{{choice}}
</li>
The $scope is for the controller itself not for every single item in ng-repeat. Try it like this
<div class="col-md-2 col-sm-10 col-lg-1 letters">
<ul>
<li ng-repeat="choice in images[num].letters track by $index" ng-click="displayLetter(choice)" class="wordList">
{{choice}}
</li>
</ul>
</div>
$scope.displayLetter = function(choice) {
console.log(choice);
};
And as Lex mentioned below if you want each choice to be an li then the ng-repeat should on li. If you 4 elements in array and have the ng-repeat in the div you would get 4 div instead of 4 li.
I do not know if I quite understand your question.
But you could do the following:
file.js
var app = angular.module('test', []);
app.controller('maincontroller', function($scope) {
$scope.displayLetter = function(clicked) {
console.log(clicked);
};
$scope.images = [{
"pics": [
"http://www.aviacaobr.com.br/wp/img.png",
"http://www.psf.gov.pk/images/icons/gallery.png",
"http://handicrafts.nic.in/dc_hc_photo_gallery.png"
],
"word": 'skull',
"length": 5,
"letters": [
'u',
randLet(),
randLet(),
randLet(),
's',
randLet(),
'k',
'l',
randLet(),
randLet(),
randLet(),
'l'
]
}];
function randLet(){
return 'a';
}
});
file.html
<li><body ng-controller="maincontroller">
<div class="col-md-2 col-sm-10 col-lg-1 letters"
ng-repeat="choice in images[0].letters ">
<ul>
<li ng-click="displayLetter(choice)" class="wordList">
{{choice}}
</li>
</ul>
</div>
The function displayLetter receive a parameter, that is the clicked item.
And in ng-repeat, when ng-click call displayLetter pass the clicked item.
See in Plunker: Click Here

Using a {{expression}} inside ng-repeat to keep a count on ng-click

This throws a Syntax error. because of {{count}} inside of a ng-repeat.
I have a button that adds +1 to count:
Add count
Controller function:
$scope.count = 0;
$scope.addCount = function() {
$scope.count++;
alert($scope.count);
};
I would like this count to control the array index of a model::
$scope.img = [[
{
'src': 'runner-922.png',
'klass': 'h9'
},
{
'src': 'tweet.jpg',
'klass': 'h3'
}
],
{
'src': 'runner-922x.png',
'klass': 'h9'
},
{
'src': 'tweetx.jpg',
'klass': 'h3'
}
]
Which will be looped through using ng-repeat:
<div ng-repeat="i in img[{{count}}]" class="item {{i.klass}}" >
<img ng-src="images/iphone/{{i.src}}" alt="">
</div>
change this
<div ng-repeat="i in img[{{count}}]" class="item {{i.klass}}" >
<img ng-src="images/iphone/{{i.src}}" alt="">
</div>
to this
<div ng-repeat="i in img[count]" class="item {{i.klass}}" >
<img ng-src="images/iphone/{{i.src}}" alt="">
</div>
See this plunker

Categories