Mean Stack App Snippets Angular Nodejs

Notes

  • App Component is registered with angular. we can now see this App Component in other component.

image app.module.ts

  declarations: [
    AppComponent
  ],
  • code in main.ts file is executed at first.

main.ts

platformBrowserDynamic()
  .bootstrapModule(AppModule)
  .catch((err) => console.error(err));

understanding angular component

image

  • we can have different components in the app eg: app-header, app-brand, app-feature … can have another components in app-header like app-nav-item, app-nav-search.

  • in this app, we have to create, edit, delete post.

adding new component

ng g c posts/post-create
  • above command will create a posts folder - then add post-create component with in the posts folder.

    final output will be like this:

image

  • here v create post-create component in posts folder.

  • add contents in post-create template file

  • add the post-create selector in app component.html

<h1>Our First App</h1>
<app-post-create></app-post-create>
  • thus the content in post-create component will be rendered in app component template.

Listening to events - event binding

event binding means a feature that allows angular to listen to events made by users. here v add input and clicking on button angular listen to that click event and execute some function there.

post-create-component.html

<button (click)="onAddPost()">Save post</button>

post-create-component.ts

 onAddPost(){
    alert("post added");
  }
  • binding click event on button. while click, the assigned method is fired.

outputting contents - property binding

  • outputting content user entered
  • can use string interpolation or property binding
  • in string interpolation, gives the value to be outputted in double curly braces.
  • in property binding, binds the value to be outputted to the value property of html element. Eg:
<textarea name="post" id="post" cols="30" rows="5" value ="newPost"></textarea>
<p>{{ newPost }}</p>

getting user input

  • accessing the element - getting the value.

  • first set a reference to the element.

<textarea
  name="post"
  id="post"
  cols="30"
  rows="5"
  [value]="newPost"
  #postInput
></textarea
><br /><br />

postInput is the reference to the textArea element.

  • other elements can access this element using postInput reference.
<button (click)="onAddPost(postInput)">Add Post</button>
  • pass the reference as an argument to onAddPost method.
onAddPost(postInput: HTMLInputElement){
    this.newPost = postInput.value;
  }
  • onAddPost method receive the element sent as argument.
  • access the value property of the same.

getting and outputting data at same time - two way binding

  • use ngModel directive - assign a property.
<textarea
  name="post"
  id="post"
  cols="30"
  rows="5"
  [(ngModel)]="postValue"
></textarea
><br /><br />

here v get the value to textArea element and output the value from textArea at same time. earlier v do property binding to output the value. and do a local reference to get the value. by using ngModel directive, v get the value from user and output the value on template at same time.

  postValue: string ="";
  ngOnInit(): void {
  }
  onAddPost(){
    this.newPost = this.postValue;
  }
  • declare postValue property here, assign it to newPost.

  • finally add FormsModule to the imports array in app module.


installing angular material

create a form using angular material

