تمرين - أنشئ تطبيق ويب أساسيًا

مكتمل

حتى الآن، لديك MongoDB وNode.js مثبتان على جهاز Ubuntu VM لديك. حان الوقت الآن لإنشاء تطبيق ويب أساسي لرؤية الأشياء في أثناء العمل. في أثناء الاستخدام، سترى مدى ملاءمة AngularJS وExpress.

تعتبر الأمثلة طريقة رائعة للتعلم. ينفذ تطبيق الويب الذي ستقوم بإنشائه، قاعدة بيانات أساسية للكتب. يتيح لك تطبيق الويب سرد معلومات حول الكتب وإضافة كتب جديدة وحذف الكتب الموجودة.

يوضح تطبيق الويب الذي ستراه هنا العديد من المفاهيم التي تنطبق على معظم تطبيقات ويب مكدس MEAN. بناءً على احتياجاتك واهتماماتك، يمكنك استكشاف الميزات التي تحتاجها لإنشاء تطبيقات مكدس MEAN لديك.

هذا هو الشكل الذي سيبدو عليه تطبيق ويب Books.

Screenshot of a web page with a form and submission button.

إليك كيفية ملائمة كل مكون من مكونات مكدس MEAN.

  • يقوم MongoDB بتخزين معلومات حول الكتب.
  • يقوم Express.js بتوجيه كل طلب من طلبات HTTP إلى المؤشر المناسب.
  • يربط AngularJS واجهة المستخدم بمنطق تسلسل عمل البرنامج.
  • يستضيف Node.js التطبيق من جانب الخادم.

هام

لأغراض التعلم، أنت هنا تقوم بإنشاء تطبيق ويب أساسي والغرض منه اختبار مكدس MEAN الخاص بك ومنحك فكرة عن كيفية عمله. التطبيق غير آمن بشكل كاف أو جاهز للاستخدام الإنتاجي.

ماذا عن Express؟

لقد قمت حتى الآن بتثبيت MongoDB وNode.js على الجهاز الظاهري الخاص بك. ماذا عن Express.js، وE في اختصار MEAN؟

Express.js هو إطار عمل خادم ويب تم إنشاؤه لـ Node.js الذي يبسط عملية إنشاء تطبيقات الويب.

الغرض الرئيسي من Express هو معالجة توجيه الطلب. يشير التوجيه إلى كيفية استجابة التطبيق لطلب إلى نقطة نهاية معينة. تتكون نقطة النهاية من مسار أو URI وطريقة طلب مثل GET أو POST. على سبيل المثال، قد تستجيب لطلب GET إلى /book نقطة النهاية عن طريق توفير قائمة بجميع الكتب في قاعدة البيانات. يمكنك الرد على طلب POST لنفس نقطة النهاية عن طريق إضافة إدخال إلى قاعدة البيانات بناءً على الحقول التي أدخلها المستخدم في نموذج ويب.

في تطبيق الويب الذي ستقوم بإنشائه قريبًا، ستستخدم Express لتوجيه طلبات HTTP وإعادة محتوى الويب إلى المستخدم الخاص بك. يمكن أن يساعد Express أيضًا تطبيقات الويب الخاصة بك في التعامل مع ملفات تعريف ارتباط HTTP ومعالجة سلاسل الاستعلام.

Express عبارة عن حزمة Node.js. يمكنك استخدام الأداة المساعدة npm ، والتي تأتي مع Node.js، لتثبيت حزم Node.js وإدارتها. لاحقا في هذه الوحدة، ستقوم بإنشاء ملف باسم package.json لتعريف Express والتبعيات الأخرى، ثم تشغيل الأمر لتثبيت هذه التبعيات npm install .

ماذا عن AngularJS؟

مثل Express، لم تقم بعد بتثبيت AngularJS، A في اختصار MEAN.

يجعل AngularJS تطبيقات الويب أسهل في الكتابة والاختبار لأنه يسمح لك بفصل مظهر صفحة الويب الخاصة بك بشكل أفضل - تعليمة HTML البرمجية - عن سلوك صفحة الويب الخاصة بك. إذا كنت على دراية بنمط نموذج-عرض-تحكم (MVC) أو مفهوم ربط البيانات، فستكون AngularJS مألوفة لك.

