AngularJS: How to use the ngRepeat directive together with arrays and subarrays? - javascript

Let's assume there is an array of categories. Contained within this array, for each category, there is a variable number of images. Additionally, each image has a sub-array of paths for varying sizes of each image. Listed below is an example.
$scope.categories = [
{ "id": 1, "name": "Fashion", "images": [
{ "id": 1, "paths": [
{ "id": 1, "pathThumb":"thumbnail_A.jpg"},
{ "id": 2, "pathFull":"full_size_A.jpg"}
]}
]},
{ "id": 2, "name": "Sunsets", "images": [
{ "id": 1, "paths": [
{ "id": 1, "pathThumb":"thumbnail_B.jpg"}
{ "id": 2, "pathFull":"full_size_B.jpg"}
]},
{ "id": 2, "paths": [
{ "id": 1, "pathThumb":"thumbnail_C.jpg"}
{ "id": 2, "pathFull":"full_size_C.jpg"}
]}
]}
];
I would like to use the AngularJS ngRepeat directive to loop through the above data to produce the following
<div class="item fashion">
<img src="thumbnail_A.jpg">
</div>
<div class="item sunset">
<img src="thumbnail_B.jpg">
</div>
<div class="item sunset">
<img src="thumbnail_C.jpg">
</div>
I assume it would look something like the following:
<div class="item {{category.name}}" ng-repeat="...">
<img src="{{category.image.path.pathThumb}}">
</div>
I am not sure how to loop through an array with sub-array and extract the appropriate data. Any help would be greatly appreciated.

Use like:
<div ng-repeat="item in categories">
<div ng-repeat="image in item.images">
<a ng-href="{{image.paths[1].pathFull}}">
<img ng-src="{{image.paths[0].pathThumb}}">
</a>
</div>
</div>

you should use a wrapper container in order to get subarray of categories..
HTML
<div ng-repeat="category in categories">
<div class="item {{category.name | lowercase}}" ng-repeat="image in category.images">
<a ng-href="{{image.paths[1].path}}">
<img ng-src="{{image.paths[0].path}}">
</a>
</div>
</div>
here I used dynamic class with ng-class and using lowercase filter to get lowercase version of name string as class...
using ng-src as src attribute of img is important here because sometimes given interpolate ({{something}}) cause fail to load resource, because of data is not fecthed...
same thing for ng-href...
here is PLUNKER...

Related

Adding some logic in v-for with v-if Vue

