Create a containing div around every 6 objects in scope - javascript

I have a $scope.movies with 6 objects inside. On my template I use a ng-repeat to show the content. Which looks like this.
The movie_container divs are created by doing a "ng-repeat" = "movie in movies".
<div id="watchlist">
<div class="movie_container"> Movie Title </div>
<div class="movie_container"> Movie Title </div>
<div class="movie_container"> Movie Title </div>
<div class="movie_container"> Movie Title </div>
<div class="movie_container"> Movie Title </div>
<div class="movie_container"> Movie Title </div>
</div>
What I would like to do is wrap every 3 items in a container div so that the result would be,
<div id="watchlist">
<div class="movie_wrap">
<div class="movie_container"> Movie Title </div>
<div class="movie_container"> Movie Title </div>
<div class="movie_container"> Movie Title </div>
</div>
<div class="movie_wrap">
<div class="movie_container"> Movie Title </div>
<div class="movie_container"> Movie Title </div>
<div class="movie_container"> Movie Title </div>
</div>
</div>
Would something like this be possible to create with angular or javascript?
The $scope.movies array looks like this, (this is obviously example data, but it looks like this).
$scope.movies = [
{
title: 'Star Wars',
release_date: '10-11-2015',
movie_id: '3381',
link: 'ePbKGoIGAXY'
}, {
title: 'Spectre',
release_date: '25-12-2015',
movie_id: '3371',
link: 'KlyknsTJk0w'
}, {
title: 'Revenant',
release_date: '02-03-2016',
movie_id: '3361',
link: 'nyc6RJEEe0U'
},
{
title: 'Star Wars',
release_date: '10-11-2015',
movie_id: '3351',
link: 'zSWdZVtXT7E'
}, {
title: 'Spectre',
release_date: '25-12-2015',
movie_id: '3441',
link: 'ePbKGoIGAXY'
}, {
title: 'Revenant',
release_date: '02-03-2016',
movie_id: '3331',
link: 'Lm8p5rlrSkY'
}
];

I would split the movies in the controller and then loop over the groups so I don't need to much logic in the view.
controller:
var i,j,temparray,chunk = 3, movieGroups=[];
for (i=0,j=movies.length; i<j; i+=chunk) {
temparray = movies.slice(i,i+chunk);
movieGroups.push(temparray);
}
$scope.movieGroups = movieGroups;
markup:
<div class="movie_wrap" ng-repeat="movieGroup in movieGroups">
<div class="movie_container" ng-repeat="movie in movieGroup">{{movie.title}}</div>
</div>

You can write a function that will split them into an array of objects for you, it will make it much easier for you. See here
Fiddle example : http://jsfiddle.net/U3pVM/20670/
Something like this :
$scope.sortMoviesForWrapper = function() {
$scope.sortedMovies = [];
while ($scope.movies.length > 0)
$scope.sortedMovies.push({"movies" : $scope.movies.splice(0, 3)});
};
//invoke function immediately (you can change when you do this if you need)
$scope.sortMoviesForWrapper();
I turn movies into sortedMovies with the function sortMoviesForWrapper and invoke it immediately then use the repeat on sortedMovies instead like so :
<div class="movie_wrap" ng-repeat="movieWrappers in sortedMovies">
<div class="movie_container" ng-repeat="movie in movieWrappers.movies">
{{movie.link}}
</div>
</div>
I used the movie.link so you can see they are unique. This is the basic idea. You can modify how you see fit. I created an array that has objects inside that has "movies" key for all your movies to make it easier to repeat over in the template. You don't have to use an object, or a key of movies for that matter, I just personally prefer working with it that way.
Just keep in mind, if your $scope.movie object changes, you will have to rerun this function. Hope this helps.

Related

How do I loop over a slice of an array using ngFor?