AngularJS هو ما يسمى بإطار عمل JavaScript للواجهة الأمامية، ما يعني أنه يجب أن يكون متاحًا فقط للعميل الذي يصل إلى التطبيق. بمعنى آخر، يعمل AngularJS في مستعرض الويب الخاص بالمستخدم، وليس على خادم الويب الخاص بك. ونظرًا إلى أن AngularJS عبارة عن JavaScript، يمكنك استخدامه لإحضار البيانات بسهولة من خادم الويب لديك لتظهر على الصفحة.

أنت لا تثبت AngularJS حقا. بدلاً من ذلك، يمكنك إضافة مرجع إلى ملف JavaScript في صفحة HTML الخاصة بك، تمامًا كما تفعل مع مكتبات JavaScript الأخرى. هناك عدة طرق لتضمين AngularJS في صفحات الويب الخاصة بك. هنا، ستقوم بتحميل AngularJS من شبكة تسليم المحتوى أو CDN. تعتبر شبكة تسليم المحتوى طريقة لتوزيع الصور والفيديو والمحتويات الأخرى جغرافيًا لتحسين سرعات التنزيل.

لم تقم بإضافة هذا الرمز بعد، ولكن إليك مثالاً يقوم بتحميل AngularJS من شبكة تسليم المحتوى. عادة ما تضيف هذه التعليمة البرمجية <head> إلى قسم من صفحة HTML.

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.2/angular.min.js"></script>

إشعار

لا تخلط بين AngularJS وAngular. في حين أن العديد من المفاهيم متشابهة بين الاثنين، فإن AngularJS عنصر سابق لـ Angular. لا يزال AngularJS مستخدمًا بشكل شائع لإنشاء تطبيقات الويب. بينما يستند AngularJS إلى JavaScript، فإن Angular يستند إلى TypeScript، وهي لغة برمجة تسهل كتابة برامج JavaScript.

كيف أُنشئ تطبيقًا؟

هنا، ستستخدم عملية أساسية. ستقوم بكتابة التعليمات البرمجية للتطبيق من Cloud Shell ثم تستخدم SCP، أو بروتوكول النسخ الآمن، لنسخ الملفات إلى الجهاز الظاهري الخاص بك. بعد ذلك، ستبدأ تشغيل تطبيق Node.js وسترى النتائج في المستعرض الخاص بك.

في الممارسة العملية، عادة ما تكتب وتختبر تطبيق الويب الخاص بك في بيئة محلية أكثر، مثل من الكمبيوتر المحمول أو من جهاز ظاهري تقوم بتشغيله محليا. يمكنك بعد ذلك تخزين التعليمات البرمجية الخاصة بك في نظام التحكم في الإصدار مثل Git واستخدام نظام تكامل مستمر وتسليم مستمر - أو CI / CD - مثل Azure DevOps لاختبار التغييرات وتحميلها إلى الجهاز الظاهري الخاص بك. سنوجهك إلى المزيد من الموارد في نهاية هذه الوحدة.

إنشاء تطبيق ويب Books

هنا، ستقوم بإنشاء جميع التعليمات البرمجية والبرامج النصية وملفات HTML التي تشكل تطبيق الويب الخاص بك. للإيجاز، سنقوم بتسليط الضوء على الأجزاء الهامة من كل ملف ولكن لن نخوض في التفاصيل الكاملة.

إذا كنت لا تزال متصلا بجهازك الظاهري عبر SSH، فقم بتشغيل exit لمغادرة جلسة SSH والعودة إلى Cloud Shell.

exit

لقد عدت الآن إلى جلسة Cloud Shell الخاصة بك.

