AbdullaScript

Writer
  • عدد منشوراتي

    30
  • تاريخ الإنضمام

  • تاريخ اخر زياره

السمعه بالموقع

13 Good

2 متابعين

عن العضو AbdullaScript

  • الرتبه
    مبدع متقدم
  • تاريخ الميلاد 01/24/91

معلومات عامة

  • الجنس
    ذكر
  • السكن
    مملكة البحرين
  • هواياتي
    Bodybuilding and web development

اخر الزوار

4421 زياره للملف الشخصي
  1. مبدع استفدت كثير منك 😍

  2. السلام عليكم ورحمة الله وبركاته.. قبل الدخول في هذا الموضوع و عرض الأمثلة، اود ان اوجه كلمة، من السهولة تعلم أي لغة برمجية تريد ولكن لن تستطيع برمجة تطبيق جيد الا من خلال فهم كيفية عمل هذه اللغة/فريمورك او الـEnvironment لذا، من المهم جدا أن تعرف كيفية عمل كل لغة تحت الغطاء، ماذا يدور في الخفاء وكيف تشتغل. في هذا الدرس سنتحدث عن اركتجر الـNodeJS ومفهوم Single Threaded Event Loop الكثير من تكنولوجيات التطبيقات تعمل على Multi-Thread Model مثل JSP, Spring MVC, ASP.NET ، ولكن مع الـNodeJS الامر مختلف فتطبيق النود هو عبارة عن تطبيق ++C يتم تحويله إلى لغة الآلة عن طريق محرك للجافاسكربت يدعى V8 حيث تعمل "بيئة" التطبيق على Single Thread سأفترض ان مبرمجين الـ Functional Languages على علم مسبق بمودل Multithreaded ولكن ما هو الفرق بينه وبين مودل الـNodeJS Single Thread Event Loop ؟ ملاحظة: سنقوم باختصار Single Thread Event Loop بـSTEL بلاتفورم الـNodeJS كما اسلفنا النود يستخدم STEL Architecture بدلا من MultiThreaded اذا كيف يتم تلبية طلبات Requests المستخدمين المختلفة بدون استخدام أكثر من Thread ، وماهو الـEvent Loop Model ؟ اولا: MultiThreaded Model كل تطبيق ويب غير مبرمج بالنود usually يستخدم هذا المودل، العميل يرسل طلب Request بعد ذلك السيرفر يقوم بعمليات معينة بناءا على طلب العميل، يجهز الجواب Response ويرسله إلى العميل. هذه المودل يستخدم بروتوكول الـHTTP و بما أن هذا البروتوكول Stateless إذا كذلك Request Response Model يعد Stateless Model على كل حال "الزبدة" ان هذا المودل يستخدم اكثر من Thread لتلبية طلبات العملاء، و لفهم الامر اكثر، اِطلع على الدايقرام الويب سيرفر في حالة infinite loop ينتظر الطلبات القادمة. العميل يرسل طلب للسيرفر. الويب سيرفر داخليا يحدد العدد المتوفر من الـThreads لتلبية طلب العميل. الطلبات تصل الى الويب سيرفر. يستقبل الطلب ، و يعينه الى احد الـThreads المتاحة من بركة الـThreads. الثرد يواصل عملية تلبية طلب العميل، يحدد ما اذا كان هناك عمليات IO ام لا بناءا على طلب العميل، يتم التلبية وإعداد الجواب Response. يُرسل الجواب العميل. يُفرغ الـThread ويتحول من busy الى await. السيرفر يدخل في عملية infinite loop ويكرر العملية المذكورة أعلاه لكل طلب ولكل عميل، هذا يعني أن في كل طلب بإستخدام هذا المودل ينشأ Thread واذا كان هنالك عمليات IO Blocking ( عمليات التواصل مع قواعد البيانات مثلا) وعدد طلبات اكثر من الـThreads (وهذا صحيح اغلب الاحيان) اذا مبدئيا كل الـThreads سيكونون في حالة تأهيل الجواب، العملاء الآخرين في حالة انتظار داخل صف queue لحين وجود Thread متاح، مع التنبيه انه وقبل تعيين الثرد الى عميل اخر والتحول من حالة busy الى await يجب ان يتم تفريغه لأنه يأخذ حيز من resources السيرفر مثل الرام واذا اردت ان يكون تطبيقك جيد يجب عليه ترقية السيزفر الذي يشتغل عليه التطبيق بشكل مستمر و بمبالغ باهضة ثانيا: اركتجر Architecture الـ NodeJS و مفهوم الـSTEL بلاتفورم النود لا يتبع المودل أعلاه بل يعمل على مفهوم آخر أساسه Single Thread مستوحى من تقنية الـcallback في الجافاسكربت، يجب ان يكون لديك فكرة ولو بسيطة عن تقنية او mechanism الـcallback لفهم ما سيتم شرحه الآن. اساس عمليه تلبية طلبات العملاء في النود هو Event Loop و بما ان Architecture النود يتبع هذه المكانيسم، اذا يستطيع تلبية طلبات العملاء الكثيرة بشكل بسيط وسريع العميل يرسل طلب الى الويب سيرفر ويب سيرفر النود داخليا يحدد عدد الـThreads المتوفره في السيرفر. ويب سيرفر النود يستقبل الطلب ويدخله في صف Queue يسمى بالـEvent Queue. يوجد مكون في النود يدعى Event loop يسمى بذلك لانه يستخدم indefinite loop ليستقبل الطلبات ويعمل على تلبيتها. indefinite loop هو بكل بساطه عن لوب ينتظر أمر ما ليحقق true وكذلك غير معلوم تكراره، في حالتنا الكوندشن هو الطلب Request. الـEvent Loop يستخدم Thread واحد فقط وهو الـMain Thread، مهمته ان يفحص ما اذا كان هناك طلب من العميل ام لا، اذا وجد الطلب ادخله في الـEvent Queue واذا لم يوجد، بقي في حالة الـindefinitely. إذا وجد طلب في Event Queue ، يبدأ بعملية التلبية. إذا كان الطلب لا يوجد به عمليات IO، يتم إعداد الجواب Response وإرساله للعميل. إذا كان طلب العميل يوجد به عمليات IO مثل التواصل مع قواعد البيانات، يعين هذا الجزء من الطلب إلى أحد الـThreads المتوفرة التي هي أساسا في C++ Thread Pool مكونة بإستخدام مكتبة libuv، حيث يعمل الـThread على عملية التواصل مع قاعدة البيانات. يجهز Response ويرسله مره اخرى الى الـMain Thread الذي يتواجد فيه الـEvent loop و الذي بدوره يرسله الى العميل. لوهلة قد تعتقد ان وجود التواصل بين الـEvent loop و الـThread هي عملية زائدة قد تطيل من بروسس تلبية طلب العميل، بينما في الحقيقة هذا الـThread غير تابع للنظام إنما كما وضحت تابع الى Thread Pool مبرمجة بالـ++C بإستخدام مكتبة Libuv، بالتالي لن يكون هناك حاجة لإنشاء Thread حقيقي لكل عميل حيث تعد هذه العملية مكلفة وتذكر دائما مقولة ان "الرام مكلفة". العدد الافتراضي للـC++ Threads هو 4 او 5، تستطيع زيادة هذا العدد من خلال كتابة هذا السطر في الكود (x) هو العدد المطلوب process.env.UV_THREADPOOL_SIZE = x؛ وإذا اردت كذلك استخدام الطريقة الاعتيادية الى الـMulti Threading تستطيع ذلك من خلال استخدام مكتبة Cluster (موجودة مسبقا مع تثبيت النود) اتمنى ان وضحت الفكرة وكيفية عمل النود، جميع الاسئلة مرحب بها هنا او على التويتر AbdullaScript
  3. السلام عليكم ورحمة الله وبركاته.. قبل الدخول في هذا الموضوع و عرض الأمثلة، اود ان اوجه كلمة، من السهولة تعلم أي لغة برمجية تريد ولكن لن تستطيع برمجة تطبيق جيد الا من خلال فهم كيفية عمل هذه اللغة/فريمورك او الـEnvironment لذا، من المهم جدا أن تعرف كيفية عمل كل لغة تحت الغطاء، ماذا يدور في الخفاء وكيف تشتغل. في هذا الدرس سنتحدث عن اركتجر الـNodeJS ومفهوم Single Threaded Event Loop الكثير من تكنولوجيات التطبيقات تعمل على Multi-Thread Model مثل JSP, Spring MVC, ASP.NET ، ولكن مع الـNodeJS الامر مختلف فتطبيق النود هو عبارة عن تطبيق ++C يتم تحويله إلى لغة الآلة عن طريق محرك للجافاسكربت يدعى V8 حيث تعمل "بيئة" التطبيق على Single Thread سأفترض ان مبرمجين الـ Functional Languages على علم مسبق بمودل Multithreaded ولكن ما هو الفرق بينه وبين مودل الـNodeJS Single Thread Event Loop ؟ ملاحظة: سنقوم باختصار Single Thread Event Loop بـSTEL بلاتفورم الـNodeJS كما اسلفنا النود يستخدم STEL Architecture بدلا من MultiThreaded اذا كيف يتم تلبية طلبات Requests المستخدمين المختلفة بدون استخدام أكثر من Thread ، وماهو الـEvent Loop Model ؟ اولا: MultiThreaded Model كل تطبيق ويب غير مبرمج بالنود usually يستخدم هذا المودل، العميل يرسل طلب Request بعد ذلك السيرفر يقوم بعمليات معينة بناءا على طلب العميل، يجهز الجواب Response ويرسله إلى العميل. هذه المودل يستخدم بروتوكول الـHTTP و بما أن هذا البروتوكول Stateless إذا كذلك Request Response Model يعد Stateless Model على كل حال "الزبدة" ان هذا المودل يستخدم اكثر من Thread لتلبية طلبات العملاء، و لفهم الامر اكثر، اِطلع على الدايقرام الويب سيرفر في حالة infinite loop ينتظر الطلبات القادمة. العميل يرسل طلب للسيرفر. الويب سيرفر داخليا يحدد العدد المتوفر من الـThreads لتلبية طلب العميل. الطلبات تصل الى الويب سيرفر. يستقبل الطلب ، و يعينه الى احد الـThreads المتاحة من بركة الـThreads. الثرد يواصل عملية تلبية طلب العميل، يحدد ما اذا كان هناك عمليات IO ام لا بناءا على طلب العميل، يتم التلبية وإعداد الجواب Response. يُرسل الجواب العميل. يُفرغ الـThread ويتحول من busy الى await. السيرفر يدخل في عملية infinite loop ويكرر العملية المذكورة أعلاه لكل طلب ولكل عميل، هذا يعني أن في كل طلب بإستخدام هذا المودل ينشأ Thread واذا كان هنالك عمليات IO Blocking ( عمليات التواصل مع قواعد البيانات مثلا) وعدد طلبات اكثر من الـThreads (وهذا صحيح اغلب الاحيان) اذا مبدئيا كل الـThreads سيكونون في حالة تأهيل الجواب، العملاء الآخرين في حالة انتظار داخل صف queue لحين وجود Thread متاح، مع التنبيه انه وقبل تعيين الثرد الى عميل اخر والتحول من حالة busy الى await يجب ان يتم تفريغه لأنه يأخذ حيز من resources السيرفر مثل الرام واذا اردت ان يكون تطبيقك جيد يجب عليه ترقية السيزفر الذي يشتغل عليه التطبيق بشكل مستمر و بمبالغ باهضة ثانيا: اركتجر Architecture الـ NodeJS و مفهوم الـSTEL بلاتفورم النود لا يتبع المودل أعلاه بل يعمل على مفهوم آخر أساسه Single Thread مستوحى من تقنية الـcallback في الجافاسكربت، يجب ان يكون لديك فكرة ولو بسيطة عن تقنية او mechanism الـcallback لفهم ما سيتم شرحه الآن. اساس عمليه تلبية طلبات العملاء في النود هو Event Loop و بما ان Architecture النود يتبع هذه المكانيسم، اذا يستطيع تلبية طلبات العملاء الكثيرة بشكل بسيط وسريع العميل يرسل طلب الى الويب سيرفر ويب سيرفر النود داخليا يحدد عدد الـThreads المتوفره في السيرفر. ويب سيرفر النود يستقبل الطلب ويدخله في صف Queue يسمى بالـEvent Queue. يوجد مكون في النود يدعى Event loop يسمى بذلك لانه يستخدم indefinite loop ليستقبل الطلبات ويعمل على تلبيتها. indefinite loop هو بكل بساطه عن لوب ينتظر أمر ما ليحقق true وكذلك غير معلوم تكراره، في حالتنا الكوندشن هو الطلب Request. الـEvent Loop يستخدم Thread واحد فقط وهو الـMain Thread، مهمته ان يفحص ما اذا كان هناك طلب من العميل ام لا، اذا وجد الطلب ادخله في الـEvent Queue واذا لم يوجد، بقي في حالة الـindefinitely. إذا وجد طلب في Event Queue ، يبدأ بعملية التلبية. إذا كان الطلب لا يوجد به عمليات IO، يتم إعداد الجواب Response وإرساله للعميل. إذا كان طلب العميل يوجد به عمليات IO مثل التواصل مع قواعد البيانات، يعين هذا الجزء من الطلب إلى أحد الـThreads المتوفرة التي هي أساسا في C++ Thread Pool مكونة بإستخدام مكتبة libuv، حيث يعمل الـThread على عملية التواصل مع قاعدة البيانات. يجهز Response ويرسله مره اخرى الى الـMain Thread الذي يتواجد فيه الـEvent loop و الذي بدوره يرسله الى العميل. لوهلة قد تعتقد ان وجود التواصل بين الـEvent loop و الـThread هي عملية زائدة قد تطيل من بروسس تلبية طلب العميل، بينما في الحقيقة هذا الـThread غير تابع للنظام إنما كما وضحت تابع الى Thread Pool مبرمجة بالـ++C بإستخدام مكتبة Libuv، بالتالي لن يكون هناك حاجة لإنشاء Thread حقيقي لكل عميل حيث تعد هذه العملية مكلفة وتذكر دائما مقولة ان "الرام مكلفة". العدد الافتراضي للـC++ Threads هو 4 او 5، تستطيع زيادة هذا العدد من خلال كتابة هذا السطر في الكود (x) هو العدد المطلوب process.env.UV_THREADPOOL_SIZE = x؛ وإذا اردت كذلك استخدام الطريقة الاعتيادية الى الـMulti Threading تستطيع ذلك من خلال استخدام مكتبة Cluster (موجودة مسبقا مع تثبيت النود) اتمنى ان وضحت الفكرة وكيفية عمل النود، جميع الاسئلة مرحب بها هنا او على التويتر AbdullaScript
  4. ماهو الcallback hell، جحيم الكولباك؟ لا يوجد شيء مميز في الجافا سكربت يسمى بجحيم الكولباك ما هو الا تسميه لأمر ما قد يقع فيه المطور وهو طريقة كتابة كود حيث يحتوي على عدة دوال Asynchronous مترابطة مع بعضها، واحدة داخل الاخرى، مثال: getData1(function(x){ getData2(x, function(y){ getData3(y, function(z){ ... }); }); }); والسبب في ذلك يعود لأن ناتج "results" كل دالة يعتمد على ما سبقتها. ملاحظة: كلمة asynchronous تعني أمر يحدث لاحقا << لا تركز على هالشي مو موضوعنا. جحيم الكولباك أمر ليس بجيد للكود، خصوصا في الحالات المعقدة التي تكون فيها دوال كثيرة مربوطه مع بعضها البعض وهنا قد يؤخر وقت انتهاء من التنفيذ و كمطور انت لا تريد ان تقع في هذه المشكلة, لحل مشكلة جحيم الكولباك، يستخدم المطورون إحدى مكتبات البرومس promises "الوعد، الوعود"، فالوعد دالة إما أن يتم العمل به fulfilled مع اظهار نتيجة الوعد value او رده rejected مع إظهار السبب error، وقد يكون الكود يحتوي على أكثر من وعد، واحد تلو الآخر. شخصيا لا اعتمد على الوعود في حل مشكلة جحيم الكولباك، عند التعامل مع قواعد البيانات MySQL رغم أنها الطريقة المثلى ولكن تتطلب الكثير مع الكود "وجهة نظر" و قد تواجهك مشاكل في أداء performance التطبيق بعد ذلك, استخدم طريقة أكثر بدائية و فعالة "تفي بالغرض" ، مبدأ الفكرة و طريقة عملها تتشابه كثيرا مع مبدأ عمل promises ولكن بكود أقل، سأرفق الطريقة مع المثال بالأسفل. للتعرف أكثر على جحيم الكولباك، سأعرض لكم مثال على التعامل مع قاعدة البيانات MySQL بالـ NodeJS وعمل اكثر من query وناتج كل query يعتمد على كل سبقتها. var express = require("express"); var app = express(); var mysql = require('mysql'); var conn = mysql.createConnection({ host: 'localhost', user: 'me', password: 'secret', database: 'my_db' }); conn.connect(); // GET method route app.get('/user/posts', function (req, res) { /* http://www.example.com/user/posts?userId=26 */ var userId = req.query.userId; conn.query('SELECT username FROM users WHERE ID = ?', [userId], function (error, results, fields) { if (error) throw error; if (results.length) { var username = results[0].username; conn.query('SELECT * FROM posts WHERE post_by = ?', [username], function (error, posts, fields) { if (error) throw error; if (posts.length) { conn.query('SELECT * FROM users_addresses WHERE username = ?', [username], function (error, address, fields) { if (error) throw error; if (statistics.length) { res.json(200, { posts: posts, address: address }); } else { res.json(200, { error: 'no statistics found' }); } }); } else { res.json(200, { error: 'no posts found' }); } }); } else { res.json(200, { error: 'no user found by ID 26' }); } }); }); للتخلص على مثل هذه المشاكل في حالة التعامل مع MySQL / MariaDB يكون بإسناد results كل عملية query في دالة مثلا var express = require("express"); var app = express(); var mysql = require('mysql'); var conn = mysql.createConnection({ host: 'localhost', user: 'me', password: 'secret', database: 'my_db' }); conn.connect(); // GET method route app.get('/user/posts', function (req, res) { /* http://www.example.com/user/posts?userId=26 */ var userId = req.query.userId; conn.query('SELECT username FROM users WHERE ID = ?', [userId], function (error, results, fields) { if (error) throw error; if (results.length) { setUsername(results[0].username); } else { res.json(200, { error: 'no user found by ID 26' }); } }); function setUsername(user) { var username = user; conn.query('SELECT * FROM posts WHERE post_by = ?', [username], function (error, posts, fields) { if (error) throw error; if (posts.length) { setPosts(posts, username); } else { res.json(200, { error: 'no posts found' }); } }); } function setPosts(po, user) { var posts = po; // po is the result of the prev query and it is an object it could be length of 1 or more doesn't matter var username = user; conn.query('SELECT * FROM users_addresses WHERE username = ?', [username], function (error, address, fields) { if (error) throw error; if (statistics.length) { res.json(200, { username: username, posts: posts, address: address }); } else { res.json(200, { error: 'no statistics found' }); } }); } }); أو بطريقة اخرى وهي عمل execution لكل query بطريقة parallel او series بإستخدام مكتبة async /* Make Sure To Install async npm install async --save */ var async = require(“async”); // Parallel Method async.parallel([ wait5SecondsAndReturn1, wait5SecondsAndReturn2 ], function (err, results){ //after 5 seconds, results will be [1, 2] }); function wait5SecondsAndReturn1(callback) { setTimeout(function(){ callback(null, 1); }, 5000); } function wait5SecondsAndReturn2(callback) { setTimeout(function(){ callback(null, 2); }, 5000); } //Series Method async.series([ wait5SecondsAndReturn1, wait5SecondsAndReturn2 ], function (err, results){ //after 10 seconds, results will be [1, 2] }); function wait5SecondsAndReturn1(callback) { setTimeout(function(){ callback(null, 1); }, 5000); } function wait5SecondsAndReturn2(callback) { setTimeout(function(){ callback(null, 2); }, 5000); } // Another one async.series([ function(callback) { setTimeout(function() { console.log(“Task 1”); callback(null, 1); }, 300); }, function(callback) { setTimeout(function() { console.log(“Task 2”); callback(null, 2); }, 200); }, function(callback) { setTimeout(function() { console.log(“Task 3“); callback(null, 3); }, 100); } ], function(error, results) { console.log(results); }); وبذلك تستطيع التخلص من جحيم الكولباك بدون promises التي يلقى الكثير من المبرمجين المبتدأئين صعوبة في التعامل مع هذا النوع من الدوال. ملاحظات حول المثال السابق: الأخذ بالاعتبار جميع الشروط من ان هذا المستخدم user لدية احصائية مسجله و بوستات, ان لم يكن تستطيع التخلص من الشرط if(posts.length) لا يهم , بالتالي سيتم تمرير اوبجكت بدون قيم. بعض المبرمجين قد لا يتفق مع العملية, احترم ذلك ولكنها تفي بالغرض مع ES2015 و مايليه .. اسناد اوبجكت الى متغير يعطيه نفس قيمه الاوبجكت بشكل مباشر.
  5. لا يخفى على الجميع تفوق لغة الجافاسكربت في الاونه الاخيره " منذ مدة في الحقيقة " بعد ظهور كمية كبيرة وضخمه جدا من المكتبات و أُطُر العمل libraries/frameworks، هذا التضخم و النمو السريع في لغة الجافاسكربت سببت ارباك لكثير من المبرمجين الذين أصبحوا لا يعرفون من اين البداية لدراسة هذه الفريمووركس واين النهايه خصوصا مع وجود تشابه مابينها بسبب المنافسة، على سبيل المثال: AngularJS VueJS ReactJS حيث يعد الإثنين أطر عمل Frontend، ومع التطور ظهر أطر عمل مساعدة لاطار عمل اساسي (ان صح التعبير بالعربي) "بطتنا بطت بطن بطتكم" مثل فريمورك ExpressJS المساعد لل Runtime Environment NodeJS حيث يعمل الإثنين في السيرفر وليس جهة المستخدم، وامثلة كثيرة اخرى لا يسعنا التطرق لهم ( يخرب بيتهم كل يوم شي جديد يطلع ويصدم اكثر من الي قبله ). في هذا الدرس سنقوم بعرض احد المكتبات المبرمجة بلغة الجافاسكربت "PressureJS" لعمل / لتطبيق فكرة الـ3D Touch الموجودة في اجهزة ابل الجديدة، والـ3D Touch هو عبارة عن الضغط بقوة اكثر من المعتاده على بعض الخيارات لإظهار خيارات اكثر او عمل، تفعيل event (حدث) معين. اولا: قم بتحميل المكتبه من خلال الضغط هنا او بإستخدام npm or bower. npm npm install pressure --save bower bower install pressure --save ثانيا: طرق استخدام المكتبة Example 1: <script src="/js/pressure.js"></script> <script src="/js/jquery.pressure.js"></script> Pressure.set('#id-name', { change: function(force){ this.innerHTML = force; } }); Example 2: var Pressure = require('pressure'); Pressure.set('#id-name', { change: function(force){ this.innerHTML = force; } }); كما هو موضح، التجربة ستكون على عنصر في الصفحة بدلالة الـId وهو id-name، تحتوي الدالة اعلاه على حدث event بإسم change تحمل متغير يسمى force، هذا الحدث change يستخدم لمعرفة التغير في قوة الضغط force على العنصر id-name ويكون المتغير force رقم مابين الصفر الى 1 كحد اقصى. ملاحظة: في المثال الذي سأطرحة حاليا، كلمة this ستنعني العنصر نفسه مثلا this.style.width Pressure.set('#element', { start: function(event){ }, end: function(){ // this is called on force end }, startDeepPress: function(event){ // this is called on "force click" / "deep press", aka once the force is greater than 0.5 }, endDeepPress: function(){ // this is called when the "force click" / "deep press" end }, change: function(force, event){ // this is called every time there is a change in pressure // force will always be a value from 0 to 1 on mobile and desktop }, unsupported: function(){ // NOTE: this is only called if the polyfill option is disabled! // this is called once there is a touch on the element and the device or browser does not support Force or 3D touch } }); او عند استخدامك الـjQuery // Select Element By Id $('#element').pressure({ // events goes here }); // Select Element By Class $('.element').pressure({ // events goes here }); لنستعرض الدالة اعلاة كل جزء على حدى: - يوجد 6 انواع من الأحداث events التي تعتمد عليها / تقدمها هذه المكتبه في كلاس Pressure وهم: حدث start: لمعرفة بدأ الضغط حدث end: لمعرفة انتهاء الضغط. حدث startDeepPress: اذا كانت قوة الضغط force اعلى من 0.5 وهي اعلى من نص القيمة العليا 1. حدث endDeepPress: عند الانتهاء من الضغط على العنصر وقيمة الضغط دخلت ضمن قيمة startDeepPress اي اعلى من 0.5. حدث change: وياخذ متغير force كما شرحناه بالاعلى، هذا الحدث سيستدعى دائما عند تغير قيمه الضغط مابين 0 و 1. حدث unsupported: اذا كان المتصفح او الجهاز لايدعم ال3D Touch. - الخيارات options، بالاضافة الى الاحداث والمتغيرات السابقة هناك ايضا متغير خارج دالة الset وهو عبارة عن اوبجكت اختياري يحتوي على عدة عناصر منها polyfill اما ان يكون true او false "يستخدم اذا كان المتصفح او الجهاز لايدعم ال3D Touch ،يكون true بشكل افتراضي. Pressure.set('#example', { change: function(force, event){ this.innerHTML = force; }, unsupported: function(){ alert("Oh no, this device does not support pressure."); } }, {polyfill: false}); - المتغير الثاني في الoptions هو polyfillSpeedUp عدد ms ويستخدم لتحديد سرعة الوصول من 0 الى 1، كل 1000 تساوي ثانية واحدة. Pressure.set('#example', { change: function(force, event){ this.innerHTML = force; } }, {polyfillSpeedUp: 5000}); // takes 5 seconds to go from a force value of 0 to 1 // only on devices that do not support pressure - المتغير polyfillSpeedDown عكس SpeedUp تماما, الوقت بالميلي سكند ms لرجوع قيمة قوة الضغط من 1 إلى 0. Pressure.set('#polyfill-speed-down', { change: function(force, event){ this.innerHTML = force; } }, {polyfillSpeedDown: 2000}); // takes 2 seconds to go from a force value of 1 to 0 // only on devices that do not support pressure - المتغير only اما ان يكون mouse, touch or pointer وهذا يعني عند الضغط على العنصر المراد تطبيق الـ3D Touch عليه اما ان يكون من خلال الفأرة فقط، الضغط بالاصبع فقط او بالبوينتر فقط. // فقط بالفأرة Pressure.set('#example',{ change: function(force, event){ console.log(force); }, }, {only: 'mouse'}); // فقط عند الضغط بالاصبع, اجهزة ابل Pressure.set('#example',{ change: function(force, event){ console.log(force); }, }, {only: 'touch'}); // فقط بالبوينتر ديفايسس مثل اجهزة تابلت الواكوم Pressure.set('#example',{ change: function(force, event){ console.log(force); }, }, {only: 'pointer'}); - متغير preventSelect يعرفه الكثير بـpreventDefault. اما ان يكون true او false , قيمة المتغير تكون true افتراضيا وتستخدم لمنع العمل الافتراضي للعناصر. Pressure.set('#example',{ change: function(force, event){ console.log(force); }, }, {preventSelect: false}); الجزء الأخير من PressureJS هو دالة لعمل Configuration عام تتشارك فيه جميع العناصر يحتوي على مجموعة من الخيارات التي تم شرحها في قسم Options مثلا. // These are the default configs set by Pressure Pressure.config({ polyfill: true, polyfillSpeedUp: 1000, polyfillSpeedDown: 0, preventSelect: true, only: null }); ولكن, تستطيع عمل override لهذه الاعدادات من خلال عمل دالة Pressure.set({...}); I وإسنادها إلى مجموعة عناصر او عنصر واحد بإستخدام الـId او الـClass. انتهى .. من لديه اسئلة يتفضل هنا او على حسابي في تويتر @AbdullaScript
  6. لا يخفى على الجميع تفوق لغة الجافاسكربت في الاونه الاخيره " منذ مدة في الحقيقة " بعد ظهور كمية كبيرة وضخمه جدا من المكتبات و أُطُر العمل libraries/frameworks، هذا التضخم و النمو السريع في لغة الجافاسكربت سببت ارباك لكثير من المبرمجين الذين أصبحوا لا يعرفون من اين البداية لدراسة هذه الفريمووركس واين النهايه خصوصا مع وجود تشابه مابينها بسبب المنافسة، على سبيل المثال: AngularJS VueJS ReactJS حيث يعد الإثنين أطر عمل Frontend، ومع التطور ظهر أطر عمل مساعدة لاطار عمل اساسي (ان صح التعبير بالعربي) "بطتنا بطت بطن بطتكم" مثل فريمورك ExpressJS المساعد لل Runtime Environment NodeJS حيث يعمل الإثنين في السيرفر وليس جهة المستخدم، وامثلة كثيرة اخرى لا يسعنا التطرق لهم ( يخرب بيتهم كل يوم شي جديد يطلع ويصدم اكثر من الي قبله ). في هذا الدرس سنقوم بعرض احد المكتبات المبرمجة بلغة الجافاسكربت "PressureJS" لعمل / لتطبيق فكرة الـ3D Touch الموجودة في اجهزة ابل الجديدة، والـ3D Touch هو عبارة عن الضغط بقوة اكثر من المعتاده على بعض الخيارات لإظهار خيارات اكثر او عمل، تفعيل event (حدث) معين. اولا: قم بتحميل المكتبه من خلال الضغط هنا او بإستخدام npm or bower. ثانيا: طرق استخدام المكتبة Example 1: <script src="/js/pressure.js"></script> <script src="/js/jquery.pressure.js"></script> Pressure.set('#id-name', { change: function(force){ this.innerHTML = force; } }); Example 2: var Pressure = require('pressure'); Pressure.set('#id-name', { change: function(force){ this.innerHTML = force; } }); كما هو موضح، التجربة ستكون على عنصر في الصفحة بدلالة الـId وهو id-name، تحتوي الدالة اعلاه على حدث event بإسم change تحمل متغير يسمى force، هذا الحدث change يستخدم لمعرفة التغير في قوة الضغط force على العنصر id-name ويكون المتغير force رقم مابين الصفر الى 1 كحد اقصى. ملاحظة: في المثال الذي سأطرحة حاليا، كلمة this ستنعني العنصر نفسه مثلا this.style.width Pressure.set('#element', { start: function(event){ }, end: function(){ // this is called on force end }, startDeepPress: function(event){ // this is called on "force click" / "deep press", aka once the force is greater than 0.5 }, endDeepPress: function(){ // this is called when the "force click" / "deep press" end }, change: function(force, event){ // this is called every time there is a change in pressure // force will always be a value from 0 to 1 on mobile and desktop }, unsupported: function(){ // NOTE: this is only called if the polyfill option is disabled! // this is called once there is a touch on the element and the device or browser does not support Force or 3D touch } }); او عند استخدامك الـjQuery // Select Element By Id $('#element').pressure({ // events goes here }); // Select Element By Class $('.element').pressure({ // events goes here }); لنستعرض الدالة اعلاة كل جزء على حدى: - يوجد 6 انواع من الأحداث events التي تعتمد عليها / تقدمها هذه المكتبه في كلاس Pressure وهم: حدث start: لمعرفة بدأ الضغط حدث end: لمعرفة انتهاء الضغط. حدث startDeepPress: اذا كانت قوة الضغط force اعلى من 0.5 وهي اعلى من نص القيمة العليا 1. حدث endDeepPress: عند الانتهاء من الضغط على العنصر وقيمة الضغط دخلت ضمن قيمة startDeepPress اي اعلى من 0.5. حدث change: وياخذ متغير force كما شرحناه بالاعلى، هذا الحدث سيستدعى دائما عند تغير قيمه الضغط مابين 0 و 1. حدث unsupported: اذا كان المتصفح او الجهاز لايدعم ال3D Touch. - الخيارات options، بالاضافة الى الاحداث والمتغيرات السابقة هناك ايضا متغير خارج دالة الset وهو عبارة عن اوبجكت اختياري يحتوي على عدة عناصر منها polyfill اما ان يكون true او false "يستخدم اذا كان المتصفح او الجهاز لايدعم ال3D Touch ،يكون true بشكل افتراضي. Pressure.set('#example', { change: function(force, event){ this.innerHTML = force; }, unsupported: function(){ alert("Oh no, this device does not support pressure."); } }, {polyfill: false}); - المتغير الثاني في الoptions هو polyfillSpeedUp عدد ms ويستخدم لتحديد سرعة الوصول من 0 الى 1، كل 1000 تساوي ثانية واحدة. Pressure.set('#example', { change: function(force, event){ this.innerHTML = force; } }, {polyfillSpeedUp: 5000}); // takes 5 seconds to go from a force value of 0 to 1 // only on devices that do not support pressure - المتغير polyfillSpeedDown عكس SpeedUp تماما, الوقت بالميلي سكند ms لرجوع قيمة قوة الضغط من 1 إلى 0. Pressure.set('#polyfill-speed-down', { change: function(force, event){ this.innerHTML = force; } }, {polyfillSpeedDown: 2000}); // takes 2 seconds to go from a force value of 1 to 0 // only on devices that do not support pressure - المتغير only اما ان يكون mouse, touch or pointer وهذا يعني عند الضغط على العنصر المراد تطبيق الـ3D Touch عليه اما ان يكون من خلال الفأرة فقط، الضغط بالاصبع فقط او بالبوينتر فقط. // فقط بالفأرة Pressure.set('#example',{ change: function(force, event){ console.log(force); }, }, {only: 'mouse'}); // فقط عند الضغط بالاصبع, اجهزة ابل Pressure.set('#example',{ change: function(force, event){ console.log(force); }, }, {only: 'touch'}); // فقط بالبوينتر ديفايسس مثل اجهزة تابلت الواكوم Pressure.set('#example',{ change: function(force, event){ console.log(force); }, }, {only: 'pointer'}); - متغير preventSelect يعرفه الكثير بـpreventDefault. اما ان يكون true او false , قيمة المتغير تكون true افتراضيا وتستخدم لمنع العمل الافتراضي للعناصر. Pressure.set('#example',{ change: function(force, event){ console.log(force); }, }, {preventSelect: false}); الجزء الأخير من PressureJS هو دالة لعمل Configuration عام تتشارك فيه جميع العناصر يحتوي على مجموعة من الخيارات التي تم شرحها في قسم Options مثلا. // These are the default configs set by Pressure Pressure.config({ polyfill: true, polyfillSpeedUp: 1000, polyfillSpeedDown: 0, preventSelect: true, only: null }); ولكن, تستطيع عمل override لهذه الاعدادات من خلال عمل دالة Pressure.set({...}); I وإسنادها إلى مجموعة عناصر او عنصر واحد بإستخدام الـId او الـClass. انتهى .. من لديه اسئلة يتفضل هنا او على حسابي في تويتر @AbdullaScript
  7. ولا يهمك نرتب الموضوع اكثر ونضيفها كمثال رابع بالتفصيل 🌹
  8. ماهو الcallback hell، جحيم الكولباك؟ لا يوجد شيء مميز في الجافا سكربت يسمى بجحيم الكولباك ما هو الا تسميه لأمر ما قد يقع فيه المطور وهو طريقة كتابة كود حيث يحتوي على عدة دوال Asynchronous مترابطة مع بعضها، واحدة داخل الاخرى، مثال: getData1(function(x){ getData2(x, function(y){ getData3(y, function(z){ ... }); }); }); والسبب في ذلك يعود لأن ناتج "results" كل دالة يعتمد على ما سبقتها. ملاحظة: كلمة asynchronous تعني أمر يحدث لاحقا << لا تركز على هالشي مو موضوعنا. جحيم الكولباك أمر ليس بجيد للكود، خصوصا في الحالات المعقدة التي تكون فيها دوال كثيرة مربوطه مع بعضها البعض وهنا قد يؤخر وقت انتهاء من التنفيذ و كمطور انت لا تريد ان تقع في هذه المشكلة, لحل مشكلة جحيم الكولباك، يستخدم المطورون إحدى مكتبات البرومس promises "الوعد، الوعود"، فالوعد دالة إما أن يتم العمل به fulfilled مع اظهار نتيجة الوعد value او رده rejected مع إظهار السبب error، وقد يكون الكود يحتوي على أكثر من وعد، واحد تلو الآخر. شخصيا لا اعتمد على الوعود في حل مشكلة جحيم الكولباك، عند التعامل مع قواعد البيانات MySQL رغم أنها الطريقة المثلى ولكن تتطلب الكثير مع الكود "وجهة نظر" و قد تواجهك مشاكل في أداء performance التطبيق بعد ذلك, استخدم طريقة أكثر بدائية و فعالة "تفي بالغرض" ، مبدأ الفكرة و طريقة عملها تتشابه كثيرا مع مبدأ عمل promises ولكن بكود أقل، سأرفق الطريقة مع المثال بالأسفل. للتعرف أكثر على جحيم الكولباك، سأعرض لكم مثال على التعامل مع قاعدة البيانات MySQL بالـ NodeJS وعمل اكثر من query وناتج كل query يعتمد على كل سبقتها. var express = require("express"); var app = express(); var mysql = require('mysql'); var conn = mysql.createConnection({ host: 'localhost', user: 'me', password: 'secret', database: 'my_db' }); conn.connect(); // GET method route app.get('/user/posts', function (req, res) { /* http://www.example.com/user/posts?userId=26 */ var userId = req.query.userId; conn.query('SELECT username FROM users WHERE ID = ?', [userId], function (error, results, fields) { if (error) throw error; if (results.length) { var username = results[0].username; conn.query('SELECT * FROM posts WHERE post_by = ?', [username], function (error, posts, fields) { if (error) throw error; if (posts.length) { conn.query('SELECT * FROM users_addresses WHERE username = ?', [username], function (error, address, fields) { if (error) throw error; if (statistics.length) { res.json(200, { posts: posts, address: address }); } else { res.json(200, { error: 'no statistics found' }); } }); } else { res.json(200, { error: 'no posts found' }); } }); } else { res.json(200, { error: 'no user found by ID 26' }); } }); }); للتخلص على مثل هذه المشاكل في حالة التعامل مع MySQL / MariaDB يكون بإسناد results كل عملية query في دالة مثلا var express = require("express"); var app = express(); var mysql = require('mysql'); var conn = mysql.createConnection({ host: 'localhost', user: 'me', password: 'secret', database: 'my_db' }); conn.connect(); // GET method route app.get('/user/posts', function (req, res) { /* http://www.example.com/user/posts?userId=26 */ var userId = req.query.userId; conn.query('SELECT username FROM users WHERE ID = ?', [userId], function (error, results, fields) { if (error) throw error; if (results.length) { setUsername(results[0].username); } else { res.json(200, { error: 'no user found by ID 26' }); } }); function setUsername(user) { var username = user; conn.query('SELECT * FROM posts WHERE post_by = ?', [username], function (error, posts, fields) { if (error) throw error; if (posts.length) { setPosts(posts, username); } else { res.json(200, { error: 'no posts found' }); } }); } function setPosts(po, user) { var posts = po; // po is the result of the prev query and it is an object it could be length of 1 or more doesn't matter var username = user; conn.query('SELECT * FROM users_addresses WHERE username = ?', [username], function (error, address, fields) { if (error) throw error; if (statistics.length) { res.json(200, { username: username, posts: posts, address: address }); } else { res.json(200, { error: 'no statistics found' }); } }); } }); أو بطريقة اخرى وهي عمل execution لكل query بطريقة parallel او series بإستخدام مكتبة async /* Make Sure To Install async npm install async --save */ var async = require(“async”); // Parallel Method async.parallel([ wait5SecondsAndReturn1, wait5SecondsAndReturn2 ], function (err, results){ //after 5 seconds, results will be [1, 2] }); function wait5SecondsAndReturn1(callback) { setTimeout(function(){ callback(null, 1); }, 5000); } function wait5SecondsAndReturn2(callback) { setTimeout(function(){ callback(null, 2); }, 5000); } //Series Method async.series([ wait5SecondsAndReturn1, wait5SecondsAndReturn2 ], function (err, results){ //after 10 seconds, results will be [1, 2] }); function wait5SecondsAndReturn1(callback) { setTimeout(function(){ callback(null, 1); }, 5000); } function wait5SecondsAndReturn2(callback) { setTimeout(function(){ callback(null, 2); }, 5000); } // Another one async.series([ function(callback) { setTimeout(function() { console.log(“Task 1”); callback(null, 1); }, 300); }, function(callback) { setTimeout(function() { console.log(“Task 2”); callback(null, 2); }, 200); }, function(callback) { setTimeout(function() { console.log(“Task 3“); callback(null, 3); }, 100); } ], function(error, results) { console.log(results); }); وبذلك تستطيع التخلص من جحيم الكولباك بدون promises التي يلقى الكثير من المبرمجين المبتدأئين صعوبة في التعامل مع هذا النوع من الدوال. ملاحظات حول المثال السابق: الأخذ بالاعتبار جميع الشروط من ان هذا المستخدم user لدية احصائية مسجله و بوستات, ان لم يكن تستطيع التخلص من الشرط if(posts.length) لا يهم , بالتالي سيتم تمرير اوبجكت بدون قيم. بعض المبرمجين قد لا يتفق مع العملية, احترم ذلك ولكنها تفي بالغرض مع ES2015 و مايليه .. اسناد اوبجكت الى متغير يعطيه نفس قيمه الاوبجكت بشكل مباشر.
  9. اذا، ماهو الـ connection pool؟ لربما سمعت بعبارة " الاتصال بقاعدة البيانات أمر مكلف " حيث كل اتصال على حدى يمر عبر سلسلة من الأمور منها الـ auth ( الى اخره) و عملية فتح و غلق الاتصال بقاعدة البيانات يعتبر مكلف للوقت و ريسورسس السيرفر, وبالعودة لـdocumentation الـMySQL في قسم B.5.2.7 Too many connections سنلاحظ حديث مطوري هذا النوع من قواعد البيانات عن الرقم المثالي والإفتراضي لعدد الاتصالات المسموح به لتقدم خدمة سريعة وهو 151 اتصال ( سابقا كان 100 اتصال فعال ) في حالة عمل الخدمة مع ويب سيرفر الـapache لتقديم خدمة افضل بالتالي وجب التنبيه بأن هناك عدد ما لكمية الاتصال بقاعدة البيانات. ولحل هذه المشكلة وجد ما يسمى بالـconnection pool الذي يعمل كناقل/ وسيط سريع بين الطلب و قاعدة البيانات ليقلل التكلفة (من الوقت و الريسورسس ) ويرفع و يزيد من الأداء اذا، ماهو / هي الـ connection pool فعليا؟ إن صح الوصف فهي عبارة عن بركة ( كما في الصوره اعلاه) تحتوي على مجموعة من الاتصالات بقاعدة البيانات مفتوحة وجاهزة للاستخدام المتكرر ( ولنفترض 10) والفائدة من ذلك توفير الوقت الذي يكون مابين اغلاق الاتصال و فتحه مرة أخرى لعملية أخرى another query بذلك وعند نفاذ كمية الاتصالات من البركة (اي ان هناك عدد معين من المستخدمين في نفس الوقت يستخدمون جميع قنوات الاتصال في هذه البركة ) يتم اضافة اتصال آخر بشكل اوتوماتيكي ( ليصبح مثلا 14 اتصال داخل البركة ) وعند الانتهاء يرجع للعدد الطبيعي (10) ويتم غلق هذه الاتصالات حمل ملف كيفية عمل كونفيقريشن لقاعدة البيانات و الـconnection pool في ملف مستقل واستخدامه في تطبيقك الـNodeJS. ملاحظة: استخدام الـ connection pool آمن جدا و مع وجود كمية اتصالات مفتوحة وفي المرحلة الاخير اي بعيدا عن connection parsing and authentication لا يعني ذلك عدم الأمان.
  10. السلام عليكم ورحمة الله وبركاته.. في هذا الدرس الثالث من دروس النود جي اس سنقوم بتعلم كيفية ضبط ويب سيرفر NGINX للعمل مع تطبيقات / مايكرو سيرفس الNodeJS و تحويل requests الى بورتات تطبيقاتك بشكل ضمني ماهو الnginx؟ ببساطة هو بروكسي سيرفر عالي الاداء او ويب سيرفر، بديل الapache. في ماذا يستخدم؟ يستخدم في ريفيرس بروكسي للبروتوكولات HTTP, HTTPS, SMTP, IMAP, POP3 و كذلك لعمل توازن الضغط على السيرفر مثلا عندما يكون هناك عدد كبير جدا من الrequests تستطيع توزيع هذه الطلبات بالupstream بين عدة بورتات في نفس السيرفر او سيرفر خارجي مثلا اذا كان التطبيق او الموقع مبرمج بالNodeJS. بالعربي: لا تحاتي بتعرف كلشي بعد شوي. اولا: تثبيت الويب سيرفر. لتثبيت الويب سيرفر nginx اتبع التعليمات / الخطوات التالية: افتح التيرمنال وقم بإيقاف الapache اذا كان مثبت sudo /etc/init.d/apache stop حدث sudo apt-get update ثبت sudo apt-get install nginx تأكد ان الويب سيرفر يعمل sudo /etc/init.d/nginx status ملاحظة: مهم جدا ايقاف Apache قبل تثبيت Nginx ثانيا: تشغيل تطبيقك الNode على الدومين الرئيسي. لتشغيل التطبيق عليك تحويل جميع الطلبات requests التي تأتي من العميل الى الPort الذي يعمل عليه التطبيق (3000 مثلا) عبر الويب سيرفر NGINX. ولعمل ذلك، افتح التيرمنال و قم بمتابعة هذه الاوامر اذهب لمجلد الويب سيرفر cd /etc/ngin/sites-available اكتب ls وسترى ملف بإسم default. افتح الملف للتعديل بإستخدام vim او nano وفي هذا الدرس سنستخدم الاول sudo vim default امسح كلشي واكتب هذا الكود server { listen 80 default_server; listen [::]:80 default_server; server_name www.example.com example.com; access_log /path/to/log/static_domain_access.log; location / { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_set_header X-NginX-Proxy true; proxy_pass http://127.0.0.1:3000; proxy_redirect off; } } اخيرا قم بإعادة تشغيل الويب سيرفر افتح المتصفح و اكتب موقعك http://www example.com Bonus إذا كان لديك تطبيق \ موقع كبير جدا او API و تريد عمل load balancing لتوزيع عمليه الطلبات في الصورتين ادناه الطريقة لذلك, لاحظ ان التطبيق يعمل على اكثر من بورت وكذلك الـproxy pass هو عباره عن اسم الـupstream .cluster تطبيق الNode كونفيقريشن الـNginx
  11. السلام عليكم ورحمة الله وبركاته.. في هذا الدرس الثالث من دروس النود جي اس سنقوم بتعلم كيفية ضبط ويب سيرفر NGINX للعمل مع تطبيقات / مايكرو سيرفس الNodeJS و تحويل requests الى بورتات تطبيقاتك بشكل ضمني ماهو الnginx؟ ببساطة هو بروكسي سيرفر عالي الاداء او ويب سيرفر، بديل الapache. في ماذا يستخدم؟ يستخدم في ريفيرس بروكسي للبروتوكولات HTTP, HTTPS, SMTP, IMAP, POP3 و كذلك لعمل توازن الضغط على السيرفر مثلا عندما يكون هناك عدد كبير جدا من الrequests تستطيع توزيع هذه الطلبات بالupstream بين عدة بورتات في نفس السيرفر او سيرفر خارجي مثلا اذا كان التطبيق او الموقع مبرمج بالNodeJS. بالعربي: لا تحاتي بتعرف كلشي بعد شوي. اولا: تثبيت الويب سيرفر. لتثبيت الويب سيرفر nginx اتبع التعليمات / الخطوات التالية: افتح التيرمنال وقم بإيقاف الapache اذا كان مثبت sudo /etc/init.d/apache stop حدث sudo apt-get update ثبت sudo apt-get install nginx تأكد ان الويب سيرفر يعمل sudo /etc/init.d/nginx status ملاحظة: مهم جدا ايقاف Apache قبل تثبيت Nginx ثانيا: تشغيل تطبيقك الNode على الدومين الرئيسي. لتشغيل التطبيق عليك تحويل جميع الطلبات requests التي تأتي من العميل الى الPort الذي يعمل عليه التطبيق (3000 مثلا) عبر الويب سيرفر NGINX. ولعمل ذلك، افتح التيرمنال و قم بمتابعة هذه الاوامر اذهب لمجلد الويب سيرفر cd /etc/ngin/sites-available اكتب ls وسترى ملف بإسم default. افتح الملف للتعديل بإستخدام vim او nano وفي هذا الدرس سنستخدم الاول sudo vim default امسح كلشي واكتب هذا الكود server { listen 80 default_server; listen [::]:80 default_server; server_name www.example.com example.com; access_log /path/to/log/static_domain_access.log; location / { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_set_header X-NginX-Proxy true; proxy_pass http://127.0.0.1:3000; proxy_redirect off; } } اخيرا قم بإعادة تشغيل الويب سيرفر افتح المتصفح و اكتب موقعك http://www example.com Bonus إذا كان لديك تطبيق \ موقع كبير جدا او API و تريد عمل load balancing لتوزيع عمليه الطلبات في الصورتين ادناه الطريقة لذلك, لاحظ ان التطبيق يعمل على اكثر من بورت وكذلك الـproxy pass هو عباره عن اسم الـupstream .cluster تطبيق الNode كونفيقريشن الـNginx
  12. السلام عليكم و رحمة الله و بركاته .. عودة الى الجزء الاول، وضحت كيفية ارسال بيانات من تطبيق الNode الى Pug وكيفية استقبال البيانات في الطرف الاخر و عرضها. في هذا الدرس سنتعامل مع قاعدة البيانات ونرسل البيانات بطريقتين مختلفتين: سيتم ارسال جميع البيانات من احد الجداول الى الواجهة و سأعرض لكم طريقة التعامل مع هذا النوع من البيانات المرسله سننشأه items عباره عن json objects وبعدها نرسلهم. الطريقة الأولى: و بالحديث عن قواعد البيانات، سنستخدم قاعدة بيانات MySQL هذه المره ( الرجاء الرجوع للدرس الاول للحصول على كافة التجهيزات لهذا الدرس). / هنا , انشأ قاعدة بيانات بإسم company وجدول employees مثلا ومن ثم اضف هذه المدخلات. -- phpMyAdmin SQL Dump -- version 4.5.4.1deb2ubuntu2 -- http://www.phpmyadmin.net -- -- Host: localhost -- Generation Time: Jun 09, 2017 at 10:41 AM -- Server version: 5.7.18-0ubuntu0.16.04.1 -- PHP Version: 7.1.5-1+deb.sury.org~xenial+2 SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; SET time_zone = "+00:00"; /*!40101 SET @[email protected]@CHARACTER_SET_CLIENT */; /*!40101 SET @[email protected]@CHARACTER_SET_RESULTS */; /*!40101 SET @[email protected]@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8mb4 */; -- -- Database: `company` -- -- -------------------------------------------------------- -- -- Table structure for table `employees` -- CREATE TABLE `employees` ( `ID` int(11) NOT NULL, `name` text COLLATE utf8_unicode_ci NOT NULL, `position` text COLLATE utf8_unicode_ci NOT NULL, `office` text COLLATE utf8_unicode_ci NOT NULL, `age` int(11) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- -- Dumping data for table `employees` -- INSERT INTO `employees` (`ID`, `name`, `position`, `office`, `age`) VALUES (1, 'Airi Satou', 'Accountant', 'Tokyo', 23), (2, 'Airi Satou', 'Accountant', 'Tokyo', 23), (3, 'Donna Snider', 'Customer Support', 'New York', 27), (4, 'Brenden Wagner', 'Software Engineer', 'San Francisco', 28), (5, 'Caesar Vance', 'Pre-Sales Support', 'New York', 23), (6, 'Cedric Kelly', 'Senior Javascript Developer', 'Edinburgh', 22), (7, 'Dai Rios', 'Personnel Lead', 'Edinburgh', 35); -- -- Indexes for dumped tables -- -- -- Indexes for table `employees` -- ALTER TABLE `employees` ADD PRIMARY KEY (`ID`); -- -- AUTO_INCREMENT for dumped tables -- -- -- AUTO_INCREMENT for table `employees` -- ALTER TABLE `employees` MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=8; /*!40101 SET [email protected]_CHARACTER_SET_CLIENT */; /*!40101 SET [email protected]_CHARACTER_SET_RESULTS */; /*!40101 SET [email protected]_COLLATION_CONNECTION */; افتح ملف app.js و انشأ هذا الاوبجكت لعمل connection بين التطبيق وقاعدة البيانات // connection var connection = mysql.createConnection({ host : 'localhost', user : '...', password : '...', database : 'db_name' }); بعد ذلك، سنقوم بتعديل صفحة الـdetails في ملف app.js - المسار الذي يعرض صفحة الـdetails - لعمل Selection query app.get('/details', function(req, res) { connection.query('SELECT * From `employees`', function (error, results, fields) { if (error) throw error; console.log('The solution is: ', results); res.render('details', { title: 'Details - Pug ExpressJS NodeJS Tutorial', employees: results }); }); }); الـsyntax بسيط جدا، اولا connection وهو متغير التوصيل مم ثم query ثابته بعد ذلك الكويري &nbsp;ومن ثم الارقيومنتس (لمعلومات اكثر تجدونها هنا https://github.com/mysqljs/mysql/blob/master/Readme.md ) مخرجات اي query دائما ما يكون json object بالتالي متغير results هو عباره عن list of objects [ { "prop1":"value1", "prop2":"value2" }, { "prop1":"value3", "prop2":"value4" } ] نرجع إلى ملف details.pug ونقوم بتعديل block الجدول إلى التالي table#example.display.nowrap.dataTable.dtr-inline.collapsed(cellspacing='0') thead tr(role='row') th.sorting(tabindex='0', aria-controls='example', rowspan='1', colspan='1', aria-label='Name: activate to sort column ascending', style='width: 142px;') Name th.sorting(tabindex='0', aria-controls='example', rowspan='1', colspan='1', aria-label='Position: activate to sort column ascending', style='width: 192px;') Position th.sorting(tabindex='0', aria-controls='example', rowspan='1', colspan='1', aria-label='Office: activate to sort column ascending', style='width: 89px;') Office th.dt-body-right.sorting_asc(tabindex='0', aria-controls='example', rowspan='1', colspan='1', aria-label='Age: activate to sort column descending', style='width: 37px;', aria-sort='ascending') Age tfoot tr th(rowspan='1', colspan='1') Name th(rowspan='1', colspan='1') Position th(rowspan='1', colspan='1') Office th.dt-body-right(rowspan='1', colspan='1') Age tbody for employee in employees tr.odd(role='row') td #{employee.name} td #{employee.position} td #{employee.office} td.dt-body-right.sorting_1 #{employee.age} نلاحظ وجود الـfor loop و طريقة استقبال وعرض المعلومات بسطر واحد فقط و ترجمته حرفيا ان لكل موظف في اوبجكت الموظفين ( الذي ارسلناه من الـNode ) قم بعمل صف في الجدول واضف مابعده for employee in employees tr.odd(role='row') td #{employee.name} td #{employee.position} td #{employee.office} td.dt-body-right.sorting_1 #{employee.age} بهذا انتهينا من الجزء الأول للدرس وننتقل للجزء الثاني: هذا الجزء من الدرس يتعلق بكيفية عمل items بشكل تخصيصي .. ان ماذا لو انك لا تريد ان تضم عمر الموظف في جدول العرض؟ سنقوم بالتالي في ملف app.js و details.pug - وكلشئ آخر يبقى كما هو: //app.js app.get('/details', function (req, res) { connection.query('SELECT * From `employees`', function (error, results, fields) { if (error) { throw error; } else { console.log('The solution is: ', results); var employees_json = []; for (var index = 0; index < results.length; index++) { var item = { name: results[index].name, position: results[index].position, office: results[index].office } employees_json.push(item); } res.render('details', { title: 'Details - Pug ExpressJS NodeJS Tutorial', employees: employees_json }); } }); }); //details.pug table#example.display.nowrap.dataTable.dtr-inline.collapsed(cellspacing='0') thead tr(role='row') th.sorting(tabindex='0', aria-controls='example', rowspan='1', colspan='1', aria-label='Name: activate to sort column ascending', style='width: 142px;') Name th.sorting(tabindex='0', aria-controls='example', rowspan='1', colspan='1', aria-label='Position: activate to sort column ascending', style='width: 192px;') Position th.sorting(tabindex='0', aria-controls='example', rowspan='1', colspan='1', aria-label='Office: activate to sort column ascending', style='width: 89px;') Office //th.dt-body-right.sorting_asc(tabindex='0', aria-controls='example', rowspan='1', colspan='1', aria-label='Age: activate to sort column descending', style='width: 37px;', aria-sort='ascending') Age tfoot tr th(rowspan='1', colspan='1') Name th(rowspan='1', colspan='1') Position th(rowspan='1', colspan='1') Office th.dt-body-right(rowspan='1', colspan='1') Age tbody for employee in employees tr.odd(role='row') td #{employee.name} td #{employee.position} td #{employee.office} //td.dt-body-right.sorting_1 #{employee.age} إضغط هنا لتحميل ملفات الدرس الثاني anwbh-app-lesson2.zip
  13. السلام عليكم و رحمة الله و بركاته .. عودة الى الجزء الاول، وضحت كيفية ارسال بيانات من تطبيق الNode الى Pug وكيفية استقبال البيانات في الطرف الاخر و عرضها. في هذا الدرس سنتعامل مع قاعدة البيانات ونرسل البيانات بطريقتين مختلفتين: سيتم ارسال جميع البيانات من احد الجداول الى الواجهة و سأعرض لكم طريقة التعامل مع هذا النوع من البيانات المرسله سننشأه items عباره عن json objects وبعدها نرسلهم. الطريقة الأولى: و بالحديث عن قواعد البيانات، سنستخدم قاعدة بيانات MySQL هذه المره ( الرجاء الرجوع للدرس الاول للحصول على كافة التجهيزات لهذا الدرس). / هنا , انشأ قاعدة بيانات بإسم company وجدول employees مثلا ومن ثم اضف هذه المدخلات. -- phpMyAdmin SQL Dump -- version 4.5.4.1deb2ubuntu2 -- http://www.phpmyadmin.net -- -- Host: localhost -- Generation Time: Jun 09, 2017 at 10:41 AM -- Server version: 5.7.18-0ubuntu0.16.04.1 -- PHP Version: 7.1.5-1+deb.sury.org~xenial+2 SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; SET time_zone = "+00:00"; /*!40101 SET @[email protected]@CHARACTER_SET_CLIENT */; /*!40101 SET @[email protected]@CHARACTER_SET_RESULTS */; /*!40101 SET @[email protected]@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8mb4 */; -- -- Database: `company` -- -- -------------------------------------------------------- -- -- Table structure for table `employees` -- CREATE TABLE `employees` ( `ID` int(11) NOT NULL, `name` text COLLATE utf8_unicode_ci NOT NULL, `position` text COLLATE utf8_unicode_ci NOT NULL, `office` text COLLATE utf8_unicode_ci NOT NULL, `age` int(11) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- -- Dumping data for table `employees` -- INSERT INTO `employees` (`ID`, `name`, `position`, `office`, `age`) VALUES (1, 'Airi Satou', 'Accountant', 'Tokyo', 23), (2, 'Airi Satou', 'Accountant', 'Tokyo', 23), (3, 'Donna Snider', 'Customer Support', 'New York', 27), (4, 'Brenden Wagner', 'Software Engineer', 'San Francisco', 28), (5, 'Caesar Vance', 'Pre-Sales Support', 'New York', 23), (6, 'Cedric Kelly', 'Senior Javascript Developer', 'Edinburgh', 22), (7, 'Dai Rios', 'Personnel Lead', 'Edinburgh', 35); -- -- Indexes for dumped tables -- -- -- Indexes for table `employees` -- ALTER TABLE `employees` ADD PRIMARY KEY (`ID`); -- -- AUTO_INCREMENT for dumped tables -- -- -- AUTO_INCREMENT for table `employees` -- ALTER TABLE `employees` MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=8; /*!40101 SET [email protected]_CHARACTER_SET_CLIENT */; /*!40101 SET [email protected]_CHARACTER_SET_RESULTS */; /*!40101 SET [email protected]_COLLATION_CONNECTION */; افتح ملف app.js و انشأ هذا الاوبجكت لعمل connection بين التطبيق وقاعدة البيانات // connection var connection = mysql.createConnection({ host : 'localhost', user : '...', password : '...', database : 'db_name' }); بعد ذلك، سنقوم بتعديل صفحة الـdetails في ملف app.js - المسار الذي يعرض صفحة الـdetails - لعمل Selection query app.get('/details', function(req, res) { connection.query('SELECT * From `employees`', function (error, results, fields) { if (error) throw error; console.log('The solution is: ', results); res.render('details', { title: 'Details - Pug ExpressJS NodeJS Tutorial', employees: results }); }); }); الـsyntax بسيط جدا، اولا connection وهو متغير التوصيل مم ثم query ثابته بعد ذلك الكويري &nbsp;ومن ثم الارقيومنتس (لمعلومات اكثر تجدونها هنا https://github.com/mysqljs/mysql/blob/master/Readme.md ) مخرجات اي query دائما ما يكون json object بالتالي متغير results هو عباره عن list of objects [ { "prop1":"value1", "prop2":"value2" }, { "prop1":"value3", "prop2":"value4" } ] نرجع إلى ملف details.pug ونقوم بتعديل block الجدول إلى التالي table#example.display.nowrap.dataTable.dtr-inline.collapsed(cellspacing='0') thead tr(role='row') th.sorting(tabindex='0', aria-controls='example', rowspan='1', colspan='1', aria-label='Name: activate to sort column ascending', style='width: 142px;') Name th.sorting(tabindex='0', aria-controls='example', rowspan='1', colspan='1', aria-label='Position: activate to sort column ascending', style='width: 192px;') Position th.sorting(tabindex='0', aria-controls='example', rowspan='1', colspan='1', aria-label='Office: activate to sort column ascending', style='width: 89px;') Office th.dt-body-right.sorting_asc(tabindex='0', aria-controls='example', rowspan='1', colspan='1', aria-label='Age: activate to sort column descending', style='width: 37px;', aria-sort='ascending') Age tfoot tr th(rowspan='1', colspan='1') Name th(rowspan='1', colspan='1') Position th(rowspan='1', colspan='1') Office th.dt-body-right(rowspan='1', colspan='1') Age tbody for employee in employees tr.odd(role='row') td #{employee.name} td #{employee.position} td #{employee.office} td.dt-body-right.sorting_1 #{employee.age} نلاحظ وجود الـfor loop و طريقة استقبال وعرض المعلومات بسطر واحد فقط و ترجمته حرفيا ان لكل موظف في اوبجكت الموظفين ( الذي ارسلناه من الـNode ) قم بعمل صف في الجدول واضف مابعده for employee in employees tr.odd(role='row') td #{employee.name} td #{employee.position} td #{employee.office} td.dt-body-right.sorting_1 #{employee.age} بهذا انتهينا من الجزء الأول للدرس وننتقل للجزء الثاني: هذا الجزء من الدرس يتعلق بكيفية عمل items بشكل تخصيصي .. ان ماذا لو انك لا تريد ان تضم عمر الموظف في جدول العرض؟ سنقوم بالتالي في ملف app.js و details.pug - وكلشئ آخر يبقى كما هو: //app.js app.get('/details', function (req, res) { connection.query('SELECT * From `employees`', function (error, results, fields) { if (error) { throw error; } else { console.log('The solution is: ', results); var employees_json = []; for (var index = 0; index < results.length; index++) { var item = { name: results[index].name, position: results[index].position, office: results[index].office } employees_json.push(item); } res.render('details', { title: 'Details - Pug ExpressJS NodeJS Tutorial', employees: employees_json }); } }); }); //details.pug table#example.display.nowrap.dataTable.dtr-inline.collapsed(cellspacing='0') thead tr(role='row') th.sorting(tabindex='0', aria-controls='example', rowspan='1', colspan='1', aria-label='Name: activate to sort column ascending', style='width: 142px;') Name th.sorting(tabindex='0', aria-controls='example', rowspan='1', colspan='1', aria-label='Position: activate to sort column ascending', style='width: 192px;') Position th.sorting(tabindex='0', aria-controls='example', rowspan='1', colspan='1', aria-label='Office: activate to sort column ascending', style='width: 89px;') Office //th.dt-body-right.sorting_asc(tabindex='0', aria-controls='example', rowspan='1', colspan='1', aria-label='Age: activate to sort column descending', style='width: 37px;', aria-sort='ascending') Age tfoot tr th(rowspan='1', colspan='1') Name th(rowspan='1', colspan='1') Position th(rowspan='1', colspan='1') Office th.dt-body-right(rowspan='1', colspan='1') Age tbody for employee in employees tr.odd(role='row') td #{employee.name} td #{employee.position} td #{employee.office} //td.dt-body-right.sorting_1 #{employee.age} إضغط هنا لتحميل ملفات الدرس الثاني anwbh-app-lesson2.zip
  14. السلام عليكم و رحمة الله و بركاته.. في هذا الموضوع سنتحدث عن عن علاقة إطار العمل ExpressJS مع الباترن NodeJS و كيفية عمل Pug as template engine اولا: حتى لا تكون هناك امور مبهمه و ابعاد الغيوم التي تدور حول اذهان الكثير من يريد العمل او التوجه لهذا الباترن يجب عرض بعض من الحقائق: الـNodeJS ليست لغة جديدة .. انما بيئة مبرمجة بالـJS. هذه البيئه تعمل في جهة السيرفر. ان كنت مبرمج AngularJS or ReactJS ليس هناك سطر محدد تربط فيه بين تطبيقك الNode مع الفريموورك الذي تعمل به. طريقة الربط تكون من خلال HTTP requests. تستطيع التعامل مع قواعد البيانات المختلفه مباشره بالـNode مثل MySQL, MariaDB و اشهرهم في هذا المجال هي MongoDB. نعود مرة اخرى للـExpressJS الذي هو يعتبر فريم وورك ابعد مما ان يقال عنه "مساعد" في تطبيقات الـNode. بإستخدام هذا الفريم وورك الذي يعمل كذلك في السيرفر، يتم تسهيل العمل و كمية الكود في تطبيق الـNode بشكل كبير جدا. مثال على تطبيق Node بإستخدام فريم وورك ExpressJS وسيتم شرح اجزائه جميعها var express = require('express'); var app = express(); app.get('/', function (req, res) { res.send('Hello World!') }); app.listen(3000, function () { console.log('Example app listening on port 3000!') }); اول سطرين استدعاء موديول الاكسبرس و من ثم دالة الاكسبرس في المتغير app الذي استخدمناه في صنع التطبيق. الشطر الثاني get هو عبارة عن نوع الـhttp request قد يكون GET, POST, DELETE, UPDATE etc …. بعد ذلك يأتي المسار - لتفاصيل اكثر حول هذا الجزء الرجاء زيارة الدرس السابق. نننقل الآن الى امر اخر في الـExpressJS وهو الـtemplating ( عمل الواجهات UI و استقبال البيانات المرسلة من الـNode ) لعمل الواجهات الديناميكيه يفضل استخدام محركات التمبليت التي تتوافق مع الاكسبرس وهي PUG - know as Jade Mustache EJS و في هذا الدرس سنستخدم المحرك الاول لعمل صفحة ويب اعتيادية و اخرى ديناميكية, لصنع واجهة بمحرك التمبليت pug يجب عليك ان تتقن او ان تاخذ فكره عن هذا المحرك لتعرف كيفية كتابه الكود ومثال على ذلك doctype html html(lang='en') head title Jade script(type='text/javascript'). foo = true; bar = function () {}; if (foo) { bar(1 + 5) } body h1 Jade - node template engine #container.col p You are amazing p | Jade is a terse and simple | templating language with a | strong focus on performance | and powerful features. يعادل في الHTML <!DOCTYPE html> <html lang="en"> <head> <title>Jade</title> <script type="text/javascript"> foo = true; bar = function () {}; if (foo) { bar(1 + 5) } </script> </head> <body> <h1>Jade - node template engine</h1> <div id="container" class="col"> <p>You are amazing</p> <p> Jade is a terse and simple templating language with a strong focus on performance and powerful features. </p> </div> </body> </html> لمعلومات اكثر حول اللغة اطلع على موقعهم الرسمي وبذلك سنقوم بإنشاء مجلدات ومستندات بهذه الطريقة حمل الـbootstrap وانقل كل من bootstrap.min.css إلى مجلد css , انقل bootstrap.min.js إلى مجلد js بعد ذلك إبدأ بتثبيت الـpackages التي سنعمل بها من خلال فتح الـterminal في مجلد myapp من خلال الاوامر التالية $ npm install express --save $ npm install cookie-parser --save $ npm install body-parser --save $ npm install mysql --save افتح ملف التطبيق app.js وقم بإستدعاء هذه البكجات var express = require('express'); var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); var mysql = require('mysql'); الآن نبدأ بإعداد config فريم وورك الـExpressJS مع cookie-parser body-parser ( سنستخدمهم لاحقا ) var router = express.Router(); var app = express(); app.use(bodyParser.json({limit: "50mb"})); app.use(cookieParser()); var urlencodedParser = bodyParser.urlencoded({ extended: true, parameterLimit:50000 }); app.use("/", router); نضيف امكانية استخدام ملفات الـcss, js , images etc من خلال استخدام الـmiddleware ـexpress static بإعطاء مسار وهمي لهذه الملفات app.use('/css', express.static(__dirname + '/public/css')); app.use('/js', express.static(__dirname + '/public/js')); نرى وجود ملفات الـcss and js في مجلد public/css ولكن المسار الذي سنكتبه في الكود هو فقط css/ وليس بالضرورة ان يكون اسم المسار الوهمي ذو علاقه بالمسار الاساسي .. تستطيع ان تسميه مثلا sambosa/ و في الحقيقة هو public/css/ تحديد نوع الـtemplate engine الذي سنستخدمه في هذا التطبيق مع تحديد مسار مجلد الـviews الذي يحتوي على صفحات الموقع او التطبيق app.set('views', __dirname + '/views'); app.set('view engine', 'pug'); بعد إنشاء ملفات الـpug في إحدى الخطوات السابقه , انسخ والصق كود الصفحتين index.pug and layout.pug details.pug كلن على حدى //layout.pug doctype html html head title= title script(src='https://code.jquery.com/jquery-3.2.1.min.js') link(rel='stylesheet', href='/css/bootstrap.min.css') script(src='/js/bootstrap.min.js') body nav.navbar.navbar-default .container .navbar-header button.navbar-toggle.collapsed(type='button', data-toggle='collapse', data-target='#navbar', aria-expanded='false', aria-controls='navbar') span.sr-only Toggle navigation span.icon-bar span.icon-bar span.icon-bar a.navbar-brand(href='/') NodeJS #navbar.navbar-collapse.collapse ul.nav.navbar-nav li.active a(href='/') Home li a(href='/details') details li.dropdown a.dropdown-toggle(href='#', data-toggle='dropdown', role='button', aria-haspopup='true', aria-expanded='false') | Dropdown span.caret ul.dropdown-menu li a(href='#') Action li a(href='#') Another action li a(href='#') Something else here li.divider(role='separator') li.dropdown-header Nav header li a(href='#') Separated link li a(href='#') One more separated link block content //index.pug extends layout block content .container h1 #{msg} p #{username} , welcome to the first templating tutorial //details.pug extends layout block content .container .table-responsive h2 user table p This is all users we have so far table.table.table-bordered#example thead tr th Firstname th Lastname th Email tbody tr td John td Doe td [email protected] tr td Mary td Moe td [email protected] tr td July td Dooley td [email protected] link(rel='stylesheet', type='text/css', href='https://cdn.datatables.net/v/dt/dt-1.10.15/datatables.min.css') script(type='text/javascript', src='https://cdn.datatables.net/v/dt/dt-1.10.15/datatables.min.js') script(). $(document).ready(function() { $('#example').DataTable(); }); ملاحظة: نستخدم extends layout لكي لا نكرر الـhead في الصفحات الاخرى , مثل include في الـPHP ( في هذا الدرس ) - الاستخدام الحقيقي للـlayout هو تصميم شكل معين لصفحات معينه .. كـtemplate خاص مثلا - معلومات اكثر من خلال الرابط هنا الأن تم تجهيز المستوى الأول من التطبيق وهو الـconfigurations , ننتقل إلى النص الآخر وهو الـroutes . اولا route الصفحة الرئيسية سيكون كالتالي app.get('/', function(req, res) { res.render('index', { title: 'Pug ExpressJS NodeJS Tutorial', username: 'abdulla', msg: 'Hey There!' }) }); نلاحظ ان مسار الـroute هو مجرد slash للامام .. بذلك يتم تحديد المسار الرئيسي للتطبيق, بعد ذلك res.render اي تعني response render حيث يقوم تطبيق الـNode بالأجابة من بعد عمل طلب request من طرف المستخدم , للتوضيح اكثر الـHTTP هو عباره عن بروتوكول طلب و اجابة وبذلك request and response - req, res فعند طلب العميل امر ما يجب عليه ان يحصل على شي ما في المقابل. و كما موضع في الدالة اعلاه ان الـresponse هو عباره عن عمل render لملف index.pug الموجود في مجلد views مع ارسال ثلاث متغيرات title, username and msg , و طريقة استقبال البيانات في الـpug هي كالتالي ( يوجد اكثر من طريقة ) h1 #{username} p Welcome to #{title} خطوة اخيره لتشغيل التطبيق يجب تحديد بورت معين ولنفترض 3000 app.listen(3000, function() { console.log('The app is running http://localhost:3000'); }); افتح الـterminal داخل مجلد التطبيق myapp واكتب الامر التالي لتشغيل التطبيق node app.js افتح المتطفح على المسار انتهى الدرس الاول .. تم من خلاله عمل configuration لتطبيق الـnode الذي سنستخدمه في الدروس القادمة تثبيت template engine و توصيله مع تطبيق الـNode ارسال بيانات من الـNode إلى pug الدرس القادم سيكون هناك سنعرض لكم كيفية التعامل مع قواعد البيانات وإرسال المعلومات الى pug للحصول على ملفات العمل تجدونهم في المرفقات, بعد تحميل الملف قم بفك الضغط انتقل إلى داخل المجلد واكتب الأمر التالي لتثبيت جميع الـpackages دفعة واحده $ npm install --save أضغط هنا لتحميل ملفات العمل anwbh-app.zip موقع مساعد لتحويل كودات الـHTML إلى Pug http://html2jade.org
  15. السلام عليكم و رحمة الله و بركاته.. في هذا الموضوع سنتحدث عن عن علاقة إطار العمل ExpressJS مع الباترن NodeJS و كيفية عمل Pug as template engine اولا: حتى لا تكون هناك امور مبهمه و ابعاد الغيوم التي تدور حول اذهان الكثير من يريد العمل او التوجه لهذا الباترن يجب عرض بعض من الحقائق: الـNodeJS ليست لغة جديدة .. انما بيئة مبرمجة بالـJS. هذه البيئه تعمل في جهة السيرفر. ان كنت مبرمج AngularJS or ReactJS ليس هناك سطر محدد تربط فيه بين تطبيقك الNode مع الفريموورك الذي تعمل به. طريقة الربط تكون من خلال HTTP requests. تستطيع التعامل مع قواعد البيانات المختلفه مباشره بالـNode مثل MySQL, MariaDB و اشهرهم في هذا المجال هي MongoDB. نعود مرة اخرى للـExpressJS الذي هو يعتبر فريم وورك ابعد مما ان يقال عنه "مساعد" في تطبيقات الـNode. بإستخدام هذا الفريم وورك الذي يعمل كذلك في السيرفر، يتم تسهيل العمل و كمية الكود في تطبيق الـNode بشكل كبير جدا. مثال على تطبيق Node بإستخدام فريم وورك ExpressJS وسيتم شرح اجزائه جميعها var express = require('express'); var app = express(); app.get('/', function (req, res) { res.send('Hello World!') }); app.listen(3000, function () { console.log('Example app listening on port 3000!') }); اول سطرين استدعاء موديول الاكسبرس و من ثم دالة الاكسبرس في المتغير app الذي استخدمناه في صنع التطبيق. الشطر الثاني get هو عبارة عن نوع الـhttp request قد يكون GET, POST, DELETE, UPDATE etc …. بعد ذلك يأتي المسار - لتفاصيل اكثر حول هذا الجزء الرجاء زيارة الدرس السابق. نننقل الآن الى امر اخر في الـExpressJS وهو الـtemplating ( عمل الواجهات UI و استقبال البيانات المرسلة من الـNode ) لعمل الواجهات الديناميكيه يفضل استخدام محركات التمبليت التي تتوافق مع الاكسبرس وهي PUG - know as Jade Mustache EJS و في هذا الدرس سنستخدم المحرك الاول لعمل صفحة ويب اعتيادية و اخرى ديناميكية, لصنع واجهة بمحرك التمبليت pug يجب عليك ان تتقن او ان تاخذ فكره عن هذا المحرك لتعرف كيفية كتابه الكود ومثال على ذلك doctype html html(lang='en') head title Jade script(type='text/javascript'). foo = true; bar = function () {}; if (foo) { bar(1 + 5) } body h1 Jade - node template engine #container.col p You are amazing p | Jade is a terse and simple | templating language with a | strong focus on performance | and powerful features. يعادل في الHTML <!DOCTYPE html> <html lang="en"> <head> <title>Jade</title> <script type="text/javascript"> foo = true; bar = function () {}; if (foo) { bar(1 + 5) } </script> </head> <body> <h1>Jade - node template engine</h1> <div id="container" class="col"> <p>You are amazing</p> <p> Jade is a terse and simple templating language with a strong focus on performance and powerful features. </p> </div> </body> </html> لمعلومات اكثر حول اللغة اطلع على موقعهم الرسمي وبذلك سنقوم بإنشاء مجلدات ومستندات بهذه الطريقة حمل الـbootstrap وانقل كل من bootstrap.min.css إلى مجلد css , انقل bootstrap.min.js إلى مجلد js بعد ذلك إبدأ بتثبيت الـpackages التي سنعمل بها من خلال فتح الـterminal في مجلد myapp من خلال الاوامر التالية $ npm install express --save $ npm install cookie-parser --save $ npm install body-parser --save $ npm install mysql --save افتح ملف التطبيق app.js وقم بإستدعاء هذه البكجات var express = require('express'); var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); var mysql = require('mysql'); الآن نبدأ بإعداد config فريم وورك الـExpressJS مع cookie-parser body-parser ( سنستخدمهم لاحقا ) var router = express.Router(); var app = express(); app.use(bodyParser.json({limit: "50mb"})); app.use(cookieParser()); var urlencodedParser = bodyParser.urlencoded({ extended: true, parameterLimit:50000 }); app.use("/", router); نضيف امكانية استخدام ملفات الـcss, js , images etc من خلال استخدام الـmiddleware ـexpress static بإعطاء مسار وهمي لهذه الملفات app.use('/css', express.static(__dirname + '/public/css')); app.use('/js', express.static(__dirname + '/public/js')); نرى وجود ملفات الـcss and js في مجلد public/css ولكن المسار الذي سنكتبه في الكود هو فقط css/ وليس بالضرورة ان يكون اسم المسار الوهمي ذو علاقه بالمسار الاساسي .. تستطيع ان تسميه مثلا sambosa/ و في الحقيقة هو public/css/ تحديد نوع الـtemplate engine الذي سنستخدمه في هذا التطبيق مع تحديد مسار مجلد الـviews الذي يحتوي على صفحات الموقع او التطبيق app.set('views', __dirname + '/views'); app.set('view engine', 'pug'); بعد إنشاء ملفات الـpug في إحدى الخطوات السابقه , انسخ والصق كود الصفحتين index.pug and layout.pug details.pug كلن على حدى //layout.pug doctype html html head title= title script(src='https://code.jquery.com/jquery-3.2.1.min.js') link(rel='stylesheet', href='/css/bootstrap.min.css') script(src='/js/bootstrap.min.js') body nav.navbar.navbar-default .container .navbar-header button.navbar-toggle.collapsed(type='button', data-toggle='collapse', data-target='#navbar', aria-expanded='false', aria-controls='navbar') span.sr-only Toggle navigation span.icon-bar span.icon-bar span.icon-bar a.navbar-brand(href='/') NodeJS #navbar.navbar-collapse.collapse ul.nav.navbar-nav li.active a(href='/') Home li a(href='/details') details li.dropdown a.dropdown-toggle(href='#', data-toggle='dropdown', role='button', aria-haspopup='true', aria-expanded='false') | Dropdown span.caret ul.dropdown-menu li a(href='#') Action li a(href='#') Another action li a(href='#') Something else here li.divider(role='separator') li.dropdown-header Nav header li a(href='#') Separated link li a(href='#') One more separated link block content //index.pug extends layout block content .container h1 #{msg} p #{username} , welcome to the first templating tutorial //details.pug extends layout block content .container .table-responsive h2 user table p This is all users we have so far table.table.table-bordered#example thead tr th Firstname th Lastname th Email tbody tr td John td Doe td [email protected] tr td Mary td Moe td [email protected] tr td July td Dooley td [email protected] link(rel='stylesheet', type='text/css', href='https://cdn.datatables.net/v/dt/dt-1.10.15/datatables.min.css') script(type='text/javascript', src='https://cdn.datatables.net/v/dt/dt-1.10.15/datatables.min.js') script(). $(document).ready(function() { $('#example').DataTable(); }); ملاحظة: نستخدم extends layout لكي لا نكرر الـhead في الصفحات الاخرى , مثل include في الـPHP ( في هذا الدرس ) - الاستخدام الحقيقي للـlayout هو تصميم شكل معين لصفحات معينه .. كـtemplate خاص مثلا - معلومات اكثر من خلال الرابط هنا الأن تم تجهيز المستوى الأول من التطبيق وهو الـconfigurations , ننتقل إلى النص الآخر وهو الـroutes . اولا route الصفحة الرئيسية سيكون كالتالي app.get('/', function(req, res) { res.render('index', { title: 'Pug ExpressJS NodeJS Tutorial', username: 'abdulla', msg: 'Hey There!' }) }); نلاحظ ان مسار الـroute هو مجرد slash للامام .. بذلك يتم تحديد المسار الرئيسي للتطبيق, بعد ذلك res.render اي تعني response render حيث يقوم تطبيق الـNode بالأجابة من بعد عمل طلب request من طرف المستخدم , للتوضيح اكثر الـHTTP هو عباره عن بروتوكول طلب و اجابة وبذلك request and response - req, res فعند طلب العميل امر ما يجب عليه ان يحصل على شي ما في المقابل. و كما موضع في الدالة اعلاه ان الـresponse هو عباره عن عمل render لملف index.pug الموجود في مجلد views مع ارسال ثلاث متغيرات title, username and msg , و طريقة استقبال البيانات في الـpug هي كالتالي ( يوجد اكثر من طريقة ) h1 #{username} p Welcome to #{title} خطوة اخيره لتشغيل التطبيق يجب تحديد بورت معين ولنفترض 3000 app.listen(3000, function() { console.log('The app is running http://localhost:3000'); }); افتح الـterminal داخل مجلد التطبيق myapp واكتب الامر التالي لتشغيل التطبيق node app.js افتح المتطفح على المسار انتهى الدرس الاول .. تم من خلاله عمل configuration لتطبيق الـnode الذي سنستخدمه في الدروس القادمة تثبيت template engine و توصيله مع تطبيق الـNode ارسال بيانات من الـNode إلى pug الدرس القادم سيكون هناك سنعرض لكم كيفية التعامل مع قواعد البيانات وإرسال المعلومات الى pug للحصول على ملفات العمل تجدونهم في المرفقات, بعد تحميل الملف قم بفك الضغط انتقل إلى داخل المجلد واكتب الأمر التالي لتثبيت جميع الـpackages دفعة واحده $ npm install --save أضغط هنا لتحميل ملفات العمل anwbh-app.zip موقع مساعد لتحويل كودات الـHTML إلى Pug http://html2jade.org

عالم البرمجة

عالم البرمجة مقالات برمجة و دورات مجانية لإحتراف البرمجة هدفنا تبسيط البرمجة ونشرها بيد الكل بشكل ممتع ومتطور ومحدث بإستمرار لمواكبة جديد تطورات البرمجة الحديثة و المتقدمة بدون مقابل