תחי ישראל - אין לנו ארץ אחרת

תחי ישראל -אין לנו ארץ אחרת

שליחת מידע ב-POST ב-Angular

מחבר:
בתאריך:

המדריך כבר לא רלוונטי. עברו למדריך שמסביר כיצד להשתמש ב- HttpClient

 

נסו בעצמכם את הקוד שנפתח במדריך

שליחת נתונים ב-POST מאפשרת העברת נתונים מהאפליקציה האנגולרית לשרת בפרוטוקול AJAX. במדריך זה נלמד להעביר מידע מהטופס בצד האנגולר לשרת שיאחסן את המידע במסד הנתונים.

במדריך זה נזין לטופס אנגולרי את שם הדגם של מכונית ואת מחירה, ונשלח את המידע באמצעות POST לצד השרת, שבו יוזן המידע למסד הנתונים באמצעות סקריפט של PHP.

כדי לעבוד עם מתודות של HTTP דוגמת POST ו-GET נשתמש בשירות אנגולרי ה-HttpClient שבא מותקן עם אנגולר. לצורך כך, נייבא את המודול HttpClientModule למודול השורש של האפליקציה.

/src/app/app.module.ts
---------------------------------------

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,

    // Import after the browser module
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

 

ה-type

נגדיר את סוג המכונית כדי שנוכל לעבוד איתו בהמשך בתוך interface:

/src/app/car.ts
---------------------------------------

export interface Car{
    id: number;
    model: string;
    price: number;
}

 

ה-service

נכתוב service משלנו שתפקידו לרכז את העברת המידע על המכוניות באמצעות AJAX.

service הוא קלאס אנגולרי שממלא תפקיד. התפקיד הנפוץ ביותר הוא לרכז טיפול במידע מסויים. בפרט אם המידע מגיע ממקור חיצוני. לדוגמה באמצעות AJAX.

נעדיף לרכז את הלוגיקה של הטיפול במידע ב-service, ולא בקונטרולר, כדי שתהיה גישה לקונטרולרים נוספים למידע במקום שנצטרך בכל פעם מחדש לכתוב את כל הלוגיקה. זה גם יאפשר לנו לשתף את המידע בין חלקי האפליקציה ממש בקלות. ולפיכך, השימוש ב-service הוא הדרך המומלצת ביותר לנהל את המידע באפליקציה אנגולרית.

service יכול גם לשמש בשביל לוגיקה שאנחנו רוצים לשתף בין כמה קומפננטות. אם יש לנו פונקציה שיכולה לשמש בכמה קונטרולרים, אז במקום לכתוב אותה בכל קונטרולר בנפרד, נשים אותה בתוך service.

אחרי שהסברנו מה זה service בוא נחזור לאפליקציה שלנו.

שמו של ה-service שלנו יהיה car.service והוא יכיל מתודות שונות שקשורות באחסנת המידע עבור המכוניות. במדריך זה נלמד להעביר מידע שמוזן לטופס על ידי המשתמש אל מסד הנתונים שנמצא בצד השרת. ובמדריכים הבאים, נוסיף מתודות לשליפת הנתונים מצד השרת, לעריכה ולמחיקה.

/src/app/car.service.ts
---------------------------------------

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

@Injectable()
export class CarService {
    constructor(private  HttpClient){}
  
}
  1. חייבים לייבא את הדקורטור Injectable כדי שנוכל להשתמש ב-service האנגולרי. במקרה שלנו, ה-HttpClient service בתוך ה- service שלנו (car.service).
  2. ה-HttpClient service נוצר על ידי המתכנתים של אנגולר, והוא זה שמאפשר לנו לעבוד ב-Ajax, ובהתאם אנחנו מייבאים אותו.
  3. בקונסטרקטור של ה-CarService נקים את המשתנה http שמממש את ה-HttpClient service האנגולרי, ומאפשר לנו להשתמש במתודות שלו כדי לשלוח ולקבל את הנתונים.
    * חובה להגדיר את סוג המשתנה כ-HttpClient בשביל הזרקת התלות על ידי ה-Injectable. המשמעות של הזרקת התלות היא שעכשיו המשתנה http הוא מקרה instance של השירות האנגולרי, ולפיכך יש לנו גישה דרכו למתודות ולתכונות של השירות.
    * לכל החברה שבאים מרקע של תכנות מונחה עצמים. השירות הוא הקלאס והמשתנה שהקמנו מממש אותו. לא צריך בשביל זה new כי אנגולר מספיק חכם בשביל לעשות את הזרקת התלויות בשבילנו.
    * תמשיכו לקרוא, תריצו את הקוד, ותראו בעצמכם שזה עובד.

המתודה של ה-service האנגולרי HttpClient שבה נשתמש לשליחת הנתונים היא post שמקבלת את המידע שאנו מעוניינים להעביר לצד השרת כמו גם את הכתובת בצד השרת שאליה צריך לפנות.