إنشاء الملفات

  1. من Cloud Shell، قم بتشغيل هذه الأوامر لإنشاء المجلدات والملفات لتطبيق الويب الخاص بك:

    cd ~
    mkdir Books
    touch Books/server.js
    touch Books/package.json
    mkdir Books/app
    touch Books/app/model.js
    touch Books/app/routes.js
    mkdir Books/public
    touch Books/public/script.js
    touch Books/public/index.html
    

    إليك ما تم تضمينه:

    • Books هو الدليل الجذر للمشروع.
      • server.js يحدد نقطة الإدخال إلى تطبيق الويب. يُحمل حزم Node.js المطلوبة، ويحدد المنفذ للاستماع إليه، ويبدأ في الاستماع لنسبة استخدام شبكة HTTP الواردة.
      • package.json يوفر معلومات حول التطبيق الخاص بك، بما في ذلك اسمه ووصفه وحزم Node.js التي يحتاج تطبيقك إلى تشغيلها.
    • Books/app يحتوي على التعليمات البرمجية التي يتم تشغيلها على الخادم.
      • model.js تعريف اتصال قاعدة البيانات والمخطط. اعتبره كنموذج بيانات لتطبيقك.
      • routes.js يعالج توجيه الطلب. على سبيل المثال، يعرف طلبات GET إلى /book نقطة النهاية من خلال توفير قائمة بجميع الكتب في قاعدة البيانات.
    • Books/public يحتوي على الملفات التي يتم تقديمها مباشرة إلى مستعرض العميل.
      • index.html يحتوي على صفحة الفهرس. يحتوي على نموذج ويب يمكّن المستخدم من إرسال معلومات حول الكتب. كما يعرض كافة الكتب في قاعدة البيانات ويمكّنك من حذف الإدخالات من قاعدة البيانات.
      • script.js يحتوي على تعليمات JavaScript البرمجية التي يتم تشغيلها في متصفح المستخدم الخاص بك. يمكنه إرسال طلبات إلى الخادم لقائمة الكتب وإضافة كتب إلى قاعدة البيانات وحذف الكتب من قاعدة البيانات.
  2. code قم بتشغيل الأمر لفتح ملفاتك من خلال محرر Cloud Shell.

    code Books
    

إنشاء نموذج البيانات

  1. من المحرر، افتح app/model.js وأضف ما يلي:

    var mongoose = require('mongoose');
    var dbHost = 'mongodb://localhost:27017/Books';
    mongoose.connect(dbHost, { useNewUrlParser: true } );
    mongoose.connection;
    mongoose.set('debug', true);
    var bookSchema = mongoose.Schema( {
        name: String,
        isbn: {type: String, index: true},
        author: String,
        pages: Number
    });
    var Book = mongoose.model('Book', bookSchema);
    module.exports = Book;
    

    هام

    كلما قمت بلصق التعليمات البرمجية أو تغييرها في ملف في المحرر، تأكد من الحفظ بعد ذلك باستخدام "..." أو مفتاح المسرع (Ctrl+S على Windows وLinux، Command+S على macOS).

    تستخدم هذه التعليمة البرمجية Mongoose لتبسيط عملية نقل البيانات داخل وخارج MongoDB. Mongoose هو نظام قائم على مخطط لبيانات النمذجة. تعرف التعليمة البرمجية مستند قاعدة بيانات يسمى "Book" مع المخطط المتوفر. يعرّف المخطط أربعة حقول تصف كتابًا واحدًا:

    • اسم أو عنوان الكتاب
    • رقم كتابه القياسي الدولي - أو ISBN - الذي يعرف الكتاب بشكل فريد
    • مؤلف الكتاب
    • عدد الصفحات التي يحتوي عليها الكتاب

    بعد ذلك، ستقوم بإنشاء معالجات HTTP التي تقوم بتعيين طلبات GET وPOST وDELETE لعمليات قاعدة البيانات.