I have some json with below results
{
"module": [
{
"id": 1,
"title": "Module 1",
"type": "URL",
"size": 1,
"image": "https://localhost/image1.png"
},
{
"id": 2,
"title": "Module 2",
"type": "YOUTUBE",
"size": 2,
"image": "https://localhost/image2.png"
}
]
}
Now i want to render it on a page with some loop and conditional, like below
<template>
<section class="page-section homescreen mt-4">
<div class="container">
<div class="row">
<div class="col-lg-3" v-bind:key="data.index" v-for="data in modules" v-if="data.size == 1">
<img class="img-fluid" :src="`${data.image}`" :alt="`${data.title}`" />
</div>
<div class="col-lg-6" v-bind:key="data.index" v-for="data in modules" v-if="data.size == 2">
<img class="img-fluid" :src="`${data.image}`" :alt="`${data.title}`" />
</div>
</div>
</div>
</section>
</template>
<script>
export default {
data() {
return {
modules: [
{
"id": 1,
"title": "Module 1",
"type": "URL",
"size": 1,
"image": "https://localhost/image1.png"
},
{
"id": 2,
"title": "Module 2",
"type": "YOUTUBE",
"size": 2,
"image": "https://localhost/image2.png"
}
]
};
}
};
</script>
But instead of success, i got some error saying
5:99 error The 'modules' variable inside 'v-for' directive should be
replaced with a computed property that returns filtered array instead.
You should not mix 'v-for' with 'v-if' vue/no-use-v-if-with-v-for
8:99 error The 'modules' variable inside 'v-for' directive should be
replaced with a computed property that returns filtered array instead.
You should not mix 'v-for' with 'v-if' vue/no-use-v-if-with-v-for
Actually i want to create the <div class="col-lg-3"> part to be dynamic based on the json, if size:1 mean to have col-lg-3 and if size:2 mean to have col-lg-6
Any explanation and suggestion will be appreciated.
Thank you
eslint-plugin-vue was telling you to do this:
<div class="col-lg-3" v-bind:key="data.index" v-for="data in modules.filter(o=>o.size == 1)">
<img class="img-fluid" :src="`${data.image}`" :alt="`${data.title}`" />
</div>
<div class="col-lg-6" v-bind:key="data.index" v-for="data in modules.filter(o=>o.size == 2)">
<img class="img-fluid" :src="`${data.image}`" :alt="`${data.title}`" />
</div>
In your case, it can be simplified as
<div :class="'col-lg-'+data.size*3" v-bind:key="data.index" v-for="data in modules">
<img class="img-fluid" :src="`${data.image}`" :alt="`${data.title}`" />
</div>
If it's essentially something with css classes, why don't you use v-bind:class or :class with your condition ?
https://v2.vuejs.org/v2/guide/class-and-style.html
But like said in the error message you'll certainly have to create a sub component and then use props on it
https://v2.vuejs.org/v2/guide/components-props.html
In case you have just two sizes:
You can use Computed Properties to achieve this requirement.
First, create two new computed properties like:
computed: {
modulesSize1: function() {
return this.modules.filter(x => x.size == 1)
},
modulesSize2: function() {
return this.modules.filter(x => x.size == 2)
}
}
Now, you can easily loop through computed properties modulesSize1 && modulesSize2 like:
<div class="row">
<div class="col-lg-3" v-bind:key="data.index" v-for="data in modulesSize1" >
<img class="img-fluid" :src="`${data.image}`" :alt="`${data.title}`" />
</div>
<div class="col-lg-6" v-bind:key="data.index" v-for="data in modulesSize2" >
<img class="img-fluid" :src="`${data.image}`" :alt="`${data.title}`" />
</div>
</div>
In case you have multiple sizes:
First, create a simple method like:
methods: {
getClass: function(size) {
return `col-lg-${size * 3}`
},
}
and then we can update template and use Class Bindings like:
<div :class="getClass(data.size)" :key="data.index" v-for="data in modules">
<img class="img-fluid" :src="`${data.image}`" :alt="`${data.title}`" />
</div>
The v-if is essentially baked in to the v-for (if modules is empty nothing will render) so it's recommended not to use them in combination. If you need it for a separate condition, like you do here, then you'll have to move your v-for on to the <img> itself.
You also won't be able to use data.size this way so you'd have to use something like v-if="modules[0].size == 1" etc.
Edit
#Palash answer is probably the more efficient way to solve this.
Edit 2
#r0ulito and #xianshenglu also makes a really good point, if it's just a class issue, use v-bind.

How to get angular js expressions to consider css values from the api and apply it to its card

I'm creating pokemon cards that take data from the API. How can I get the CSS of a particular card get applied to its respective card from the API directly just like the information which I've rendered using angular-js.
I'm done retrieving the data like name, description and image.I used angular-js directives to get the data.Similarly, the API consists of CSS styling for each of their respective cards.How can I get the CSS of a particular card get applied to its respective card from the API directly just like the information which I've rendered using angular-js.
JSON:
[{
"cardColors": {
"bg": "#47C67B",
"imgbg": "#80EDAC",
"tagbg": "#8edbae",
"text": "#ffffff",
"textbg": "#66CF91"
},
"description": "Bulbasaur is a small quadruped Pokemon that has turquoise skin with darker teal patches ",
"name": "Bulbasaur",
"sprite": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1.png",
"tag": "Grass"
}, {
"cardColors": {
"bg": "#f88321",
"imgbg": "#ffb047",
"tagbg": "#fab275",
"text": "#ffffff",
"textbg": "#f99847"
},
"description": "Pikachu is a Mouse Pokemon and the evolved form of
Pichu. Pikachu's tail is sometimes struck by lightning as it raises it to
check its surroundings.",
"name": "Pikachu",
"sprite": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/25.png",
"tag": "Electric"
}]
JS:
var app = angular.module('myApp', []);
app.controller('pokemonCtrl', function($scope, $http) {
$http.get("pokemondata.json").then(function (response) {
$scope.myData = response.data;
});
});
HTML:
<div class="container" ng-app="myApp" ng-controller="pokemonCtrl">
<div class="row">
<div class="col-sm-4" ng-repeat="x in myData">
<h4>{{x.name}}</h4><br/>
<p>{{x.description}}</p><br/>
<img class="cards" ng-src="{{x.sprite}}"><br/>
</div>
</div>
</div>
You should use ngStyle to apply styles from your controller data to your list elements. Something like:
<div class="row">
<div class="col-sm-4" ng-repeat="x in myData" ng-style="{'background-color': x.bg, color: x.text}">
<h4>{{x.name}}</h4><br/>
<p ng-style="{'background-color': x.textbg}">{{x.description}}</p><br/>
<img class="cards" ng-src="{{x.sprite}}"><br/>
</div>
</div>