באפליקציה שלנו, נעביר ל- post את המידע על המכונית:

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

@Injectable()
export class CarService {
   constructor(private  HttpClient){}
   
   store(car:{unique_id:string, model:string, price:number}){
        return this.http.post("//localhost/store.php", car);
   }
   

}
  1. המתודה store מצפה לקבל אובייקט ובו השדות unique_id, model ו-price. ואת האובייקט הזה היא מעבירה לכתובת ה-url ב-POST.
  2. את ה-post צריך להחזיר באמצעות return.

זה לא מספיק שכתבנו את ה-service, צריך גם ליידע את האפליקציה על קיומו של השירות.

/src/app/app.module.ts
---------------------------------------

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

import { AppComponent } from './app.component';

import { CarService } from './car.service';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
     HttpModule
  ],
    providers: [CarService],
    bootstrap: [AppComponent]
})
export class AppModule { }

שימו לב, גם ייבאנו את ה-service באמצעות import, וגם הוספנו אותו כפריט למערך ה-providers.

 

הקומפננטה

הקומפוננטה האנגולרית מכילה את המתודה addCar, שמקבל את המידע מטופס ההטמ"ל, ושולחת את המידע ל-service.

נתחיל מייבוא ה-CarService:

/src/app/app.component.ts
---------------------------------------

import { Component } from '@angular/core';
import { CarService } from './car.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {

  cars = [];

  constructor(private carService: CarService){}

  addCar(newModel, newPrice){ }

}

לא מספיק לייבא ה-service, צריך לאתחל אותו בקונסטרקטור, ולכן:

constructor(private carService: CarService){}

המערך cars יאחסן את המכוניות באופן מקומי, על האפליקציה האנגולרית.

המתודה addCar מקבלת את הפרמטרים של מודל המכונית ומחירה, וצריכה לעשות שני דברים:

  1. לשלוח ל-service כדי לאחסן בצד השרת
  2. להוסיף פריט למערך cars.

מכיוון שאנחנו שולחים את המידע ל-service, שהוא observable, אנחנו נרשמים אליו באמצעות subscribe. ובגלל שהמתודה שאליה נרשמנו היא מסוג http נצפה לקבל שני סוגי מידע:

  1. response
  2. error

כך נראית הקריאה למתודה store.

this.carService.store({unique_id: newId, model:newModel.value, price:newPrice.value})
  .subscribe(
    (response) => console.log(response),
    (error)    => console.log(error)
  );
  1. שימו לב! אנחנו לא סתם קוראים למתודה store מה-service. אנחנו נרשמים אליה.
  2. מעבירים כפרמטר את האובייקט שמכיל את הנתונים שנאספו מהטופס.
  3. נרשמים לתגובה באמצעות subscribe.
  4. מצפים לקבל שתי סוגי תגובות, response אם המידע עבר כמו שצריך או error במקרה של שגיאה.
  5. את התגובה נציג בקונסולה באמצעות console.log

נכתוב את המתודה addCar:

addCar(newModel, newPrice){
    let newId = this.generateId();

    this.carService.store({unique_id: newId, model:newModel.value, price:newPrice.value})
     .subscribe(
       (response) => console.log(response),
       (error)    => console.log(error)
     );

    this.cars.push({unique_id: newId, model:newModel.value, price:newPrice.value});
}

מה עושה המתודה addCar?

  1. קוראת למתודה generateId כדי לקבל מחרוזת אקראית שתזהה את המכונית בהמשך (מיד נכתוב את המתודה).
  2. נרשמת לשירות ה-store של ה-service.
  3. מוסיפה את הפריט לעותק המקומי של מערך המכוניות באמצעות push.

המתודה generateId מייצרת מחרוזת אקראית באורך 10 תווים שתשמש בתור המזהה הייחודי של כל פריט מכונית שנוסיף:

private generateId(){
    let str = "";
    let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";

    for (var i = 0; i < 10; i++){
        str += possible.charAt(Math.floor(Math.random() * possible.length));
    }

    return str;
}

כך נראית הקומפוננטה הכוללת את הקוד שפיתחנו עד כה:

/src/app/app.component.ts
---------------------------------------

import { Component } from '@angular/core';
import { CarService } from './car.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  cars = [];

  constructor(private carService: CarService){}

  addCar(newModel, newPrice){
    let newId = this.generateId();

    this.carService.store({unique_id: newId, model:newModel.value, price:newPrice.value})
      .subscribe(
        (response) => console.log(response),
        (error)    => console.log(error)
      );

      this.cars.push({unique_id: newId, model:newModel.value, price:newPrice.value});
    }

    private generateId(){
        let str = "";
        let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";

        for (var i = 0; i < 10; i++){
            str += possible.charAt(Math.floor(Math.random() * possible.length));
        }

        return str;
    }

}

 