قم بإنشاء مسارات Express.js التي تعالج طلبات HTTP

  1. من المحرر، افتح app/routes.js التعليمات البرمجية التالية وأضفها:

    var path = require('path');
    var Book = require('./model');
    var routes = function(app) {
        app.get('/book', function(req, res) {
            Book.find({}, function(err, result) {
                if ( err ) throw err;
                res.json(result);
            });
        });
        app.post('/book', function(req, res) {
            var book = new Book( {
                name:req.body.name,
                isbn:req.body.isbn,
                author:req.body.author,
                pages:req.body.pages
            });
            book.save(function(err, result) {
                if ( err ) throw err;
                res.json( {
                    message:"Successfully added book",
                    book:result
                });
            });
        });
        app.delete("/book/:isbn", function(req, res) {
            Book.findOneAndRemove(req.query, function(err, result) {
                if ( err ) throw err;
                res.json( {
                    message: "Successfully deleted the book",
                    book: result
                });
            });
        });
        app.get('*', function(req, res) {
            res.sendFile(path.join(__dirname + '/public', 'index.html'));
        });
    };
    module.exports = routes;
    

    تقوم هذه التعليمة البرمجية بإنشاء أربعة مسارات للتطبيق. إليك لمحة موجزة لكل منها.

    فعل HTTP نقطة النهاية ‏‏الوصف
    GET /book استرداد كافة الكتب من قاعدة البيانات.
    POST /book إنشاء كائن Book استنادا إلى الحقول التي وفرها المستخدم على نموذج ويب وكتابة هذا الكائن إلى قاعدة البيانات.
    حذف /book/:isbn يحذف الكتاب كما هو معرف بواسطة ISBN الخاصة به من قاعدة البيانات.
    GET * إرجاع صفحة الفهرس عند عدم مطابقة أي مسار آخر.

    يمكن ل Express.js تقديم استجابات HTTP مباشرة في التعليمات البرمجية لمعالجة المسار، أو يمكن أن تقدم محتوى ثابتا من الملفات. تُظهر هذه التعليمة البرمجية كليهما. تقوم المسارات الثلاثة الأولى بإرجاع بيانات JSON لطلبات واجهة برمجة التطبيقات الخاصة بالكتاب. يقوم المسار الرابع (الحالة الافتراضية) بإرجاع محتويات ملف الفهرس، index.html.

قم بإنشاء تطبيق JavaScript من جانب العميل

  1. من المحرر، افتح public/script.js هذه التعليمة البرمجية وأضفها:

    var app = angular.module('myApp', []);
    app.controller('myCtrl', function($scope, $http) {
        var getData = function() {
            return $http( {
                method: 'GET',
                url: '/book'
            }).then(function successCallback(response) {
                $scope.books = response.data;
            }, function errorCallback(response) {
                console.log('Error: ' + response);
            });
        };
        getData();
        $scope.del_book = function(book) {
            $http( {
                method: 'DELETE',
                url: '/book/:isbn',
                params: {'isbn': book.isbn}
            }).then(function successCallback(response) {
                console.log(response);
                return getData();
            }, function errorCallback(response) {
                console.log('Error: ' + response);
            });
        };
        $scope.add_book = function() {
            var body = '{ "name": "' + $scope.Name +
            '", "isbn": "' + $scope.Isbn +
            '", "author": "' + $scope.Author +
            '", "pages": "' + $scope.Pages + '" }';
            $http({
                method: 'POST',
                url: '/book',
                data: body
            }).then(function successCallback(response) {
                console.log(response);
                return getData();
            }, function errorCallback(response) {
                console.log('Error: ' + response);
            });
        };
    });
    

    لاحظ كيف تعرّف هذه التعليمة البرمجية وحدة تسمى "myApp" ووحدة تحكم تسمى "myCtrl". لن نخوض في التفاصيل الكاملة حول كيفية عمل الوحدة ووحدات التحكم هنا، ولكنك ستستخدم هذه الأسماء في الخطوة التالية لربط واجهة المستخدم (رمز التعليمة البرمجية HTML) بمنطق تسلسل عمل التطبيق.

    في وقت سابق، قمت بإنشاء أربعة مسارات تعالج العديد من عمليات GET وPOST وDELETE على الخادم. تشبه هذه التعليمة البرمجية تلك العمليات نفسها، ولكن من جانب العميل (مستعرض ويب الخاص بالمستخدم).

    getData ترسل الدالة، على سبيل المثال، طلب GET إلى /book نقطة النهاية. تذكر أن الخادم يعالج هذا الطلب عن طريق استرداد معلومات حول جميع الكتب من قاعدة البيانات وإعادة تلك المعلومات كبيانات JSON. لاحظ كيفية تعيين بيانات JSON الناتجة إلى $scope.books المتغير. ستتعرف على كيفية تأثير ذلك على ما يراه المستخدم على صفحة الويب في الخطوة التالية.

    تستدعي هذه التعليمة البرمجية الدالة getData عند تحميل الصفحة. يمكنك فحص del_book الدالتين و add_book للحصول على فهم لكيفية عملها. لا تحتاج إلى تعليمات برمجية من جانب العميل لمطابقة المعالج الافتراضي للخادم، لأن المعالج الافتراضي يرجع صفحة الفهرس وليس بيانات JSON.