How to use ng-repeat only if object type is array?

I have a complex object as shown below:
$scope.document =
{
"GENERAL_FIELDS": {
"Source_Type": "custom",
"Annotations": [
"216/content/Factiva_CM_001/Proteins",
"216/content/Factiva_CM_001/Fact"
],
"Content": [
" Baculovirus; Budded virus; Ultrastructure; Cryo-EM;"
],
"Title": [
"Budded baculovirus particle structure revisited"
]
},
"stn": {
"Document_Type": [
"Journal",
"Article"
]
}
}
I want to display all the fields present in "GENERAL_FIELDS" and "stn". Fields' value can either be string or array of strings. If it is array, I further want to ng-repeat on it and display the content. Following is my html:
<div id="titsec" class="comdocdet" ng-repeat="(category, group) in document">
<div ng-repeat="(key, value) in group">
<div class="pTitle">
{{key}}
</div>
<div class="contdesc">
<div ng-if="Array.isArray(value)">
<div ng-repeat="v in value">
{{v}}
</div>
</div>
<div ng-if="!Array.isArray(value)">
{{value}}
</div>
</div>
</div>
</div>
But ng-if="Array.isArray(value)" is never true and array fields are being displayed in object form: ["Journal","Article"]. What am I missing ?
Or add this in your controller and leave rest like it is.
$scope.isArray = angular.isArray;
html would be like this :
<div ng-if="isArray(value)">
<div ng-repeat="v in value">
{{v}}
</div>
</div>
<div ng-if="!isArray(value)">
{{value}}
</div>
Instead of accessing a method on the Array object directly in the template, you should do in your controller. So for example:
<div ng-if="vm.isValueAnArray(value)">
// Some html
</div>
Your controller:
function isValueAnArray(val) {
return Array.isArray(val);
}
I haven't tested it, but logic should be in the controller, not in the template.
This is an issue of Scoping
The scope of the template is relative to $scope in the controller, so when it looks for Array, it will look for that in the controller scope (e.g. $scope.Array).
One option is to use ng-if="window.Array.isArray(value)". See the working example below.
Another option is to set $scope.Array = Array.prototype in the controller. That way there is no need to reference window before calling Array.isArray().
Another option is to create an alias for Array.isArray() in the controller scope:
$scope.isValueAnArray = Array.isArray;
Then call that function to determine if the value is an array.
angular.module('ang', [])
.controller('cont', function($scope) {
//use this to avoid referencing window in the template
//$scope.Array = Array.prototype;
$scope.document = {
"GENERAL_FIELDS": {
"Source_Type": "custom",
"Annotations": [
"216/content/Factiva_CM_001/Proteins",
"216/content/Factiva_CM_001/Fact"
],
"Content": [
" Baculovirus; Budded virus; Ultrastructure; Cryo-EM;"
],
"Title": [
"Budded baculovirus particle structure revisited"
]
},
"stn": {
"Document_Type": [
"Journal",
"Article"
]
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ang" ng-controller="cont">
<div id="titsec" class="comdocdet" ng-repeat="(category, group) in document">
<div ng-repeat="(key, value) in group">
<div class="pTitle">
{{key}}
</div>
<div class="contdesc">
<div ng-if="window.Array.isArray(value)">
<div ng-repeat="v in value">
{{v}}
</div>
</div>
<div ng-if="!window.Array.isArray(value)">
{{value}}
</div>
</div>
</div>
</div>
</div>

displaying bootstrap thumbnails in angularjs

I have a simple angularjs app with a simple array in a controller.
(function () {
"use strict";
angular.module('stylistFormulas').controller('ServiceTypesCtrl', ServiceTypesCtrl);
function ServiceTypesCtrl() {
var vm = this;
vm.serviceTypes = [
{
"text": "Retouch",
"image": "serviceTypes/images/Retouch.jpg",
"id": "retouch"
},
{
"text": "All Over",
"image": "serviceTypes/images/AllOver.jpg",
"id": "allover"
},
{
"text": "Highlights/Lowlights",
"image": "serviceTypes/images/Highlight.jpg",
"id": "highlight"
},
{
"text": "Ombre, Balayage, Sombre",
"image": "serviceTypes/images/Balayage.jpg",
"id": "ombre"
}
]
};
}());
I want to display these records similar to bootstraps thumbnail, custom content example but using bootstraps grid with two columns for larger screens and 1 for xs.
i.e.
image image
Title of Image Title of image
image image
Title of Image Title of image
image
Title of Image
I have tried numerous examples I have found on the web with no luck. I usually get just one column or nothing but the layout without the data.
My latest attempt...
<div ng-repeat="serviceType in vm.serviceTypes">
<div class="row | $index % 3 == 0">
<div class="col-sm-6">
<div class="thumbnail">
<img ng-src="{{serviceType.image}}" />
<div class="caption">
<h3>{{serviceType.text}}</h3>
</div>
</div>
</div>
</div>
</div>
How can I display this data using the ng-repeat or something else?
Thanks
<div class="row | $index % 3 == 0"> instead
can you try with
<div ng-class="row | $index % 3 == 0">
Because you cant do manipulations inside a HTML attribute.

ng-repeat disable my div

I for my stage have to modifie a web site for that i need angularjs i wanted to use the command ng-repeat to display some documentation but when i add ng-repeat in the div it "destroy" it and i cant figure out why...
So there is the code hope u can help me.
There is my js
App.controller('doccontroller', [ function(){
return {
scope: {},
restrict: 'A',
link: function ($scope){
$scope.docs = [
{
"id_section" : 0,
"description": "RANDOM STUFF",
"source": [
{
"python": "TEXTE",
"ruby": "TEXTE",
"javascript": "TEXTE"
}
]
},
{
"id_section" : 1,
"description": "RANDOM STUFF",
"source": [
{
"python": "TEXTE",
"ruby": "TEXTE",
"javascript": "TEXTE"
}
]
},
{
"id_section" : 2,
"description": "RANDOM STUFF",
"source": [
{
"python": "TEXTE",
"ruby": "TEXTE",
"javascript": "TEXTE"
}
]
}
]
}
}
}
])
`There is my route to "include" the controller
$routeProvider.when '/docs',
templateUrl : config.BaseHtml+'/Page/docs.html'
controller : 'doccontroller'
`
and to finish the html :)
<div id="api-docs">
<div id="methods">
<div class="languages">
<a class="language selected" data-lang="ruby" href="#">Ruby</a>
<a class="language" data-lang="python" href="#">Python</a>
<a class="language" data-lang="php" href="#">PHP</a>
</div>
<div>
<div class="method" id="intro">
<div class="method-section clearfix">
<div class="method-description" ng-repeat="doc in docs">
<h3>Introduction</h3>
<p>
{{doc.description}}
</p>
</div>
<div class="method-example">
<pre>
<code class="ruby"># All this code is just folololol
React.api_key = "In here goes your api key!"</code><code class="python"># All this code is just for demonstration purposes
react.api_key = "In here goes your api key!"</code><code class="php"># All this code is just for demonstration purposes
React::setApiKey("In here goes your api key!");</code>
</pre>
</div>
</div>
</div>
</div>
</div>
</div>
So to say it again, what i need to do is to fill create 1 div / id_section and to fill it with the
descrition for now.
Change:
ng-repeat="docs in docs"
to:
ng-repeat="doc in docs"
Also, in your code you have a call to {{ doc.des }}, which probably should be {{ doc.description }}

Categories

Resources