I am creating a FAQ page with accordion buttons, with groups of buttons under sub-headers. I designed it using an ngFor statement in the faq.html file.
<h1>Frequently Asked Questions</h1>
<div *ngFor="let item of data; let i = index;">
<button class="accordion" (click)="toggleAccordion($event, i)"> {{item.parentName}} </button>
<div class="panel" *ngFor="let child of item.childProperties" hide="!item.isActive">
<p> {{child.propertyName}} </p>
</div>
Here is a snippet of the faq.ts file.
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-faq',
templateUrl: './faq.html',
styleUrls: ['./faq.scss']
})
export class FaqComponent implements OnInit {
data: any = [
{
parentName: 'Question 1A',
childProperties: [{ propertyName: 'Answer 1A' }],
},
{
parentName: 'Question 2A',
childProperties: [{ propertyName: 'Answer 2A' }],
},
{
parentName: 'Question 3A',
childProperties: [{ propertyName: 'Answer 3A' }],
},
{
parentName: 'Question 1B',
childProperties: [{ propertyName: 'Answer 1B' }],
},
{
parentName: 'Question 2B',
childProperties: [{ propertyName: 'Answer 2B' }],
},
];
}
I want to put sub-headers over Section A (Questions 1A, 2A, and 3A), and Section B (Questions 1B and 2B). I think I can use slice in the ngFor statement in faq.html, but the code won't compile.
I tried this slice pipe:
<div *ngFor="let item of data | slice:0:2; let i = index;">
What should I change to get it to compile and break up the FAQ sections? Is the slice pipe the way to go?
Slicing Your Data
The problem here is that the slice pipe returns your data as type unknown. There are a couple of ways around this:
$any
<p *ngFor="let item of data | slice:2:4">
{{ $any(item).parentName }}
</p>
Bracket notation
<p *ngFor="let item of data | slice:2:4">
{{ item['parentName'] }}
</p>
A function
slicedData(data : any[]) : any[] {
return data.slice(2,4)
}
<p *ngFor="let item of slicedData(data)">
{{ item['parentName'] }}
</p>
You might want to properly type your data though, instead of using any. It is called Typescript for a reason after all.
Here are some examples in a Stackblitz.
I had to change the html to access the properties in different way and it got compiled:
<div *ngFor="let item of data; let i = index;">
<button class="accordion" (click)="toggleAccordion($event, i)"> {{item['parentName']}} </button>
<div class="panel" *ngFor="let child of item['childProperties'] | slice:0:2; let i = index;" hide="!item.isActive">
<p> {{child['propertyName']}} </p>
</div>
You can just add one *ngIf and check if i < 3:
<h1>Frequently Asked Questions</h1>
<div *ngFor="let item of data; let i = index;">
<ng-container *ngIf="i < 3">
<button class="accordion" (click)="toggleAccordion($event, i)"> {{item.parentName}} </button>
<div class="panel" *ngFor="let child of item.childProperties" hide="!item.isActive">
<p> {{child.propertyName}} </p>
</ng-container>
</div>
Thank you for your help, everyone. I changed faq.html to:
<h1>Frequently Asked Questions</h1>
<h2>General</h2>
<div *ngFor="let item of data; let i = index;">
<ng-container *ngIf="i < 3">
<button class="accordion" (click)="toggleAccordion($event, i)"> {{item.parentName}} </button>
<div class="panel" hide="!item.isActive">
<p> {{item.childName}} </p>
</div>
and it worked.

Make <li> list per item of array within an object VueJs

I was trying to find the way in the docs, but I couldn't. In Vue JS, I have this data structure:
recipes: [
{
name: string,
ingredients: [array],
link: string
}
]
I can make a v-for and get my app to show the name, the link and the ingredients. However, I cannot find the way to loop through the ingredients array and make it show like a list.
This is how it looks like now:
And I want it to be like:
tomatoes
bread
garlic
So, basically I need to know how to ask for an iteration of items in an array within an object.
This is my component's code:
<!-- This creates a card per recipe -->
<template >
<div v-if="cardView" class="recipes container section">
<div class="row">
<div v-for="recipe in recipes" class="col s12 m6">
<div class="card blue-grey darken-1">
<div class="card-content white-text">
<h1 class="card-title">{{recipe.name}}</h1>
<h2>Ingredients</h2>
<ul>
<li v-for="(recipe,index) in recipes" :key="index">{{recipe.ingredients}}</li>
</ul>
<ul>
<li v-for="recipe in recipes">{{recipe.meals}}</li>
</ul>
</div>
<div class="card-action">
Full Recipe
Add to Week Menu
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { db } from "#/firebase/config";
export default {
name: "RecipesCards",
data() {
return {
recipes: []
};
},
methods: {},
created() {
db.collection("Recipes")
.get()
.then(snapshot => {
snapshot.forEach(doc => {
let recipe = doc.data();
recipe.id = doc.id;
this.recipes.push(recipe);
console.log(this.recipes);
});
});
},
props: ["cardView"]
};
</script>
If I've understood correctly then you want to change this:
<li v-for="(recipe,index) in recipes" :key="index">{{recipe.ingredients}}</li>
to this:
<li v-for="(ingredient, index) in recipe.ingredients" :key="index">{{ ingredient }}</li>
Here recipe is defined by the surrounding v-for on the div, which you already have.
Makes sense, cause you're itterating over the same array and returning ingredients values, and value of each object is an array.
Since your list is inside recipes loop, you may iterate over the array of each object of that array, so your v-for of a list should look like this
<li v-for="(ingredient, index) in recipe.ingredients" :key="index">
{{ingredient}}</li>
Another approach - make component for each card and return ingredients as an array through computed properties.