إنشاء واجهة المستخدم

  1. من المحرر، افتح public/index.html هذه التعليمة البرمجية وأضفها:

    <!doctype html>
    <html ng-app="myApp" ng-controller="myCtrl">
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.2/angular.min.js"></script>
        <script src="script.js"></script>
    </head>
    <body>
        <div>
        <table>
            <tr>
            <td>Name:</td>
            <td><input type="text" ng-model="Name"></td>
            </tr>
            <tr>
            <td>Isbn:</td>
            <td><input type="text" ng-model="Isbn"></td>
            </tr>
            <tr>
            <td>Author:</td>
            <td><input type="text" ng-model="Author"></td>
            </tr>
            <tr>
            <td>Pages:</td>
            <td><input type="number" ng-model="Pages"></td>
            </tr>
        </table>
        <button ng-click="add_book()">Add</button>
        </div>
        <hr>
        <div>
        <table>
            <tr>
            <th>Name</th>
            <th>Isbn</th>
            <th>Author</th>
            <th>Pages</th>
            </tr>
            <tr ng-repeat="book in books">
            <td><input type="button" value="Delete" data-ng-click="del_book(book)"></td>
            <td>{{book.name}}</td>
            <td>{{book.isbn}}</td>
            <td>{{book.author}}</td>
            <td>{{book.pages}}</td>
            </tr>
        </table>
        </div>
    </body>
    </html>
    

    تنشئ هذه التعليمة البرمجية نموذج HTML أساسي، مع أربعة حقول لإرسال بيانات الكتاب وجدول يعرض جميع الكتب المخزنة في قاعدة البيانات.

    على الرغم من أن هذا هو رمز HTML القياسي، ng- قد تكون سمات HTML غير مألوفة لك. تربط سمات HTML التعليمة البرمجية AngularJS بواجهة المستخدم. على سبيل المثال، عند تحديد Add، يستدعي AngularJS الدالة add_book التي ترسل بيانات النموذج إلى الخادم.

    يمكنك فحص التعليمات البرمجية هنا للحصول على فكرة عن كيفية اتصال كل سمة من سمات ng- بمنطق تسلسل عمل التطبيق.

إنشاء خادم Express.js لاستضافة التطبيق

  1. من المحرر، افتح server.js هذه التعليمة البرمجية وأضفها:

    var express = require('express');
    var bodyParser = require('body-parser');
    var app = express();
    app.use(express.static(__dirname + '/public'));
    app.use(bodyParser.json());
    require('./app/routes')(app);
    app.set('port', 80);
    app.listen(app.get('port'), function() {
        console.log('Server up: http://localhost:' + app.get('port'));
    });
    

    تقوم هذه التعليمة البرمجية بإنشاء تطبيق ويب نفسه. وهو يخدم الملفات الثابتة من public الدليل ويستخدم المسارات التي حددتها مسبقا لمعالجة الطلبات.

تعريف معلومات الحزمة وتبعياتها

تذكر أنه package.json يوفر معلومات حول التطبيق الخاص بك، بما في ذلك اسمه ووصفه وحزم Node.js التي يحتاج تطبيقك إلى تشغيلها.

  1. من المحرر، افتح package.json هذه التعليمة البرمجية وأضفها:

    {
      "name": "books",
      "description": "Sample web app that manages book information.",
      "license": "MIT",
      "repository": {
        "type": "git",
        "url": "https://github.com/MicrosoftDocs/mslearn-build-a-web-app-with-mean-on-a-linux-vm"
      },
      "main": "server.js",
      "dependencies": {
        "express": "~4.16",
        "mongoose": "~5.3",
        "body-parser": "~1.18"
      }
    }
    

ترى معلومات أو بيانات تعريف حول التطبيق الخاص بك بما في ذلك الاسم والوصف والترخيص.

repository يحدد الحقل مكان الاحتفاظ بالتعليمات البرمجية. للحصول على مرجع، يمكنك مراجعة التعليمة البرمجية على GitHub في عنوان URL المعروض هنا.

main يعرف الحقل نقطة إدخال التطبيق. يتم توفيره هنا للاكتمال، ولكنه ليس مهما لأنك لا تخطط لنشر تطبيقك كحزمة Node.js للآخرين لتنزيله واستخدامه.

الحقل dependenciesحقل مهم. يعرف حزم Node.js يحتاجها التطبيق الخاص بك. قريبا، ستتصل بجهازك الظاهري مرة ثانية وتشغل npm install الأمر لتثبيت هذه الحزم.

