Cosmic JS Blog Stay tuned for community news, company announcements and updates from the Cosmic JS team.

How to Build an Angular Image Feed


by Ivan Larionov on July 6, 2017

In this tutorial I’m going to show you how to build a user-driven photo gallery, powered by Angular, hosted on the Cosmic JS App Server.

TL;DR

View Demo
View the full source code on GitHub
Install the App and deploy to the Cosmic App Server

Prerequisites

You’ll need the node JS, npm and Angular cli pre-installed. Make sure you already have them before you start. Please refer to Angular docs on how to do this.

Getting Started

First of all we’ll need to create the Angular project. We’ll use ng cli to do it. So once you’ll have all prerequisites installed, you’ll need to setup the new Angular project:

ng new cosmic-angular

After you’ll setup this project you’ll be able to run

cd cosmic-angular
ng serve --open

And play with your app in browser

Doing everything using the existing git repo

First of all, you have to be sure you have node > 6.x installed, than run the following commands:

npm install -g @angular/cli
git clone https://github.com/cosmicjs/angular-image-feed
cd cosmic-angular
npm install
ng serve --open

The most recent ng cli version at the article creation moment was 1.1.3. Browser window will open automatically once you'll run the last command

Setting up Cosmic JS library

First of all, install the Cosmic JS npm module for Angular/JavaScript applications:

npm install cosmicjs --save

Now you should be able to import Cosmic object and perform Cosmic JS API calls like following:

import Cosmic from 'cosmicjs';
const bucket = { slug: 'your-bucket-slug' };

Cosmic.getObjects({ bucket }, (err, res) => {
  console.log(res.objects);
});

Setting up things with Cosmic JS

Create the bucket and remember the bucket name (‘cosmic-angular’ in our case):

Than create a new object type named Photo and please remember the object type slug (photos’).

We also need a way to store the picture itself. Please enter the “Metafields Template” tab and add “Image/File” type Metafield with key 'photo'. This Metafield will store the image. We don’t need anything more, so just set the name and save Object Type. After save you’ll be redirected to ‘New Photo’ page. Create some photos using this page and save them - we'll use them as test data.

You'll also need to create the Bucket write key. It's necessary to allow users upload pictures and create photo Objects. Open Settings page and click 'Generate new key' on API Write Access Key than copy generated key and save the changes.

Angular environments

Edit the src/environments/environment.ts to match the following:

export const environment = {
  production: false,
  write_key: 'YOURWRITEKEY',
  bucket_name: 'YOURBUCKETNAME',
  photos_type: 'photos'
};

Configuration service for Angular

We're planning to use Cosmic JS Objects in more than one Angular component. In such case makes sense to create a dedicated configuration service and store all Cosmic JS related things such as bucket name, write key, etc in a single place. Let's create src/services/cosmic_config.ts with the following contents:

import {Injectable} from '@angular/core';

@Injectable()
export class CosmicConfigService {
    private write_key;
    private bucket_name;
    private photos_type;

    constructor() {
        this.photos_type = environment.photos_type;
        this.write_key = environment.write_key;
        this.bucket_name = environment.bucket_name
    }

    public getReadCfg(): any {
        return {
            bucket: {
                slug: this.bucket_name
            }
        };
    }

    public getWriteCfg(): any {
        return {
            bucket: {
                slug: this.bucket_name,
                write_key: this.write_key
            }
        };
    }

    public buildPhotoUploadObj(title, file): any {
        return {
            write_key: this.write_key,
            type_slug: this.photos_type,
            title: title,
            metafields: [{
                key: 'picture',
                type: 'file',
                value: file
            }]
        };
    }

    getPhotoSlug() {
        return this.photos_type;
    }
}

This service has a few methods:

  • getReadCfg - returns config object for reading data
  • getWriteCfg - return config object for writing data (with write_key specified)
  • buildPhotoUploadObj - builds object to create photo using file name and title
  • getPhotoSlug - returns object type slug for photos