How to loop trough an array of images within an array in vue js

I would like go display the images inside an array named "image" within another array named product.
So basically if a product contain an array of 3 images i would like to display 3 images ,etc...
here's my code
<template>
<div class="details">
<div class="container">
<div class="row">
<div class="col-md-12" v-for="(product,index) in products" :key="index">
<div v-if="proId == product.productId">
<h1>{{product.productTitle}}</h1>
<h2>{{product.productId}}</h2>
<img :src="product.image[0]" class="img-fluid">
</div>
</div>
<div class="col-md-12" v-for="(product,index) in products" :key="index">
<div v-if="proId == product.productId">
<img :src="product.image[1]" class="img-fluid">
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "details",
data() {
return {
proId: this.$route.params.Pid,
title: "details",
products: [
{
productTitle: "ABCN",
image: [
require("../assets/images/autoportrait.jpg"),
require("../assets/images/bagel.jpg")
],
productId: 1
},
{
productTitle: "KARMA",
image: [require("../assets/images/bagel.jpg")],
productId: 2
},
{
productTitle: "Tino",
image: [require("../assets/images/bagel2.jpg")],
productId: 3
},
{
productTitle: "EFG",
image: [require("../assets/images/bagel3.jpg")],
productId: 4
}
]
};
}
};
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
I im able to display information within the first array example the product title, the product id, but the only way i found to display more images from the second array is to duplicate my code in the vue template and change the value of the index "product.image[0]", "product.image[1]".
There must be a better way to do this...
Thank a lot for the help
You can iterate over product images using v-for directive, just like you iterate over products:
<div class="col-md-12" v-for="(product, index) in products" :key="index">
<div v-if="proId == product.productId">
<h1>{{product.productTitle}}</h1>
<h2>{{product.productId}}</h2>
<div v-for="(image, imageIndex) in product.image">
<img :src="image" class="img-fluid" :key="imageIndex" />
</div>
</div>
</div>

Angular 1.6 - ng-repeat orderby and groupby items

So what I want to achieve is:
loop through array of messages (ng-repeat)
sort messages by date
then group current message with previous one, if author is matching (store in the same div element)
create new div if current messages's author is not matching previous one
continue loop
I'm stuck here:
<div class="message" ng-repeat="msg in chatDetails.msgList">
<p>{{ msg.text }}</p>
</div>
I need to keep exact order - simply, if previous element doesn't match with current - new box should be created.
Is that even possible in angular? If so, could you show me how, please?
Thank you!
edit
Heres sample result of chatDetails:
{
msgList: [
{ author: 0, text: 'hi', date: 1493050181799 },
{ author: 1, text: 'hola!', date: 1493050181801 },
{ author: 1, text: 'wilkomen', date: 1493050181802 },
{ author: 0, text: 'czesc', date: 1493050181803 }
{ author: 0, text: 'ciao', date: 1493050181804 }
{ author: 1, text: 'bonjour', date: 1493050181805 }
]
}
Somehow desired result:
<div class="message-list">
<div class="message-group" data-author="1">
<div class="message">
<p>hola</p>
</div>
<div class="message">
<p>ciao</p>
</div>
</div>
<div class="message-group" data-author="0">
<div class="message">
<p>hola</p>
</div>
</div>
<div class="message-group" data-author="1">
<div class="message">
<p>hola</p>
</div>
<div class="message">
<p>hola</p>
</div>
</div>
</div>
I believe your problem can be solved with "orderBy".
https://docs.angularjs.org/api/ng/filter/orderBy
It would look something like:
<div class="message" ng-repeat="msg in chatDetails.msgList | orderBy: 'date'">
<p>{{ msg.text }}</p>
</div>
If your msg objects have some date or index value, you can sort by that number.