הטופס

כך נראה הטופס:

מראה הטופס שמשמש לשליחה של משתנים ב-POST באמצעות Angular

וזה קוד ההטמ"ל שמייצר את הטופס:

/src/app/app.component.html
---------------------------------------

<div id="theForm">
  <h2>The form</h2>

  <p>
    <label>Model</label>
    <input type="text" #carModel>
  </p>

  <p>
    <label>Price</label>
    <input type="text" #carPrice>
 </p>

  <input type="button" value="Add" (click)="addCar(carModel, carPrice)">
</div>

המידע מוזן לשדות, ונשלח עם המתודה addCar בהקלקה.

הדרך שבה מועבר המידע שמוזן לשדות היא באמצעות local reference. לדוגמה,#caPrice על שדה המחיר מעביר את המידע על השדה כפרמטר למתודה. האיור הבא מבהיר את העניין:

הדגמה של אופן הכתיבה של local reference ב-Angular2

 

הקוד בצד השרת

את הקוד בצד השרת כתבתי כסקריפט -PHP שיושב על מסד נתונים mySQL. הכי פשוט שאפשר. העקרונות נכונים לכל שפה.

להלן קוד ה-mysql לטבלה cars שמשמשת לאחסון המידע באפליקציה:

CREATE TABLE IF NOT EXISTS `cars` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `unique_id` varchar(20) NOT NULL,
  `model` varchar(60) NOT NULL DEFAULT '',
  `price` varchar(25) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

הקובץ connect.php כולל שני חלקים:

  1. ה-headers שנדרשים כדי לקלוט את המידע שנשלח ב-POST משרת חיצוני (השרת שעליו רצה האפליקציה האנגולרית).
  2. קוד להתקשרות עם מסד הנתונים.

connect.php
----------------

<?php
/** 
 * Set the headers	
 */
header("Access-Control-Allow-Origin: //localhost:4200"); // *
header("Access-Control-Allow-Credentials: true");
header("Access-Control-Allow-Methods: GET,POST"); // HEAD,OPTIONS
header("Access-Control-Allow-Headers: Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");		
	
/** 
 * Handle the database
 */	
// db credentials
define('DB_HOST', 'localhost');
define('DB_USER', 'root');
define('DB_PASS', '');
define('DB_NAME', 'angular2_tutorial');

// Connect with the database.
function connect()
{
  $connect = mysqli_connect(DB_HOST ,DB_USER ,DB_PASS ,DB_NAME);

  if (mysqli_connect_errno($connect))
  {
    die("Failed to connect:" . mysqli_connect_error());
  }

  mysqli_set_charset($connect, "utf8");


  return $connect;
}

$con = connect();

צריך לזכור שהמידע מגיע מהשרת שעליו רצה האפליקציה האנגולרית, ולכן חובה לקנפג את ה-headers כדי שידעו לקלוט את המידע.

header('Access-Control-Allow-Origin: //localhost:4200'); // *

מורה לשרת לקבל את הפניות שמקורם בכתובת //localhost:4200 שעליה רצה האפליקציה האנגולרית שלי (אני מריץ את שני השרתים זה של האפליקציה וזה של ה-PHP על מחשבי האישי).

יתר ה-headers מתירים שליחה או קבלת נתונים (GET או POST) והתקשרות עם השרת החיצוני.

הפונקציה connect יוצרת את ההתקשרות עם מסד הנתונים.

 

צד השרת כולל קובץ נוסף. הקובץ store.php מקבל, וממצה את הנתונים שנשלחים ב-post, מוודא את המידע, ומאחסן במסד הנתונים.

store.php
------------

<?php
/**
 * Store the new data
 */
require 'connect.php';

// Get the posted data.
$postdata = file_get_contents("php://input");
if(isset($postdata) && !empty($postdata))
{
  // Extract the data.
  $request = json_decode($postdata);
	
  // Validate.
  if(trim($request->unique_id) == '' || trim($request->model) == '' || (int)$request->price < 1)
  {
    return;
  }
    
  // Sanitize.
  $uid   = mysqli_real_escape_string($con, $request->unique_id);
  $model = mysqli_real_escape_string($con, $request->model);
  $price = mysqli_real_escape_string($con, $request->price);

  // Store.
  $sql = "INSERT INTO `cars`(`id`,`unique_id`,`model`,`price`) VALUES (null,'$uid', '$model','$price')";

  mysqli_query($con,$sql);
}

המידע ב-POST נקלט על ידי השורה הבאה:

$postdata = file_get_contents("php://input");

כי אני חושב שיותר נוח לעבוד עם אובייקטים מאשר עם מערכים.

המידע שמתקבל הוא אובייקט ולכן המיצוי נעשה באמצעות json_decode.