We'll call these methods from our Angular components.

View the gallery - Angular part

Create src/components/picture/picture.ts file with the following content:

import { Component, Input } from '@angular/core';

@Component({
    selector: 'picture',
    templateUrl: './picture.html'
})
export class Picture {
    @Input() picture: any;

    constructor() {
    }
}

Than create a template for Picture component:

<div class="ui card picture-item">
  <div class="image">
    <img class="ui fluid image" [src]="picture.metafield.picture.url" alt="{{ picture.title }}">
  </div>
  <div class="content">
    <div class="description">{{ picture.title }}</div>
  </div>
</div>

We'll use this component to display a single gallery picture item.

Now create src/components/picture_upload/picture_upload.ts file with the following content:

import { Component, Input, Output, EventEmitter } from '@angular/core';
import Cosmic from 'cosmicjs';
import { CosmicConfigService } from '../../services/cosmic_config';

@Component({
    selector: 'picture-upload',
    templateUrl: './picture_upload.html'
})
export class PictureUpload {
    private fl;
    private title;
    public uploading;
    @Output() onUpload = new EventEmitter<any>();

    constructor(
        private cosmicConfig: CosmicConfigService
    ) {
        this.uploading = false;
        this.fl = null;
        this.title = "";
    }

    onFileChange(ev) {
        if (ev.target.files && ev.target.files.length) {
            this.fl = ev.target.files[0];
        }
    }

    upload() {
        this.uploading = true;
        Cosmic.addMedia(this.cosmicConfig.getWriteCfg(), {
            media: this.fl,
            folder: this.fl.name
        }, (error, response) => {
            Cosmic.addObject(this.cosmicConfig.getWriteCfg(),
                this.cosmicConfig.buildPhotoUploadObj(this.title, response.body.media.name),
            (error, response) => {
                this.title = '';
                this.fl = null;
                this.uploading = false;
                this.onUpload.emit({});
            });
        });
    }
}

Than add the following template:

<div class="picture-upload">
    <div class="ui form" [ngClass]="{ 'active dimmer': uploading }">
        <div class="ui grid" *ngIf="!uploading">
            <div class="five wide column">
                <div class="field">
                    <input type="text" placeholder="Title..." (input)="title = $event.target.value" [value]="title"/>
                </div>
            </div>
            <div class="six wide column">
                <div class="field">
                    <input type="file" (change)="onFileChange($event)"/>
                </div>
            </div>
            <div class="five wide column">
                <button class="ui primary button fluid" (click)="upload()">Upload photo</button>
            </div>
        </div>
        <div *ngIf="uploading" class="ui text loader">Upload is in progress</div>
    </div>
</div>

Add Picture and PictureUpload components to app.module.ts as it’s done with other components like AppComponent. This will allow us to use it in our app.

What happens here?

Our Picture component doesn't perform anything interesting - it just displays object properties. However PuctureUpload component doing much more interesting thing. It creates a record on Cosmic JS servers, but this record has an image attached, so this makes the whole process more complicated:

  • upload the image to Cosmic JS servers (using addMedia method)
  • obtain uploaded image name
  • create the new Cosmic JS object (using addObject method) passing it obtained image name
  • fire an event to notify parent component about finished upload

Concatenating everything together

Now we need to modify our AppComponent to use these newly created components. Modify src/app/app.component.ts to look like the following:

import { Component } from '@angular/core';
import Cosmic from 'cosmicjs';
import {CosmicConfigService} from '../services/cosmic_config';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  public items = [];
  page = 0;
  page_size = 2;
  scrollEnabled = true;

  constructor(
    public cosmicCfg: CosmicConfigService
  ) {
    this.reload();
  }

  reload() {
    this.items = [];
    this.page = 0;
    this.scrollEnabled = true;
    let params = {
      type_slug: this.cosmicCfg.getPhotoSlug(),
      limit: this.page_size,
      skip: 0
    };
    Cosmic.getObjectType(this.cosmicCfg.getReadCfg(), params, (err, res) => {
      this.items = res.objects.all;
    });
  }

  onUpload() {
    this.reload();
  }

  onScroll() {
    if (!this.scrollEnabled) {
      return;
    }
    this.page++;
    let params = {
      type_slug: this.cosmicCfg.getPhotoSlug(),
      limit: this.page_size,
      skip: this.page * this.page_size
    };
    Cosmic.getObjectType(this.cosmicCfg.getReadCfg(), params, (err, res) => {
      if (res.objects && res.objects.all) {
        res.objects.all.forEach((itm) => {
          this.items.push(itm);
        });
      }
      else {
        this.scrollEnabled = false;
      }
    });
  }
}

And make its template like following:

<div infiniteScroll [infiniteScrollDistance]="1" [infiniteScrollThrottle]="300" (scrolled)="onScroll()">
    <picture-upload (onUpload)="onUpload()"></picture-upload>
<picture *ngFor="let item of items" [picture]="item"></picture>
</div>

We're planning to have the infinite-scrollable gallery (and you can already see it's directives in code). This means we have to install the right library:

npm install --save ngx-infinite-scroll

What’s happening here?

  • until we got empty response, we're assuming there are more photos on server
  • we're fetching photos in bulks of 2 (page_size property)
  • we're fetching only 'photo' type objects (this is useful in case if we have more object types)
  • once we got empty response, we're setting the flag and stopping try to fetch more photos
  • once we're getting an event from PhotoUpload component, we're resetting the whole list.

Deploy to Cosmic JS servers

Cosmic JS has some requirements for deploying apps:

In our case we actually have HTML5 app, so we'll need some additional software.

Prepare config

Create a prepare.js file in your project directory:

var fs = require('fs');

var str = `
    export const environment = {
        production: true,
        write_key: '${process.env.COSMIC_WRITE_KEY}',
        bucket_name: '${process.env.COSMIC_BUCKET}',
        photos_type: 'photos'
    };
`;
fs.writeFile("./src/environments/environment.prod.ts", str, function(err) {
    if(err) {
        return console.log(err);
    }
    console.log("The file was saved!");
}); 

This script will rewrite default Angular production settings file to use your Cosmic JS bucket write key and bucket name.

Modify package.json

Angular cli adds some packaged on package.json as devDependencies. We have to move them in dependencies to make our scripts work:

...
"dependencies": {
    "@angular/cli": "^1.1.3",
    "@angular/compiler-cli": "^4.0.0",
    ...
},
...

Prepare software

We'll also need something to serve our Angular app. We'll use Express framework:

npm install --save express

Add the following to your package.json:

{
  ...
  "scripts": {
    ...
    "start": "node app.js"
  },
  ...
  "engines": {
    "node": "6.9.4",
    "npm": "4.2.0"
  }
  ...
}

The main point is to have start command defined in the scripts section (you can safely replace default angular start command). This is the command which will be run to start our app. So now we have the only thing left - create the app.js file:

const express = require('express')
const app = express()

app.use(express.static('./dist'));

app.listen(process.env.PORT, function () {
});

This is a simple Express app which serves dist dir as dir of static files. Please take note - app listens on port specified via PORT environment variable, it's important to run apps on Cosmic JS App Server.

Build Angular app for production

We'll use app.json to do this (dokku predeploy section):

{
    "scripts": {
        "dokku": {
            "predeploy": "node prepare.js && ng build --aot --prod"
        }
    }
}

This script will be executed before we'll launch our express app to build the Angular app for production.

Run it!

Now you can enter 'Deploy Web App' page in your Cosmic JS Dashboard.

Simply enter your repo URL and click 'Deploy to Web' - deploy process will be started and app become ready in a couple of minutes.

Conclusion

