6

I'm very new to Angular and currently I'm learning Angular 6, which is the latest version of Angular.

Here, I'm trying to design my blog page with articles which are retrieved from a JSON and displaying them in 2 columns, as in picture below:

enter image description here

The number next to title are index of article in array of articles. It's easily to see the problem: indexes from 1 are duplicated twice.

This is my code, please show me why I'm wrong in details and how can I fix that.

BlogService:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class BlogService {

  constructor(private http: HttpClient) { }

  getArticleList() {
    return this.http.get('https://jsonplaceholder.typicode.com/posts');
  }
}

ArticleListComponent:

import { Component, OnInit } from '@angular/core'; import { BlogService } from '../blog.service';

@Component({
  selector: 'app-blog',
  templateUrl: './article-list.component.html',
  styleUrls: ['./article-list.component.css']
})
export class ArticleListComponent implements OnInit {
  articles: Object;

  constructor(private data: BlogService) { }

  ngOnInit() {
    this.data.getArticleList().subscribe(
      data => this.articles = data
    );
  }
}

HTML file:

<div class="article-list-page">
    <div class="container">
      <h3>This is blog page</h3>
      <p>The industry's top wizards, doctors, and other experts offer their best advice, research, how-tos, 
          and insights, all in the name of helping you level-up your SEO and online marketing skills. Looking for the YouMoz Blog? </p>
        <span *ngFor="let index of articles; index as i">
            <div style="display: table; border: 1px solid black">
                <div class="posts-left col-md-6" style="display: table-row;">
                    <a>{{ articles[i].title }} -- {{i}}</a>
                    <p>{{ articles[i].body }}</p>
                </div>
                <div class="posts-right col-md-6" style="display: table-row;">
                    <a>{{ articles[i + 1].title }} -- {{i + 1}}</a>
                    <p>{{ articles[i + 1].body }}</p>
                </div>
            </div>
        </span>
    </div>
</div>
Trung Bún
  • 1,117
  • 5
  • 22
  • 47
  • if you want to display all the articles, you don;t need to do {{ articles[i].title }}... you should do {{ articles.title }} only to have them listed... instead of index... you can also display {{ articles.id}}... which is the ID field in your data – Akber Iqbal Oct 11 '18 at 03:58

3 Answers3

11

Instead of using a table and trying to apply indexes that way, you will be better off using CSS. For example:

In my component.ts:

numArray=[100,200,300,400,500,600,700,800];

In my view:

  <ul>
    <li *ngFor="let n of numArray; index as i">{{n}}, Index is {{i}}</li>
  </ul>

In the css:

ul{
  width:400px;
}
li{
  float: left;
  list-style: none;
  width:150px;
  margin: 0px;
}
li:nth-child(even){
  margin-right: 0px;
}

This will give the output as:

enter image description here

You can modify the example I have given to suit your needs.

codingsplash
  • 4,785
  • 12
  • 51
  • 90
1

TL;DR; Just replace this block in your HTML file

From:

<div style="display: table; border: 1px solid black">
    <div class="posts-left col-md-6" style="display: table-row;">
        <a>{{ articles[i].title }} -- {{i}}</a>
        <p>{{ articles[i].body }}</p>
    </div>
    <div class="posts-right col-md-6" style="display: table-row;">
        <a>{{ articles[i + 1].title }} -- {{i + 1}}</a>
        <p>{{ articles[i + 1].body }}</p>
    </div>
</div>

To: (removed 4 lines)

<div style="display: table; border: 1px solid black">
    <div class="posts-left col-md-6" style="display: table-row;">
        <a>{{ articles[i].title }} -- {{i}}</a>
        <p>{{ articles[i].body }}</p>
    </div>
</div>

From your screenshot, it appears that only the 0th element that is not repeated twice. And based on your HTML, this is expected since you're rendering it twice.

Your current template

<span *ngFor="let index of articles; index as i">
    <div style="display: table; border: 1px solid black">
      <div class="posts-left col-md-6" style="display: table-row;">

          <!-- 1. Render the CURRENT 'article' -->
          <a>{{ articles[i].title }} -- {{i}}</a>
          <p>{{ articles[i].body }}</p>

      </div>
      <div class="posts-right col-md-6" style="display: table-row;">

          <!-- 2. Render the NEXT 'article' -->
          <a>{{ articles[i + 1].title }} -- {{i + 1}}</a>
          <p>{{ articles[i + 1].body }}</p>

      </div>
    </div>
</span>

Following is the steps of how angular renders your articles.

  1. Render the 0th element (Your template renders 0th and 1st article)
  2. Render the 1st element (Your template renders 1st and 2nd article)
  3. Render the 2nd element (Your template renders 2nd and 3rd article)
  4. And so on..

If you just want to render once per element, simply do it like,

<div class="posts-left col-md-6" style="display: table-row;">
    <!-- 1. Render ONLY THE CURRENT 'article' -->
    <a>{{ articles[i].title }} -- {{i}}</a>
    <p>{{ articles[i].body }}</p>
</div>

Note: There is already a post who makes a good use of ngFor HERE.

choz
  • 17,242
  • 4
  • 53
  • 73
  • is there anyway to make this loop working as for loop in Java?! Since I want to display articles side by side instead of just a list of articles in 1 column. – Trung Bún Oct 11 '18 at 04:12
  • Can you elaborate more what you mean by side by side? If you're concerned with the UI, it can be styled so with css. – choz Oct 11 '18 at 04:14
  • well the displaying style in the picture is really what I mean, but the problem is duplicated index. Is there any reason why angular render my articles in that order? Why don't they start from the very last index computed in the loop body? – Trung Bún Oct 11 '18 at 04:30
  • @TrungBún Didn't you read my answer at all? However, I have updated my answer. Have a look at in `TL;DR;` – choz Oct 11 '18 at 04:34
  • I totally understand your answer!! My only question is that why Angular directive ngFor doesn't remember the last index computed in the loop like in Java? If that a convention then it's ok, no more question are added. – Trung Bún Oct 11 '18 at 04:45
  • @TrungBún, you would need to do it through css. Take a look at my answer – codingsplash Oct 11 '18 at 05:23
  • 2
    @TrungBún It **does** remember your last index. But you're rendering it twice!! – choz Oct 11 '18 at 05:42
  • Ok I got it, thank @choz!!! I have another question: I googled around to find a way to declare a local variable and assign value to it. But Angular doesn't support declaring local variable. In my problem, is there anyway to store the value of `i+1`, such as `i = i+1` or `i++` so that the new value of `i` will be used in the next round? – Trung Bún Oct 11 '18 at 08:32
  • @TrungBún I am kinda lost here. Can you post a new question and describe what is it you're trying to achieve? – choz Oct 11 '18 at 08:35
  • Ok, i will quote my new question here! – Trung Bún Oct 11 '18 at 08:44
  • @choz here is the new question: https://stackoverflow.com/questions/52756067/angular-6-storing-value-to-use-in-ngfor – Trung Bún Oct 11 '18 at 08:55
0

Create an array of the even indexes

this.indexes = data.reduce((indexes, _, i) => i % 2 ? indexes : [...indexes, i], []);

And in your view you can ngFor on just the even indexes

<span *ngFor="let index of indexes">
Adrian Brand
  • 20,384
  • 4
  • 39
  • 60