במדריך הבא, נלמד לקבל נתונים מצד השרת באמצעות GET ב-Angular.

ועוד הערה קטנה לסיום, המתודה generateId יושבת במדריך בתוך הקומפוננטה כדי לפשט את ההסבר, אבל המקום היותר מוצלח לשבץ אותה הוא בתוך service כי אז היא תוכל לשרת מספר קומפננטות. אז מה שאני ממליץ, בתור תרגיל, זה לפתוח service שאפשר לקרוא לו utilis.service, ובתוכו לשים את המתודה ועוד מתודות שיכולות להועיל לנו ביותר ממקום אחד. ואחרי שתכתבו את ה-service אל תשכחו ליידע את האפליקציה, ולהשתמש בקומפוננטה היכן שצריך.

למדריכים נוספים בסדרת האנגולר

 

אהבתם? לא אהבתם? דרגו!

0 הצבעות, ממוצע 0 מתוך 5 כוכבים

 

 

המדריכים באתר עוסקים בנושאי תכנות ופיתוח אישי. הקוד שמוצג משמש להדגמה ולצרכי לימוד. התוכן והקוד המוצגים באתר נבדקו בקפידה ונמצאו תקינים. אבל ייתכן ששימוש במערכות שונות, דוגמת דפדפן או מערכת הפעלה שונה ולאור השינויים הטכנולוגיים התכופים בעולם שבו אנו חיים יגרום לתוצאות שונות מהמצופה. בכל מקרה, אין בעל האתר נושא באחריות לכל שיבוש או שימוש לא אחראי בתכנים הלימודיים באתר.

למרות האמור לעיל, ומתוך רצון טוב, אם נתקלת בקשיים ביישום הקוד באתר מפאת מה שנראה לך כשגיאה או כחוסר עקביות נא להשאיר תגובה עם פירוט הבעיה באזור התגובות בתחתית המדריכים. זה יכול לעזור למשתמשים אחרים שנתקלו באותה בעיה ואם אני רואה שהבעיה עקרונית אני עשוי לערוך התאמה במדריך או להסיר אותו כדי להימנע מהטעיית הציבור.

שימו לב! הסקריפטים במדריכים מיועדים למטרות לימוד בלבד. כשאתם עובדים על הפרויקטים שלכם אתם צריכים להשתמש בספריות וסביבות פיתוח מוכחות, מהירות ובטוחות.

המשתמש באתר צריך להיות מודע לכך שאם וכאשר הוא מפתח קוד בשביל פרויקט הוא חייב לשים לב ולהשתמש בסביבת הפיתוח המתאימה ביותר, הבטוחה ביותר, היעילה ביותר וכמובן שהוא צריך לבדוק את הקוד בהיבטים של יעילות ואבטחה. מי אמר שלהיות מפתח זו עבודה קלה ?

השימוש שלך באתר מהווה ראייה להסכמתך עם הכללים והתקנות שנוסחו בהסכם תנאי השימוש.

הוסף תגובה חדשה

 

 

ענה על השאלה הפשוטה הבאה כתנאי להוספת תגובה:

מתי הוקמה המדינה?

 

תמונת המגיב

איתי בתאריך: 12.11.2017

תוכל להרחיב בבקשה על השורה:
$postdata =
file_get_contents("php://input");

תמונת המגיב

יוסי בן הרוש בתאריך: 12.11.2017

זה אמצעי להעביר נתונים ב-post בפורמט של json.

תמונת המגיב

דניאל בתאריך: 16.04.2018

פה: constructor(private Http){} זה לא עבד לי עד שהצהרתי על המשתנה כ http constructor(private Http:Http){}

תמונת המגיב

לא משנה בתאריך: 05.11.2019

ההסבר מצוין אבל הייתי שמח לדעת איך עושים את זה עם #C עם MVC!

תמונת המגיב

מנור בתאריך: 13.02.2020

האם web Api יכול לתפקד כצד שרת באותה דרך שהPHP מתפקד

תמונת המגיב

רוטנר בתאריך: 23.04.2020

שלום רציתי לבקש הסבר איך ניתן לשמור את פרטי הגולש הנוכחי ב angular ע"י sessionStorage תודה

תמונת המגיב

נוי בתאריך: 03.05.2020

מאמר מוסבר מעולה!ישלי שאלה , בקובץ store.php מה ההסבר של השורה אני הופכת את הדוגמא לuser שיש לו name,email,password) תודה!!

תמונת המגיב

בנימין בתאריך: 18.01.2021

אם אני מבין נכון הקוד PHP הוא לא קשור לאנגולר?
תודה רבה!

תמונת המגיב

יוסי בן הרוש בתאריך: 22.01.2021

נכון. זה צד השרת שבמקרה זה כתוב ב-PHP.

תמונת המגיב

מלכה בתאריך: 03.12.2021

wow.... so professional