تستخدم حزم العقدة عادة نظام تعيين الإصدار الدلالي. يحتوي رقم الإصدار على ثلاثة مكونات: الإصدار الرئيسي والإصدار الثانوي، والتصحيح. يخبر تدوين tilde ~ هنا npm بتثبيت أحدث إصدار تصحيح ضمن الإصدارات الرئيسية والثانوية المتوفرة. الإصدارات التي تراها هنا هي الأحدث التي تم اختبار هذه الوحدة بها. من الناحية العملية، يمكنك زيادة الإصدار بمرور الوقت في أثناء تحديث واختبار تطبيقك لاستخدام أحدث الميزات التي توفرها كل حزمة تابعة.

انسخ الملفات إلى الجهاز الظاهري الخاص بك

قبل المتابعة، تأكد من أن عنوان IP الخاص بالجهاز الظاهري الخاص بك متاح في متناول يدك. إذا لم يكن لديك، فقم بتشغيل هذه الأوامر من Shell Cloud لاسترداده:

ipaddress=$(az vm show \
  --name MeanStack \
  --resource-group "<rgn>[sandbox resource group name]</rgn>" \
  --show-details \
  --query [publicIps] \
  --output tsv)
echo $ipaddress
  1. لقد انتهيت من عملية تحرير الملفات. تأكد من حفظ التغييرات على كل ملف ثم أغلق المحرر.

    لإغلاق المحرر، حدد علامات الحذف في الزاوية العلوية اليسرى، ثم حدد إغلاق المحرر.

  2. قم بتشغيل الأمر التالي scp لنسخ محتويات ~/Books الدليل في جلسة عمل Cloud Shell إلى نفس اسم الدليل على الجهاز الظاهري الخاص بك:

    scp -r ~/Books azureuser@$ipaddress:~/Books
    

ثبت حزم عقدة إضافية

لنفترض أنه في أثناء عملية التطوير، قمت بتحديد حزم عقدة إضافية التي تريد استخدامها. على سبيل المثال، تذكر أن app/model.js يبدأ بهذا السطر.

var mongoose = require('mongoose');

تذكر أن التطبيق يستخدم Mongoose للمساعدة في نقل البيانات داخل وخارج قاعدة بيانات MongoDB.

يتطلب التطبيق أيضاً الحزم Express.js وbody-parser. Body-parser هو مكون إضافي يمكّن Express من العمل مع البيانات من نموذج الويب المرسل من قبل العميل.

دعونا نتصل بجهازك الظاهري ونثبت الحزم التي حددتها في package.json.

  1. قبل الاتصال بجهازك الظاهري، تأكد من أن لديك عنوان IP الخاص بجهازك الظاهري سهلاً. إذا لم يكن لديك، فقم بتشغيل أوامر Cloud Shell في القسم السابق لاسترداده.

  2. كما فعلت سابقا، قم بإنشاء اتصال SSH بجهازك الظاهري:

    ssh azureuser@$ipaddress
    
  3. الانتقال إلى Books الدليل ضمن الدليل الرئيسي:

    cd ~/Books
    
  4. قم بتشغيل npm install لتثبيت الحزم التابعة:

    sudo apt install npm -y && npm install
    

احتفظ باتصال SSH مفتوحا للقسم التالي.

اختبر التطبيق

أنت الآن على استعداد لاختبار تطبيق الويب Node.js الخاص بك.

  1. ~/Books من الدليل، قم بتشغيل هذا الأمر لبدء تشغيل تطبيق الويب:

    sudo nodejs server.js
    

    يقوم هذا الأمر ببدء التطبيق عن طريق الاستماع على المنفذ 80 لطلبات HTTP الواردة.

  2. من علامة تبويب مستعرض منفصلة، انتقل إلى عنوان IP العام الخاص بالجهاز الظاهري.

    ترى صفحة الفهرس، التي تتضمن نموذج ويب.

    Screenshot of the book web page with a form and submission button.

    حاول إضافة بعض الكتب إلى قاعدة البيانات. في كل مرة تقوم فيها بإضافة كتاب، تحدث الصفحة قائمة الكتب الكاملة.

    Screenshot of the book web page with sample data populated.

    لحذف كتاب من قاعدة البيانات، يمكنك أيضا تحديد حذف.