AngularJS expression doesn't work in a different view

So I have this problem with creating a new view. The routeing works just fine because it shows the required HTML perfectly, but the expressions which worked just fine in the main view don't show up in the new.
<a href="#/view/{{todo.id}}" id="super" ng-repeat="todo in todos | filter: search">
<div id="main" ng-class="{'done': todo.done}">
<input type="checkbox" ng-model="todo.done" />
<span>{{todo.title}}</span>
<button id="info">i</button>
<div class="inf">
<span>Prioritás: {{todo.priority}}</span>
</div>
<div class="inf" id="sec">
<span>Határidő: {{todo.deadLine | date: "yyyy. MMMM. dd"}}</span>
</div>
</div>
</a>
These are the expressions in the main view, they work like a charm.
myTodoList.config(['$routeProvider', function($routeProvider, $locationProvider) {
$routeProvider
.when('', {
templateUrl: 'index.html',
controller: "mainController"
})
.when('/view/:id', {
templateUrl: 'view.html',
controller: "mainController"
}).otherwise({
redirectTo: ''
});
This is the routeing part, this is working.
<div id="mainContent">Master Detail
<div ng-controller="mainController" ng-view></div>
</div>
This is the div where the new view goes.
<div>
<p>Should be the ID: {{todo.id}}</p>
<p> should be the title: {{todo.title}}</p>
<p> this works: {{1+2}}</p></div>
And this is the view.html. The third expression is working, so I have the problem with de other two expressions. I think I messed up this because I can't reach the data I want. todo.id and todo.title are data created by a function in real time.
$scope.addTodo = function(title, priority, deadLine, id) {
$scope.todos.push({
'id': lastId,
'title': $scope.newTodo,
'done': false,
'priority': $scope.newPriority,
'deadLine': $scope.newDeadLine
});
$scope.newTodo = ''
$scope.newPriority = ''
$scope.newDeadLine = ''
lastId++;
}
This is the function I am using. I hope I described the problem well.
Are you using ng-repeat in your new view ?
<div>
<p>Should be the ID: {{todo.id}}</p>
<p> should be the title: {{todo.title}}</p>
<p> this works: {{1+2}}</p></div>
Your Scope variable name is $socpe.todos but here you are trying to access todo make sure if you are inside the ng-repeat if this is not your problem then share your full code on fiddle or codepen.
Just like Jeyenthaaran Nanjappan said, i think your div should look like this:
<div ng-repeat="todo in todos">
<p>Should be the ID: {{todo.id}}</p>
<p> should be the title: {{todo.title}}</p>
<p> this works: {{1+2}}</p>
</div>
Okay, I solved the problem. I needed to separate the different objects. So I made a function.
$scope.currentTodo = $scope.todos[0];
$scope.selectTodo = function(todo) {
$scope.currentTodo = todo;
}
I made a div and called this function with it.
<div ng-click="selectTodo(todo);" id="super" ng-repeat="todo in todos | filter: search">
And the problematic div now looks like this.
<div id="mainContent">Master Detail
<div>
<p>Should be the ID: {{currentTodo.id}}</p>
<p> should be the title: {{currentTodo.title}}</p>
</div>
</div>
Thank you for your help! I think both of you helped me through this problem.
Some issues in the nesting of the div and html. Change this:
<div id="mainContent">Master Detail
<div ng-repeat="todo in todos" ng-controller="mainController" ng-view></div>
<p>Should be the ID: {{todo.id}}</p>
<p> should be the title: {{todo.title}}</p>
<p> this works: {{1+2}}</p>
</div>
to this:
<div id="mainContent">Master Detail
<div ng-repeat="todo in todos" ng-controller="mainController">
<p>Should be the ID: {{todo.id}}</p>
<p> should be the title: {{todo.title}}</p>
<p> this works: {{1+2}}</p>
</div>
</div>

Categories