Using the Cosmic JS App Server allows us to quickly deploy the application to hosting using a git repo and you don't worry about server configuration and software installation - everything will be done by Cosmic JS servers.

You may also like


This Appointment Scheduler lets users select a day and a one-hour time slot between 9AM and 5PM to meet with us. It integrates with Twilio to send a confirmation text that their appointment has been scheduled. This also comes with a Cosmic JS Extension so we can manage the appointments right from within the Cosmic JS dashboard.

Brand Managers manage, well, brands.  Seems simple enough right?  It does in title and theory alone.  Managing a brand means managing the brand’s tone, voice, messaging, consumer segmentation, price points, marketing, advertising and all of the subsequent elements and assets that fall out of such an engagement.  Having managed brands for years myself, I can attest to the magnitude of the job at hand.  Once the client has agreed upon a general direction, it becomes the brand manager’s responsibility to ignite passion and results from the internal agency team that services the account.  As my workload would increase month over month as business heated up or an account was grown, I went through the whole Automation | Delegation | Elimination routine to see what I could cut out of my schedule as busy work.  The problem?  I was only one person.  I could only affect hours tallies on my end, but had little to no influence over design, development, copywriting & production. 


The area that always seemed to be the weak link in terms of staying on budget was in development.  Boutique agencies struggle to attract qualified talent with their long work hours and subpar compensation, and then after taking a gamble on a more junior talent that is developed over time, retention is a beating.  Companies line up with development jobs that pay 2X, sometimes 3X what a boutique agency is willing to part with for that developer’s compensation package.  This is all before taking into account staging servers, hosting servers, CMS logins, local installations of CMS systems and all of the red tape and bureaucracy associated that can bog down a productive workflow.  As a result, my quotes back to clients for websites, microsite, landing pages and applications were always a bit higher than they were expecting.  I started searching for a cloud-based solution to my CMS woes to cut out some of the middle men and see if there was an easier way to 'get this digital property live'. 

I found 
Cosmic JS.  Had I been told as a Brand Manager that I could eliminate the local CMS, the hosting server and the shared logins of content editing, I would have seen the value immediately.  No longer having to build APIs on a per-CMS / per-client basis, no longer having to build out a proprietary backend, yet still attaining the same custom-value would have been a lifesaver and a half for a brand manager focused on the bottom line.  It would have easily cut my back end developers’ hours estimates by 40%, eliminated costly hosting servers and would have streamlined content-centric employees within the agency to not have to deal with the red tape of updating content within a traditional CMS. 

Whether we picked out a
content-ready application or plugged GitHub into Cosmic JS, I’m seeing time and cost savings at every turn.  Music to a brand manager’s ears, and music to a boutique agency’s margin and bottom line.  As it turns out, it also benefits the client as their content is put first, their content is pushed live more quickly and is devourable globally on any device.

In this Cosmic JS Developer Spotlight, we sat down with Jazib Sawar, a full stack web developer at Bitbytes.  Jazib has a Bachelor's Degree in Computer Science from the National University of Computer and Emerging Sciences and has led teams on interactive projects for several years.  Jazib is the mastermind behind the Cosmic JS Sticky Notes App that was recently adapted by Life is Good for their Gratitude Wall

In this tutorial, we are going to be creating a simple inventory management application with Laravel and Vue.js as our frontend.

At Cosmic, it's our mission to help teams of developers and content creators build great content-powered apps together. We're happy to announce the new Additional Users Add-On to offer a flexible pricing option for growing teams.

In this installment of the Cosmic JS Developer Spotlight Series, we sat down with Sumit Kharche, a Full Stack Software Developer residing in Pune, India. Submit is an active member of the Cosmic JS Community, having recently built the new React Static Blog, which is available in the Cosmic JS Apps Marketplace. With more community projects on the way, we're excited to interview one of our own for this Spotlight. 😎 Follow Sumit on Twitter, LinkedIn and GitHub, and enjoy the conversation.