(Angular Material)[https://material.angular.io/guide/getting-started]

(Angular Component)[https://material.angular.io/components/categories]

<div class="container">
  <mat-card>
    <h3>Add Posts</h3>
    <mat-form-field class="example-form-field">
      <textarea
        rows="5"
        matInput
        [(ngModel)]="postValue"
      ></textarea></mat-form-field
    ><br />
    <button mat-raised-button color="primary" (click)="onAddPost()">
      Add Post
    </button>
    <br />

    <p>{{ newPost }}</p>
  </mat-card>
</div>

app.module.ts

import { MatInputModule } from '@angular/material/input';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';

imports: [MatInputModule, MatButtonModule, MatCardModule];

adding toolbar

to add header to the page. create header component in post component.

ng g component posts/header --skipTests=true

import MatToolbar Module in app module add same in imports array.

(Angular Toolbar)[https://material.angular.io/components/toolbar/overview]

header.component.html

<p>
  <mat-toolbar color="primary">
    <span>My Application</span>
  </mat-toolbar>
</p>

outputting posts using Expansion Panel Component.

create post-list component in posts component

ng g component posts.post-list --skipTests=true

add post-list in component app component template using Expansion panel to output the post. import MatExpansion Module -add to imports array.

populating the expansion panel with contents

structural directive- ngIf, ngFor
  • declare an array,
posts: Array<Object> = [];
<mat-accordion *ngIf="posts.length > 0">
  <mat-expansion-panel hideToggle *ngFor="let post of posts">
    <mat-expansion-panel-header>
      <mat-panel-title> {{post.title}} </mat-panel-title>
    </mat-expansion-panel-header>
    <p>{{post.content}}</p>
  </mat-expansion-panel>
</mat-accordion>
<p class="mat-body-1 info-text" *ngIf="posts.length <= 0">No posts added yet</p>

creating posts with property and event binding.

  • attach ngModel directive for the inputs for two-way binding.
  • so here v get and output the value at same time post-create.component.html
<mat-form-field class="example-full-width">
  <input
    matInput
    type="text"
    placeholder="Title goes Here"
    [(ngModel)]="postTitle"
  />
</mat-form-field>
<mat-form-field class="example-form-field">
  <textarea
    rows="5"
    matInput
    [(ngModel)]="postContent"
    placeholder="content goes here"
  ></textarea></mat-form-field
><br />

post-create.component.ts

constructor() { }
  postTitle: string ="";
  postContent: string ="";
  ngOnInit(): void {
  }
  onAddPost(){
    // create post object to store title and content
    const post = {title: this.postTitle, content: this.postContent};
  }

here v got the values - next need to output those, first need to emit an event - create an event emitter also need to listen to this event from other components. for that add Output decorator to the event. thus v can listen it from the parent component.

@Output() postEvent = new EventEmitter();
  ngOnInit(): void {
  }
  onAddPost(){
    // create post object to store title and content
    const post = {title: this.postTitle, content: this.postContent};
    // emit the post object
    this.postEvent.emit(post);
  }

app.component.html

access the event emitted in app component. here $event argument receives the data emitted

<app-post-create (postEvent)="onPostAdded($event)"></app-post-create>

app.component.ts

push the post to posts array

 // empty array
  storedPosts:Array<Object> = [];
  // define function, get post
  onPostAdded(post: Object){
    // push the received post object to posts Array
    this.posts.push(post);
  }

next v have to pass the posts Array to post list component.

for that make post property in post-list component bindable from outside thru property binding.

for that v use Input decorator,

post-list.component.ts

  // make posts property bindable from outside thru property binding
  @Input() posts: Array<Object> = [];

app.component.html

<!--property binding of posts property on storedPosts array-->
<app-post-list [posts]="storedPosts "></app-post-list>

app.component.ts

  storedPosts:Array<Object> = [];
  // define function, get post
  onPostAdded(post: Object){
    // push the received post object to posts Array
    this.storedPosts.push(post);
  }

Creating Post Model

  • create post model in posts folder.
ng generate class posts/post --type=model
  • later declare fields in the model.
export class Post {
  title: string;
  content: string;
}
  • now v can use this Post class in components.

app.component.ts

  // empty array
  storedPosts:Post[] = [];
  • storedArray is an array of type Post.

post-create.component.ts

@Output() postEvent = new EventEmitter<Post>();
onAddPost(){
    // create post object to store title and content
    const post: Post = {title: this.postTitle, content: this.postContent};
    // emit the post object
    this.postEvent.emit(post);
  }
  • data v emit is of type Post by setting generic type to event emitter.
  • post object is of type Post class.

post-list.component.ts

@Input() posts: Post[] = [];
  • here posts property accept only Post array type data.

adding forms

add ngModel directive to the each inputs. thus it register the inputs as a control to the forms. but have to add name attribute for the inputs since angular needs to know how to name the input. change button type to submit. while submitting, it triggers an event submit that v can listen to. add submit event to the form. assign the method to it.

<form (submit)="onAddPost()">
  <mat-form-field>
    <input
      matInput
      type="text"
      placeholder="Title goes Here"
      ngModel
      name="title"
    />
  </mat-form-field>
  <mat-form-field>
    <textarea
      rows="5"
      matInput
      ngModel
      placeholder="content goes here"
      name="content"
    ></textarea>
  </mat-form-field>
  <br />
  <button mat-raised-button color="primary" type="submit">Add Post</button>
  <br />
</form>
  • now need to get access to values in the form.

    For that purpose, v set a local reference in the form element- assign ngForm directive to the local reference as string - pass that local reference as an argument to the method.

    • by doing this, v get access to form object and its values.

adding validations to the form

  • use required attribute on each inputs.
  • can also use min-length attribute as well.
  • displaying error message using mat-error component of material.

post-create.component.html

<input
  matInput
  type="text"
  placeholder="Title goes Here"
  ngModel
  name="title"
  required
  minlength="5"
  #title="ngModel"
/>
<mat-error *ngIf="title.invalid">Please give valid title</mat-error>
  • give both the inputs a local reference. thus can use in the ngIf.

  • this form creation is done by template driven approach.


getting post from post-create component to post-list component

  • here v use services.
  • create a service class in posts folder.
ng generate service posts/post --skipTests=true

create a posts array of type Post. get posts and later add posts to the post array.

post.service.ts

export class PostService {
  constructor() {}
  // create a private property of type Post array
  private posts: Post[] = [];
  getPosts() {
    // return copy of post array, use spread operator, so changes only affected on its copy, not the original array
    return [...this.posts];
  }
  // adding new post
  addPost(post: Post) {
    this.posts.push(post);
  }
}
  • next v have to inject the instance of this service class to needed component. called dependency injection

  • for that add a constructor in component > define an a public property of type PostService type.

  • that property will store incoming value of PostService.


Calling GET post

add OnInIt interface on component. later define ngOnInIt method - call the getPosts method on postService property.

post-list.component.ts

  ngOnInit(): void {
    // call get post method here
    this.postService.getPosts();
}

also do the same in post-create component for creating post as well.

  • inject service to the constructor.
constructor(public postService: PostService) { }
  onAddPost(form: NgForm){
    // create post object to store title and content
    const post: Post = {title: form.value.title, content: form.value.postContent};
    // emit the post object
    this.postService.addPost(post);
  }

here the after entering inputs posts wont be listed, since v fetched the post before adding them. to solve it, first remove the bindings in app component. next use rxjs package. import Subject from rxjs package. create new instance of Subject. call next on that subject instance - pass the post array copy.

post.service.ts

private postUpdated = new Subject<Post[]>();

  // adding new post
  addPost(post: Post){
    this.posts.push(post);
    // call next() on subject - pass copy of posts array as argument.
    this.postUpdated.next([...this.posts]);
  }

next u have to listen to that subject whenever it emits a post. create new method - call asObservable() on subject and return it. it will return an object that v can listen to.

  getPostUpdateListener(){
    // call asObservable method on postUpdated. which in turn returns an object.
    return this.postUpdated.asObservable();
  }

next v have to subscribe to this listener object from post-list component call subscribe(). subscribe takes a function as an argument which will b called when new data is emitted.

post-list.component.ts

  ngOnInit(): void {
    // call get post method here
    this.posts = this.postService.getPosts();
    this.postService.getPostUpdateListener()
    .subscribe((post: Post[]) => {
      // set posts property with post array received.
      this.posts = post;
    });

  }

next have to store the subscription in a property. finally unsubscribe the subscription. implement OnDestroy method.

post-list.component.ts

  private postSub: Subscription;
  ngOnInit(): void {
    // call get post method here
    this.posts = this.postService.getPosts();
    this.postSub = this.postService.getPostUpdateListener()
    .subscribe((post: Post[]) => {
      // set posts property with post array received.
      this.posts = post;
    });
  }
  ngOnDestroy(){
    // unsubscribe
      this.postSub.unsubscribe()
  }

Observables, Observers, Subscriptions

observer subscribes to the observable, simply means they establishes the subscription. 3 methods in observer,

  1. next()
  2. error()
  3. complete()

v call next() on subject, subject is an observable. v invoke error() on observable when needed to throw some error. can call complete() on observable when no more data left to be emitted.

Screenshot Image

Example

  • An observable wraps a callback of http request.
  • wraps an http request with in an observable that takes that callback and whenever the request gives us the response, v use the observable to emit response data by invoking next(). or error message if error pops up. v can also complete the observable once got response by calling complete(). this how v manage http request in observable.

Screenshot Image

Subject

a subject is a kind of observable. normal observable is passive. eg: wraps callback, emitting events subject is active observable. v also subcribe to the subject, but here v call next() manually. for example, when v add a new post, v actively can notify our entire application. it can be done with subject.

Screenshot Image

consider observables/subjects as a stream of values/data. and these values r emitted over time.

observer - have set of functions we can call,

  1. next() - call next() for emitting a normal value, also call if v hav an observable that wraps an http request.
  2. error()
  3. complete() - call complete() when an observable ends emitting values.

this is how observables work.

working on forms

  • resetting the form on submit.
  • adding edit and delete button on expansion panel. use Action Bar in expansion panel.

Download Details:

Author: jisshub

Source Code: https://github.com/jisshub/mean-stack-app-snippets-angular-nodejs

#nodejs #node #javascript

Mean Stack App Snippets Angular Nodejs
4.10 GEEK