<?xml version="1.0"?>
<rss version="2.0"><channel><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: Angular</title><link>https://academy.hsoub.com/programming/javascript/angular/?d=2</link><description>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: Angular</description><language>ar</language><item><title>&#x625;&#x636;&#x627;&#x641;&#x629; &#x62E;&#x627;&#x635;&#x64A;&#x629; &#x62A;&#x631;&#x634;&#x64A;&#x62D; &#x644;&#x62A;&#x637;&#x628;&#x64A;&#x642; Angular &#x648;&#x62A;&#x62C;&#x647;&#x64A;&#x632;&#x647; &#x644;&#x644;&#x646;&#x634;&#x631;</title><link>https://academy.hsoub.com/programming/javascript/angular/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%AE%D8%A7%D8%B5%D9%8A%D8%A9-%D8%AA%D8%B1%D8%B4%D9%8A%D8%AD-%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-angular-%D9%88%D8%AA%D8%AC%D9%87%D9%8A%D8%B2%D9%87-%D9%84%D9%84%D9%86%D8%B4%D8%B1-r1900/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_02/567674698_-----Angular--.png.5ca5e4053e05eb99496cd1201f1d075d.png" /></p>
<p>
	بدأنا في مقال إنشاء تطبيق قائمة مهام باستخدام Angular ببناء تطبيق مهام ثم أضفنا المكونات له في المقال السابق إنشاء مكونات Components في تطبيق Angular وسننتقل الآن إلى إضافة وظائف للسماح للمستخدِمين بترشيح عناصر مهامهم حتى يتمكنوا من عرض العناصر النشطة أو المكتملة أو جميع العناصر، كما سنوضّح في هذا المقال كيفية بناء تطبيق جاهز للإنتاج، وسنوفر موارد إضافية لمواصلة رحلة تعلمك <a href="https://academy.hsoub.com/programming/javascript/angular/" rel="">لإطار عمل Angular</a>.
</p>

<ul>
	<li>
		<strong>المتطلبات الأساسية</strong>: يوصَى على الأقل بأن تكون على دراية بأساسيات لغات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت JavaScript</a>، ومعرفة باستخدام <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471" rel="">سطر الأوامر أو الطرفية</a>.
	</li>
	<li>
		<strong>الهدف</strong>: إضافة وظيفة الترشيح Filtering لتطبيقنا، ومعرفة كيفية بناء تطبيق Angular.
	</li>
</ul>

<h2>
	شيفرة الترشيح
</h2>

<p>
	يعتمد ترشيح العناصر على الخاصية <code>filter</code> التي أضفناها مسبقًا إلى الملف app.component.ts:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_600_8" style=""><span class="pln">filter</span><span class="pun">:</span><span class="pln"> </span><span class="str">'all'</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="str">'active'</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="str">'done'</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'all'</span><span class="pun">;</span></pre>

<p>
	القيمة الافتراضية للخاصية <code>filter</code> هي <code>all</code>، ولكن يمكن أن تكون <code>active</code> أو <code>done</code>.
</p>

<h2>
	إضافة عناصر تحكم الترشيح
</h2>

<p>
	أضِف شيفرة <a href="https://academy.hsoub.com/programming/html/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-html-r1687/" rel="">HTML</a> التالية في الملف app.component.html قبل زر "الإضافة Add" وفوق القسم الذي يعطي قائمةً بالعناصر، وتكون فيما يلي الأقسام الموجودة في ملف HTML ضمن تعليقات لتتمكن من معرفة مكان وضع الأزرار بالضبط.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_600_11" style=""><span class="com">&lt;!-- &lt;button class="btn-primary" (click)="addItem(newItem.value)"&gt;Add&lt;/button&gt;
 --&gt;</span><span class="pln">

  </span><span class="com">&lt;!-- الأزرار التي تعرض جميع العناصر أو العناصر النشطة أو العناصر المكتملة عند النقر عليها --&gt;</span><span class="pln">
  </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"btn-wrapper"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;button</span><span class="pln">
      </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"btn btn-menu"</span><span class="pln">
      [</span><span class="atn">class</span><span class="pln">.</span><span class="atn">active</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"filter == 'all'"</span><span class="pln">
      (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"filter = 'all'"</span><span class="tag">&gt;</span><span class="pln">
      All
    </span><span class="tag">&lt;/button&gt;</span><span class="pln">

    </span><span class="tag">&lt;button</span><span class="pln">
      </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"btn btn-menu"</span><span class="pln">
      [</span><span class="atn">class</span><span class="pln">.</span><span class="atn">active</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"filter == 'active'"</span><span class="pln">
      (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"filter = 'active'"</span><span class="tag">&gt;</span><span class="pln">
      To Do
    </span><span class="tag">&lt;/button&gt;</span><span class="pln">

    </span><span class="tag">&lt;button</span><span class="pln">
      </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"btn btn-menu"</span><span class="pln">
      [</span><span class="atn">class</span><span class="pln">.</span><span class="atn">active</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"filter == 'done'"</span><span class="pln">
      (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"filter = 'done'"</span><span class="tag">&gt;</span><span class="pln">
      Done
    </span><span class="tag">&lt;/button&gt;</span><span class="pln">
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">

  </span><span class="com">&lt;!-- &lt;h2&gt;{{items.length}} item(s)&lt;/h2&gt;
        &lt;ul&gt;... --&gt;</span></pre>

<p>
	يؤدي النقر على الأزرار إلى تغيير قيم خاصية الترشيح <code>filter</code> التي تحدِّد العناصر <code>items</code> المعروضة والتنسيق الذي يطبّقه إطار عمل Angular على الزر النشط.
</p>

<ul>
	<li>
		إذا نقر المستخدِم على زر "جميع العناصر All"، فستظهر كافة العناصر.
	</li>
	<li>
		إذا نقر المستخدِم على زر " العناصر النشطة To do"، فستظهر العناصر التي تكون فيها قيمة <code>done</code> هي <code>false</code>.
	</li>
	<li>
		إذا نقر المستخدِم على زر "العناصر المكتملة Done"، فستظهر العناصر التي تكون فيها قيمة <code>done</code> هي <code>true</code>.
	</li>
</ul>

<p>
	يتحكم ربط سِمة الصنف باستخدام الأقواس المربعة <code>[]</code> في لون نص الأزرار، إذ يطبِّق ربط الأصناف <code>[class.active]</code> الصنفَ <code>active</code> عندما تتطابق قيمة الخاصية <code>filter</code> مع التعبير، فإذا نقر المستخدِم مثلًا على زر "العناصر المكتملة Done" الذي يضبط الخاصية <code>filter</code> على القيمة <code>done</code>، فسيُقيَّم تعبير ربط الأصناف <code>filter == 'done'‎</code> على أنه <code>true</code>، كما يطبق إطار عمل Angular الصنف <code>active</code> على زر "العناصر المكتملة Done" عندما تكون قيمة الخاصية <code>filter</code> هي <code>done</code> لجعل لون النص أخضر، فإذا نقر المستخدِم على أحد الأزرار الأخرى، فلن تكون قيمة الخاصية <code>filter</code> هي <code>done</code>، وبالتالي لن يكون لون النص أخضرًا.
</p>

<h2>
	بناء تطبيق Angular النهائي
</h2>

<p>
	يمكنك الآن بعد أن انتهيت من تطوير تطبيقك تشغيل أمر البناء <code>build</code> في واجهة سطر الأوامر CLI في إطار عمل Angular، فإذا شغّلتَ الأمر <code>build</code> في المجلد todo، فسيُصرَّف تطبيقك في مجلد خرج بالاسم <code>dist/‎</code>.
</p>

<p>
	شغّل الأمر التالي في سطر الأوامر في المجلد todo:
</p>

<pre class="ipsCode">ng build -c production
</pre>

<p>
	تصرِّف واجهة CLI التطبيق وتضع الخرج في مجلد dist جديد، كما تتخلص الراية <code>--configuration production/-c production</code> مع الأمر <code>ng build</code> من الأشياء التي لا تحتاجها في عملية الإنتاج.
</p>

<h3>
	نشر تطبيقك
</h3>

<p>
	يمكنك نسخ محتويات المجلد dist/my-project-name إلى خادم الويب لنشر تطبيقك، وبما أنّ هذه الملفات ساكنة static files، فيمكنك استضافتها على أيّ خادم ويب يمتلك القدرة على تشغيل الملفات مثل:
</p>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/java/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-java-%D9%85%D8%A7-%D9%87%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9%D8%9F-r371/" rel="">Java</a>
	</li>
	<li>
		‎.NET
	</li>
</ul>

<p>
	يمكنك استخدام أيّ منصة واجهة خلفية مثل Firebase أو Google Cloud أو App Engine.
</p>

<h2>
	الخلاصة
</h2>

<p>
	بما أنه كان لديك شيفرة المرشح <code>filter</code> في الملف app.component.ts مسبقًا، فكل ما عليك فعله هو تعديل القالب لتوفير عناصر التحكم بترشيح العناصر والتعلم به، كما تناولنا في هذا المقال كيفية بناء تطبيق Angular جاهز للإنتاج.
</p>

<p>
	أنشأت حتى الآن تطبيقًا أساسيًا، لكن رحلتك في تعلم إطار عمل Angular بدأت للتو، ويمكنك معرفة المزيد من خلال الاطلاع على <a href="https://academy.hsoub.com/programming/javascript/angular/" rel="">سلسلة مقالات Angular في أكاديمية حسوب</a> التي تعزّز معرفتك في رحلة تعلّمك لإطار عمل Angular.
</p>

<p>
	ترجمة -وبتصرُّف- للمقالين <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_filtering" rel="external nofollow">Filtering our to-do items</a> و<a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_building" rel="external nofollow">Building Angular applications and further resources</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-angular-%D9%88%D9%86%D8%B4%D8%B1%D9%87%D8%A7-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1388/" rel="">تهيئة بيئة تطبيقات Angular ونشرها على الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%AA%D9%86%D9%82%D9%84-%D9%88%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-angular-r1382/" rel="">إضافة التنقل وإدارة البيانات في تطبيق Angular</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-angular-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1380/" rel="">كيفية استعمال Angular في بناء تطبيقات الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-r1409/" rel="">مقدمة في بناء تطبيقات الويب باستخدام إطار العمل Angular وقاعدة بيانات Firestore</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1900</guid><pubDate>Sat, 25 Feb 2023 16:00:00 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x625;&#x646;&#x634;&#x627;&#x621; &#x645;&#x643;&#x648;&#x646;&#x627;&#x62A; Components &#x641;&#x64A; &#x62A;&#x637;&#x628;&#x64A;&#x642; Angular</title><link>https://academy.hsoub.com/programming/javascript/angular/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-components-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-angular-r1899/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_02/1821485466_----Angular.png.88e240f2f01a9208afe8872bc23a421e.png" /></p>
<p>
	توفّر المكونات طريقةً لتنظيم تطبيقك، لذا سنرشدك في هذا المقال لإنشاء مكوِّن للتعامل مع العناصر الفردية في قائمة المهام الذي بدأنا العمل عليه في <a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%82%D8%A7%D8%A6%D9%85%D8%A9-%D9%85%D9%87%D8%A7%D9%85-%D9%88%D8%AA%D9%86%D8%B3%D9%8A%D9%82%D9%87-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-angular-r1898/" rel="">المقال السابق</a>، وإضافة وظائف تحديد المهام وتعديلها وحذفها، حيث سنغطي نموذج أحداث Angular.
</p>

<ul>
	<li>
		<strong>المتطلبات الأساسية</strong>: يوصَى على الأقل بأن تكون على دراية بأساسيات لغات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت JavaScript</a>، ومعرفة باستخدام <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471" rel="">سطر الأوامر أو الطرفية</a>.
	</li>
	<li>
		<strong>الهدف</strong>: معرفة المزيد حول المكونات بما في ذلك كيفية عمل الأحداث للتعامل مع التحديثات، وإضافة وظائف تحديد المهام وتعديلها وحذفها.
	</li>
</ul>

<h2>
	إنشاء مكون جديد
</h2>

<p>
	أنشئ مكونًا بالاسم <code>item</code> باستخدام أمر واجهة سطر الأوامر CLI التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_188_8" style=""><span class="pln">ng generate component item</span></pre>

<p>
	ينشئ الأمر <code>ng generate component</code> مكونًا ومجلدًا بالاسم الذي تحدده، حيث سيكون في مثالنا اسم المجلد والمكوِّن هو <code>item</code>، كما يمكنك العثور على مجلد العناصر <code>item</code> ضمن المجلد <code>app</code>.
</p>

<p>
	يتكون المكوِّن <code>ItemComponent</code> من الملفات التالية مثل المكون <code>AppComponent</code>:
</p>

<ul>
	<li>
		الملف item.component.html الخاص بالتوصيف باستخدام <a href="https://academy.hsoub.com/programming/html/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-html-r1687/" rel="">لغة HTML</a>.
	</li>
	<li>
		الملف item.component.ts الخاص بالشيفرة البرمجية أو بالمنطق باستخدام <a href="https://academy.hsoub.com/programming/javascript/typescript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-typescript-r1214/" rel="">لغة TypeScript</a> +الملف item.component.css الخاص بالتنسيق باستخدام <a href="https://academy.hsoub.com/programming/css/%d8%aa%d8%b9%d8%b1%d9%91%d9%81-%d8%b9%d9%84%d9%89-%d8%a3%d8%b3%d8%a7%d8%b3%d9%8a%d8%a7%d8%aa-css-r70/" rel="">لغة CSS</a>.
	</li>
</ul>

<p>
	يمكنك رؤية مرجع لملفات HTML وCSS في البيانات الوصفية لعنصر التصميم <code>‎@Component()‎</code> في الملف item.component.ts كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_188_17" style=""><span class="lit">@Component</span><span class="pun">({</span><span class="pln">
  selector</span><span class="pun">:</span><span class="pln"> </span><span class="str">'app-item'</span><span class="pun">,</span><span class="pln">
  templateUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./item.component.html'</span><span class="pun">,</span><span class="pln">
  styleUrls</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'./item.component.css'</span><span class="pun">],</span><span class="pln">
</span><span class="pun">})</span></pre>

<h2>
	إضافة توصيف HTML الخاص بالمكون ItemComponent
</h2>

<p>
	يمكن أن يتولى المكوِّن <code>ItemComponent</code> مهمة منح المستخدِم طريقة لتحديد العناصر بوصفها مكتملةً أو لتعديلها أو حذفها، لذا أضِف شيفرة HTML لإدارة العناصر من خلال استبدال محتوى العنصر البديل Placeholder في الملف item.component.html بما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_188_19" style=""><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"item"</span><span class="tag">&gt;</span><span class="pln">

  </span><span class="tag">&lt;input</span><span class="pln"> [</span><span class="atn">id</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"item.description"</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"checkbox"</span><span class="pln"> (</span><span class="atn">change</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"item.done = !item.done"</span><span class="pln"> [</span><span class="atn">checked</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"item.done"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;label</span><span class="pln"> [</span><span class="atn">for</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"item.description"</span><span class="tag">&gt;</span><span class="pln">{{item.description}}</span><span class="tag">&lt;/label&gt;</span><span class="pln">

  </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"btn-wrapper"</span><span class="pln"> *</span><span class="atn">ngIf</span><span class="pun">=</span><span class="atv">"!editable"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"btn"</span><span class="pln"> (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"editable = !editable"</span><span class="tag">&gt;</span><span class="pln">Edit</span><span class="tag">&lt;/button&gt;</span><span class="pln">
    </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"btn btn-warn"</span><span class="pln"> (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"remove.emit()"</span><span class="tag">&gt;</span><span class="pln">Delete</span><span class="tag">&lt;/button&gt;</span><span class="pln">
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">

  ‏&lt;-- ‫يظهر هذا القسم فقط في حالة نقر المستخدم على زر "التعديل Edit" –!&gt;
  </span><span class="tag">&lt;div</span><span class="pln"> *</span><span class="atn">ngIf</span><span class="pun">=</span><span class="atv">"editable"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"sm-text-input"</span><span class="pln"> </span><span class="atn">placeholder</span><span class="pun">=</span><span class="atv">"edit item"</span><span class="pln"> [</span><span class="atn">value</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"item.description"</span><span class="pln"> #</span><span class="atn">editedItem</span><span class="pln"> (</span><span class="atn">keyup</span><span class="pln">.</span><span class="atn">enter</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"saveItem(editedItem.value)"</span><span class="tag">&gt;</span><span class="pln">

    </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"btn-wrapper"</span><span class="tag">&gt;</span><span class="pln">
      </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"btn"</span><span class="pln"> (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"editable = !editable"</span><span class="tag">&gt;</span><span class="pln">Cancel</span><span class="tag">&lt;/button&gt;</span><span class="pln">
      </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"btn btn-save"</span><span class="pln"> (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"saveItem(editedItem.value)"</span><span class="tag">&gt;</span><span class="pln">Save</span><span class="tag">&lt;/button&gt;</span><span class="pln">
    </span><span class="tag">&lt;/div&gt;</span><span class="pln">
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">

</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	حقل الإدخال Input الأول هو مربع اختيار بحيث يمكن للمستخدِمين تحديد العناصر عند اكتمال أحدها، كما تشير الأقواس المزدوجة المعقوصة <code>{{}}</code> في حقل الإدخال <code>&lt;input&gt;</code> والتسمية <code>&lt;label&gt;</code> الخاصين بمربع الاختيار إلى استخدام إطار عمل Angular الذي يستخدِم الصيغة <code>{{item.description}}</code> لاسترداد وصف العنصر <code>item</code> الحالي من مصفوفة العناصر <code>items</code>، وسنوضّح في القسم التالي كيفية مشاركة المكونات للبيانات بالتفصيل.
</p>

<p>
	كما يوجد زران لتعديل العنصر الحالي وحذفه ضمن <a href="https://wiki.hsoub.com/HTML/div" rel="external">الوسم &lt;div&gt;</a> الذي يوجد فيه الموجّه <code>‎*ngIf</code>، وهو موجّه Angular مبني مسبقًا يمكنك استخدامه لتغيير بنية <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-dom-r644/" rel="">نموذج DOM</a> ديناميكيًا، إذ يشير الموجّه <code>‎*ngIf</code> إلى أنه إذا كانت قيمة المتغير <code>editable</code> هي <code>false</code>، فإنّ العنصر <code>&lt;div&gt;</code> موجود في نموذج DOM؛ وإذا كانت قيمة المتغير <code>editable</code> هي <code>true</code>، فسيزيل إطار عمل Angular العنصر <code>&lt;div&gt;</code> من نموذج DOM.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_188_23" style=""><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"btn-wrapper"</span><span class="pln"> *</span><span class="atn">ngIf</span><span class="pun">=</span><span class="atv">"!editable"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"btn"</span><span class="pln"> (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"editable = !editable"</span><span class="tag">&gt;</span><span class="pln">Edit</span><span class="tag">&lt;/button&gt;</span><span class="pln">
  </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"btn btn-warn"</span><span class="pln"> (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"remove.emit()"</span><span class="tag">&gt;</span><span class="pln">Delete</span><span class="tag">&lt;/button&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	إذا نقر المستخدِم على زر "التعديل Edit"، فستصبح قيمة المتغير <code>editable</code> هي <code>true</code>، مما يؤدي إلى إزالة العنصر <code>&lt;div&gt;</code> وأبنائه من نموذج DOM، فإذا نقر المستخدِم على زر "الحذف Delete"، فسيرفع المكوِّن <code>ItemComponent</code> حدثًا يعلِم فيه المكوِّن <code>AppComponent</code> بعملية الحذف.
</p>

<p>
	لاحظ أيضًا وجود الموجه <code>‎*ngIf</code> في عنصر <code>&lt;div&gt;</code> التالي، ولكن ضُبِط المتغير <code>editable</code> على القيمة <code>true</code>، فإذا كانت قيمة المتغير <code>editable</code> هي <code>true</code> في هذه الحالة، فسيضع إطار عمل Angular العنصر <code>&lt;div&gt;</code> وعناصره الأبناء <code><a href="https://wiki.hsoub.com/HTML/input" rel="external">&lt;input&gt;</a></code> و <code><a href="https://wiki.hsoub.com/HTML/button" rel="external">&lt;button&gt;</a></code> في نموذج DOM.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_188_27" style=""><span class="pln">‏&lt;-- ‫يظهر هذا القسم فقط في حالة نقر المستخدم على زر "التعديل Edit" –!&gt;
</span><span class="tag">&lt;div</span><span class="pln"> *</span><span class="atn">ngIf</span><span class="pun">=</span><span class="atv">"editable"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"sm-text-input"</span><span class="pln"> </span><span class="atn">placeholder</span><span class="pun">=</span><span class="atv">"edit item"</span><span class="pln"> [</span><span class="atn">value</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"item.description"</span><span class="pln"> #</span><span class="atn">editedItem</span><span class="pln"> (</span><span class="atn">keyup</span><span class="pln">.</span><span class="atn">enter</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"saveItem(editedItem.value)"</span><span class="tag">&gt;</span><span class="pln">

  </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"btn-wrapper"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"btn"</span><span class="pln"> (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"editable = !editable"</span><span class="tag">&gt;</span><span class="pln">Cancel</span><span class="tag">&lt;/button&gt;</span><span class="pln">
    </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"btn btn-save"</span><span class="pln"> (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"saveItem(editedItem.value)"</span><span class="tag">&gt;</span><span class="pln">Save</span><span class="tag">&lt;/button&gt;</span><span class="pln">
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	تكون قيمة العنصر <code>&lt;input&gt;</code> مرتبطةً بوصف <code>description</code> العنصر الحالي باستخدام الصيغة <code>‎[value]="item.description"‎</code>، وبالتالي يكون وصف <code>description</code> العنصر هو قيمة العنصر <code>&lt;input&gt;</code>، لذا إذا كانت قيمة الوصف <code>description</code> هي <code>eat</code>، فسيكون الوصف <code>description</code> موجودًا مسبفًا في العنصر <code>&lt;input&gt;</code>، وبذلك إذا عدّل المستخدِم العنصر، فستكون قيمة العنصر <code>&lt;input&gt;</code> هي <code>eat</code> مسبقًا.
</p>

<p>
	يعني متغير القالب <code>‎#editedItem</code> في العنصر <code>&lt;input&gt;</code> أنّ إطار عمل Angular يخزّن كل ما يكتبه المستخدِم في العنصر <code>&lt;input&gt;</code> في متغير اسمه <code>editedItem</code>، إذ يستدعي الحدث <code>keyup</code> التابع <code>saveItem()‎</code> ويمرّر قيمة المتغير <code>editedItem</code> إذا اختار المستخدِم الضغط على مفتاح <code>Enter</code> بدلًا من النقر على زر "الحفظ Save".
</p>

<p>
	إذا نقر المستخدِم على زر "الإلغاء Cancel"، فستتبدّل قيمة المتغير <code>editable</code> إلى <code>false</code>، مما يزيل حقل الإدخال والأزرار الخاصة بالتعديل من نموذج DOM، فإذا كانت قيمة المتغير <code>editable</code> هي <code>false</code>، فسيعيد <a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-angular-r1393/" rel="">إطار عمل Angular</a> وضع العنصر <code>&lt;div&gt;</code> مع زرَّي "التعديل Edit" و"الحذف Delete" في نموذج DOM.
</p>

<p>
	يؤدي النقر على زر "الحفظ Save" إلى استدعاء التابع <code>saveItem()‎</code> الذي يأخذ القيمة من المتغير <code>‎#editedItem</code> الخاص بالعنصر <code>&lt;input&gt;</code> ويغير وصف <code>description</code> العنصر إلى السلسلة النصية <code>editedItem.value</code>.
</p>

<h2>
	إعداد المكون AppComponent
</h2>

<p>
	سنضيف في القسم التالي شيفرةً برمجيةً تعتمد على الاتصال بالمكونين <code>AppComponent</code> و <code>ItemComponent</code>، إذ يجب إعداد المكون <code>AppComponent</code> أولًا عبر إضافة السطر التالي لاستيراد <code>Item</code> في بداية الملف app.component.ts:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_188_32" style=""><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Item</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">"./item"</span><span class="pun">;</span></pre>

<p>
	بعد ذلك اضبط المكون <code>AppComponent</code> بإضافة ما يلي في نفس ملف الصنف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_188_34" style=""><span class="pln">remove</span><span class="pun">(</span><span class="pln">item</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">allItems</span><span class="pun">.</span><span class="pln">splice</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">allItems</span><span class="pun">.</span><span class="pln">indexOf</span><span class="pun">(</span><span class="pln">item</span><span class="pun">),</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يستخدِم التابع <code>remove()‎</code> تابع <a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1689/" rel="">جافاسكربت</a> <code>Array.splice()‎</code> لإزالة عنصر واحد عند فهرس <code>indexOf</code> العنصر ذي الصلة، ويعني ذلك أن التابع <code>splice()‎</code> يزيل العنصر من المصفوفة.
</p>

<h2>
	إضافة الشيفرة البرمجية أو المنطق إلى المكون ItemComponent
</h2>

<p>
	يمكن استخدام واجهة المستخدِم الخاصة بالمكوِّن <code>ItemComponent</code> من خلال إضافة شيفرة برمجية إلى المكون مثل إضافة دوال وطرق لدخول البيانات وخروجها.
</p>

<p>
	عدّل تعليمات استيراد جافاسكربت في الملف item.component.ts كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_188_37" style=""><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Component</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Input</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Output</span><span class="pun">,</span><span class="pln"> </span><span class="typ">EventEmitter</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Item</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">"../item"</span><span class="pun">;</span></pre>

<p>
	تسمح إضافة <code>Input</code> و <code>Output</code> و <code>EventEmitter</code> للمكوِّن <code>ItemComponent</code> بمشاركة البيانات مع المكوِّن <code>AppComponent</code>، كما يمكن أن يفهم المكوِّن <code>ItemComponent</code> ما هو العنصر <code>item</code> من خلال استيراد <code>Item</code>.
</p>

<p>
	استبدل الصنف <code>ItemComponent</code> الناتج في الملف item.component.ts بما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_188_39" style=""><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ItemComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  editable </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">

  </span><span class="lit">@Input</span><span class="pun">()</span><span class="pln"> item</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Item</span><span class="pun">;</span><span class="pln">
  </span><span class="lit">@Input</span><span class="pun">()</span><span class="pln"> newItem</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">;</span><span class="pln">
  </span><span class="lit">@Output</span><span class="pun">()</span><span class="pln"> remove </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">EventEmitter</span><span class="pun">&lt;</span><span class="typ">Item</span><span class="pun">&gt;();</span><span class="pln">

  saveItem</span><span class="pun">(</span><span class="pln">description</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">description</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">editable </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">item</span><span class="pun">.</span><span class="pln">description </span><span class="pun">=</span><span class="pln"> description</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تساعد الخاصية <code>editable</code> في تبديل قسم من القالب حيث يمكن للمستخدِم تعديل عنصر، إذ تُعَدّ الخاصية <code>editable</code> هي الخاصية نفسها في توصيف HTML ضمن تعليمة <code>‎*ngIf</code> التي هي <code>‎*ngIf="editable"‎</code>، فإذا أردتَ استخدام خاصية في القالب، فيجب التصريح عنها في الصنف.
</p>

<p>
	يسهّل كل من <code>‎@Input()‎</code> و <code>‎@Output()‎</code> و <code>EventEmitter</code> الاتصال بين مكونيك، إذ يعمل <code>‎@Input()‎</code> بوصفه وسيلةً لدخول البيانات إلى المكوِّن، ويعمل <code>‎@Output()‎</code> بوصفه وسيلةً لخروج البيانات من المكوِّن، كما يجب أن يكون <code>‎@Output()‎</code> من النوع <code>EventEmitter</code> بحيث يمكن للمكوِّن رفع حدث عندما تكون هناك بيانات جاهزة لمشاركتها مع مكوِّن آخر.
</p>

<p>
	استخدم <code>‎@Input()‎</code> لتحديد أن قيمة الخاصية يمكن أن تأتي من خارج المكوِّن، واستخدم <code>‎@Output()‎</code> مع <code>EventEmitter</code> لتحديد أن قيمة الخاصية يمكن أن تترك المكوِّن بحيث يمكن لمكوِّن آخر تلقي تلك البيانات.
</p>

<p>
	يأخذ التابع <code>saveItem()‎</code> وسيطًا هو الوصف <code>description</code> الذي يُعَدّ النص الذي يدخله المستخدِم في حقل الإدخال <code>&lt;input&gt;</code> عند تعديل عنصر في القائمة، كما يُعَدّ الوصف <code>description</code> السلسلة النصية نفسها التي تأتي من العنصر <code>&lt;input&gt;</code> مع متغير القالب <code>‎#editedItem</code>.
</p>

<p>
	إذا لم يدخِل المستخدِم أيّ قيمة ولكنه نقر على زر "الحفظ Save"، فلن يعيد التابع <code>saveItem()‎</code> شيئًا ولن يُحدِّث الوصف <code>description</code>، فإذا لم تكن تعليمة <code>if</code> موجودةً، فيمكن للمستخدِم النقر على زر "الحفظ Save" بدون وجود أيّ شيء في حقل الإدخال <code>&lt;input&gt;</code> وسيصبح الوصف <code>description</code> سلسلةً نصيةً فارغةً.
</p>

<p>
	إذا أدخل المستخدِم نصًا ونقر على زر "الحفظ Save"، فسيضبط التابع <code>saveItem()‎</code> الخاصية <code>editable</code> على القيمة <code>false</code>، مما يتسبب في أن يزيل الموجّه <code>‎*ngIf</code> في القالب ميزة التعديل ويصيّر أزرار "التعديل Edit" و"الحذف Delete" مرةً أخرى.
</p>

<p>
	يجب أن يُصرَّف التطبيق في هذه المرحلة، ولكن يجب استخدام المكوِّن <code>ItemComponent</code> ضمن المكوِّن <code>AppComponent</code> لتتمكّن من رؤية الميزات الجديدة في المتصفح.
</p>

<h2>
	استخدام المكون ItemComponent ضمن المكون AppComponent
</h2>

<p>
	يمنحك تضمين أحد المكونات ضمن مكوِّن آخر في سياق علاقة المكوِّن الأب بالمكوِّن الابن المرونةَ في استخدام المكونات حيثما تريد ذلك، إذ يُعَدّ المكوِّن <code>AppComponent</code> صدَفةً shell للتطبيق بحيث يمكنك تضمين مكونات أخرى.
</p>

<p>
	يمكنك استخدام المكوِّن <code>ItemComponent</code> ضمن المكوِّن <code>AppComponent</code> من خلال وضع محدّد المكوِّن <code>ItemComponent</code> في قالب المكون <code>AppComponent</code>، كما يحدّد إطار عمل Angular محدّد المكوِّن في البيانات الوصفية لعنصر التصميم <code>‎@Component()‎</code>، إذ يكون المحدّد Selector في مثالنا هو <code>app-item</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_188_41" style=""><span class="lit">@Component</span><span class="pun">({</span><span class="pln">
  selector</span><span class="pun">:</span><span class="pln"> </span><span class="str">'app-item'</span><span class="pun">,</span><span class="pln">
  templateUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./item.component.html'</span><span class="pun">,</span><span class="pln">
  styleUrls</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'./item.component.css'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	يمكنك استخدام محدّد المكون <code>ItemComponent</code> ضمن المكوِّن <code>AppComponent</code> من خلال إضافة العنصر <code>&lt;app-item&gt;</code> الذي يتوافق مع المحدّد الذي عرّفته لصنف المكوِّن في الملف app.component.html، لذا استبدل القائمة غير المرتبة الحالية في الملف app.component.html بما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_188_43" style=""><span class="pun">&lt;</span><span class="pln">h2</span><span class="pun">&gt;{{</span><span class="pln">items</span><span class="pun">.</span><span class="pln">length</span><span class="pun">}}</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">span </span><span class="pun">*</span><span class="pln">ngIf</span><span class="pun">=</span><span class="str">"items.length === 1; else elseBlock"</span><span class="pun">&gt;</span><span class="pln">item</span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">ng</span><span class="pun">-</span><span class="pln">template </span><span class="pun">#</span><span class="pln">elseBlock</span><span class="pun">&gt;</span><span class="pln">items</span><span class="pun">&lt;</span><span class="str">/ng-template&gt;&lt;/</span><span class="pln">h2</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;</span><span class="pln">ul</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">li </span><span class="pun">*</span><span class="pln">ngFor</span><span class="pun">=</span><span class="str">"let item of items"</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">app</span><span class="pun">-</span><span class="pln">item </span><span class="pun">(</span><span class="pln">remove</span><span class="pun">)=</span><span class="str">"remove(item)"</span><span class="pln"> </span><span class="pun">[</span><span class="pln">item</span><span class="pun">]=</span><span class="str">"item"</span><span class="pun">&gt;&lt;/</span><span class="pln">app</span><span class="pun">-</span><span class="pln">item</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="pln">li</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">ul</span><span class="pun">&gt;</span></pre>

<p>
	تدخِل صيغة الأقواس المزدوجة المتعرجة <code>{{}}</code> في <a href="https://wiki.hsoub.com/HTML/h1-h6" rel="external">العنصر &lt;h2&gt;</a> طول المصفوفة <code>items</code> وتعرضه.
</p>

<p>
	يستخدِم <a href="https://wiki.hsoub.com/HTML/span" rel="external">العنصر &lt;span&gt;</a> في عنصر العنوان <code>&lt;h2&gt;</code> الموجّه <code>‎*ngIf</code> و<code>else</code> لتحديد ما إذا كان يجب أن يشير العنصر <code>&lt;h2&gt;</code> إلى "العنصر item" أو مجموعة "العناصر items"، فإذا كان هناك عنصر واحد فقط في القائمة، فسيُعرَض العنصر <code>&lt;span&gt;</code> الذي يحتوي على الكلمة "عنصر item"، في حين إذا كان طول المصفوفة <code>items</code> هو أيّ شيء آخر غير القيمة <code>1</code>، فسيظهر العنصر <code>&lt;ng-template&gt;</code> الذي أطلقنا عليه الاسم <code>elseBlock</code> بالصورة <code>‎#elseBlock</code> بدلًا من العنصر <code>&lt;span&gt;</code>، كما يمكنك استخدام العنصر <code>&lt;ng-template&gt;</code> في إطار عمل Angular عندما لا تريد عرض المحتوى افتراضيًا، وبالتالي إذا كان طول مصفوفة العناصر <code>items</code> يساوي <code>1</code>، فسيعرض الموجّه <code>‎*ngIf</code> العنصر <code>elseBlock</code> وليس العنصر <code>&lt;span&gt;</code>.
</p>

<p>
	يستخدِم <a href="https://wiki.hsoub.com/HTML/li" rel="external">العنصر &lt;li&gt;</a> موجّه التكرار <code>‎*ngFor</code> للمرور على جميع العناصر في مصفوفة <code>items</code>، إذ يُعَدّ الموجَّه <code>‎*ngFor</code> الخاص بإطار عمل Angular موجّهًا آخرًا يساعدك على تغيير بنية <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-dom-r644/" rel="">نموذج DOM</a> مع كتابة شيفرة برمجية أقل، كما يكرّر إطار عمل Angular العنصر <code>&lt;li&gt;</code> وكل شيء بداخله لكل عنصر <code>item</code> حيث يتضمن العنصر <code>&lt;li&gt;</code> العنصر <code>&lt;app-item&gt;</code>، وهذا يعني أنّ إطار عمل Angular ينشئ نسخةً أخرى من العنصر <code>&lt;app-item&gt;</code> لكل عنصر في المصفوفة، وبالتالي سينشئ إطار عمل Angular العديد من عناصر <code>&lt;li&gt;</code> لأيّ عدد من العناصر في المصفوفة، كما يمكنك استخدام الموجّه <code>‎*ngFor</code> مع عناصر أخرى مثل <code><a href="https://wiki.hsoub.com/HTML/div" rel="external">&lt;div&gt;</a></code> أو <code>&lt;span&gt;</code> أو <code><a href="https://wiki.hsoub.com/HTML/p" rel="external">&lt;p&gt;</a></code>.
</p>

<p>
	يحتوي المكوِّن <code>AppComponent</code> على التابع <code>remove()‎</code> لإزالة العنصر المرتبط بالخاصية <code>remove</code> من المكوِّن <code>ItemComponent</code>، كما تربط الخاصية <code>item</code> الموجودة بين قوسين مربعين <code>[]</code> قيمة العنصر <code>item</code> بين المكونين <code>AppComponent</code> و <code>ItemComponent</code>.
</p>

<p>
	يجب أن تكون الآن قادرًا على تعديل العناصر وحذفها من القائمة، ويجب أن يتغير عدد العناصر عند إضافة عناصر أو حذفها، كما يمكنك جعل القائمة أسهل استخدامًا من خلال إضافة تنسيق إلى المكوِّن <code>ItemComponent</code>.
</p>

<h2>
	إضافة التنسيق إلى المكون ItemComponent
</h2>

<p>
	يمكنك استخدام ملف تنسيق لمكوِّن لإضافة تنسيقات خاصة به، إذ يضيف تنسيق <a href="https://academy.hsoub.com/programming/css/%d8%aa%d8%b9%d8%b1%d9%91%d9%81-%d8%b9%d9%84%d9%89-%d8%a3%d8%b3%d8%a7%d8%b3%d9%8a%d8%a7%d8%aa-css-r70/" rel="">CSS</a> التالي التنسيقات الأساسية ونموذج الصندوق المرن Flexbox للأزرار ومربعات الاختيار المخصَّصة.
</p>

<p>
	الصق التنسيق التالي في الملف item.component.css:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_188_52" style=""><span class="pun">.</span><span class="pln">item </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">.5rem</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">.75rem</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">text-align</span><span class="pun">:</span><span class="pln"> left</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">font-size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.2rem</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">btn-wrapper </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">margin-top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1rem</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">margin-bottom</span><span class="pun">:</span><span class="pln"> </span><span class="lit">.5rem</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">btn </span><span class="pun">{</span><span class="pln">
  </span><span class="com">/* menu buttons flexbox styles */</span><span class="pln">
  </span><span class="kwd">flex-basis</span><span class="pun">:</span><span class="pln"> </span><span class="lit">49%</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">btn-save </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#000</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#fff</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">border-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#000</span><span class="pun">;</span><span class="pln">

</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="kwd">btn-save</span><span class="pun">:</span><span class="pln">hover </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#444242</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="kwd">btn-save</span><span class="pun">:</span><span class="pln">focus </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#fff</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#000</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">checkbox-wrapper </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">.5rem</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">btn-warn </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#b90000</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#fff</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">border-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#9a0000</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="kwd">btn-warn</span><span class="pun">:</span><span class="pln">hover </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#9a0000</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="kwd">btn-warn</span><span class="pun">:</span><span class="pln">active </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#e30000</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">border-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#000</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">sm-text-input </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100%</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">.5rem</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2px</span><span class="pln"> solid </span><span class="lit">#555</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">display</span><span class="pun">:</span><span class="pln"> block</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">box-sizing</span><span class="pun">:</span><span class="pln"> border-box</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">font-size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1rem</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1rem</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">/* Custom checkboxes
Adapted from https://css-tricks.com/the-checkbox-hack/#custom-designed-radio-buttons-and-checkboxes */</span><span class="pln">

</span><span class="com">/* Base for label styling */</span><span class="pln">
</span><span class="pun">[</span><span class="pln">type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pun">]:</span><span class="pln">not</span><span class="pun">(:</span><span class="pln">checked</span><span class="pun">),</span><span class="pln">
</span><span class="pun">[</span><span class="pln">type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pun">]:</span><span class="pln">checked </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">position</span><span class="pun">:</span><span class="pln"> absolute</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">left</span><span class="pun">:</span><span class="pln"> </span><span class="pun">-</span><span class="lit">9999px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">[</span><span class="pln">type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pun">]:</span><span class="pln">not</span><span class="pun">(:</span><span class="pln">checked</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> label</span><span class="pun">,</span><span class="pln">
</span><span class="pun">[</span><span class="pln">type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pun">]:</span><span class="pln">checked </span><span class="pun">+</span><span class="pln"> label </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">position</span><span class="pun">:</span><span class="pln"> relative</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">padding-left</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.95em</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">cursor</span><span class="pun">:</span><span class="pln"> pointer</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">/* checkbox aspect */</span><span class="pln">
</span><span class="pun">[</span><span class="pln">type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pun">]:</span><span class="pln">not</span><span class="pun">(:</span><span class="pln">checked</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="kwd">label</span><span class="pun">:</span><span class="pln">before</span><span class="pun">,</span><span class="pln">
</span><span class="pun">[</span><span class="pln">type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pun">]:</span><span class="pln">checked </span><span class="pun">+</span><span class="pln"> </span><span class="kwd">label</span><span class="pun">:</span><span class="pln">before </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">content</span><span class="pun">:</span><span class="pln"> </span><span class="str">''</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">position</span><span class="pun">:</span><span class="pln"> absolute</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">left</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> </span><span class="kwd">top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.25em</span><span class="pun">;</span><span class="pln"> </span><span class="kwd">height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.25em</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2px</span><span class="pln"> solid </span><span class="lit">#ccc</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">background</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#fff</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">/* checked mark aspect */</span><span class="pln">
</span><span class="pun">[</span><span class="pln">type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pun">]:</span><span class="pln">not</span><span class="pun">(:</span><span class="pln">checked</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="kwd">label</span><span class="pun">:</span><span class="pln">after</span><span class="pun">,</span><span class="pln">
</span><span class="pun">[</span><span class="pln">type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pun">]:</span><span class="pln">checked </span><span class="pun">+</span><span class="pln"> </span><span class="kwd">label</span><span class="pun">:</span><span class="pln">after </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">content</span><span class="pun">:</span><span class="pln"> </span><span class="str">'\2713\0020'</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">position</span><span class="pun">:</span><span class="pln"> absolute</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">.15em</span><span class="pun">;</span><span class="pln"> </span><span class="kwd">left</span><span class="pun">:</span><span class="pln"> </span><span class="lit">.22em</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">font-size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.3em</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">line-height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.8</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#0d8dee</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">transition</span><span class="pun">:</span><span class="pln"> all </span><span class="lit">.2s</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">font-family</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Lucida Sans Unicode'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Arial Unicode MS'</span><span class="pun">,</span><span class="pln"> Arial</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">/* checked mark aspect changes */</span><span class="pln">
</span><span class="pun">[</span><span class="pln">type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pun">]:</span><span class="pln">not</span><span class="pun">(:</span><span class="pln">checked</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="kwd">label</span><span class="pun">:</span><span class="pln">after </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">opacity</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">transform</span><span class="pun">:</span><span class="pln"> scale</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">[</span><span class="pln">type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pun">]:</span><span class="pln">checked </span><span class="pun">+</span><span class="pln"> </span><span class="kwd">label</span><span class="pun">:</span><span class="pln">after </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">opacity</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">transform</span><span class="pun">:</span><span class="pln"> scale</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">/* accessibility */</span><span class="pln">
</span><span class="pun">[</span><span class="pln">type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pun">]:</span><span class="kwd">checked</span><span class="pun">:</span><span class="pln">focus </span><span class="pun">+</span><span class="pln"> </span><span class="kwd">label</span><span class="pun">:</span><span class="pln">before</span><span class="pun">,</span><span class="pln">
</span><span class="pun">[</span><span class="pln">type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pun">]:</span><span class="pln">not</span><span class="pun">(:</span><span class="pln">checked</span><span class="pun">):</span><span class="pln">focus </span><span class="pun">+</span><span class="pln"> </span><span class="kwd">label</span><span class="pun">:</span><span class="pln">before </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2px</span><span class="pln"> dotted blue</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	الخلاصة
</h2>

<p>
	يجب أن يكون لديك الآن تطبيق قائمة المهام باستخدام إطار عمل Angular الذي يمكنه إضافة العناصر وتعديلها وإزالتها، والخطوة التالية هي إضافة الترشيح Filtering بحيث يمكنك عرض العناصر التي تحقق معايير محددة.
</p>

<p>
	ترجمة -وبتصرُّف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_item_component" rel="external nofollow">Creating an item component</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-angular-r1393/" rel="">مقدمة في مفاهيم Angular</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-angular-%D9%88%D9%86%D8%B4%D8%B1%D9%87%D8%A7-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1388/" rel="">تهيئة بيئة تطبيقات Angular ونشرها على الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-angular-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1380/" rel="">كيفية استعمال Angular في بناء تطبيقات الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A7%D9%84%D9%85%D8%B1%D8%B4%D8%AD%D8%A7%D8%AA-filters-%D9%81%D9%8A-angularjs-r201/" rel="">المرشحات (filters) في AngularJS</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A7%D9%84%D8%AE%D8%AF%D9%85%D8%A7%D8%AA-services-%D9%81%D9%8A-angularjs-r200/" rel="">الخدمات (Services) في AngularJS</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1899</guid><pubDate>Sat, 25 Feb 2023 16:00:00 +0000</pubDate></item><item><title>&#x625;&#x646;&#x634;&#x627;&#x621; &#x62A;&#x637;&#x628;&#x64A;&#x642; &#x642;&#x627;&#x626;&#x645;&#x629; &#x645;&#x647;&#x627;&#x645; &#x648;&#x62A;&#x646;&#x633;&#x64A;&#x642;&#x647; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; Angular</title><link>https://academy.hsoub.com/programming/javascript/angular/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%82%D8%A7%D8%A6%D9%85%D8%A9-%D9%85%D9%87%D8%A7%D9%85-%D9%88%D8%AA%D9%86%D8%B3%D9%8A%D9%82%D9%87-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-angular-r1898/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_02/1471004407_--------Angular.png.882fd58e342c50410dbad08dd00ecc17.png" /></p>
<p>
	أصبحنا الآن جاهزين للبدء بإنشاء تطبيق قائمة المهام باستخدام إطار عمل Angular، إذ سيعرض التطبيق النهائي قائمةً بعناصر المهام وسيتضمن ميزات التعديل والحذف والإضافة، كما سنتعرّف في هذا المقال على بنية تطبيق Angular وسنعمل على عرض قائمة أساسية من عناصر المهام ومعرفة كيفية تنسيق التطبيق.
</p>

<ul>
	<li>
		<strong>المتطلبات الأساسية</strong>: يوصَى على الأقل بأن تكون على دراية بأساسيات لغات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت JavaScript</a>، ومعرفة باستخدام <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471" rel="">سطر الأوامر أو الطرفية</a>.
	</li>
	<li>
		<strong>الهدف</strong>: إنشاء بنية تطبيقك الأساسية، وعرض قائمة بعناصر المهام، وفهم مفاهيم Angular الأساسية مثل بنية المكونات ومشاركة البيانات بين المكونات وإنشاء حلقات المحتوى ومعرفة كيفية تنسيق تطبيق Angular.
	</li>
</ul>

<h2>
	بنية تطبيق قائمة المهام
</h2>

<p>
	يحتوي تطبيق <a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D8%A7-%D9%87%D9%8A-angular%D8%9F-r1379/" rel="">Angular</a> على الملف index.html مثل أيّ تطبيق أساسي لا يستخدِم إطار عمل، ويستخدِم عنصرًا خاصًا هو <code>&lt;app-root&gt;</code> ضمن <a href="https://wiki.hsoub.com/HTML/body" rel="external">الوسم &lt;body&gt;</a>. في الملف <code>index.html</code> لإدراج مكوِّنك الرئيسي الذي يتضمن بدوره المكونات الأخرى التي تنشئها، كما لا تحتاج إلى تعديل الملف <code>index.html</code> بصورة عامة، وإنما يجب أن تركّز اهتمامك على مناطق متخصصة من تطبيقك هي المكونات Components.
</p>

<h3>
	تنظيم تطبيقك ضمن المكونات
</h3>

<p>
	تُعَدّ المكونات لبنة البناء المركزية لتطبيقات Angular، إذ يحتوي تطبيق قائمة المهام على مكونين هما مكوِّن يمثّل أساس تطبيقك ومكوِّن للتعامل مع عناصر المهام.
</p>

<p>
	يتكون كل مكوِّن من صنف TypeScript وشيفرة <a href="https://academy.hsoub.com/programming/html/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-html-r1687/" rel="">HTML</a> و<a href="https://academy.hsoub.com/programming/css/%d8%aa%d8%b9%d8%b1%d9%91%d9%81-%d8%b9%d9%84%d9%89-%d8%a3%d8%b3%d8%a7%d8%b3%d9%8a%d8%a7%d8%aa-css-r70/" rel="">CSS</a>، إذ تُترجَم أو تُحوَّل شيفرة Typescript إلى شيفرة جافاسكربت، مما يعني أنّ تطبيقك يتحوّل في النهاية إلى شيفرة جافاسكربت عادية مع القدرة على استخدام ميزات <a href="https://academy.hsoub.com/programming/javascript/typescript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-typescript-r1214/" rel="">لغة Typescript</a> الموسَّعة وصيغتها المبسَّطة.
</p>

<h3>
	تعديل واجهة المستخدم ديناميكيا باستخدام الموجهين ‎ngIf* و‎*ngFor
</h3>

<p>
	سنغطي في هذا المقال أيضًا موجّهَين Directives مهمين في Angular لتعديل بنية <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-dom-r644/" rel="">نموذج DOM</a> ديناميكيًا، إذ يشبه الموجّه الأمر الذي يمكنك استخدامه في لغة HTML لإحداث تغيير في تطبيقك.
</p>

<p>
	الموجّه الأول هو المكرِّر <code>‎*ngFor</code> الذي يمكنه إنشاء عناصر DOM ديناميكيًا بناءً على عناصر في مصفوفة؛ أما الموجّه الثاني فهو <code>‎*ngIf</code> الذي يمكنك استخدامه لإضافة عناصر أو إزالتها من نموذج DOM بناءً على شرط معيّن، فإذا أراد المستخدِمون مثلًا تعديل عنصر في قائمة المهام، فيمكنك تزويدهم بالوسائل اللازمة لتعديل هذا العنصر؛ وإذا لم يرغبوا في تعديل عنصر، فيمكنك إزالة واجهة التعديل.
</p>

<h3>
	مشاركة البيانات بين المكونات
</h3>

<p>
	يجب إعداد مكوناتك لمشاركة البيانات في هذا التطبيق، إذ يجب على المكوِّن الرئيسي إرسال العنصر الجديد إلى المكوِّن الثاني لإضافة عناصر جديدة إلى قائمة المهام، ويدير هذا المكوِّن الثاني العناصر ويهتم بتعديل العناصر الفردية ووضع علامة عليها بوصفها مكتملةً وحذفها.
</p>

<p>
	يمكن تحقيق مشاركة البيانات بين مكونات Angular باستخدام عناصر تصميم خاصة هي <code>‎@Input()‎</code> و <code>‎@Output()‎</code> التي يمكنك استخدامها لتحديد أنّ بعض الخاصيات تسمح للبيانات بالدخول إلى مكون أو الخروج منه، كما يجب رفع حدث في أحد المكونات لاستخدام عنصر التصميم <code>‎@Output()‎</code> حتى يعرف المكوِّن الآخر أنّ هناك بيانات متاحة.
</p>

<h2>
	تعريف عنصر
</h2>

<p>
	أنشئ ملفًا جديدًا بالاسم <code>item.ts</code> بالمحتويات التالية في المجلد <code>app</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4907_13" style=""><span class="kwd">export</span><span class="pln"> </span><span class="kwd">interface</span><span class="pln"> </span><span class="typ">Item</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  description</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">;</span><span class="pln">
  done</span><span class="pun">:</span><span class="pln"> boolean</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لن تستخدِم هذا الملف حتى وقت لاحق، ولكن هذا هو الوقت المناسب لمعرفة ما هو العنصر <code>item</code>. تنشئ واجهة العنصر <code>Item</code> نموذج كائن عنصر بحيث يفهم تطبيقك ما هو العنصر، في حين يُعَدّ العنصر في تطبيق قائمة المهام كائنًا له وصف ويمكن إكماله.
</p>

<h2>
	إضافة شيفرة إلى القالب AppComponent
</h2>

<p>
	يمكنك الآن -بعد أن عرفت ما هو العنصر <code>item</code>- وضع بعض العناصر في تطبيقك عبر إضافتها إلى ملف TypeScript وهو app.component.ts واستبدال محتوياته بما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4907_15" style=""><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Component</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@Component</span><span class="pun">({</span><span class="pln">
  selector</span><span class="pun">:</span><span class="pln"> </span><span class="str">'app-root'</span><span class="pun">,</span><span class="pln">
  templateUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./app.component.html'</span><span class="pun">,</span><span class="pln">
  styleUrls</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'./app.component.css'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">})</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AppComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  title </span><span class="pun">=</span><span class="pln"> </span><span class="str">'todo'</span><span class="pun">;</span><span class="pln">

  filter</span><span class="pun">:</span><span class="pln"> </span><span class="str">'all'</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="str">'active'</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="str">'done'</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'all'</span><span class="pun">;</span><span class="pln">

  allItems </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> description</span><span class="pun">:</span><span class="pln"> </span><span class="str">'eat'</span><span class="pun">,</span><span class="pln"> done</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> description</span><span class="pun">:</span><span class="pln"> </span><span class="str">'sleep'</span><span class="pun">,</span><span class="pln"> done</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> description</span><span class="pun">:</span><span class="pln"> </span><span class="str">'play'</span><span class="pun">,</span><span class="pln"> done</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> description</span><span class="pun">:</span><span class="pln"> </span><span class="str">'laugh'</span><span class="pun">,</span><span class="pln"> done</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">];</span><span class="pln">

  </span><span class="kwd">get</span><span class="pln"> items</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">filter </span><span class="pun">===</span><span class="pln"> </span><span class="str">'all'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">allItems</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">allItems</span><span class="pun">.</span><span class="pln">filter</span><span class="pun">((</span><span class="pln">item</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">filter </span><span class="pun">===</span><span class="pln"> </span><span class="str">'done'</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> item</span><span class="pun">.</span><span class="pln">done </span><span class="pun">:</span><span class="pln"> </span><span class="pun">!</span><span class="pln">item</span><span class="pun">.</span><span class="pln">done</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span></pre>

<p>
	السطر الأول هو تعليمة استيراد بلغة جافاسكربت تستورد إطار عمل Angular، إذ يحدد عنصر التصميم <code>‎@Component()‎</code> بيانات <code>AppComponent</code> الوصفية، وتكون خاصيات البيانات الوصفية الافتراضية كما يلي:
</p>

<ul>
	<li>
		<code>selector</code>: يخبرك باسم محدّد CSS الذي تستخدِمه في قالب لإنشاء نسخة من هذا المكوِّن وهو هنا <code>'app-root'</code>، وقد أضافت واجهة Angular CLI الوسم <code>&lt;app-root&gt;&lt;/app-root&gt;</code> في الملف index.html ضمن الوسم <code>body</code> عند إنشاء تطبيقك، كما يمكنك استخدام جميع محددات المكونات بالطريقة نفسها من خلال إضافتها إلى قوالب HTML الخاصة بالمكونات الأخرى.
	</li>
	<li>
		<code>templateUrl</code>: يحدّد ملف HTML لربطه بهذا المكوِّن، وهو هنا <code>'‎./app.component.html'</code>.
	</li>
	<li>
		<code>styleUrls</code>: يوفّر موقع واسم ملف التنسيق الذي ينطبق تحديدًا على هذا المكوِّن، وهو هنا <code>'‎./app.component.css'</code>.
	</li>
</ul>

<p>
	تكون خاصية الترشيح <code>filter</code> من النوع <code>union</code>، وبالتالي يمكن أن يكون للخاصية <code>filter</code> القيمة <code>all</code> أو <code>active</code> أو <code>done</code>، فإذا ارتكبت خطأً مطبعيًا أثناء كتابة القيمة التي تسندها إلى الخاصية <code>filter</code> مع النوع <code>union</code>، فستتيح لغة TypeScript معرفة ذلك لتتمكن من اكتشاف الخطأ مبكرًا، كما يوضح لك هذا المقال كيفية إضافة الترشيح في خطوة لاحقة، ولكن يمكنك استخدام خاصية الترشيح لإظهار القائمة الافتراضية لجميع العناصر.
</p>

<p>
	تحتوي المصفوفة <code>allItems</code> على عناصر المهام وما إذا كانت مكتملةً <code>done</code> أم لا، وتكون قيمة الخاصية <code>done</code> للعنصر الأول<code>eat</code> هي <code>true</code>.
</p>

<p>
	يعمل التابع الجالب <code>get items()‎</code> على استرداد العناصر من المصفوفة <code>allItems</code> إذا كانت الخاصية <code>filter</code> مساويةً للقيمة <code>all</code>، وإلّا فسيعيد الجالب <code>get items()‎</code> العناصر المكتملة <code>done</code> أو العناصر المعلَّقة اعتمادًا على كيفية ترشيح المستخدِم للعرض، إذ ينشئ الجالب Getter مصفوفةً بالاسم <code>items</code> التي سنستخدِمها لاحقًا.
</p>

<h2>
	إضافة شيفرة HTML إلى القالب AppComponent
</h2>

<p>
	استبدل محتويات الملف app.component.html بتوصيف HTML التالي للاطلاع على قائمة العناصر في المتصفح:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4907_17" style=""><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"main"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;h1&gt;</span><span class="pln">My To Do List</span><span class="tag">&lt;/h1&gt;</span><span class="pln">
  </span><span class="tag">&lt;h2&gt;</span><span class="pln">What would you like to do today?</span><span class="tag">&lt;/h2&gt;</span><span class="pln">

  </span><span class="tag">&lt;ul&gt;</span><span class="pln">
    </span><span class="tag">&lt;li</span><span class="pln"> *</span><span class="atn">ngFor</span><span class="pun">=</span><span class="atv">"let item of items"</span><span class="tag">&gt;</span><span class="pln">{{item.description}}</span><span class="tag">&lt;/li&gt;</span><span class="pln">
  </span><span class="tag">&lt;/ul&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	يحتوي <a href="https://wiki.hsoub.com/HTML/li" rel="external">العنصر &lt;li&gt;</a> على الموجّه <code>‎*ngFor</code>، وهو موجّه Angular مضمَّن يُكرَّر على العناصر في المصفوفة <code>items</code>، إذ ينشئ الموجّه <code>‎*ngFor</code> عنصر <code>&lt;li&gt;</code> جديد لكل عنصر، كما توجّه الأقواس المزدوجة المعقوصة التي تحتوي على <code>item.description</code> إطار عمل Angular لملء كل عنصر <code>&lt;li&gt;</code> بنص يصِف كل عنصر.
</p>

<p>
	سترى قائمة العناصر في المتصفح كما يلي:
</p>

<pre class="ipsCode" id="ips_uid_4907_22">My To Do List
What would you like to do today?

* eat
* sleep
* play
* laugh</pre>

<h2>
	إضافة عناصر إلى القائمة
</h2>

<p>
	تحتاج قائمة المهام لطريقة ما لإضافة عناصر، لذا أضِف التابع التالي إلى الصنف في الملف app.component.ts:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4907_24" style=""><span class="pln">addItem</span><span class="pun">(</span><span class="pln">description</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">allItems</span><span class="pun">.</span><span class="pln">unshift</span><span class="pun">({</span><span class="pln">
    description</span><span class="pun">,</span><span class="pln">
    done</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يأخذ التابع <code>addItem()‎</code> عنصرًا يوفّره المستخدِم ويضيفه إلى المصفوفة عندما ينقر المستخدِم على زر "الإضافة Add"، ويستخدِم هذا التابع تابع المصفوفة <code>unshift()‎</code> لإضافة عنصر جديد إلى بداية المصفوفة وأعلى القائمة، أو يمكنك بدلًا من ذلك استخدام التابع <code>push()‎</code> الذي سيضيف العنصر الجديد إلى نهاية المصفوفة وأسفل القائمة، كما يمكنك استخدام التابع <code>addItem()‎</code> من خلال تعديل قسم HTML في القالب <code>AppComponent</code>.
</p>

<p>
	استبدل <a href="https://wiki.hsoub.com/HTML/h1-h6" rel="external">العنصر &lt;h2&gt;</a> بما يلي في الملف app.component.html:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4907_27" style=""><span class="tag">&lt;label</span><span class="pln"> </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"addItemInput"</span><span class="tag">&gt;</span><span class="pln">What would you like to do today?</span><span class="tag">&lt;/label&gt;</span><span class="pln">

</span><span class="tag">&lt;input</span><span class="pln">
  #</span><span class="atn">newItem</span><span class="pln">
  </span><span class="atn">placeholder</span><span class="pun">=</span><span class="atv">"add an item"</span><span class="pln">
  (</span><span class="atn">keyup</span><span class="pln">.</span><span class="atn">enter</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"addItem(newItem.value); newItem.value = ''"</span><span class="pln">
  </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"lg-text-input"</span><span class="pln">
  </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"addItemInput"</span><span class="pln">
</span><span class="tag">/&gt;</span><span class="pln">

</span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"btn-primary"</span><span class="pln"> (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"addItem(newItem.value)"</span><span class="tag">&gt;</span><span class="pln">Add</span><span class="tag">&lt;/button&gt;</span></pre>

<p>
	إذا كتب المستخدِم عنصرًا جديدًا في <a href="https://wiki.hsoub.com/HTML/input" rel="external">عنصر الإدخال &lt;input&gt;</a> وضغط على مفتاح <code>Enter</code>، فسيضيف التابع <code>addItem()‎</code> هذه القيمة إلى المصفوفة <code>items</code>، كما يؤدي الضغط على مفتاح <code>Enter</code> إلى إعادة ضبط قيمة حقل الإدخال <code>&lt;input&gt;</code> إلى سلسلة نصية فارغة، أو يمكن للمستخدِم النقر على زر "الإضافة Add" الذي يستدعي التابع <code>addItem()‎</code> نفسه.
</p>

<h2>
	تنسيق تطبيق Angular
</h2>

<p>
	أصبحت بنية التطبيق الأساسية جاهزةً، إذًا لنطّلع على كيفية تعامل إطار عمل Angular مع تنسيق التطبيقات.
</p>

<h3>
	إضافة التنسيق إلى تطبيق Angular
</h3>

<p>
	تنشئ واجهة Angular CLI نوعين من ملفات التنسيق هما:
</p>

<ul>
	<li>
		تنسيق المكونات: توفِّر واجهة Angular CLI لكل مكوِّن ملفه الخاص بالتنسيق الذي يُطبَّق على هذا المكوِّن فقط.
	</li>
	<li>
		styles.css: يوجد هذا الملف في المجلد src، ويُطبَّق التنسيق الموجود في هذا الملف على التطبيق بأكمله ما لم تحدّد تنسيقًا على مستوى المكوِّن.
	</li>
</ul>

<p>
	يمكن أن يختلف الامتداد الموجود في ملفات CSS اعتمادًا على ما إذا أردت معالج CSS المُسبَق، كما يدعم Angular لغات CSS و SCSS وSass و Less و Stylus.
</p>

<p>
	الصق ما يلي في الملف src/styles.css:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_4907_30" style=""><span class="pln">body </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">font-family</span><span class="pun">:</span><span class="pln"> Helvetica</span><span class="pun">,</span><span class="pln"> Arial</span><span class="pun">,</span><span class="pln"> sans-serif</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">btn-wrapper </span><span class="pun">{</span><span class="pln">
  </span><span class="com">/* flexbox */</span><span class="pln">
  </span><span class="kwd">display</span><span class="pun">:</span><span class="pln"> flex</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">flex-wrap</span><span class="pun">:</span><span class="pln"> nowrap</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">justify-content</span><span class="pun">:</span><span class="pln"> space-between</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">btn </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#000</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#fff</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2px</span><span class="pln"> solid </span><span class="lit">#cecece</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.35rem</span><span class="pln"> </span><span class="lit">1rem</span><span class="pln"> </span><span class="lit">0.25rem</span><span class="pln"> </span><span class="lit">1rem</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">font-size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1rem</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="kwd">btn</span><span class="pun">:</span><span class="pln">hover </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#ecf2fd</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="kwd">btn</span><span class="pun">:</span><span class="pln">active </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#d1e0fe</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="kwd">btn</span><span class="pun">:</span><span class="pln">focus </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">outline</span><span class="pun">:</span><span class="pln"> none</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">border</span><span class="pun">:</span><span class="pln"> black solid </span><span class="lit">2px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">btn-primary </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#fff</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#000</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100%</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.75rem</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">font-size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.3rem</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">border</span><span class="pun">:</span><span class="pln"> black solid </span><span class="lit">2px</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1rem</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="kwd">btn-primary</span><span class="pun">:</span><span class="pln">hover </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#444242</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="kwd">btn-primary</span><span class="pun">:</span><span class="pln">focus </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#000</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">outline</span><span class="pun">:</span><span class="pln"> none</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#000</span><span class="pln"> solid </span><span class="lit">2px</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#d7ecff</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="kwd">btn-primary</span><span class="pun">:</span><span class="pln">active </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#212020</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُطبَّق قسم CSS في الملف src/styles.css على التطبيق بأكمله، ولكن لا يؤثر هذا التنسيق على كل شيء في الصفحة، فالخطوة التالية هي إضافة التنسيق الذي يُطبَّق على الملف AppComponent على وجه التحديد.
</p>

<p>
	أضِف التنسيق التالي في الملف app.component.css:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_4907_32" style=""><span class="pun">.</span><span class="pln">main </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">max-width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">500px</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">85%</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2rem</span><span class="pln"> auto</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1rem</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">text-align</span><span class="pun">:</span><span class="pln"> center</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">box-shadow</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">2px</span><span class="pln"> </span><span class="lit">4px</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> rgba</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0.2</span><span class="pun">),</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">2.5rem</span><span class="pln"> </span><span class="lit">5rem</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> rgba</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0.1</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">@media</span><span class="pln"> screen and </span><span class="pun">(</span><span class="kwd">min-width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">600px</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">main </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">70%</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

label </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">font-size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.5rem</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">font-weight</span><span class="pun">:</span><span class="pln"> bold</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">display</span><span class="pun">:</span><span class="pln"> block</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">padding-bottom</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1rem</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">lg-text-input </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100%</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1rem</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2px</span><span class="pln"> solid </span><span class="lit">#000</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">display</span><span class="pun">:</span><span class="pln"> block</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">box-sizing</span><span class="pun">:</span><span class="pln"> border-box</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">font-size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1rem</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">btn-wrapper </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">margin-bottom</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2rem</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">btn-menu </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">flex-basis</span><span class="pun">:</span><span class="pln"> </span><span class="lit">32%</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">active </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">color</span><span class="pun">:</span><span class="pln"> green</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

ul </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">padding-inline-start</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

ul li </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">list-style</span><span class="pun">:</span><span class="pln"> none</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الخطوة الأخيرة هي زيارة المتصفح مرةً أخرى وإلقاء نظرة على كيفية تحديث تنسيق التطبيق
</p>

<h2>
	الخلاصة
</h2>

<p>
	يجب الآن أن يكون لديك قائمة المهام الأساسية معروضةً في متصفحك مع تنسيق جيد لتطبيق Angular، وسننشئ في المقال التالي مكونًا مناسبًا لعناصر المهام بحيث يمكنك تحديد عناصر المهام وتعديلها وحذفها.
</p>

<p>
	ترجمة -وبتصرُّف- للمقالين <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_todo_list_beginning" rel="external nofollow">Beginning our Angular todo list app</a> و<a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_styling" rel="external nofollow">Styling our Angular app</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A7%D9%84%D8%A8%D8%AF%D8%A1-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-angular-r1897/" rel="">البدء باستخدام إطار عمل Angular</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-r1409/" rel="">مقدمة في بناء تطبيقات الويب باستخدام إطار العمل Angular وقاعدة بيانات Firestore</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D8%AF%D9%88%D9%86%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%AA%D8%AF%D9%88%D9%8A%D9%86%D8%A7%D8%AA-%D9%88%D8%B9%D8%B1%D8%B6%D9%87%D8%A7-r1478/" rel="">بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore - إضافة التدوينات وعرضها</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1898</guid><pubDate>Sat, 18 Feb 2023 16:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x628;&#x62F;&#x621; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x625;&#x637;&#x627;&#x631; &#x639;&#x645;&#x644; Angular</title><link>https://academy.hsoub.com/programming/javascript/angular/%D8%A7%D9%84%D8%A8%D8%AF%D8%A1-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-angular-r1897/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_02/1289186870_----Angular.png.2c663c01cb530fd966e39464df1feba3.png" /></p>
<p>
	حان الوقت الآن للتعرّف على إطار العمل Angular من جوجل، وهو خيار شائع آخر ستصادفه كثيرًا، إذ سنلقي في هذا المقال نظرةً على ما يقدمه إطار Angular وكيفية تثبيت المتطلبات الأساسية وإعداد تطبيق نموذجي، كما سنتعرّف على معمارية إطار Angular الأساسية.
</p>

<ul>
	<li>
		<strong>المتطلبات الأساسية</strong>: يوصَى على الأقل بأن تكون على دراية بأساسيات لغات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت JavaScript</a>، ومعرفة باستخدام <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471" rel="">سطر الأوامر أو الطرفية</a>.
	</li>
	<li>
		<strong>الهدف</strong>: إعداد بيئة تطوير Angular محلية وإنشاء تطبيق البداية وفهم أساسيات كيفية عمله.
	</li>
</ul>

<h2>
	ما هو إطار Angular؟
</h2>

<p>
	يُعَدّ <a href="https://academy.hsoub.com/programming/javascript/angular/" rel="">إطار عمل Angular</a> منصة تطوير مبنية على <a href="https://academy.hsoub.com/programming/javascript/typescript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-typescript-r1214/" rel="">لغة TypeScript</a>، ويشمل Angular بوصفه منصةً ما يلي:
</p>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-framework/" rel="">إطار عمل</a> يعتمد على المكونات لبناء تطبيقات ويب قابلة للتوسع.
	</li>
	<li>
		مجموعة من المكتبات المتكاملة جيدًا التي تغطي مجموعة متنوعة من الميزات بما في ذلك التوجيه وإدارة النماذج والاتصال بين الخادم والعميل وغير ذلك.
	</li>
	<li>
		مجموعة من أدوات المطورين لمساعدتك على تطوير وبناء واختبار وتحديث شيفرتك البرمجية.
	</li>
</ul>

<p>
	إذا أنشأت تطبيقات باستخدام <a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D8%A7-%D9%87%D9%8A-angular%D8%9F-r1379/" rel="">إطار عمل Angular</a>، فستستفيد من المنصة التي يمكنها التوسع من مشاريع مطور واحد إلى تطبيقات على مستوى المؤسسة، وقد صُمِّم إطار عمل Angular لتسهيل عملية التحديث قدر الإمكان، بحيث يمكنك الاستفادة من أحدث التطورات بأقل جهد ممكن، وأفضل ما في الأمر هو أنّ نظام Angular البيئي يتكون من مجموعة متنوعة تضم أكثر من 1.7 مليون مطور ومؤلف مكتبات ومنشئ محتوى.
</p>

<p>
	يجب أن تكون على دراية بواجهة CLI الخاصة بإطار عمل Angular قبل أن تبدأ في استكشاف منصة Angular، إذ تُعَدّ Angular CLI الطريقة الأسرع والأسهل والموصَى بها لتطوير تطبيقات Angular، وتسهّل عددًا من المهام، وإليك بعض الأمثلة عن هذه الأوامر:
</p>

<ul>
	<li>
		<a href="https://angular.io/cli/build" rel="external nofollow">‎<code>ng build</code></a>: يصرّف تطبيق Angular في مجلد الخرج.
	</li>
	<li>
		<a href="https://angular.io/cli/serve" rel="external nofollow">‎<code>ng serve</code></a>: يبني تطبيقك ويشغّله ويعيد بناءه عند حدوث تغييرات في الملف.
	</li>
	<li>
		<a href="https://angular.io/cli/generate" rel="external nofollow">‎<code>ng generate</code></a>: يولّد أو يعدّل الملفات على أساس تخطيطي.
	</li>
	<li>
		<a href="https://angular.io/cli/test" rel="external nofollow">‎<code>ng test</code></a>: يشغّل اختبارات الوحدة على مشروع معيّن.
	</li>
	<li>
		<a href="https://angular.io/cli/e2e" rel="external nofollow">‎<code>ng e2e</code></a>: يبني تطبيق Angular ويشغّله ثم يجري اختبارات شاملة.
	</li>
</ul>

<p>
	ستجد أنّ واجهة سطر الأوامر CLI في Angular أداة قيّمة لبناء تطبيقاتك.
</p>

<p>
	سترشدك هذه السلسلة من المقالات خلال عملية إنشاء تطبيق قائمة المهام، إذ ستتعلم كيفية استخدام إطار عمل Angular لإدارة العناصر وتعديلها وإضافتها وحذفها وترشيحها.
</p>

<h2>
	المتطلبات المسبقة
</h2>

<p>
	تحتاج إلى ما يلي لتثبيت Angular على نظامك المحلي:
</p>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">بيئة Node.js</a> الخاصة بإطار عمل Angular التي تتطلب <a href="https://nodejs.org/en/about/releases/" rel="external nofollow">الإصدار الحالي أو الإصدار النشط للدعم طويل الأمد LTS أو إصدار الصيانة للدعم طويل الأمد</a> من Node.js، ويمكنك الحصول على معلومات حول متطلبات الإصدار المحددة من خلال الاطلاع مفتاح <code>engines</code> في الملف <a href="https://unpkg.com/@angular/cli@13.2.5/package.json" rel="external nofollow">package.json</a>، كما يمكنك الحصول على مزيد من المعلومات حول تثبيت Node.js من خلال زيارة موقع <a href="https://nodejs.org/" rel="external nofollow">nodejs.org</a>، فإذا لم تكن متأكدًا من إصدار Node.js الذي يعمل على نظامك، فشغّل الأمر <code>node -v</code> في نافذة الطرفية.
	</li>
	<li>
		يعتمد <a href="https://academy.hsoub.com/programming/javascript/%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-r1225/" rel="">مدير الحزم npm</a> الخاص بإطار Angular وواجهة Angular CLI وتطبيقات Angular على حزم npm في العديد من الميزات والدوال، ويمكنك تنزيل حزم npm وتثبيتها باستخدام مدير حزم npm، كما سنستخدِم في هذا المقال <a href="https://docs.npmjs.com/cli/install" rel="external nofollow">واجهة سطر أوامر العميل في npm</a> التي تُثبَّت افتراضيًا مع Node.js، وشغّل الأمر <code>node -v</code> في نافذة الطرفية للتحقق من تثبيت عميل npm.
	</li>
</ul>

<h2>
	إعداد التطبيق
</h2>

<p>
	يمكنك استخدام واجهة Angular CLI لتشغيل الأوامر في طرفيتك لإنشاء تطبيقات Angular وبنائها واختبارها ونشرها، لذا شغّل الأمر التالي في طرفيتك لتثبيت Angular CLI:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7641_10" style=""><span class="pln">npm install </span><span class="pun">-</span><span class="pln">g </span><span class="lit">@angular</span><span class="pun">/</span><span class="pln">cli</span></pre>

<p>
	تبدأ أوامر Angular CLI جميعها بالاختصار <code>ng</code> متبوعة بما تريد أن تفعله واجهة سطر الأوامر CLI، لذا استخدم أمر <code>ng new</code> التالي في مجلد سطح المكتب لإنشاء تطبيق جديد بالاسم <code>todo</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7641_12" style=""><span class="pln">ng </span><span class="kwd">new</span><span class="pln"> todo </span><span class="pun">--</span><span class="pln">routing</span><span class="pun">=</span><span class="kwd">false</span><span class="pln"> </span><span class="pun">--</span><span class="pln">style</span><span class="pun">=</span><span class="pln">css</span></pre>

<p>
	ينشئ الأمر <code>ng new</code> تطبيق Angular صغير على سطح المكتب، إذ تحدد الرايات الإضافية <code>‎--routing</code> و <code>‎--style</code> كيفية التعامل مع التنقل والتنسيقات في التطبيق، وسنشرح هذه الميزات بمزيد من التفصيل لاحقًا، فإذا ظهرت رسالة تطالبك بفرض فحص أنواع أكثر صرامة، فيمكنك الرد بنعم.
</p>

<p>
	انتقل إلى مشروعك الجديد باستخدام أمر <code>cd</code> التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7641_14" style=""><span class="pln">cd todo</span></pre>

<p>
	استخدم الأمر التالي لتشغيل التطبيق <code>todo</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7641_16" style=""><span class="pln">ng serve</span></pre>

<p>
	إذا أظهرت واجهة CLI رسالةً تطالبك بالتحليلات، فأجب بلا.
</p>

<p>
	انتقل إلى المضيف المحلي <code><a href="http://localhost:4200/%E2%80%8E" ipsnoembed="false" rel="external nofollow">http://localhost:4200/‎</a></code> في متصفحك لرؤية تطبيق البدء الجديد، فإذا عدّلتَ أيًا من ملفات المصدر، فسيُعاد تحميل التطبيق تلقائيًا.
</p>

<p>
	يمكن أن ترغب في فتح تبويب أو نافذة طرفية ثانية لتشغيل الأوامر أثناء تشغيل الأمر <code>ng serve</code>، فإذا أردت في أيّ وقت التوقف عن تشغيل تطبيقك، فاضغط على الاختصار <code>Ctrl+c</code> في الطرفية.
</p>

<h2>
	تعرف على تطبيق Angular
</h2>

<p>
	توجد ملفات التطبيق التي سنركّز عليها في هذا المقال في المجلد src/app، إذ تتضمن الملفات الأساسية التي تنشئها واجهة CLI تلقائيًا ما يلي:
</p>

<ol>
	<li>
		<code>app.module.ts</code>: يحدّد الملفات التي يستخدمها التطبيق، ويعمل هذا الملف على أساس موزّع مركزي للملفات الأخرى في تطبيقك.
	</li>
	<li>
		<code>app.component.ts</code>: المعروف بوصفه صنفًا، ويحتوي على شيفرة صفحة التطبيق الرئيسية.
	</li>
	<li>
		<code>app.component.html</code>: يحتوي على <a href="https://academy.hsoub.com/programming/html/%D8%AA%D9%86%D9%82%D9%8A%D8%AD-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-html-r1812/" rel="">شيفرة HTML</a> الخاصة بالمكوِّن <code>AppComponent</code>، وتُعرَف محتويات هذا الملف بوصفها قالبًا، إذ يحدد القالب العرض أو ما تراه في المتصفح.
	</li>
	<li>
		<code>app.component.css</code>: يحتوي على تنسيق المكوِّن <code>AppComponent</code>، إذ يمكنك استخدام هذا الملف عندما تريد تعريف التنسيق الذي ينطبق على مكوِّن معيّن فقط وليس على تطبيقك الكامل.
	</li>
</ol>

<p>
	يتكون المكوِّن في إطار عمل Angular من ثلاثة أجزاء رئيسية هي القالب والتنسيق والصنف، إذ تشكّل جميع الملفات <code>app.component.ts</code> و <code>app.component.html</code> و <code>app.component.css</code> مثلًا المكوِّن <code>AppComponent</code>، كما تفصل هذه البنية بين الشيفرة البرمجية والعرض والتنسيق بحيث يكون التطبيق أكثر قابلية للصيانة والتوسع، وبالتالي ستستخدِم أفضل الممارسات منذ البداية باستخدام هذه الطريقة.
</p>

<p>
	كما تنشئ واجهة Angular CLI ملفًا لاختبار المكوِّن بالاسم <code>app.component.spec.ts</code>، ولكننا لن نتطرق للاختبار، لذا يمكنك تجاهل هذا الملف.
</p>

<p>
	تنشئ واجهة CLI هذه الملفات الأربعة في مجلد بالاسم الذي تحدده عندما تنشئ مكوِّنًا.
</p>

<h2>
	بنية تطبيق Angular
</h2>

<p>
	إطار عمل Angular مبني باستخدام لغة <a href="https://wiki.hsoub.com/TypeScript" rel="external">TypeScript</a> والتي تُعَدّ مشتقة من <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-javascript-r664/" rel="">لغة جافاسكربت</a> ومُوسِّعة لها، مما يعني أنّ أيّ شيفرة جافاسكربت صالحة هي شيفرة TypeScript صالحة، كما توفر لغة TypeScript أنواعًا وصيغةً أكثر إيجازًا من التي توفرها لغة جافاسكربت، مما يمنحك أداةً لإنشاء شيفرة برمجية أكثر قابليةً للصيانة مع أخطاء أقل.
</p>

<p>
	تُعَدّ المكونات اللبنات الأساسية لتطبيق Angular، إذ يشتمل المكوِّن على صنف TypeScript الذي يحتوي على عنصر التصميم <code>‎@Component()‎</code> الذي يحدد البيانات الوصفية له مثل قالب HTML وملف التنسيق.
</p>

<h3>
	الصنف
</h3>

<p>
	يُعَدّ الصنف المكان الذي تضع فيه أيّ شيفرة برمجية أو منطقًا يحتاجه المكوِّن، ويمكن أن تتضمن هذه الشيفرة دوالًا ومستمعي أحداث وخاصيات ومراجع للخدمات على سبيل المثال. يوجد الصنف في ملف باسم مثل الاسم <code>feature.component.ts</code>، حيث <code>feature</code> هو اسم مكونك، لذا يمكن أن يكون لديك ملفات بأسماء مثل header.component.ts أو signup.component.ts أو feed.component.ts، ويمكنك إنشاء مكوِّن باستخدام عنصر التصميم <code>‎@Component()‎</code> الذي يحتوي على بيانات وصفية تخبر إطار Angular بمكان العثور على ملفات <a href="https://academy.hsoub.com/programming/html/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-html-r1687/" rel="">HTML</a> و<a href="https://academy.hsoub.com/programming/css/%d8%aa%d8%b9%d8%b1%d9%91%d9%81-%d8%b9%d9%84%d9%89-%d8%a3%d8%b3%d8%a7%d8%b3%d9%8a%d8%a7%d8%aa-css-r70/" rel="">CSS</a>.
</p>

<p>
	إليك المكوِّن التالي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_7641_24" style=""><span class="pln">import </span><span class="pun">{</span><span class="pln"> Component </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">

</span><span class="pun">@</span><span class="pln">Component</span><span class="pun">({</span><span class="pln">
  </span><span class="kwd">selector</span><span class="pun">:</span><span class="pln"> </span><span class="str">'app-item'</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">//</span><span class="pln"> </span><span class="pun">تحدّد</span><span class="pln"> </span><span class="pun">البيانات</span><span class="pln"> </span><span class="pun">الوصفية</span><span class="pln"> </span><span class="pun">التالية</span><span class="pln"> </span><span class="pun">موقع</span><span class="pln"> </span><span class="pun">الأجزاء</span><span class="pln"> </span><span class="pun">الأخرى</span><span class="pln"> </span><span class="pun">من</span><span class="pln"> </span><span class="pun">المكون</span><span class="pln">
  </span><span class="kwd">templateUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./item.component.html'</span><span class="pun">,</span><span class="pln">
  </span><span class="kwd">styleUrls</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'./item.component.css'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">})</span><span class="pln">

export class ItemComponent </span><span class="pun">{</span><span class="pln">
</span><span class="pun">//</span><span class="pln"> </span><span class="pun">ضع</span><span class="pln"> </span><span class="pun">شيفرتك</span><span class="pln"> </span><span class="pun">البرمجية</span><span class="pln"> </span><span class="pun">هنا</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يسمى هذا المكوِّن بالاسم <code>ItemComponent</code> ومحدّده selector بالاسم <code>app-item</code>، إذ يمكنك استخدام المحددات مثل وسوم HTML العادية من خلال وضعها ضمن قوالب أخرى، ويصيّر Render المتصفحُ قالب ذلك المكوِّن عندما يكون المحدِّد ضمن قالب، كما سنرشدك في المقالات اللاحقة لإنشاء مكوِّنين واستخدام أحدهما ضمن الآخر.
</p>

<p>
	<strong>ملاحظة</strong>: لاحظ أن اسم المكون في الأعلى ItemComponent هو نفسه اسم الصنف فما السبب؟ ببساطة الاسمان متماثلان لأن المكون ما هو إلا صنف يصفه المزخرف <code>‎@Component</code>. يوفر نموذج مكونات Angular تغليفًا قويًا وبنية تطبيق سهلة الاستخدام، وتعمل المكونات على تسهيل اختبار الوحدة الخاصة بتطبيقك ويمكنها تحسين قابلية قراءة شيفرتك البرمجية الكلية.
</p>

<h3>
	قالب HTML
</h3>

<p>
	يحتوي كل مكوِّن على قالب HTML يوضح كيفية تصيير هذا المكوِّن، ويمكنك تعريف هذا القالب إما بطريقة مضمَّنة أو باستخدام مسار ملف.
</p>

<p>
	استخدم الخاصية <code>templateUrl</code> للإشارة إلى ملف HTML خارجي كما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7641_26" style=""><span class="pln">@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})

export class AppComponent {
}</span></pre>

<p>
	استخدم الخاصية <code>template</code> واكتب شيفرة HTML بين فواصل عليا مائلة Backticks لكتابة شيفرة HTML مضمّنة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7641_28" style=""><span class="pln">@Component({
  selector: 'app-root',
  template: `</span><span class="tag">&lt;h1&gt;</span><span class="pln">Hi!</span><span class="tag">&lt;/h1&gt;</span><span class="pln">`,
})

export class AppComponent {
}</span></pre>

<p>
	يوسّع إطار عمل Angular لغة HTML باستخدام صيغة إضافية تتيح إدراج قيم ديناميكية من مكونك، كما يحدّث تلقائيًا <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-dom-r644/" rel="">نموذج DOM</a> المصيَّر عندما تتغير حالة المكون، وأحد استخدامات هذه الميزة هو إدراج نص ديناميكي كما هو موضح في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7641_31" style=""><span class="tag">&lt;h1&gt;</span><span class="pln">{{ title }}</span><span class="tag">&lt;/h1&gt;</span></pre>

<p>
	توجّه الأقواس المزدوجة المعقوصة إطار عمل Angular لإدخال المحتويات ضمنها، وتأتي قيمة <code>title</code> من صنف المكوِّن:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7641_34" style=""><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Component</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@Component</span><span class="pln"> </span><span class="pun">({</span><span class="pln">
  selector</span><span class="pun">:</span><span class="pln"> </span><span class="str">'app-root'</span><span class="pun">,</span><span class="pln">
  templateUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./app.component.html'</span><span class="pun">,</span><span class="pln">
  styleUrls</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'./app.component.css'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">})</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AppComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    title </span><span class="pun">=</span><span class="pln"> </span><span class="str">'To do application'</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يرى المتصفح ما يلي عندما يحمّل التطبيقُ المكوِّنَ وقالبه:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7641_36" style=""><span class="tag">&lt;h1&gt;</span><span class="pln">To do application</span><span class="tag">&lt;/h1&gt;</span></pre>

<h3>
	التنسيق
</h3>

<p>
	يمكن للمكوِّن أن يرث التنسيق العام من الملف styles.css الخاص بالتطبيق ثم يعدّل أو يعيد كتابته باستخدام تنسيقه الخاص، ويمكنك كتابة تنسيق خاص بالمكوِّن مباشرةً في عنصر التصميم <code>‎@Component()‎</code> أو تحديد المسار إلى ملف CSS.
</p>

<p>
	استخدِم الخاصية <code>styles</code> لتضمين التنسيق مباشرةً في عنصر التصميم كما يلي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_7641_38" style=""><span class="pun">@</span><span class="pln">Component</span><span class="pun">({</span><span class="pln">
  </span><span class="kwd">selector</span><span class="pun">:</span><span class="pln"> </span><span class="str">'app-root'</span><span class="pun">,</span><span class="pln">
  </span><span class="kwd">templateUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./app.component.html'</span><span class="pun">,</span><span class="pln">
  </span><span class="kwd">styles</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'h1 { color: red; }'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	يستخدِم المكوِّن عادةً تنسيقًا موجودًا ضمن ملف منفصل باستخدام الخاصية <code>styleUrls</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_7641_40" style=""><span class="pun">@</span><span class="pln">Component</span><span class="pun">({</span><span class="pln">
  </span><span class="kwd">selector</span><span class="pun">:</span><span class="pln"> </span><span class="str">'app-root'</span><span class="pun">,</span><span class="pln">
  </span><span class="kwd">templateUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./app.component.html'</span><span class="pun">,</span><span class="pln">
  </span><span class="kwd">styleUrls</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'./app.component.css'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	يمكنك تنظيم ملف CSS بحيث يسهل صيانته ونقله باستخدام التنسيق الخاص بالمكونات.
</p>

<h2>
	الخلاصة
</h2>

<p>
	يجب أن تكون الآن جاهزًا لإنشاء تطبيق Angular وأن يكون لديك فهم أساسي لكيفية عمله، وسنطور في المقال التالي هذه المعرفة ونبدأ ببناء بنية تطبيق قائمة المهام.
</p>

<p>
	ترجمة -وبتصرُّف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_getting_started" rel="external nofollow">Getting started with Angular</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D8%A8%D8%A7%D8%AF%D8%A6-angularjs-r176/" rel="">مبادئ AngularJS</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-r1409/" rel="">مقدمة في بناء تطبيقات الويب باستخدام إطار العمل Angular وقاعدة بيانات Firestore</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D8%AF%D9%88%D9%86%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%AA%D8%AF%D9%88%D9%8A%D9%86%D8%A7%D8%AA-%D9%88%D8%B9%D8%B1%D8%B6%D9%87%D8%A7-r1478/" rel="">بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore - إضافة التدوينات وعرضها</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1897</guid><pubDate>Sat, 11 Feb 2023 16:05:00 +0000</pubDate></item><item><title>&#x646;&#x634;&#x631; &#x645;&#x62F;&#x648;&#x646;&#x629; &#x645;&#x628;&#x646;&#x64A;&#x629; &#x639;&#x628;&#x631; Angular &#x639;&#x644;&#x649; Firebase</title><link>https://academy.hsoub.com/programming/javascript/angular/%D9%86%D8%B4%D8%B1-%D9%85%D8%AF%D9%88%D9%86%D8%A9-%D9%85%D8%A8%D9%86%D9%8A%D8%A9-%D8%B9%D8%A8%D8%B1-angular-%D8%B9%D9%84%D9%89-firebase-r1508/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_03/623c44325064e_----Angular--Firebase-----Angular-firestore-firebase-blog-.png.5cace52fa975d8104f8edb40d44b29c0.png" /></p>

<p>
	تعلمنا في الأجزاء السابقة من هذه السلسلة كيفية إنشاء مدونة وإضافة التدوينات وعرضها وحذف كل تدوينة والتعديل عليها وكذلك إضافة استيثاق جوجل وأضفنا صلاحيات للمستخدمين للوصول للمدونة، أما هذه المرة سنكمل ما بقي عمل على المدونة ونضيف اللمسات النهائية عليها مثل إضافة الملف الشخصي لكل كاتب ونشر التعليقات ومشاركة التدوينات على الشبكات الاجتماعية وغيرها ثم سننشر المدونة التي طورناها محليًا على الإنترنت باستعمال Firebase.
</p>

<p>
	هذا المقال جزء من سلسلة عن بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore
</p>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-r1409/" rel="">مقدمة في بناء تطبيقات الويب باستخدام إطار العمل Angular وقاعدة بيانات Firestore</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D8%AF%D9%88%D9%86%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%AA%D8%AF%D9%88%D9%8A%D9%86%D8%A7%D8%AA-%D9%88%D8%B9%D8%B1%D8%B6%D9%87%D8%A7-r1478/" rel="">بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore - إضافة التدوينات وعرضها</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D8%AF%D9%88%D9%86%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-%D8%AA%D8%B9%D8%AF%D9%8A%D9%84-%D8%A7%D9%84%D8%AA%D8%AF%D9%88%D9%8A%D9%86%D8%A7%D8%AA-r1479/" rel="">بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore - تعديل التدوينات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D8%AF%D9%88%D9%86%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%AB%D9%8A%D8%AB%D8%A7%D9%82-r1507/" rel="">بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore - إضافة الاستثيثاق</a>
	</li>
	<li>
		نشر مدونة مبنية عبر Angular على Firebase
	</li>
</ul>
<h2>
	إضافة الملف الشخصي للكاتب
</h2>

<p>
	سنعرض الملف الشخصي للكاتب على الصفحة الرئيسية، فنفِّذ الأمر التالي لإنشاء مكون <code>author-profile</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_13" style="">
<span class="pln">ng g c components</span><span class="pun">/</span><span class="pln">author</span><span class="pun">-</span><span class="pln">profile</span></pre>

<p>
	سنعرض صورة للكاتب مع روابط لحساباته الاجتماعية، وستأتي الصورة من التطبيق نفسه، ونضعها داخل مجلد <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/tree/master/src/assets" rel="external nofollow"><code>src/assets</code></a>.
</p>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/author-profile/author-profile.component.html" rel="external nofollow">component.html</a> وضع الشيفرة التالية فيه:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9167_15" style="">
<span class="tag">&lt;mat-card</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"rightpanel-card mat-elevation-z2"</span><span class="tag">&gt;</span><span class="pln">
 </span><span class="tag">&lt;mat-card-content&gt;</span><span class="pln">
  </span><span class="tag">&lt;h4</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"rightdivtext"</span><span class="tag">&gt;</span><span class="pln">
   Author
  </span><span class="tag">&lt;/h4&gt;</span><span class="pln">
 </span><span class="tag">&lt;/mat-card-content&gt;</span><span class="pln">
 </span><span class="tag">&lt;mat-card-content&gt;</span><span class="pln">
  </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"authorimagecontainer"</span><span class="tag">&gt;</span><span class="pln">
   </span><span class="tag">&lt;img</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"authorimage"</span><span class="pln"> </span><span class="atn">mat-card-avatar</span><span class="pln">
</span><span class="atn">src</span><span class="pun">=</span><span class="atv">"../../../assets/ankit-sharma.jpg"</span><span class="tag">&gt;</span><span class="pln">
   </span><span class="tag">&lt;h5&gt;</span><span class="pln">Ankit Sharma</span><span class="tag">&lt;/h5&gt;</span><span class="pln">
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">
 </span><span class="tag">&lt;/mat-card-content&gt;</span><span class="pln">
 </span><span class="tag">&lt;mat-divider&gt;&lt;/mat-divider&gt;</span><span class="pln">
 </span><span class="tag">&lt;mat-card-content&gt;</span><span class="pln">
  </span><span class="tag">&lt;h4</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"rightdivtext"</span><span class="tag">&gt;</span><span class="pln">
   Follow Me
  </span><span class="tag">&lt;/h4&gt;</span><span class="pln">
 </span><span class="tag">&lt;/mat-card-content&gt;</span><span class="pln">
 </span><span class="tag">&lt;mat-card-content&gt;</span><span class="pln">
  </span><span class="tag">&lt;a</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"https://www.facebook.com/Ankit.Sharma.0709"</span><span class="pln">
</span><span class="atn">target</span><span class="pun">=</span><span class="atv">"_blank"</span><span class="tag">&gt;&lt;i</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"fa fa-facebook-square"</span><span class="pln">
 </span><span class="atn">aria-hidden</span><span class="pun">=</span><span class="atv">"true"</span><span class="tag">&gt;&lt;/i&gt;&lt;/a&gt;</span><span class="pln">
  </span><span class="tag">&lt;a</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"https://twitter.com/ankitsharma_007"</span><span class="pln"> </span><span class="atn">target</span><span class="pun">=</span><span class="atv">"_blank"</span><span class="tag">&gt;&lt;i</span><span class="pln">
</span><span class="atn">class</span><span class="pun">=</span><span class="atv">"fa fa-twitter-square"</span><span class="pln">
 </span><span class="atn">aria-hidden</span><span class="pun">=</span><span class="atv">"true"</span><span class="tag">&gt;&lt;/i&gt;&lt;/a&gt;</span><span class="pln">
  </span><span class="tag">&lt;a</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"https://www.linkedin.com/in/ankitsharma-007/"</span><span class="pln">
</span><span class="atn">target</span><span class="pun">=</span><span class="atv">"_blank"</span><span class="tag">&gt;&lt;i</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"fa fa-linkedin-square"</span><span class="pln">
 </span><span class="atn">aria-hidden</span><span class="pun">=</span><span class="atv">"true"</span><span class="tag">&gt;&lt;/i&gt;&lt;/a&gt;</span><span class="pln">
  </span><span class="tag">&lt;a</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"https://github.com/AnkitSharma-007"</span><span class="pln"> </span><span class="atn">target</span><span class="pun">=</span><span class="atv">"_blank"</span><span class="tag">&gt;&lt;i</span><span class="pln">
</span><span class="atn">class</span><span class="pun">=</span><span class="atv">"fa fa-github-square"</span><span class="pln">
 </span><span class="atn">aria-hidden</span><span class="pun">=</span><span class="atv">"true"</span><span class="tag">&gt;&lt;/i&gt;&lt;/a&gt;</span><span class="pln">
 </span><span class="tag">&lt;/mat-card-content&gt;</span><span class="pln">
</span><span class="tag">&lt;/mat-card&gt;</span></pre>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/author-profile/author-profile.component.scss" rel="external nofollow">component.scss</a> وضع تعريفات التنسيق التالية فيه:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_9167_19" style="">
<span class="pun">.</span><span class="pln">fa</span><span class="pun">-</span><span class="pln">twitter</span><span class="pun">-</span><span class="pln">square </span><span class="pun">{</span><span class="pln">
 color</span><span class="pun">:</span><span class="pln"> </span><span class="com">#55acee;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">fa</span><span class="pun">-</span><span class="pln">facebook</span><span class="pun">-</span><span class="pln">square </span><span class="pun">{</span><span class="pln">
 color</span><span class="pun">:</span><span class="pln"> </span><span class="com">#3b5998;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">fa</span><span class="pun">-</span><span class="pln">linkedin</span><span class="pun">-</span><span class="pln">square </span><span class="pun">{</span><span class="pln">
 color</span><span class="pun">:</span><span class="pln"> </span><span class="com">#0976b4;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">fa</span><span class="pun">-</span><span class="pln">github</span><span class="pun">-</span><span class="pln">square </span><span class="pun">{</span><span class="pln">
 color</span><span class="pun">:</span><span class="pln"> </span><span class="com">#333;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">fa </span><span class="pun">{</span><span class="pln">
 font</span><span class="pun">-</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3em</span><span class="pun">;</span><span class="pln">
 width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1em</span><span class="pun">;</span><span class="pln">
 margin</span><span class="pun">-</span><span class="pln">top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5px</span><span class="pun">;</span><span class="pln">
 cursor</span><span class="pun">:</span><span class="pln"> pointer</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">card</span><span class="pun">-</span><span class="pln">avatar </span><span class="pun">{</span><span class="pln">
 width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100px</span><span class="pun">;</span><span class="pln">
 height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100px</span><span class="pun">;</span><span class="pln">
 margin</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">auto</span><span class="pln">
 padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">authorimagecontainer </span><span class="pun">{</span><span class="pln">
 text</span><span class="pun">-</span><span class="pln">align</span><span class="pun">:</span><span class="pln"> center</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">rightdivtext </span><span class="pun">{</span><span class="pln">
 color</span><span class="pun">:</span><span class="pln"> </span><span class="com">#636467;</span><span class="pln">
 text</span><span class="pun">-</span><span class="pln">transform</span><span class="pun">:</span><span class="pln"> uppercase</span><span class="pun">;</span><span class="pln">
 padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">rightpanel</span><span class="pun">-</span><span class="pln">card </span><span class="pun">{</span><span class="pln">
 margin</span><span class="pun">-</span><span class="pln">bottom</span><span class="pun">:</span><span class="pln"> </span><span class="lit">15px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إذا أردنا عرض الملف الشخصي للكاتب على الصفحة الرئيسية، نحتاج إلى إضافة <code>AuthorProfileComponent</code> إلى <code>HomeComponent</code>.
</p>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/home/home.component.html" rel="external nofollow">home.component.html</a> وحدِّث المحتوى الموجود فيه كما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9167_23" style="">
<span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"row left-panel"</span><span class="tag">&gt;</span><span class="pln">
 </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"col-md-9"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;app-blog-card&gt;&lt;/app-blog-card&gt;</span><span class="pln">
 </span><span class="tag">&lt;/div&gt;</span><span class="pln">
 </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"col-md-3"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;app-author-profile&gt;&lt;/app-author-profile&gt;</span><span class="pln">
 </span><span class="tag">&lt;/div&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	افتح المتصفح لترى الملف الشخصي للكاتب على الجانب الأيمن من الصفحة الرئيسية، وسيعرض الملف الشخصي صورة الكاتب مع روابط حساباته الاجتماعية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94658" href="https://academy.hsoub.com/uploads/monthly_2022_03/profile.png.3b7f65dd18d638e9e3c0efc8083969cd.png" rel=""><img alt="profile.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94658" data-unique="cly1xn8lt" src="https://academy.hsoub.com/uploads/monthly_2022_03/profile.thumb.png.f6fdb6a48bbdc70b5972f74449d710c1.png" style="width: 650px; height: auto;"></a>
</p>

<h2>
	إضافة خيار تمرير إلى أعلى صفحة التدوينة
</h2>

<p>
	سنضيف خيارًا للتمرير إلى أعلى صفحة التدوينة عندما يمرر المستخدم الصفحة إلى الأسفل، بحيث يعرض زر Scroll To top، وإذا نقر المستخدم عليه ينقله إلى أعلى الصفحة بتأثير انتقال ناعم وسلس.
</p>

<p>
	نفّذ الأمر التالي لإنشاء مكون التمرير:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_25" style="">
<span class="pln">ng g c components</span><span class="pun">/</span><span class="typ">Scroller</span></pre>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/scroller/scroller.component.ts" rel="external nofollow">component.ts</a> واستبدل الشيفرة التالية بالموجودة فيه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_28" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Component</span><span class="pun">,</span><span class="pln"> </span><span class="typ">HostListener</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@Component</span><span class="pun">({</span><span class="pln">
 selector</span><span class="pun">:</span><span class="pln"> </span><span class="str">'app-scroller'</span><span class="pun">,</span><span class="pln">
 templateUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./scroller.component.html'</span><span class="pun">,</span><span class="pln">
 styleUrls</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'./scroller.component.scss'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ScrollerComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

 showScroller</span><span class="pun">:</span><span class="pln"> boolean</span><span class="pun">;</span><span class="pln">
 showScrollerPosition </span><span class="pun">=</span><span class="pln"> </span><span class="lit">100</span><span class="pun">;</span><span class="pln">

 </span><span class="lit">@HostListener</span><span class="pun">(</span><span class="str">'window:scroll'</span><span class="pun">)</span><span class="pln">
 checkScroll</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> scrollPosition </span><span class="pun">=</span><span class="pln"> window</span><span class="pun">.</span><span class="pln">pageYOffset </span><span class="pun">||</span><span class="pln">
document</span><span class="pun">.</span><span class="pln">documentElement</span><span class="pun">.</span><span class="pln">scrollTop </span><span class="pun">||</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">scrollTop </span><span class="pun">||</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">scrollPosition </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">showScrollerPosition</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">showScroller </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">showScroller </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">

 gotoTop</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  window</span><span class="pun">.</span><span class="pln">scroll</span><span class="pun">({</span><span class="pln">
   top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
   left</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
   behavior</span><span class="pun">:</span><span class="pln"> </span><span class="str">'smooth'</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنعيِّن قيمة المتغير <code>showScrollerPosition</code> لتكون 100، وهي تمثل عدد البكسلات التي يكون الممرِّر مرئيًا بعدها، وسيراقب التابع <code>checkScroll</code> أحداث التمرير باستخدام المزخرِف <code>‎@HostListener</code>، ونحسب موضع التمرير الحالي للصفحة، فإن كان أكبر من <code>showScrollerPosition</code> فإننا نعرض زر التمرير إلى الأعلى على الصفحة، ويعيّن التابع <code>gotoTop</code> سلوك الممرِّر، بحيث يمرِّر الصفحة إلى الأعلى بتأثير ناعم.
</p>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/scroller/scroller.component.html" rel="external nofollow">scroller.component.html</a> واستبدل الشيفرة التالية بالموجود فيه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_40" style="">
<span class="pun">&lt;</span><span class="pln">div </span><span class="pun">*</span><span class="pln">ngIf</span><span class="pun">=</span><span class="str">"showScroller"</span><span class="pln"> </span><span class="pun">(</span><span class="pln">click</span><span class="pun">)=</span><span class="str">"gotoTop()"</span><span class="pln"> </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"scroll-to-top"</span><span class="pun">&gt;&lt;</span><span class="pln">i </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"fa fa-angle-up"</span><span class="pun">&gt;&lt;</span><span class="str">/i&gt;&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span></pre>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/scroller/scroller.component.scss" rel="external nofollow">scroller.component.scss</a> وضع الشيفرة التالية فيه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_36" style="">
<span class="pun">.</span><span class="pln">scroll</span><span class="pun">-</span><span class="pln">to</span><span class="pun">-</span><span class="pln">top </span><span class="pun">{</span><span class="pln">
 display</span><span class="pun">:</span><span class="pln"> block</span><span class="pun">;</span><span class="pln">
 background</span><span class="pun">:</span><span class="pln"> rgba</span><span class="pun">(</span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0.4</span><span class="pun">);</span><span class="pln">
 color</span><span class="pun">:</span><span class="pln"> </span><span class="pun">#</span><span class="pln">ffffff</span><span class="pun">;</span><span class="pln">
 bottom</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4</span><span class="pun">%;</span><span class="pln">
 cursor</span><span class="pun">:</span><span class="pln"> pointer</span><span class="pun">;</span><span class="pln">
 position</span><span class="pun">:</span><span class="pln"> fixed</span><span class="pun">;</span><span class="pln">
 right</span><span class="pun">:</span><span class="pln"> </span><span class="lit">20px</span><span class="pun">;</span><span class="pln">
 z</span><span class="pun">-</span><span class="pln">index</span><span class="pun">:</span><span class="pln"> </span><span class="lit">999</span><span class="pun">;</span><span class="pln">
 font</span><span class="pun">-</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">24px</span><span class="pun">;</span><span class="pln">
 text</span><span class="pun">-</span><span class="pln">align</span><span class="pun">:</span><span class="pln"> center</span><span class="pun">;</span><span class="pln">
 width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">45px</span><span class="pun">;</span><span class="pln">
 height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">45px</span><span class="pun">;</span><span class="pln">
 border</span><span class="pun">-</span><span class="pln">radius</span><span class="pun">:</span><span class="pln"> </span><span class="lit">50</span><span class="pun">%;</span><span class="pln">

 </span><span class="pun">.</span><span class="pln">fa </span><span class="pun">{</span><span class="pln">
  font</span><span class="pun">-</span><span class="pln">weight</span><span class="pun">:</span><span class="pln"> </span><span class="lit">900</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">scroll</span><span class="pun">-</span><span class="pln">to</span><span class="pun">-</span><span class="pln">top</span><span class="pun">:</span><span class="pln">hover </span><span class="pun">{</span><span class="pln">
 background</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> </span><span class="pun">#</span><span class="pln">b2b2b2</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنضيف الآن <code>ScrollerComponent</code> إلى <code>BlogComponent</code>، فافتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/blog/blog.component.html" rel="external nofollow">blog.component.html</a> وأضف السطر التالي في نهاية الملف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_43" style="">
<span class="pun">&lt;</span><span class="pln">app</span><span class="pun">-</span><span class="pln">scroller</span><span class="pun">&gt;&lt;/</span><span class="pln">app</span><span class="pun">-</span><span class="pln">scroller</span><span class="pun">&gt;</span></pre>

<p>
	وهكذا نكون قد أنشأنا ممرِّرًا لصفحة التدوينة بنجاح، وسنرى في القسم التالي عرضًا لذلك الممرِّر مع خاصية التعليقات.
</p>

<h2>
	نشر التعليقات على التدوينة
</h2>

<p>
	نضيف الآن خاصية أخرى إلى التدوينات، وهي إمكانية نشر التعليقات على كل تدوينة، بحيث يستطيع أي مستخدم سجل دخوله أن يعلِّق على التدوينات الموجودة، ويكون للمستخدم المدير صلاحية حذف أي تعليق. كذلك تُحذف التعليقات على تدوينة ما بمجرد حذف التدوينة نفسها.
</p>

<h3>
	إنشاء نموذج التعليق
</h3>

<p>
	أنشئ الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/models/comment.ts" rel="external nofollow">comment.ts</a> وضع الشيفرة التالية فيه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_45" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Comments</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 commentId</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">;</span><span class="pln">
 blogId</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">;</span><span class="pln">
 email</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">;</span><span class="pln">
 commentedBy</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">;</span><span class="pln">
 content</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">;</span><span class="pln">
 commentDate</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	إنشاء خدمة التعليقات
</h3>

<p>
	سننشئ خدمة تتولى معالجة العمليات المتعلقة بقاعدة البيانات فيما يخص التعليقات، فنفّذ الأمر التالي لإنشاء هذه الخدمة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_47" style="">
<span class="pln">ng g s services</span><span class="pun">/</span><span class="typ">Comment</span></pre>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/services/comment.service.ts" rel="external nofollow">comment.service.ts</a> وأضف تعليمات الاستيراد التالية في أعلاه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_50" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AngularFirestore</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/fire/firestore'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Observable</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'rxjs'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> map </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'rxjs/operators'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Comments</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'../models/comment'</span></pre>

<p>
	والآن، أضف تعريفات التوابع التالية داخل الصنف <code>CommentService</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_52" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CommentService</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

 constructor</span><span class="pun">(</span><span class="kwd">private</span><span class="pln"> db</span><span class="pun">:</span><span class="pln"> </span><span class="typ">AngularFirestore</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span><span class="pln">

 saveComment</span><span class="pun">(</span><span class="pln">comment</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Comments</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> commentData </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">comment</span><span class="pun">));</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">db</span><span class="pun">.</span><span class="pln">collection</span><span class="pun">(</span><span class="str">'comments'</span><span class="pun">).</span><span class="pln">add</span><span class="pun">(</span><span class="pln">commentData</span><span class="pun">);</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">

 getAllCommentsForBlog</span><span class="pun">(</span><span class="pln">blogId</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">):</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Comments</span><span class="pun">[]&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> comments </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">db</span><span class="pun">.</span><span class="pln">collection</span><span class="pun">&lt;</span><span class="typ">Comments</span><span class="pun">&gt;(</span><span class="str">'comments'</span><span class="pun">,</span><span class="pln">
   ref </span><span class="pun">=&gt;</span><span class="pln"> ref</span><span class="pun">.</span><span class="pln">where</span><span class="pun">(</span><span class="str">'blogId'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'=='</span><span class="pun">,</span><span class="pln"> blogId</span><span class="pun">).</span><span class="pln">orderBy</span><span class="pun">(</span><span class="str">'commentDate'</span><span class="pun">,</span><span class="pln">
</span><span class="str">'desc'</span><span class="pun">)).</span><span class="pln">snapshotChanges</span><span class="pun">().</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">
    map</span><span class="pun">(</span><span class="pln">actions </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
     </span><span class="kwd">return</span><span class="pln"> actions</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">
      c </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">({</span><span class="pln">
       commentId</span><span class="pun">:</span><span class="pln"> c</span><span class="pun">.</span><span class="pln">payload</span><span class="pun">.</span><span class="pln">doc</span><span class="pun">.</span><span class="pln">id</span><span class="pun">,</span><span class="pln">
       </span><span class="pun">...</span><span class="pln">c</span><span class="pun">.</span><span class="pln">payload</span><span class="pun">.</span><span class="pln">doc</span><span class="pun">.</span><span class="pln">data</span><span class="pun">()</span><span class="pln">
      </span><span class="pun">}));</span><span class="pln">
    </span><span class="pun">}));</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> comments</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">

 deleteAllCommentForBlog</span><span class="pun">(</span><span class="pln">blogId</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> commentsToDelete </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">db</span><span class="pun">.</span><span class="pln">collection</span><span class="pun">(</span><span class="str">'comments'</span><span class="pun">,</span><span class="pln"> ref </span><span class="pun">=&gt;</span><span class="pln">
ref</span><span class="pun">.</span><span class="pln">where</span><span class="pun">(</span><span class="str">'blogId'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'=='</span><span class="pun">,</span><span class="pln"> blogId</span><span class="pun">)).</span><span class="pln">snapshotChanges</span><span class="pun">();</span><span class="pln">
  commentsToDelete</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">(</span><span class="pln">
   commentList </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    commentList</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">(</span><span class="pln">comment </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
     </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">db</span><span class="pun">.</span><span class="pln">doc</span><span class="pun">(</span><span class="str">'comments/'</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> comment</span><span class="pun">.</span><span class="pln">payload</span><span class="pun">.</span><span class="pln">doc</span><span class="pun">.</span><span class="pln">id</span><span class="pun">).</span><span class="kwd">delete</span><span class="pun">();</span><span class="pln">
     </span><span class="pun">});</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">

 deleteSingleComment</span><span class="pun">(</span><span class="pln">commentId</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">db</span><span class="pun">.</span><span class="pln">doc</span><span class="pun">(</span><span class="str">'comments/'</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> commentId</span><span class="pun">).</span><span class="kwd">delete</span><span class="pun">();</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سيقبل التابع <code>saveComment</code> كائنًا من النوع <code>Comments</code> كمعامِل، وسنحلل المعامِل إلى كائن JSON ونضيفه إلى تجميعة <code>comments</code> في قاعدة بياناتنا، فإذا كانت التجميعة موجودة مسبقًا فسيضاف <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-json-r604/" rel="">كائن JSON</a> إليها، أما إذا لم تكن موجودة في قاعدة البيانات فسينشئ تابع الإضافة تجميعة ويضيف الكائن الجديد إليها.
</p>

<p>
	سيقبل التابع <code>getAllCommentsForBlog</code> معرِّف التدوينة كمعامِل، وسيستعلم هذا التابع في تجميعة comments ويعيد قائمة بجميع التعليقات المتوافقة مع معرِّف التدوينة الممرر إليه. ويرتب <code>commentDate</code> قائمة التعليقات تنازليًا وفق التاريخ، لضمان عرض التعليق الأخير في قمة التعليقات.
</p>

<p>
	يقبل التابع <code>deleteAllCommentForBlog</code> معرِّف التدوينة كمعامِل له، ويحذف جميع التعليقات من تجميعة comments وفقًا لمعرِّف التدوينة الممرر إليه، أما التابع <code>deleteSingleComment</code> فسيأخذ معرِّف التعليق كمعامِل له، ويحذف تعليقًا واحدًا من تجميعة comments وفقًا لمعرِّف التعليق الذي مُرِّر إليه.
</p>

<h3>
	إنشاء مكون التعليق
</h3>

<p>
	نفِّذ الأمر التالي لإنشاء مكون التعليقات الذي يعالج التعليقات التي ينشرها المستخدم:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_55" style="">
<span class="pln">ng g c components</span><span class="pun">/</span><span class="typ">Comments</span></pre>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/comments/comments.component.ts" rel="external nofollow">comments.component.ts</a> وأضف تعليمات الاستيراد التالية في أعلاه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_59" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Input</span><span class="pun">,</span><span class="pln"> </span><span class="typ">OnDestroy</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">DatePipe</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/common'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AppUser</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'src/app/models/appuser'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Comments</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'src/app/models/comment'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">CommentService</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'src/app/services/comment.service'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AuthService</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'src/app/services/auth.service'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">SnackbarService</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'src/app/services/snackbar.service'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Subject</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'rxjs'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> takeUntil </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'rxjs/operators'</span><span class="pun">;</span></pre>

<p>
	أضف مزود DataPipe في قسم مزخرِف <code>‎@component</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_61" style="">
<span class="lit">@Component</span><span class="pun">({</span><span class="pln">
 </span><span class="pun">...</span><span class="pln">
 providers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">DatePipe</span><span class="pun">]</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنحدِّث الآن صنف <code>CommentsComponent</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_63" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CommentsComponent</span><span class="pln"> implements </span><span class="typ">OnInit</span><span class="pun">,</span><span class="pln"> </span><span class="typ">OnDestroy</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

 </span><span class="lit">@Input</span><span class="pun">()</span><span class="pln">
 blogId</span><span class="pun">;</span><span class="pln">

 appUser</span><span class="pun">:</span><span class="pln"> </span><span class="typ">AppUser</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">public</span><span class="pln"> comments </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Comments</span><span class="pun">();</span><span class="pln">
 commentList</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Comments</span><span class="pun">[]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
 </span><span class="kwd">private</span><span class="pln"> unsubscribe$ </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Subject</span><span class="pun">&lt;</span><span class="kwd">void</span><span class="pun">&gt;();</span><span class="pln">

 constructor</span><span class="pun">(</span><span class="kwd">private</span><span class="pln"> datePipe</span><span class="pun">:</span><span class="pln"> </span><span class="typ">DatePipe</span><span class="pun">,</span><span class="pln">
  </span><span class="kwd">private</span><span class="pln"> commentService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">CommentService</span><span class="pun">,</span><span class="pln">
  </span><span class="kwd">private</span><span class="pln"> authService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">AuthService</span><span class="pun">,</span><span class="pln">
  </span><span class="kwd">private</span><span class="pln"> snackBarService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">SnackbarService</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سيقبل هذا المكون معرِّف التدوينة <code>blogId</code> كمُدخَل له، وسنحقن الخدمات في منشئ الصنف، والآن، أضف تعريفات التوابع التالية داخل الصنف <code>CommentsComponent</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_65" style="">
<span class="pln">ngOnInit</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">authService</span><span class="pun">.</span><span class="pln">appUser$</span><span class="pun">.</span><span class="pln">subscribe</span><span class="pun">(</span><span class="pln">appUser </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">appUser </span><span class="pun">=</span><span class="pln">
appUser</span><span class="pun">);</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">getAllComments</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

onCommentPost</span><span class="pun">(</span><span class="pln">commentForm</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">comments</span><span class="pun">.</span><span class="pln">commentDate </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">datePipe</span><span class="pun">.</span><span class="pln">transform</span><span class="pun">(</span><span class="typ">Date</span><span class="pun">.</span><span class="pln">now</span><span class="pun">(),</span><span class="pln"> </span><span class="str">'MMdd-yyyy HH:mm:ss'</span><span class="pun">);</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">comments</span><span class="pun">.</span><span class="pln">blogId </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">blogId</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">commentService</span><span class="pun">.</span><span class="pln">saveComment</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">comments</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">
  commentForm</span><span class="pun">.</span><span class="pln">resetForm</span><span class="pun">()</span><span class="pln">
 </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

getAllComments</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">commentService</span><span class="pun">.</span><span class="pln">getAllCommentsForBlog</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">blogId</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">takeUntil</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">unsubscribe$</span><span class="pun">))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">subscribe</span><span class="pun">(</span><span class="pln">result </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">commentList </span><span class="pun">=</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

deleteComment</span><span class="pun">(</span><span class="pln">commentId</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">confirm</span><span class="pun">(</span><span class="str">'Do you want to delete this comment!!!'</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">commentService</span><span class="pun">.</span><span class="pln">deleteSingleComment</span><span class="pun">(</span><span class="pln">commentId</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">
   </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">snackBarService</span><span class="pun">.</span><span class="pln">showSnackBar</span><span class="pun">(</span><span class="str">'</span><span class="typ">Comment</span><span class="pln"> </span><span class="typ">Deleted</span><span class="pln">
successfully</span><span class="str">');</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
 </span><span class="pun">);</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

login</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">authService</span><span class="pun">.</span><span class="pln">login</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

ngOnDestroy</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">unsubscribe$</span><span class="pun">.</span><span class="pln">next</span><span class="pun">();</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">unsubscribe$</span><span class="pun">.</span><span class="pln">complete</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنعيّن <code>commentDate</code> داخل التابع <code>onCommentPost</code> ليكون التاريخ الحالي، ونعيّن خاصية <code>blogId</code> الخاصة بكائن التعليق لتكون معرِّف التدوينة التي نُشر عليها التعليق، وسنستدعي التابع <code>saveComment</code> الخاص بالخدمة <code>CommentService</code> ليخزن التعليق في قاعدة البيانات.
</p>

<p>
	كذلك فإن التابع <code>getAllComments</code> سيستدعي <code>getAllCommentsForBlog</code> من الخدمة <code>CommentService</code> ويستخدم <code>blogId</code> كمعامِل، وسيجلب هذا التابع قائمة التعليقات المنشورة على التدوينة.
</p>

<p>
	سيسمح التابع <code>deleteComment</code> لنا بحذف تعليق بعينه، وسيعرض صندوق تأكيد يستدعي التابع <code>deleteSingleComment</code> من الخدمة <code>CommentService</code> إذا أكد المستخدم خيار حذف ذلك التعليق. أما التابع <code>login</code> فسيسمح للمستخدم أن يسجل الدخول إلى التطبيق باستخدام حساب جوجل.
</p>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/comments/comments.component.html" rel="external nofollow">comments.component.html</a> واستبدل الشيفرة التالية بالموجودة فيه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_68" style="">
<span class="pun">&lt;</span><span class="pln">ng</span><span class="pun">-</span><span class="pln">template </span><span class="pun">#</span><span class="pln">anonymousUser</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">card </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"comment-card mat-elevation-z2"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">a </span><span class="pun">(</span><span class="pln">click</span><span class="pun">)=</span><span class="str">"login()"</span><span class="pun">&gt;</span><span class="typ">Login</span><span class="pln"> </span><span class="kwd">with</span><span class="pln"> </span><span class="typ">Google</span><span class="pun">&lt;/</span><span class="pln">a</span><span class="pun">&gt;</span><span class="pln"> to post comments
 </span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">card</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">ng</span><span class="pun">-</span><span class="pln">template</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">card </span><span class="pun">*</span><span class="pln">ngIf</span><span class="pun">=</span><span class="str">"appUser; else anonymousUser"</span><span class="pln"> </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"comment-card matelevation-z2"</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">card</span><span class="pun">-</span><span class="pln">title</span><span class="pun">&gt;</span><span class="pln">
 LEAVE A REPLY
 </span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">card</span><span class="pun">-</span><span class="pln">title</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">card</span><span class="pun">-</span><span class="pln">subtitle</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="typ">Your</span><span class="pln"> email address will not be published</span><span class="pun">.</span><span class="pln"> </span><span class="typ">Required</span><span class="pln"> fields are
marked </span><span class="pun">*</span><span class="pln">
 </span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">card</span><span class="pun">-</span><span class="pln">subtitle</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">card</span><span class="pun">-</span><span class="pln">content</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">form </span><span class="pun">#</span><span class="pln">commentForm</span><span class="pun">=</span><span class="str">"ngForm"</span><span class="pln"> </span><span class="pun">(</span><span class="pln">ngSubmit</span><span class="pun">)=</span><span class="str">"</span><span class="pln">commentForm</span><span class="pun">.</span><span class="pln">form</span><span class="pun">.</span><span class="pln">valid
</span><span class="pun">&amp;&amp;</span><span class="pln"> onCommentPost</span><span class="pun">(</span><span class="pln">commentForm</span><span class="pun">)</span><span class="str">"</span><span class="pln"> novalidate</span><span class="pun">&gt;</span><span class="pln">
   </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">form</span><span class="pun">-</span><span class="pln">field </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"full-width"</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">input matInput placeholder</span><span class="pun">=</span><span class="str">"Name"</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"commentedBy"</span><span class="pln">
</span><span class="pun">[(</span><span class="pln">ngModel</span><span class="pun">)]=</span><span class="str">"comments.commentedBy"</span><span class="pln">
 </span><span class="pun">#</span><span class="pln">commentedBy</span><span class="pun">=</span><span class="str">"ngModel"</span><span class="pln"> required</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">error </span><span class="pun">*</span><span class="pln">ngIf</span><span class="pun">=</span><span class="str">"</span><span class="pln">commentForm</span><span class="pun">.</span><span class="pln">submitted </span><span class="pun">&amp;&amp;</span><span class="pln">
commentedBy</span><span class="pun">.</span><span class="pln">errors</span><span class="pun">?.</span><span class="pln">required</span><span class="str">"&gt;</span><span class="typ">Name</span><span class="pln"> is required</span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">error</span><span class="pun">&gt;</span><span class="pln">
   </span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">form</span><span class="pun">-</span><span class="pln">field</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">form</span><span class="pun">-</span><span class="pln">field </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"full-width"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">input matInput placeholder</span><span class="pun">=</span><span class="str">"Email"</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"email"</span><span class="pln">
</span><span class="pun">[(</span><span class="pln">ngModel</span><span class="pun">)]=</span><span class="str">"comments.email"</span><span class="pln"> </span><span class="pun">#</span><span class="pln">email</span><span class="pun">=</span><span class="str">"ngModel"</span><span class="pln"> email
 required</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">error </span><span class="pun">*</span><span class="pln">ngIf</span><span class="pun">=</span><span class="str">"</span><span class="pln">commentForm</span><span class="pun">.</span><span class="pln">submitted </span><span class="pun">&amp;&amp;</span><span class="pln">
email</span><span class="pun">.</span><span class="pln">errors</span><span class="pun">?.</span><span class="pln">required</span><span class="str">"&gt;</span><span class="typ">Email</span><span class="pln"> is required</span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">error</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">error </span><span class="pun">*</span><span class="pln">ngIf</span><span class="pun">=</span><span class="str">"</span><span class="pln">commentForm</span><span class="pun">.</span><span class="pln">submitted </span><span class="pun">&amp;&amp;</span><span class="pln">
email</span><span class="pun">.</span><span class="pln">errors</span><span class="pun">?.</span><span class="pln">email</span><span class="str">"&gt;</span><span class="typ">Invalid</span><span class="pln"> email</span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">error</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">form</span><span class="pun">-</span><span class="pln">field</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">form</span><span class="pun">-</span><span class="pln">field </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"full-width"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">textarea matInput placeholder</span><span class="pun">=</span><span class="str">"Comment"</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"content"</span><span class="pln">
</span><span class="pun">[(</span><span class="pln">ngModel</span><span class="pun">)]=</span><span class="str">"comments.content"</span><span class="pln">
 </span><span class="pun">#</span><span class="pln">content</span><span class="pun">=</span><span class="str">"ngModel"</span><span class="pln"> required</span><span class="pun">&gt;&lt;/</span><span class="pln">textarea</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">error </span><span class="pun">*</span><span class="pln">ngIf</span><span class="pun">=</span><span class="str">"</span><span class="pln">commentForm</span><span class="pun">.</span><span class="pln">submitted </span><span class="pun">&amp;&amp;</span><span class="pln">
content</span><span class="pun">.</span><span class="pln">errors</span><span class="pun">?.</span><span class="pln">required</span><span class="str">"&gt;</span><span class="typ">Comment</span><span class="pln"> is required</span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">error</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">form</span><span class="pun">-</span><span class="pln">field</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">card</span><span class="pun">-</span><span class="pln">actions</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"</span><span class="pln">
 submit</span><span class="str">" mat-raised-button color="</span><span class="pln">primary</span><span class="str">"&gt;</span><span class="typ">Post</span><span class="pln">
</span><span class="typ">Comment</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
   </span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">card</span><span class="pun">-</span><span class="pln">actions</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="pln">form</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">card</span><span class="pun">-</span><span class="pln">content</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">card</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">card </span><span class="pun">*</span><span class="pln">ngFor</span><span class="pun">=</span><span class="str">"let comment of commentList"</span><span class="pln"> </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"comment-card matelevation-z2"</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">card</span><span class="pun">-</span><span class="pln">title</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">div </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"comment-card-title"</span><span class="pun">&gt;</span><span class="pln">
   </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">{{</span><span class="pln">comment</span><span class="pun">.</span><span class="pln">commentedBy</span><span class="pun">}}</span><span class="pln">
   </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
   </span><span class="pun">&lt;</span><span class="pln">div </span><span class="pun">*</span><span class="pln">ngIf</span><span class="pun">=</span><span class="str">"appUser?.isAdmin"</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">button mat</span><span class="pun">-</span><span class="pln">icon</span><span class="pun">-</span><span class="pln">button matTooltip</span><span class="pun">=</span><span class="str">"Delete comment"</span><span class="pln">
matTooltipPosition</span><span class="pun">=</span><span class="str">"before"</span><span class="pln"> color</span><span class="pun">=</span><span class="str">"accent"</span><span class="pln">
 </span><span class="pun">(</span><span class="pln">click</span><span class="pun">)=</span><span class="str">"deleteComment(comment.commentId)"</span><span class="pun">&gt;</span><span class="pln">
     </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">icon</span><span class="pun">&gt;</span><span class="kwd">delete</span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">icon</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
   </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">card</span><span class="pun">-</span><span class="pln">title</span><span class="pun">&gt;</span><span class="pln">

 </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">card</span><span class="pun">-</span><span class="pln">subtitle</span><span class="pun">&gt;{{</span><span class="pln">comment</span><span class="pun">.</span><span class="pln">commentDate </span><span class="pun">|</span><span class="pln"> date</span><span class="pun">:</span><span class="str">'medium'</span><span class="pun">}}&lt;/</span><span class="pln">matcard</span><span class="pun">-</span><span class="pln">subtitle</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">card</span><span class="pun">-</span><span class="pln">content</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">p</span><span class="pun">&gt;{{</span><span class="pln">comment</span><span class="pun">.</span><span class="pln">content</span><span class="pun">}}&lt;/</span><span class="pln">p</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">card</span><span class="pun">-</span><span class="pln">content</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">card</span><span class="pun">&gt;</span></pre>

<p>
	تتاح ميزة نشر التعليقات على التدوينة للمستخدمين الذين سجلوا دخولهم فقط، فإذا لم يكن المستخدم مسجلًا دخوله فنعرض رابط Login with Google لنطلب من المستخدم أن يسجل دخوله بحساب جوجل كي يترك تعليقًا.
</p>

<p>
	ونحن نستخدم استمارة قالبية template-driven لالتقاط تعليقات المستخدم، وهي تستدعي التابع <code>onCommentPost</code> عند الإرسال الناجح، وستحتوي الاستمارة على الحقول الثلاثة التالية:
</p>

<ul>
<li>
		Name: هذا الحقل إجباري ويُستخدم لالتقاط اسم الشخص الذي ينشر التعليق.
	</li>
	<li>
		Email: هذا الحقل إجباري ويُستخدم لالتقاط بريد الشخص الذي ينشر التعليق.
	</li>
	<li>
		Comment: هذا الحقل إجباري أيضًا ويُستخدم لالتقاط نص التعليق.
	</li>
</ul>
<p>
	سنعرض التعليقات المنشورة على التدوينة في تخطيط بطاقة باستخدام العنصر <code>&lt;mat-card&gt;</code>، وسنعرض اسم الشخص الذي ينشر التعليق وتاريخ ذلك النشر ونص التعليق نفسه، وتُعرض قائمة التعليقات أسفل استمارة التعليق مباشرة. أما إذا كان المستخدم له صلاحية المدير فسنعرض أيقونة حذف في كل تعليق لتمكينه من حذف التعليق.
</p>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/comments/comments.component.scss" rel="external nofollow">comments.component.scss</a> وانسخ الشيفرة التالية فيه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_71" style="">
<span class="pln">a</span><span class="pun">:</span><span class="pln">not</span><span class="pun">([</span><span class="pln">href</span><span class="pun">]):</span><span class="pln">not</span><span class="pun">([</span><span class="pln">tabindex</span><span class="pun">])</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 text</span><span class="pun">-</span><span class="pln">decoration</span><span class="pun">:</span><span class="pln"> underline</span><span class="pun">;</span><span class="pln">
 cursor</span><span class="pun">:</span><span class="pln"> pointer</span><span class="pun">;</span><span class="pln">
 color</span><span class="pun">:</span><span class="pln"> </span><span class="pun">#</span><span class="lit">1565C0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">comment</span><span class="pun">-</span><span class="pln">card</span><span class="pun">-</span><span class="pln">title</span><span class="pun">{</span><span class="pln">
 display</span><span class="pun">:</span><span class="pln"> flex</span><span class="pun">;</span><span class="pln">
 justify</span><span class="pun">-</span><span class="pln">content</span><span class="pun">:</span><span class="pln"> space</span><span class="pun">-</span><span class="pln">between</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">comment</span><span class="pun">-</span><span class="pln">card </span><span class="pun">{</span><span class="pln">
 margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10px</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">15px</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">full</span><span class="pun">-</span><span class="pln">width </span><span class="pun">{</span><span class="pln">
 width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنضيف الآن المكون <code>CommentsComponent</code> إلى <code>BlogComponent</code>، فافتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/blog/blog.component.html" rel="external nofollow">blog.component.html</a> وأضف الشيفرة التالية في نهاية الملف، مباشرة قبل السطر الذي أضفنا فيه مكون الممرِّر من قبل <code>ScrollerComponent</code>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9167_75" style="">
<span class="tag">&lt;mat-divider&gt;&lt;/mat-divider&gt;</span><span class="pln">
</span><span class="tag">&lt;app-comments</span><span class="pln"> [</span><span class="atn">blogId</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"postId"</span><span class="tag">&gt;&lt;/app-comments&gt;</span></pre>

<h3>
	تحديث مكون قائمة التدوينات
</h3>

<p>
	سنضيف الآن خاصية حذف جميع التعليقات المرتبطة بتدوينة عند حذف تلك التدوينة، فافتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/blog-card/blog-card.component.ts" rel="external nofollow">blogcard.component.ts</a> وأضف تعريفات الاستيراد الخاصة بالخدمة <code>CommentService</code>، وسنحقن الخدمة في المنشئ:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_79" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">CommentService</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'src/app/services/comment.service'</span><span class="pun">;</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
constructor</span><span class="pun">(</span><span class="pln">
 </span><span class="com">// حقن خدمة</span><span class="pln">
 </span><span class="kwd">private</span><span class="pln"> commentService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">CommentService</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	نحدِّث الآن تابع الحذف داخل صنف <code>BlogCardComponent</code>، ونستدعي التابع <code>deleteAllCommentForBlog</code> المعرَّف في <code>CommentService</code>، فأضف سطر الشيفرة التالي داخل كتلة التابع <code>delete</code>، مباشرة قبل استدعاء التابع <code>showSnackBar</code>، وذلك لضمان أن جميع التعليقات المرتبطة بتدوينة تُحذف عند حذف التدوينة نفسها.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_81" style="">
<span class="kwd">this</span><span class="pun">.</span><span class="pln">commentService</span><span class="pun">.</span><span class="pln">deleteAllCommentForBlog</span><span class="pun">(</span><span class="pln">postId</span><span class="pun">);</span></pre>

<h3>
	إنشاء فهرس في قاعدة بيانات Firebase
</h3>

<p>
	إذا فتحنا المتصفح الآن وانتقلنا إلى صفحة تفاصيل التدوينة سنحصل على خطأ في طرفية المتصفح يقول "ERROR FirebaseError: The query requires an index":
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94659" href="https://academy.hsoub.com/uploads/monthly_2022_03/index.png.2a18224a9d3a8ba5bc3ce36a9eea3756.png" rel=""><img alt="index.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94659" data-unique="9jra46ucm" src="https://academy.hsoub.com/uploads/monthly_2022_03/index.thumb.png.b04ecedd60cd11b9de0ee3ac6634a849.png" style="width: 700px; height: auto;"></a>
</p>

<p>
	بما أننا نستخدم شرط <code>where</code> مع معامل تساوي داخل التابع <code>getAllCommentsForBlog</code>، فنحتاج إلى إنشاء فهرس في قاعدة بياناتنا، وهذا الفهرس ضروري كي يعمل استعلامنا، ونستطيع أن نرى أن رسالة الخطأ توفر رابطًا لإنشاء الفهرس كذلك، فإذا نقرنا على ذلك الرابط سننتقل إلى طرفية firebase، كما نرى في لقطة الشاشة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94660" href="https://academy.hsoub.com/uploads/monthly_2022_03/composite.png.2a8d2b7a88bb6a381aadb02de45e0997.png" rel=""><img alt="composite.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94660" data-unique="x18tqcmwx" src="https://academy.hsoub.com/uploads/monthly_2022_03/composite.thumb.png.13dd992621348c1f95b7525e3bf089a6.png" style="width: 700px; height: auto;"></a>
</p>

<p>
	هنا نرى نافذة Create a composite index على الشاشة مع إعدادات الفهرس، فانقر على زر Create index لإنشاء الفهرس، وسيستغرق ذلك بضع دقائق لبنائه، ثم بمجرد تمام إنشائه تتغير حالة الفهرس إلى Enabled:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94661" href="https://academy.hsoub.com/uploads/monthly_2022_03/enabled.png.b3dba2688377f82dd60551408d5a109e.png" rel=""><img alt="enabled.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94661" data-unique="z4mfh867i" src="https://academy.hsoub.com/uploads/monthly_2022_03/enabled.thumb.png.6ca89e3b138a3e3ed1edf7528b84510e.png" style="width: 750px; height: auto;"></a>
</p>

<h3>
	اختبار التعليقات
</h3>

<p>
	افتح المتصفح وسجل الخروج من التطبيق إذا كنت قد سجلت الدخول إليه، وانتقل إلى صفحة أي تدوينة، ثم مرر إلى أسفل الصفحة، يجب أن ترى زر scroll to top على الجانب الأيمن من الصفحة، فإذا نقرت على ذلك الزر ستعود الصفحة إلى الأعلى بتأثير انتقال ناعم، انظر الصورة أدناه:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94662" href="https://academy.hsoub.com/uploads/monthly_2022_03/scroll.png.a4a7ce86f1ab092e6414b60812639239.png" rel=""><img alt="scroll.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94662" data-unique="ptm5o8riw" src="https://academy.hsoub.com/uploads/monthly_2022_03/scroll.thumb.png.86ec2d316a5d626db0ad749bed60ce04.png" style="width: 650px; height: auto;"></a>
</p>

<p>
	كذلك تستطيع أن ترى رسالة تطلب منك تسجيل الدخول بحساب جوجل لتتمكن من نشر التعليقات، فسجل الدخول به لترى نموذج نشر التعليق. اكتب تفاصيل التعليق واضغط على زر Post Comment ليُنشر التعليق ويُعرض في بطاقة تحت النموذج مباشرة.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94663" href="https://academy.hsoub.com/uploads/monthly_2022_03/comment.png.ddacd8febf683bb2c4859f47e9ec3288.png" rel=""><img alt="comment.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94663" data-unique="2bhvrpwrh" src="https://academy.hsoub.com/uploads/monthly_2022_03/comment.thumb.png.6cd4c08cb29f9fad5eb09529f87c4016.png" style="width: 650px; height: auto;"></a>
</p>

<p>
	ستعرض بطاقة التعليق اسم الشخص الذي كتبه مع تاريخ ووقت نشر التعليق، فإذا سجلت الدخول كمستخدم مدير فسترى كذلك زر Delete Comment في الجانب العلوي الأيمن من بطاقة التعليق.
</p>

<h2>
	إضافة خيار مشاركة للتدوينة
</h2>

<p>
	سنضيف الآن خاصية مشاركة التدوينة بحيث نوفر خيار المشاركة من خلال <a href="https://academy.hsoub.com/marketing/social-media/" rel="">الشبكات الاجتماعية</a> و<a href="https://academy.hsoub.com/devops/servers/mail/" rel="">البريد الإلكتروني</a>، ونستخدم لذلك مكتبة <code>ngx-sharebuttons</code> لإضافة خيار النشر، وهي مكتبة مفتوحة المصدر تزودنا بحل جاهز لإضافة أزرار النشر وأيقوناته لأغلب منصات الرسائل والشبكات الاجتماعية.
</p>

<h3>
	تثبيت ngx-sharebuttons
</h3>

<p>
	نفذ الأمر التالي لتثبيت حزم ngx-share:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_83" style="">
<span class="pln">npm i </span><span class="pun">-</span><span class="pln">S </span><span class="lit">@ngx</span><span class="pun">-</span><span class="pln">share</span><span class="pun">/</span><span class="pln">core </span><span class="lit">@ngx</span><span class="pun">-</span><span class="pln">share</span><span class="pun">/</span><span class="pln">button </span><span class="lit">@ngx</span><span class="pun">-</span><span class="pln">share</span><span class="pun">/</span><span class="pln">buttons
</span><span class="lit">@angular</span><span class="pun">/</span><span class="pln">cdk</span></pre>

<p>
	كما سنثبت حزم الأيقونات باستخدام الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_93" style="">
<span class="pln">npm i </span><span class="pun">-</span><span class="pln">S </span><span class="lit">@fortawesome</span><span class="pun">/</span><span class="pln">fontawesome</span><span class="pun">-</span><span class="pln">svg</span><span class="pun">-</span><span class="pln">core </span><span class="lit">@fortawesome</span><span class="pun">/</span><span class="pln">angularfontawesome </span><span class="lit">@fortawesome</span><span class="pun">/</span><span class="pln">free</span><span class="pun">-</span><span class="pln">solid</span><span class="pun">-</span><span class="pln">svg</span><span class="pun">-</span><span class="pln">icons </span><span class="lit">@fortawesome</span><span class="pun">/</span><span class="pln">free</span><span class="pun">-</span><span class="pln">brandssvg</span><span class="pun">-</span><span class="pln">icons</span></pre>

<p>
	استورد السمة الخاصة بأزرار المشاركة إلى التنسيق العام في الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/styles.scss" rel="external nofollow"><code>app/src/style.scss</code></a>:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_9167_89" style="">
<span class="lit">@import</span><span class="pln"> </span><span class="str">'~@ngx-share/button/themes/circles/circles-dark-theme'</span><span class="pun">;</span></pre>

<p>
	كذلك، استورد كلًا من <code>ShareButtonsModule</code> و <code>ShareButtonsConfig</code> و <code>HttpClientModule</code> إلى الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/app.module.ts" rel="external nofollow"><code>src/app/app.module.ts</code></a>. سننشئ إعدادات خاصة للوحدة <code>ShareButtonsModule</code> كما يظهر في الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_95" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">ShareButtonsConfig</span><span class="pun">,</span><span class="pln"> </span><span class="typ">ShareModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@ngx-share/core'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">FontAwesomeModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@fortawesome/angular-fontawesome'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">HttpClientModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/common/http'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> customConfig</span><span class="pun">:</span><span class="pln"> </span><span class="typ">ShareButtonsConfig</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 twitterAccount</span><span class="pun">:</span><span class="pln"> </span><span class="str">'ankitsharma_007'</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="lit">@NgModule</span><span class="pun">({</span><span class="pln">
 </span><span class="pun">...</span><span class="pln">
 imports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
  </span><span class="typ">HttpClientModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">FontAwesomeModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">ShareModule</span><span class="pun">.</span><span class="pln">withConfig</span><span class="pun">(</span><span class="pln">customConfig</span><span class="pun">),</span><span class="pln">
 </span><span class="pun">],</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	سنعيِّن اسم حساب تويتر <code>twitterAccount</code> الخاص بكاتب التدوينة في الإعدادات المخصصة، وذلك لضمان الإشارة إلى ذلك الحساب في كل مرة ننشر فيها تدوينة على موقع تويتر.
</p>

<h3>
	إنشاء مكون social-share
</h3>

<p>
	نفذ الأمر التالي لإنشاء مكون social-share:
</p>

<pre class="ipsCode">
ng g c components\social-share
</pre>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/social-share/social-share.component.html" rel="external nofollow">social-share.component.html</a> وضع الشيفرة التالية فيه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_98" style="">
<span class="pun">&lt;</span><span class="pln">p</span><span class="pun">&gt;&lt;</span><span class="pln">strong</span><span class="pun">&gt;</span><span class="typ">Found</span><span class="pln"> </span><span class="kwd">this</span><span class="pln"> article helpful</span><span class="pun">!!!</span><span class="pln"> </span><span class="typ">Share</span><span class="pln"> </span><span class="kwd">this</span><span class="pln"> </span><span class="kwd">with</span><span class="pln"> your </span><span class="typ">Friends</span><span class="pun">&lt;</span><span class="str">/strong&gt;&lt;/</span><span class="pln">p</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;</span><span class="pln">button mat</span><span class="pun">-</span><span class="pln">fab shareButton</span><span class="pun">=</span><span class="str">"facebook"</span><span class="pln"> </span><span class="pun">[</span><span class="pln">style</span><span class="pun">.</span><span class="pln">backgroundColor</span><span class="pun">]=</span><span class="str">"share.prop.facebook.color"</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">fa</span><span class="pun">-</span><span class="pln">icon </span><span class="pun">[</span><span class="pln">icon</span><span class="pun">]=</span><span class="str">"share.prop.facebook.icon"</span><span class="pln"> size</span><span class="pun">=</span><span class="str">"lg"</span><span class="pun">&gt;&lt;/</span><span class="pln">fa</span><span class="pun">-</span><span class="pln">icon</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">button mat</span><span class="pun">-</span><span class="pln">fab shareButton</span><span class="pun">=</span><span class="str">"twitter"</span><span class="pln"> </span><span class="pun">[</span><span class="pln">style</span><span class="pun">.</span><span class="pln">backgroundColor</span><span class="pun">]=</span><span class="str">"share.prop.twitter.color"</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">fa</span><span class="pun">-</span><span class="pln">icon </span><span class="pun">[</span><span class="pln">icon</span><span class="pun">]=</span><span class="str">"share.prop.twitter.icon"</span><span class="pln"> size</span><span class="pun">=</span><span class="str">"lg"</span><span class="pun">&gt;&lt;/</span><span class="pln">fa</span><span class="pun">-</span><span class="pln">icon</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">button mat</span><span class="pun">-</span><span class="pln">fab shareButton</span><span class="pun">=</span><span class="str">"linkedin"</span><span class="pln"> </span><span class="pun">[</span><span class="pln">style</span><span class="pun">.</span><span class="pln">backgroundColor</span><span class="pun">]=</span><span class="str">"share.prop.linkedin.color"</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">fa</span><span class="pun">-</span><span class="pln">icon </span><span class="pun">[</span><span class="pln">icon</span><span class="pun">]=</span><span class="str">"share.prop.linkedin.icon"</span><span class="pln"> size</span><span class="pun">=</span><span class="str">"lg"</span><span class="pun">&gt;&lt;/</span><span class="pln">fa</span><span class="pun">-</span><span class="pln">icon</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">button mat</span><span class="pun">-</span><span class="pln">fab shareButton</span><span class="pun">=</span><span class="str">"reddit"</span><span class="pln">
</span><span class="pun">[</span><span class="pln">style</span><span class="pun">.</span><span class="pln">backgroundColor</span><span class="pun">]=</span><span class="str">"share.prop.reddit.color"</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">fa</span><span class="pun">-</span><span class="pln">icon </span><span class="pun">[</span><span class="pln">icon</span><span class="pun">]=</span><span class="str">"share.prop.reddit.icon"</span><span class="pln"> size</span><span class="pun">=</span><span class="str">"lg"</span><span class="pun">&gt;&lt;/</span><span class="pln">fa</span><span class="pun">-</span><span class="pln">icon</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">button mat</span><span class="pun">-</span><span class="pln">fab shareButton</span><span class="pun">=</span><span class="str">"whatsapp"</span><span class="pln"> </span><span class="pun">[</span><span class="pln">style</span><span class="pun">.</span><span class="pln">backgroundColor</span><span class="pun">]=</span><span class="str">"share.prop.whatsapp.color"</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">fa</span><span class="pun">-</span><span class="pln">icon </span><span class="pun">[</span><span class="pln">icon</span><span class="pun">]=</span><span class="str">"share.prop.whatsapp.icon"</span><span class="pln"> size</span><span class="pun">=</span><span class="str">"lg"</span><span class="pun">&gt;&lt;/</span><span class="pln">fa</span><span class="pun">-</span><span class="pln">icon</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">button mat</span><span class="pun">-</span><span class="pln">fab shareButton</span><span class="pun">=</span><span class="str">"telegram"</span><span class="pln"> </span><span class="pun">[</span><span class="pln">style</span><span class="pun">.</span><span class="pln">backgroundColor</span><span class="pun">]=</span><span class="str">"share.prop.telegram.color"</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">fa</span><span class="pun">-</span><span class="pln">icon </span><span class="pun">[</span><span class="pln">icon</span><span class="pun">]=</span><span class="str">"share.prop.telegram.icon"</span><span class="pln"> size</span><span class="pun">=</span><span class="str">"lg"</span><span class="pun">&gt;&lt;/</span><span class="pln">fa</span><span class="pun">-</span><span class="pln">icon</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">button mat</span><span class="pun">-</span><span class="pln">fab shareButton</span><span class="pun">=</span><span class="str">"print"</span><span class="pln"> </span><span class="pun">[</span><span class="pln">style</span><span class="pun">.</span><span class="pln">backgroundColor</span><span class="pun">]=</span><span class="str">"share.prop.print.color"</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">fa</span><span class="pun">-</span><span class="pln">icon </span><span class="pun">[</span><span class="pln">icon</span><span class="pun">]=</span><span class="str">"share.prop.print.icon"</span><span class="pln"> size</span><span class="pun">=</span><span class="str">"lg"</span><span class="pun">&gt;&lt;/</span><span class="pln">fa</span><span class="pun">-</span><span class="pln">icon</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">button mat</span><span class="pun">-</span><span class="pln">fab shareButton</span><span class="pun">=</span><span class="str">"email"</span><span class="pln"> </span><span class="pun">[</span><span class="pln">style</span><span class="pun">.</span><span class="pln">backgroundColor</span><span class="pun">]=</span><span class="str">"share.prop.email.color"</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">fa</span><span class="pun">-</span><span class="pln">icon </span><span class="pun">[</span><span class="pln">icon</span><span class="pun">]=</span><span class="str">"share.prop.email.icon"</span><span class="pln"> size</span><span class="pun">=</span><span class="str">"lg"</span><span class="pun">&gt;&lt;/</span><span class="pln">fa</span><span class="pun">-</span><span class="pln">icon</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span></pre>

<p>
	سنضيف <code>SocialShareComponent</code> في <code>BlogComponent</code>، فافتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/blog/blog.component.html" rel="external nofollow">blog.component.html</a> وضع السطر التالي فيه، مباشرة قبل وسم <code>&lt;mat-divider&gt;</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_100" style="">
<span class="pun">&lt;</span><span class="pln">app</span><span class="pun">-</span><span class="pln">social</span><span class="pun">-</span><span class="pln">share</span><span class="pun">&gt;&lt;/</span><span class="pln">app</span><span class="pun">-</span><span class="pln">social</span><span class="pun">-</span><span class="pln">share</span><span class="pun">&gt;</span></pre>

<h3>
	إعداد حزمة الأيقونات
</h3>

<p>
	تستخدم مكتبة <code>ngx-sharebuttons</code> على المستوى الداخلي مكتبة <code>FortAwesome/angularfontawesome</code> لحزم أيقوناتها، لذا نحتاج إلى إعداد حزم أيقونات fontawesome في <code>SocialShareComponent</code> من أجل استخدام أيقوناتها لأزرار المشاركة.
</p>

<p>
	أنشئ ملفًا جديدًا باسم <code>icons.ts</code> داخل مجلد <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/tree/master/src" rel="external nofollow"><code>src</code></a>، وضع فيه الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_104" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> faTelegramPlane </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@fortawesome/free-brands-svgicons/faTelegramPlane'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> faFacebookF </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@fortawesome/free-brands-svgicons/faFacebookF'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> faTwitter </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@fortawesome/free-brands-svgicons/faTwitter'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> faRedditAlien </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@fortawesome/free-brands-svgicons/faRedditAlien'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> faLinkedinIn </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@fortawesome/free-brands-svgicons/faLinkedinIn'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> faWhatsapp </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@fortawesome/free-brands-svgicons/faWhatsapp'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> faPrint </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@fortawesome/free-solid-svg-icons/faPrint'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> faEnvelope </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@fortawesome/free-solid-svgicons/faEnvelope'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> iconpack </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
 faFacebookF</span><span class="pun">,</span><span class="pln"> faTwitter</span><span class="pun">,</span><span class="pln"> faLinkedinIn</span><span class="pun">,</span><span class="pln"> faRedditAlien</span><span class="pun">,</span><span class="pln">
 faTelegramPlane</span><span class="pun">,</span><span class="pln"> faWhatsapp</span><span class="pun">,</span><span class="pln"> faEnvelope</span><span class="pun">,</span><span class="pln"> faPrint
</span><span class="pun">];</span></pre>

<p>
	لقد استوردنا جميع الأيقونات التي سنستخدمها في تطبيقنا من مكتبة <code>fortawesome</code>، وسنصدر تلك الحزمة الآن ليستخدمها المكون.
</p>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/social-share/social-share.component.ts" rel="external nofollow">social-share.component.ts</a> وأضف تعليمة الاستيراد التالية في أعلاه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_106" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">FaIconLibrary</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@fortawesome/angular-fontawesome'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> iconpack </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'src/icons'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">ShareService</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@ngx-share/core'</span><span class="pun">;</span></pre>

<p>
	حدِّث منشئ الصنف <code>SocialShareComponent</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_109" style="">
<span class="pln">constructor</span><span class="pun">(</span><span class="pln">library</span><span class="pun">:</span><span class="pln"> </span><span class="typ">FaIconLibrary</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> share</span><span class="pun">:</span><span class="pln"> </span><span class="typ">ShareService</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 library</span><span class="pun">.</span><span class="pln">addIcons</span><span class="pun">(...</span><span class="pln">iconpack</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/social-share/social-share.component.scss" rel="external nofollow">social-share.component.scss</a> وأضف تعريفات التنسيق التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_112" style="">
<span class="pln">button</span><span class="pun">{</span><span class="pln">
 margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	اختبار أيقونات المشاركة
</h3>

<p>
	افتح المتصفح وانتقل إلى صفحة أي تدوينة ومرِّر إلى الأسفل، سترى قائمة من أزرار النشر معروضة فيها، وستحصل على خيارات النشر التي أعددناها من قبل، انظر الصورة التالية:
</p>

<p style="text-align: center;">
	<img alt="share.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94664" data-unique="th8lhbbf6" src="https://academy.hsoub.com/uploads/monthly_2022_03/share.png.86fdd508f9073abc02dac29619269034.png" style="width: 600px; height: auto;"></p>

<p>
	إذا نقرت على أي زر من أزرار النشر تلك فستفتح صفحة التطبيق الموافقة لها لتطلب منك تسجيل الدخول، وعند نجاح عملية تسجيل الدخول ستحصل على زر مشاركة للتدوينة، انظر الصورة التالية لترى خيار مشاركة تويتر.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94665" href="https://academy.hsoub.com/uploads/monthly_2022_03/twitter.png.e4e93f5ef1a7286f8aad1b4534228d93.png" rel=""><img alt="twitter.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94665" data-unique="473773ubi" src="https://academy.hsoub.com/uploads/monthly_2022_03/twitter.thumb.png.bdae00311dd2b41f5f4530d25dc1102f.png" style="width: 580px; height: auto;"></a>
</p>

<p>
	كما ترى فقد تمت الإشارة إلى حساب ‎@ankitsharma_007 (صاحب التدوينة) على تويتر، وخاصية الإشارة إلى الحساب تلك متاحة في تويتر فقط.
</p>

<h2>
	نشر التطبيق على Firebase
</h2>

<p>
	تتبقى آخر خطوة وهي نشر التطبيق على Firebase، من خلال الخطوات الموضحة أدناه:
</p>

<p>
	الخطوة الأولى، ثبّت أدوات firebase CLI من خلال npm، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_9167_114" style="">
<span class="pln">npm install </span><span class="pun">-</span><span class="pln">g firebase</span><span class="pun">-</span><span class="pln">tools</span></pre>

<p>
	الخطوة الثانية، شغّل الأمر التالي لبناء التطبيق بإعدادات الإنتاج:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_9167_116" style="">
<span class="pln">ng build </span><span class="pun">--</span><span class="pln">prod</span></pre>

<p>
	سيعين الخيار prod إعدادات البناء إلى هدف الإنتاج، وهو مُعد في إعدادات مساحة العمل بحيث تستفيد جميع عمليات البناء من الربط bundling، وعمليات الإزالة المحدودة للشيفرة الميتة Dead Code -وهي عملية تحذف الشيفرة التي لا تؤثر على نتيجة البرنامج-، وكذلك عمليات حت الأشجار المحدود tree shaking -وهو مصطلح في <a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت</a> لنفس مفهوم إزالة الشيفرة الميتة.
</p>

<p>
	الخطوة الثالثة، افتح نافذة الطرفية داخل مجلد <code>‎/blogsite/dist</code>، وشغّل الأمر التالي لتسجيل الدخول إلى firebase:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_9167_118" style="">
<span class="pln">firebase login</span></pre>

<p>
	سيفتح ذلك نافذة متصفح تطلب تسجيل الدخول إلى firebase باستخدام حساب جوجل. بعد التسجيل، ارجع إلى الطرفية لتنفيذ الأمر التالي.
</p>

<p>
	الخطوة الرابعة، نفّذ الأمر التالي لتهيئة التطبيق:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_120" style="">
<span class="pln">firebase init</span></pre>

<p>
	سيبدأ هذا الأمر مشروع firebase، وسيُطلب منك بضعة أسئلة، فأجب عليها كما يلي:
</p>

<ul>
<li>
		Are you ready to proceed?‎ أدخل y
	</li>
	<li>
		?Which Firebase CLI features do you want to set up for this folder اختر Hosting
	</li>
	<li>
		Please select an option اختر use an existing project
	</li>
	<li>
		Select a default Firebase project for this directory: اختر اسم مشروعك من القائمة.
	</li>
	<li>
		?What do you want to use as your public directory اختر blogsite.
	</li>
	<li>
		?Configure as a single-page app (rewrite all urls to /index.html) أدخل y
	</li>
	<li>
		?File blogsite/index.html already exists. Overwrite أدخل N. ستحصل الآن على رسالة Firebase initialization complete.
	</li>
</ul>
<p>
	الخطوة الخامسة، انشر التطبيق على firebase، من خلال الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9167_122" style="">
<span class="pln">firebase deploy</span></pre>

<p>
	هذا الأمر سينشر التطبيق على Firebase، وسيعطيك رابط استضافة، فانتقل إليه لترى التطبيق منشورًا هناك، انظر الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94666" href="https://academy.hsoub.com/uploads/monthly_2022_03/deploy.png.d2335143aa1e6ff24b31aaa9027e136f.png" rel=""><img alt="deploy.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94666" data-unique="6l2as6x61" src="https://academy.hsoub.com/uploads/monthly_2022_03/deploy.thumb.png.83d9a0c615ee7bd024d9407c95f897b0.png" style=""></a>
</p>

<p>
	كذلك تستطيع الوصول إلى رابط الاستضافة من لوحة تحكم firebase، فانتقل إلى صفحة Page Overview الخاصة بمشروع Firebase واختر Hosting من قائمة Develop. تستطيع رؤية أسماء النطاقات لتطبيق الويب الخاص بك على اليمين.
</p>

<h2>
	خاتمة
</h2>

<p>
	وهكذا نكون قد ختمنا في هذا الفصل عملية بناء مدونة متكاملة كتطبيق وحيد الصفحة باستخدام <a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-angular-r1393/" rel="">إطار العمل Angular</a> للواجهة الأمامية، وقاعدة بيانات Firestore للواجهة الخلفية، وتصبح بإكمالك لكامل السلسلة قادرًا على بناء مدونتك الشخصية ونشرها على الإنترنت ومشاركة أصدقائك تدويناتك ويمكنك إضافة أي ميزات أو خصائص أخرى غير التي ذكرناها وهنا نترك الباب مفتوحًا لإبدعائك.
</p>

<p>
	ترجمة -وبتصرف- لفصول من كتاب Build a full stack web application using angular and firebase لصاحبه Ankit Sharma.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%88%D9%8A%D8%A8-%D9%83%D8%A7%D9%85%D9%84-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-angular-%D9%88%D9%85%D9%86%D8%B5%D8%A9-firebase-r1394/" rel="">بناء تطبيق ويب كامل باستخدام Angular ومنصة Firebase</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D8%A7-%D9%87%D9%8A-angular%D8%9F-r1379/" rel="">ما هي Angular؟</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-angular-%D9%88%D9%86%D8%B4%D8%B1%D9%87%D8%A7-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1388/" rel="">تهيئة بيئة تطبيقات Angular ونشرها على الويب</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1508</guid><pubDate>Thu, 24 Mar 2022 11:18:04 +0000</pubDate></item><item><title>&#x628;&#x646;&#x627;&#x621; &#x645;&#x62F;&#x648;&#x646;&#x629; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x625;&#x637;&#x627;&#x631; &#x627;&#x644;&#x639;&#x645;&#x644; Angular &#x648;&#x642;&#x627;&#x639;&#x62F;&#x629; &#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; Firestore - &#x625;&#x636;&#x627;&#x641;&#x629; &#x627;&#x644;&#x627;&#x633;&#x62A;&#x62B;&#x64A;&#x62B;&#x627;&#x642;</title><link>https://academy.hsoub.com/programming/javascript/angular/%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D8%AF%D9%88%D9%86%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%AB%D9%8A%D8%AB%D8%A7%D9%82-r1507/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_03/623c38c53039f_-------Angular---Firestore-------Angular-firestore-firebase-blog-.png.15ac5018334443ccd598330bcddbdf05.png" /></p>

<p>
	تعلمنا في الجزء الأول من هذا المقال كيفية بناء مدونة كتطبيق وحيد الصفحة باستخدام <a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-angular-r1393/" rel="">إطار العمل Angular</a> للواجهة الأمامية، وقاعدة بيانات Firestore، وإضافة محرر للمدونة، ثم إضافة تدوينات جديدة وعرضها على الصفحة الرئيسية، ثم إضافة ميزات للتعديل على التدوينات وحذفها وتنسيق التدوينات في الصفحة الرئيسية وسنكمل في هذا الجزء العمل على المدونة وسنتعلم كيفية إضافة الاستيثاق authentication لتقييد وصول المستخدمين إلى المدونة وإعطاء كل مستخدم صلاحيات مناسبة لإضافة التدونيات والتعديل عليها.
</p>

<p>
	هذا المقال جزء من سلسلة عن بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore
</p>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-r1409/" rel="">مقدمة في بناء تطبيقات الويب باستخدام إطار العمل Angular وقاعدة بيانات Firestore</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D8%AF%D9%88%D9%86%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%AA%D8%AF%D9%88%D9%8A%D9%86%D8%A7%D8%AA-%D9%88%D8%B9%D8%B1%D8%B6%D9%87%D8%A7-r1478/" rel="">بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore - إضافة التدوينات وعرضها</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D8%AF%D9%88%D9%86%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-%D8%AA%D8%B9%D8%AF%D9%8A%D9%84-%D8%A7%D9%84%D8%AA%D8%AF%D9%88%D9%8A%D9%86%D8%A7%D8%AA-r1479/" rel="">بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore - تعديل التدوينات</a>.
	</li>
	<li>
		بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore - إضافة الاستثيثاق.
	</li>
	<li>
		نشر مدونة مبنية عبر Angular على Firebase
	</li>
</ul>
<h2>
	إضافة استيثاق جوجل
</h2>

<p>
	سنضيف الآن ميزة التسجيل بحساب جوجل إلى التطبيق الخاص بنا، ونحتاج هنا إلى تفعيل خاصية الاستيثاق authentication من طرفية Firebase كما يلي:
</p>

<ol>
<li>
		انتقل إلى صفحة Project Overview الخاصة بمشروع Firebase.
	</li>
	<li>
		في القائمة اليسرى، تحت تبويب Develop، اختر Authentication.
	</li>
	<li>
		انتقل إلى التبويب Sign-in method.
	</li>
	<li>
		اختر Google من القائمة التي تظهر تحته.
	</li>
	<li>
		انقر على زر Enable المقابل لخيار Google، ثم انقر على Save للحفظ.
	</li>
</ol>
<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94645" href="https://academy.hsoub.com/uploads/monthly_2022_03/gglauth.png.751b49033fc328583ba400dfc2ee17d1.png" rel=""><img alt="gglauth.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94645" data-unique="qtu1g1trn" src="https://academy.hsoub.com/uploads/monthly_2022_03/gglauth.thumb.png.500d9ea7d1276ddc093b82712ece0de9.png" style=""></a>
</p>

<p>
	سنهيئ التطبيق الآن ليستخدم استيثاق جوجل من Firebase.
</p>

<h3>
	إنشاء نموذج AppUser
</h3>

<p>
	أنشئ الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/models/appuser.ts" rel="external nofollow">appuser.ts</a> وضع الشيفرة التالية فيه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_37" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AppUser</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 name</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">;</span><span class="pln">
 email</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">;</span><span class="pln">
 isAdmin</span><span class="pun">:</span><span class="pln"> boolean</span><span class="pun">;</span><span class="pln">
 photoURL</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	إنشاء خدمة الاستيثاق
</h3>

<p>
	لإنشاء خدمة تعالج عملية الاستيثاق، نكتب الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_39" style="">
<span class="pln">ng g s services</span><span class="pun">/</span><span class="pln">auth</span></pre>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/services/auth.service.ts#L2-L8" rel="external nofollow">auth.service.ts</a> وضع تعريفات الاستيراد التالية فيه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_19" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AppUser</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'../models/appuser'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">,</span><span class="pln"> of </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'rxjs'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AngularFireAuth</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/fire/auth'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">ActivatedRoute</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Router</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/router'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AngularFirestore</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/fire/firestore'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> switchMap </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'rxjs/operators'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> as firebase from </span><span class="str">'firebase/app'</span><span class="pun">;</span></pre>

<p>
	صرِّح عن الكائن الملاحَظ observable الذي من النوع <code>AppUser</code> في الصنف <code>AuthService</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_21" style="">
<span class="pln">appUser$</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">AppUser</span><span class="pun">&gt;;</span></pre>

<p>
	ثم احقن الخدمات في المنشئ:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_23" style="">
<span class="pln">constructor</span><span class="pun">(</span><span class="pln">
 </span><span class="kwd">public</span><span class="pln"> afAuth</span><span class="pun">:</span><span class="pln"> </span><span class="typ">AngularFireAuth</span><span class="pun">,</span><span class="pln">
 </span><span class="kwd">private</span><span class="pln"> route</span><span class="pun">:</span><span class="pln"> </span><span class="typ">ActivatedRoute</span><span class="pun">,</span><span class="pln">
 </span><span class="kwd">private</span><span class="pln"> router</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Router</span><span class="pun">,</span><span class="pln">
 </span><span class="kwd">private</span><span class="pln"> db</span><span class="pun">:</span><span class="pln"> </span><span class="typ">AngularFirestore</span><span class="pln">
</span><span class="pun">)</span></pre>

<p>
	يحصل الكائن الملاحَظ <code>appUser$‎</code> على حالة الاستيثاق للمستخدم، فإذا كان قد سجل دخوله سيجلب بيانات المستخدم من قاعدة بيانات Firebase، وإلا يعيد قيمة غير معرَّفة <code>null</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_43" style="">
<span class="kwd">this</span><span class="pun">.</span><span class="pln">appUser$ </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">afAuth</span><span class="pun">.</span><span class="pln">authState</span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">
 switchMap</span><span class="pun">(</span><span class="pln">user </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">user</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">db</span><span class="pun">.</span><span class="pln">doc</span><span class="pun">&lt;</span><span class="typ">AppUser</span><span class="pun">&gt;(`</span><span class="pln">appusers</span><span class="pun">/</span><span class="pln">$</span><span class="pun">{</span><span class="pln">user</span><span class="pun">.</span><span class="pln">uid</span><span class="pun">}`).</span><span class="pln">valueChanges</span><span class="pun">();</span><span class="pln">
 </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> of</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">);</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
 </span><span class="pun">})</span><span class="pln">
</span><span class="pun">);</span></pre>

<p>
	كذلك، سنضيف التابع <code>updateUserData</code> لحفظ بيانات المستخدم في قاعدة بياناتنا عند نجاح تسجيل الدخول، فنخزن الاسم وعنوان البريد ورابط الصورة التي في حساب جوجل لكل مستخدم في قاعدة البيانات.
</p>

<p>
	نضيف التابع <code>updateUserData</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_45" style="">
<span class="kwd">private</span><span class="pln"> updateUserData</span><span class="pun">(</span><span class="pln">user</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> userRef </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">db</span><span class="pun">.</span><span class="pln">doc</span><span class="pun">(`</span><span class="pln">appusers</span><span class="pun">/</span><span class="pln">$</span><span class="pun">{</span><span class="pln">user</span><span class="pun">.</span><span class="pln">uid</span><span class="pun">}`);</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> data </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> user</span><span class="pun">.</span><span class="pln">displayName</span><span class="pun">,</span><span class="pln">
  email</span><span class="pun">:</span><span class="pln"> user</span><span class="pun">.</span><span class="pln">email</span><span class="pun">,</span><span class="pln">
  photoURL</span><span class="pun">:</span><span class="pln"> user</span><span class="pun">.</span><span class="pln">photoURL
 </span><span class="pun">};</span><span class="pln">
 </span><span class="kwd">return</span><span class="pln"> userRef</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="pln">data</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> merge</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ونحن نخزن هذه البيانات لسببين:
</p>

<ul>
<li>
		معرفة المستخدمين الذين سجلوا الدخول إلى تطبيقنا.
	</li>
	<li>
		عرض اسم المستخدم وصورته في شريط التنقل في حالة تسجيل الدخول الناجح.
	</li>
</ul>
<p>
	أضف تعريف التابع التالي لتسجيل الدخول والخروج في صنف <code>AuthService</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_47" style="">
<span class="pln">async login</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> returnUrl </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">route</span><span class="pun">.</span><span class="pln">snapshot</span><span class="pun">.</span><span class="pln">queryParamMap</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'returnUrl'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">||</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">router</span><span class="pun">.</span><span class="pln">url</span><span class="pun">;</span><span class="pln">
 localStorage</span><span class="pun">.</span><span class="pln">setItem</span><span class="pun">(</span><span class="str">'returnUrl'</span><span class="pun">,</span><span class="pln"> returnUrl</span><span class="pun">);</span><span class="pln">

 </span><span class="kwd">const</span><span class="pln"> credential </span><span class="pun">=</span><span class="pln"> await </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">afAuth</span><span class="pun">.</span><span class="pln">auth</span><span class="pun">.</span><span class="pln">signInWithPopup</span><span class="pun">(</span><span class="kwd">new</span><span class="pln">
firebase</span><span class="pun">.</span><span class="pln">auth</span><span class="pun">.</span><span class="typ">GoogleAuthProvider</span><span class="pun">());</span><span class="pln">
 </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">updateUserData</span><span class="pun">(</span><span class="pln">credential</span><span class="pun">.</span><span class="pln">user</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

async logout</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 await </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">afAuth</span><span class="pun">.</span><span class="pln">auth</span><span class="pun">.</span><span class="pln">signOut</span><span class="pun">().</span><span class="pln">then</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">router</span><span class="pun">.</span><span class="pln">navigate</span><span class="pun">([</span><span class="str">'/'</span><span class="pun">]);</span><span class="pln">
 </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنحصل على الرابط <code>returnUrl</code> من الموجه route ثم نخزن قيمته في الذاكرة المحلية، وذلك لضمان عدم فقد القيمة أثناء إعادة توجيه الصفحة أو تحديثها.
</p>

<p>
	سيستوثق التابع <code>signInWithPopup</code> من عميل Firebase باستخدام تدفق توثيق OAuth منبثق -popup-based OAuth authentication flow-، فإذا نجحت عملية تسجيل الدخول سيعيد مستخدمًا مسجَّلًا دخوله مع اعتماديات المزود، أما إذا فشل فسيعيد كائن خطأ يحتوي على معلومات حول هذا الخطأ، وسينفذ التابع <code>logout</code> عملية تسجيل الخروج للمستخدم الحالي ويوجهه إلى الصفحة الرئيسية.
</p>

<h3>
	تحديث AppComponent
</h3>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/app.component.ts" rel="external nofollow">app.component.ts</a> وأضف تعريفات الاستيراد التالية في بدايته:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_31" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AuthService</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./services/auth.service'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Router</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/router'</span><span class="pun">;</span></pre>

<p>
	ثم احقن الخدمات في المنشئ كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_35" style="">
<span class="pln">constructor</span><span class="pun">(</span><span class="pln">
 </span><span class="kwd">private</span><span class="pln"> authService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">AuthService</span><span class="pun">,</span><span class="pln">
 </span><span class="kwd">private</span><span class="pln"> router</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Router</span><span class="pln">
</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	سنشترك في الكائن الملاحَظ <code>appUser$‎</code> داخل التابع <code>ngOnInit</code> الخاص بالصنف <code>AppComponent</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_51" style="">
<span class="kwd">this</span><span class="pun">.</span><span class="pln">authService</span><span class="pun">.</span><span class="pln">appUser$</span><span class="pun">.</span><span class="pln">subscribe</span><span class="pun">(</span><span class="pln">user </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">user</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">const</span><span class="pln"> returnUrl </span><span class="pun">=</span><span class="pln"> localStorage</span><span class="pun">.</span><span class="pln">getItem</span><span class="pun">(</span><span class="str">'returnUrl'</span><span class="pun">);</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">returnUrl</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
     </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   localStorage</span><span class="pun">.</span><span class="pln">removeItem</span><span class="pun">(</span><span class="str">'returnUrl'</span><span class="pun">);</span><span class="pln">
   </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">router</span><span class="pun">.</span><span class="pln">navigateByUrl</span><span class="pun">(</span><span class="pln">returnUrl</span><span class="pun">);</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	إذا كان المستخدم قد سجل دخوله فسنجلب قيمة الرابط المعاد من الذاكرة المحلية، فإذا كان ذلك الرابط متاحًا يُوجَّه المستخدم إليه، ثم نحذف الرابط من الذاكرة المحلية.
</p>

<h3>
	إضافة زر تسجيل الدخول في شريط التنقل
</h3>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/nav-bar/nav-bar.component.ts" rel="external nofollow">nav-bar.component.ts</a> وأضف تعريفات الاستيراد التالية في بدايته:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_53" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AuthService</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'src/app/services/auth.service'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AppUser</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'src/app/models/appuser'</span><span class="pun">;</span></pre>

<p>
	سنصرِّح عن خاصية تحتفظ ببيانات المستخدم، كما سنحقن <code>AuthService</code> في المنشئ، انظر الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_55" style="">
<span class="pln">appUser</span><span class="pun">:</span><span class="pln"> </span><span class="typ">AppUser</span><span class="pun">;</span><span class="pln">

constructor</span><span class="pun">(</span><span class="kwd">private</span><span class="pln"> authService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">AuthService</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span></pre>

<p>
	كما سنشترك في الكائن <code>appUser$‎</code> الخاص بالخدمة <code>AuthService</code> ونعيِّن الخاصية <code>appUser</code>، فنفِّذ الواجهة <code>OnInit</code> على صنف <code>NavBarComponent</code>، وأضف السطر التالي إلى التابع <code>ngOnInit</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_57" style="">
<span class="kwd">this</span><span class="pun">.</span><span class="pln">authService</span><span class="pun">.</span><span class="pln">appUser$</span><span class="pun">.</span><span class="pln">subscribe</span><span class="pun">(</span><span class="pln">appUser </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">appUser </span><span class="pun">=</span><span class="pln"> appUser</span><span class="pun">);</span></pre>

<p>
	كما سنضيف التابع من أجل معالجة تسجيل الدخول والخروج من تطبيقنا داخل الصنف <code>NavBarComponent</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_60" style="">
<span class="pln">login</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">authService</span><span class="pun">.</span><span class="pln">login</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

logout</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">authService</span><span class="pun">.</span><span class="pln">logout</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنحدِّث الآن قالب شريط التنقل، فافتح الملف الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/nav-bar/nav-bar.component.html" rel="external nofollow">nav-bar.component.html</a> واستبدل الشيفرة التالية بالموجود فيه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_62" style="">
<span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">toolbar </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"nav-bar mat-elevation-z2"</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">button mat</span><span class="pun">-</span><span class="pln">button </span><span class="pun">[</span><span class="pln">routerLink</span><span class="pun">]=</span><span class="str">'["/"]'</span><span class="pun">&gt;</span><span class="pln"> </span><span class="typ">My</span><span class="pln"> blog </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">ng</span><span class="pun">-</span><span class="pln">container </span><span class="pun">*</span><span class="pln">ngIf</span><span class="pun">=</span><span class="str">"appUser"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">button mat</span><span class="pun">-</span><span class="pln">button </span><span class="pun">[</span><span class="pln">routerLinkActive</span><span class="pun">]=</span><span class="str">'["link-active"]'</span><span class="pln">
</span><span class="pun">[</span><span class="pln">routerLink</span><span class="pun">]=</span><span class="str">'["/addpost"]'</span><span class="pun">&gt;</span><span class="pln">
   </span><span class="typ">Add</span><span class="pln"> </span><span class="typ">Post</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;/</span><span class="pln">ng</span><span class="pun">-</span><span class="pln">container</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">span </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"spacer"</span><span class="pun">&gt;&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">

 </span><span class="pun">&lt;</span><span class="pln">ng</span><span class="pun">-</span><span class="pln">template </span><span class="pun">#</span><span class="pln">anonymousUser</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">button mat</span><span class="pun">-</span><span class="pln">button </span><span class="pun">(</span><span class="pln">click</span><span class="pun">)=</span><span class="str">"login()"</span><span class="pun">&gt;</span><span class="typ">Login</span><span class="pln"> </span><span class="kwd">with</span><span class="pln"> </span><span class="typ">Google</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;/</span><span class="pln">ng</span><span class="pun">-</span><span class="pln">template</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">ng</span><span class="pun">-</span><span class="pln">container </span><span class="pun">*</span><span class="pln">ngIf</span><span class="pun">=</span><span class="str">"appUser; else anonymousUser"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">img mat</span><span class="pun">-</span><span class="pln">card</span><span class="pun">-</span><span class="pln">avatar </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"user-avatar"</span><span class="pln"> src</span><span class="pun">={{</span><span class="pln">appUser</span><span class="pun">.</span><span class="pln">photoURL</span><span class="pun">}}&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">button mat</span><span class="pun">-</span><span class="pln">button </span><span class="pun">[</span><span class="pln">matMenuTriggerFor</span><span class="pun">]=</span><span class="str">"menu"</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">{{</span><span class="pln">appUser</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}}&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">icon</span><span class="pun">&gt;</span><span class="pln">arrow_drop_down</span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">icon</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">menu </span><span class="pun">#</span><span class="pln">menu</span><span class="pun">=</span><span class="str">"matMenu"</span><span class="pun">&gt;</span><span class="pln">
   </span><span class="pun">&lt;</span><span class="pln">button mat</span><span class="pun">-</span><span class="pln">menu</span><span class="pun">-</span><span class="pln">item </span><span class="pun">(</span><span class="pln">click</span><span class="pun">)=</span><span class="pln">logout</span><span class="pun">()&gt;</span><span class="typ">Logout</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">menu</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;/</span><span class="pln">ng</span><span class="pun">-</span><span class="pln">container</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">toolbar</span><span class="pun">&gt;</span></pre>

<p>
	إذا كان المستخدم قد سجل الدخول سنعرض كلا من الاسم والصورة اللذين في حساب جوجل له، أما إذا لم يسجل الدخول فسنعرض زر Login with Google على شريط التنقل.
</p>

<p>
	سنضيف تعريف التنسيق التالي في الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/nav-bar/nav-bar.component.scss" rel="external nofollow">nav-bar.component.scss</a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_64" style="">
<span class="pun">.</span><span class="pln">user</span><span class="pun">-</span><span class="pln">avatar </span><span class="pun">{</span><span class="pln">
 height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">40px</span><span class="pun">;</span><span class="pln">
 width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">40px</span><span class="pun">;</span><span class="pln">
 border</span><span class="pun">-</span><span class="pln">radius</span><span class="pun">:</span><span class="pln"> </span><span class="lit">50</span><span class="pun">%;</span><span class="pln">
 flex</span><span class="pun">-</span><span class="pln">shrink</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	تحديث نموذج التطبيق
</h3>

<p>
	استورد <code>AngularFireAuthModule</code> إلى الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/app.module.ts" rel="external nofollow">app.module.ts</a> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_66" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AngularFireAuthModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/fire/auth'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@NgModule</span><span class="pun">({</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
  imports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
   </span><span class="pun">...</span><span class="pln">
   </span><span class="typ">AngularFireAuthModule</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
</span><span class="pun">})</span></pre>

<h3>
	المستخدم ذو صلاحيات التحرير والحذف
</h3>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/blog-card/blog-card.component.ts" rel="external nofollow">blog-card.component.ts</a> وأضف تعريفي الاستيراد التاليين:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_68" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AppUser</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'src/app/models/appuser'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AuthService</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'src/app/services/auth.service'</span><span class="pun">;</span></pre>

<p>
	سنصرِّح عن خاصية في صنف <code>BlogCardComponent</code> -كما في مكون شريط التنقل-، ونحقن <code>AuthService</code> في المنشئ، انظر الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_70" style="">
<span class="pln">appUser</span><span class="pun">:</span><span class="pln"> </span><span class="typ">AppUser</span><span class="pun">;</span><span class="pln">

constructor</span><span class="pun">(</span><span class="pln">
 </span><span class="com">// خدمات أخرى</span><span class="pln">
 </span><span class="kwd">private</span><span class="pln"> authService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">AuthService</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span></pre>

<p>
	سنشترك في الكائن الملاحَظ <code>appUser$‎</code> من <code>AuthService</code> ونعيّن الخاصية <code>appUser</code>، فأضف السطر التالي إلى التابع <code>ngOnInit</code> الخاص بالصنف <code>BlogCardComponent</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_72" style="">
<span class="kwd">this</span><span class="pun">.</span><span class="pln">authService</span><span class="pun">.</span><span class="pln">appUser$</span><span class="pun">.</span><span class="pln">subscribe</span><span class="pun">(</span><span class="pln">appUser </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">appUser </span><span class="pun">=</span><span class="pln"> appUser</span><span class="pun">);</span></pre>

<p>
	ثم افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/blog-card/blog-card.component.html#L19" rel="external nofollow">blog-card.components.html</a> وأضف توجيه <code>ngIf</code> إلى الوسم <code>&lt;ng-container&gt;</code>، وهو يحتوي أزرار Edit و Delete، وذلك لقصر استخدام هذين الزرين على المستخدمين الذين سجلوا دخولهم.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_74" style="">
<span class="pun">&lt;</span><span class="pln">ng</span><span class="pun">-</span><span class="pln">container </span><span class="pun">*</span><span class="pln">ngIf</span><span class="pun">=</span><span class="str">"appUser"</span><span class="pun">&gt;</span></pre>

<p>
	نحن الآن نسمح بخاصية التعديل والحذف لأي مستخدم قد سجل دخوله، لكن في القسم التالي سنضيف خاصية الترخيص أو الإذن authorization، ثم نحدِّث الشيفرة لتقصر تلك المزايا على المستخدمين المدراء فقط.
</p>

<h3>
	الحصول على اسم الكاتب
</h3>

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

<p>
	أضف تعريفات الاستيراد التالية إلى الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/blog-card/blog-card.component.ts" rel="external nofollow">blog-card.components.ts</a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_76" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AuthService</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'src/app/services/auth.service'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AppUser</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'src/app/models/appuser'</span><span class="pun">;</span></pre>

<p>
	سنصرِّح عن خاصية لتحتوي بيانات المستخدم، ثم نحقن <code>AuthService</code> في المنشئ الخاص بالمكون <code>BlogEditorComponent</code>. كذلك، سنشترك في الكائن الملاحَظ <code>appUser$‎</code> من <code>AuthService</code>، ونعيّن الخاصية <code>appUser</code>، انظر الشيفرة أدناه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_78" style="">
<span class="pln">appUser</span><span class="pun">:</span><span class="pln"> </span><span class="typ">AppUser</span><span class="pun">;</span><span class="pln">

constructor</span><span class="pun">(</span><span class="pln">
 </span><span class="com">// حقن آخر للخدمات</span><span class="pln">
 </span><span class="kwd">private</span><span class="pln"> authService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">AuthService</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span><span class="pln">

 ngOnInit</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="pun">...</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">authService</span><span class="pun">.</span><span class="pln">appUser$</span><span class="pun">.</span><span class="pln">subscribe</span><span class="pun">(</span><span class="pln">appUser </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">appUser </span><span class="pun">=</span><span class="pln">
appUser</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نعيّن خاصية الكاتب للكائن <code>postData</code> أثناء حفظ التدوينة الجديدة، فأضف السطر التالي داخل قسم else من التابع <code>saveBlogPost</code>، مباشرة قبل استدعاء التابع <code>createPost</code> الخاص بالصنف <code>BlogService</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_80" style="">
<span class="kwd">this</span><span class="pun">.</span><span class="pln">postData</span><span class="pun">.</span><span class="pln">author </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">appUser</span><span class="pun">.</span><span class="pln">name</span><span class="pun">;</span></pre>

<p>
	أضف السطر التالي في الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/blog/blog.component.html" rel="external nofollow">blog.component.html</a>، بعد السطر الذي نربط فيه <code>createdDate</code> داخل الوسم <code>&lt;mat-card-subtitle&gt;</code>. يُستخدم هذا لعرض اسم الكاتب إلى جانب تاريخ إنشاء التدوينة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_82" style="">
<span class="pun">&lt;</span><span class="pln">i </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"fa fa-user"</span><span class="pln"> aria</span><span class="pun">-</span><span class="pln">hidden</span><span class="pun">=</span><span class="str">"true"</span><span class="pun">&gt;&lt;/</span><span class="pln">i</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{{</span><span class="pln">postData</span><span class="pun">.</span><span class="pln">author</span><span class="pun">}}</span></pre>

<h3>
	تأمين الوجهات
</h3>

<p>
	سنضيف حاجز استيثاق auth guard إلى التطبيق لتقييد الوصول غير المصرح له إلى وجهات routes بعينها لا يجب على أي مستخدم الوصول لها خصوصًا المستخدم غير المسجل.
</p>

<p>
	شغِّل الأمر التالي لإضافة حاجز جديد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_84" style="">
<span class="pln">ng g g guards</span><span class="pun">/</span><span class="pln">auth </span><span class="pun">--</span><span class="pln">implements </span><span class="typ">CanActivate</span></pre>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/guards/auth.guard.ts" rel="external nofollow"><code>src/app/guards/auth.guard.ts</code></a> وأضف تعريفات الاستيراد التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_86" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Router</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/router'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AuthService</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'../services/auth.service'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> map </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'rxjs/operators'</span><span class="pun">;</span></pre>

<p>
	أضف منشئًا في الصنف <code>AuthGuard</code>، واحقن <code>Router</code> و <code>AuthService</code> في المنشئ كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_88" style="">
<span class="pln">constructor</span><span class="pun">(</span><span class="pln">
 </span><span class="kwd">private</span><span class="pln"> router</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Router</span><span class="pun">,</span><span class="pln">
 </span><span class="kwd">private</span><span class="pln"> authService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">AuthService</span><span class="pln">
</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	سنحدِّث التابع <code>canActivate</code> ليعالج الوصول غير المصدَّق له unauthenticated إلى الوجهات فأضف الشيفرة التالية إلى هذا التابع:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_90" style="">
<span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">authService</span><span class="pun">.</span><span class="pln">appUser$</span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">map</span><span class="pun">(</span><span class="pln">user </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">user</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">router</span><span class="pun">.</span><span class="pln">navigate</span><span class="pun">([</span><span class="str">'/'</span><span class="pun">],</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> queryParams</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> returnUrl</span><span class="pun">:</span><span class="pln"> state</span><span class="pun">.</span><span class="pln">url </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">
 </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}));</span></pre>

<p>
	سنشترك في الكائن <code>appUser$‎</code> لجلب حالة الاستيثاق authentication state للمستخدم، فإذا سجل المستخدم الدخول نعيد true، أما إذا لم يسجل الدخول فتحدث ثلاثة أشياء:
</p>

<ul>
<li>
		نعيّن معامِل استعلام query parameter يسمى <code>returnUrl</code> على قيمة الرابط الحالي.
	</li>
	<li>
		ننقل المستخدم إلى الصفحة الرئيسية.
	</li>
	<li>
		نعيد false من التابع.
	</li>
</ul>
<h3>
	إضافة حواجز الوجهة route guards في نموذج التطبيق
</h3>

<p>
	إذا أردنا تفعيل حاجز الوجة لوجهة بعينها في التطبيق، نحتاج إلى إضافة الخاصية <code>canActivate</code> إلى الوجهة في الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/app.module.ts" rel="external nofollow"><code>app.module.ts</code></a>، وسنؤمن الوجهات الخاصة بإضافة تدوينة جديدة من خلال إضافة الخاصية <code>canActivate</code>.
</p>

<p>
	أضف تعريف الاستيراد التالي في الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/app.module.ts" rel="external nofollow"><code>app.module.ts</code></a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_92" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AuthGuard</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./guards/auth.guard'</span><span class="pun">;</span></pre>

<p>
	وحدِّث الاتجاه الخاص بالمكون <code>addpost</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_94" style="">
<span class="pun">{</span><span class="pln"> path</span><span class="pun">:</span><span class="pln"> </span><span class="str">'addpost'</span><span class="pun">,</span><span class="pln"> component</span><span class="pun">:</span><span class="pln"> </span><span class="typ">BlogEditorComponent</span><span class="pun">,</span><span class="pln"> canActivate</span><span class="pun">:[</span><span class="typ">AuthGuard</span><span class="pun">]</span><span class="pln"> </span><span class="pun">},</span></pre>

<p>
	إذا حاول المستخدم أن يصل إلى الوجهة <code>addpost</code> فسيُستدعى الصنف <code>AuthGuard</code>، فإذا أعاد هذا الصنف true يُسمح للمستخدم أن يصل إلى تلك الوجهة لإضافة تدوينة جديدة، أما إذا أعاد false فلا يُسمح له بالوصول إلى تلك الوجهة ويُعاد إلى الصفحة الرئيسية.
</p>

<h3>
	اختبار عملية الاستيثاق
</h3>

<p>
	افتح المتصفح لترى أن بطاقة التدوينة لا تحتوي على زري التعديل والحذف، كذلك لا يظهر زر Add post في شريط التنقل، وذلك لأن المستخدم لم يسجل الدخول بعد، لهذا نرى زر Login with Google في الركن العلوي الأيمن من شريط التنقل.
</p>

<p>
	إذا ضغطنا على ذلك الزر ستظهر نافذة منبثقة تطلب منا تسجيل الدخول باستخدام حساب جوجل:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94646" href="https://academy.hsoub.com/uploads/monthly_2022_03/chkpt7.png.59f509d11bb6fca4de88100340110f21.png" rel=""><img alt="chkpt7.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94646" data-unique="ibg171b2w" src="https://academy.hsoub.com/uploads/monthly_2022_03/chkpt7.thumb.png.7b121c1eea518fc108149d217eebf84d.png" style="width: 780px; height: auto;"></a>
</p>

<p>
	ستحدِّث الصفحة نفسها بمجرد نجاح تصديق جوجل، ونرى أن الاتجاه الخاص بـ Add post قد ظهر على شريط التنقل، كما ظهر اسم المستخدم الذي سجل الدخول على الشريط، وسيظهر كل من زر Edit و Delete على بطاقة التدوينة، انظر:
</p>

<p style="text-align: center;">
	<img alt="loggedin.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94647" data-unique="p4jvabbwv" src="https://academy.hsoub.com/uploads/monthly_2022_03/loggedin.png.74971535db1056f5114d17dd96a0e298.png" style="width: 450px; height: auto;"></p>

<h2>
	تطبيق التصاريح
</h2>

<p>
	سنطبِّق الآن تصريحًا مبنيًا على قاعدة rule-based لموقعنا، وسنعرِّف دورًا -وهو دور المدير admin- بحيث يستطيع المستخدمون المدراء فقط أن يعدِّلوا ويحذفوا التدوينة، ثم سنضيف إمكانية نشر التعليقات على التدوينة لاحقًا، والتي يستطيع المدير حذفها هي أيضًا.
</p>

<h3>
	إعداد قاعدة بيانات Firebase لدور المدير
</h3>

<p>
	لقد عرَّفنا الخاصية البوليانية <code>isAdmin</code> في الصنف <code>AppUser</code>، وتُستخدم هذه الخاصية لتعريف دور المدير للمستخدم، وسنضيف -يدويًا- حقلًا جديدًا باسم <code>isAdmin</code> للمستخدمين الذين نريد منحهم صلاحية المدير، وسننفذ التغييرات في تجميعة <code>appusers</code> داخل قاعدة بيانات Firebase.
</p>

<p>
	انتقل إلى قاعدة بيانات Firebase وافتح تجميعة <code>appusers</code> التي تخزن سجلًا لجميع المستخدمين الذين سجلوا الدخول إلى تطبيقنا، انظر الصورة أدناه:
</p>

<p style="text-align: center;">
	<img alt="addfield.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94649" data-unique="g6zcy8nkf" src="https://academy.hsoub.com/uploads/monthly_2022_03/addfield.png.2d187396522c637a6a87f2ce365ff814.png" style="width: 650px; height: auto;"></p>

<p>
	انقر على زر Add field لتظهر نافذة تطلب تعريف حقل جديد، فاجعل اسم الحقل isAdmin، ونوعه boolean، وقيمته True، ثم انقر على زر Add لإضافته، انظر الصورة أدناه:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94650" href="https://academy.hsoub.com/uploads/monthly_2022_03/isadmin.png.88dac8af971e8f70a1c557b37399d302.png" rel=""><img alt="isadmin.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94650" data-unique="d56t6b2h5" src="https://academy.hsoub.com/uploads/monthly_2022_03/isadmin.thumb.png.452e2a28dc33015524189e03ea0700fd.png" style="width: 700px; height: auto;"></a>
</p>

<p>
	سيضاف الحقل الجديد إلى التجميعة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94651" href="https://academy.hsoub.com/uploads/monthly_2022_03/isadmintrue.png.67ca4714055ea139c4f4ac3c1a5e461b.png" rel=""><img alt="isadmintrue.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94651" data-unique="jtad8b9i4" src="https://academy.hsoub.com/uploads/monthly_2022_03/isadmintrue.thumb.png.3013062372f12937dcc6e534b4d3c3bf.png" style="width: 650px; height: auto;"></a>
</p>

<p>
	عند نجاح عملية تسجيل الدخول سنجلب بيانات المستخدم من تجميعة <code>appusers</code> ونربطها بكائن يكون نوعه هو الصنف <code>AppUser</code>، ثم نستخدم الخاصية <code>isAdmin</code> بعدها لتقييد الدخول للمستخدم.
</p>

<p>
	وبما أننا ننشئ تطبيقًا للتوضيح والشرح فقط يحتوي على دور واحد، فإننا نعيّن خاصية <code>isAdmin</code> يدويًا، أما التطبيقات الكبيرة فلا يُنصح فيها بهذا إذ قد نحتاج إلى التعامل مع أدوار كثيرة مختلفة، ففي تلك الحالة ننشئ تطبيقًا منفصلًا لإدارة الأدوار لجميع المستخدمين.
</p>

<h3>
	إنشاء حاجز admin-auth
</h3>

<p>
	نفّذ الأمر التالي لإنشاء حاجز جديد باسم <code>AdminAuthGuard</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_96" style="">
<span class="pln">ng g g guards</span><span class="pun">/</span><span class="pln">admin</span><span class="pun">-</span><span class="pln">auth </span><span class="pun">--</span><span class="pln">implements </span><span class="typ">CanActivate</span><span class="pln"> </span></pre>

<p>
	ثم افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/guards/admin-auth.guard.ts" rel="external nofollow"><code>src/app/guards/admin-auth.guard.ts</code></a> وأضف تعليمات الاستيراد التالية في أعلاه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_98" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AuthService</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'../services/auth.service'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> map </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'rxjs/operators'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AppUser</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'../models/appuser'</span><span class="pun">;</span></pre>

<p>
	أضف منشئًا واحقن الموجِّه <code>Router</code> والخدمة <code>AuthService</code> فيه كما يظهر في الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_100" style="">
<span class="pln">constructor</span><span class="pun">(</span><span class="pln">
 </span><span class="kwd">private</span><span class="pln"> router</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Router</span><span class="pun">,</span><span class="pln">
 </span><span class="kwd">private</span><span class="pln"> authService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">AuthService</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	سنحدِّث التابع <code>canActivate</code> لمعالجة الوصول غير المصرح له إلى الوجهات، وذلك بإضافة الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_102" style="">
<span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">authService</span><span class="pun">.</span><span class="pln">appUser$</span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">map</span><span class="pun">((</span><span class="pln">user</span><span class="pun">:</span><span class="pln"> </span><span class="typ">AppUser</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">user </span><span class="pun">&amp;&amp;</span><span class="pln"> user</span><span class="pun">.</span><span class="pln">isAdmin</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">router</span><span class="pun">.</span><span class="pln">navigate</span><span class="pun">([</span><span class="str">'/'</span><span class="pun">],</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> queryParams</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> returnUrl</span><span class="pun">:</span><span class="pln"> state</span><span class="pun">.</span><span class="pln">url </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">
 </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">}));</span></pre>

<p>
	سنشترك في الكائن <code>appUser$‎</code> لجلب حالة التصديق/التصريح للمستخدم، فإذا كان قد سجل دخوله وكانت قيمة الخاصية <code>isAdmin</code> للمستخدم على القيمة true فإننا نعيد القيمة true أيضًا.
</p>

<p>
	بالمثل في سلوك الصنف <code>AuthGuard</code> فإذا لم يكن المستخدم قد سجل دخوله، فستحدث هذه الأشياء الثلاثة:
</p>

<ul>
<li>
		نعين معامِل استعلام باسم <code>returnUrl</code> على قيمة الرابط الحالي.
	</li>
	<li>
		ننقل المستخدم إلى الصفحة الرئيسية.
	</li>
	<li>
		نعيد false من التابع.
	</li>
</ul>
<h3>
	إضافة صلاحيات مدير للتطبيق
</h3>

<p>
	لقد حدَّثنا المكون <code>BlogCardComponent</code> في القسم السابق ليقيد الوصول إلى أزرار Edit و Delete ويقصره على المستخدمين الذين سجلوا الدخول، أما الآن فسنحدِّث الشيفرة مرة أخرى لتقصر الوصول على المستخدمين المدراء فقط.
</p>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/blog-card/blog-card.component.html" rel="external nofollow"><code>src/app/components/blog-card.component.html</code></a>، وحدِّث التوجيه <code>ngIf</code> الذي في الوسم <code>&lt;ng-container&gt;</code>، والذي بدوره يحتوي على الزر Edit والزر Delete -انظر الشيفرة أدناه- وهذا سيقصر إمكانية التعديل والحذف على المستخدمين الذين سجلوا الدخول فقط.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_104" style="">
<span class="pun">&lt;</span><span class="pln">ng</span><span class="pun">-</span><span class="pln">container </span><span class="pun">*</span><span class="pln">ngIf</span><span class="pun">=</span><span class="str">"appUser?.isAdmin"</span><span class="pun">&gt;</span></pre>

<p>
	أضف تعليمة الاستيراد التالية في الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/app.module.ts" rel="external nofollow"><code>app.module.ts</code></a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_106" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AdminAuthGuard</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./guards/admin-auth.guard'</span><span class="pun">;</span></pre>

<p>
	حدِّث الاتجاه الخاص بالمكون <code>editpost</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_108" style="">
<span class="pun">{</span><span class="pln"> path</span><span class="pun">:</span><span class="pln"> </span><span class="str">'editpost/:id'</span><span class="pun">,</span><span class="pln"> component</span><span class="pun">:</span><span class="pln"> </span><span class="typ">BlogEditorComponent</span><span class="pun">,</span><span class="pln"> canActivate</span><span class="pun">:[</span><span class="typ">AdminAuthGuard</span><span class="pun">]</span><span class="pln"> </span><span class="pun">},</span></pre>

<p>
	لقد قصرنا إمكانية التعديل على التدوينة هنا لتكن للمستخدمين المدراء فقط، فحين يحاول المستخدم الوصول إلى اتجاه <code>editpost</code>، فسيُستدعى الصنف <code>AdminAuthGuard</code>، فإذا أعاد هذا الصنف true يُسمح للمستخدم بالوصول إلى الوجهة editpost لتعديل التدوينة، أما إذا أعاد false فلا يُسمح له بالذهاب لتلك الوجهة لتعديل التدوينة.
</p>

<h3>
	اختبار التصاريح
</h3>

<p>
	افتح المتصفح وسجل الدخول إلى التطبيق بمستخدم غير مدير، سترى رابط AddPost في شريط التنقل، لكن بطاقة التدوينة لن يكون فيها زري Edit و Delete، وذلك يعني أن المستخدم يستطيع إضافة تدوينة جديدة لكنه لا يستطيع تعديل تدوينة موجودة أو أن يحذفها. انظر الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94654" href="https://academy.hsoub.com/uploads/monthly_2022_03/non-admin.png.e200f8a10ff4683586b805cdf2437693.png" rel=""><img alt="non-admin.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94654" data-unique="j25cebhc1" src="https://academy.hsoub.com/uploads/monthly_2022_03/non-admin.thumb.png.ed91171a2d5416ded27b5714cd6d58e2.png" style="width: 550px; height: auto;"></a>
</p>

<p>
	انتقل إلى قاعدة بيانات Firebase وأضف حقل isAdmin جديد للمستخدم كما سبق شرحه، أو يمكنك تسجيل الخروج وإعادة تسجيل الدخول بمستخدم آخر يكون فيه حقل isAdmin يحمل القيمة true، وهنا تستطيع أن ترى زري Edit و Delete على بطاقة التدوينة. انظر الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94656" href="https://academy.hsoub.com/uploads/monthly_2022_03/isadmin-edit.png.67106531fb1d026991c8dc62a0d7d91c.png" rel=""><img alt="isadmin-edit.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94656" data-unique="t41srs4oa" src="https://academy.hsoub.com/uploads/monthly_2022_03/isadmin-edit.thumb.png.b3b96135deb9413336ff1838f0ac60da.png" style="width: 550px; height: auto;"></a>
</p>

<h2>
	تحديث قواعد الأمان لقاعدة بيانات Firebase
</h2>

<p>
	لقد أنشأنا قاعدة البيانات في وضع الاختبار، وسنحدِّث القاعدة الأمنية لقاعدة البيانات الآن لتسمح بعملية الكتابة للمستخدم المصرَّح له فقط.
</p>

<p>
	من القائمة التي على اليسار، اختر Database في قسم Develop، ثم اختر تبويب Rules، وحدِّث قواعد الأمان كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7740_110" style="">
<span class="pln">service cloud</span><span class="pun">.</span><span class="pln">firestore </span><span class="pun">{</span><span class="pln">
 match </span><span class="pun">/</span><span class="pln">databases</span><span class="pun">/{</span><span class="pln">database</span><span class="pun">}/</span><span class="pln">documents </span><span class="pun">{</span><span class="pln">
  match </span><span class="pun">/{</span><span class="pln">document</span><span class="pun">=**}</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  allow read</span><span class="pun">;</span><span class="pln">
  allow write</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> request</span><span class="pun">.</span><span class="pln">auth</span><span class="pun">.</span><span class="pln">uid </span><span class="pun">!=</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ثم انقر على زر Publish لنشر القاعدة، انظر الصورة أدناه:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94657" href="https://academy.hsoub.com/uploads/monthly_2022_03/rules.png.62a7c7e085615bb5c615cde2937c1c7f.png" rel=""><img alt="rules.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94657" data-unique="gg8eavag6" src="https://academy.hsoub.com/uploads/monthly_2022_03/rules.thumb.png.d217dcad7ab47210531c786de14fc33b.png" style="width: 600px; height: auto;"></a>
</p>

<h2>
	الخاتمة
</h2>

<p>
	تعلمنا في هذا الجزء من سلسلة بناء مدونة باستخدام إطار العمل <a href="http://Angular%20https://academy.hsoub.com/programming/javascript/angular/%D9%85%D8%A7-%D9%87%D9%8A-angular%D8%9F-r1379/" rel="external nofollow">Angular</a> وقاعدة بيانات Firestore كيفية إضافة استيثاق جوجل للتأكد من تقييد وصول المستخدمين إلى مختلف أجزاء التطبيق وإعطاء كل مستخدم صلاحيات محددة بعد تسجيله في <a href="https://academy.hsoub.com/programming/javascript/react/%D9%86%D9%85%D9%88%D8%B0%D8%AC-%D8%B9%D9%86-%D8%A8%D9%86%D8%A7%D8%A1-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%88%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D9%8A%D9%86-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%88%D9%8A%D8%A8-r1211/" rel="">قاعدة بيانات التطبيق</a>. أما في الجزء الخامس والأخير من هذه السلسلة، فسنكمل إضافة اللمسات النهائية على المدونة، مثل إضافة الملف الشخصي لكل كاتب ونشر التعليقات ومشاركة التدوينات على الشبكات الاجتماعية وغيرها ثم سنتعلم كيفية نشر المدونة على الإنترنت باستعمال Firebase.
</p>

<p>
	ترجمة -وبتصرف- لفصول من كتاب Build a full stack web application using angular and firebase لصاحبه Ankit Sharma.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%88%D9%8A%D8%A8-%D9%83%D8%A7%D9%85%D9%84-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-angular-%D9%88%D9%85%D9%86%D8%B5%D8%A9-firebase-r1394/" rel="">بناء تطبيق ويب كامل باستخدام Angular ومنصة Firebase</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-angular-%D9%88%D9%86%D8%B4%D8%B1%D9%87%D8%A7-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1388/" rel="">تهيئة بيئة تطبيقات Angular ونشرها على الويب</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1507</guid><pubDate>Thu, 24 Mar 2022 11:17:33 +0000</pubDate></item><item><title>&#x628;&#x646;&#x627;&#x621; &#x645;&#x62F;&#x648;&#x646;&#x629; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x625;&#x637;&#x627;&#x631; &#x627;&#x644;&#x639;&#x645;&#x644; Angular &#x648;&#x642;&#x627;&#x639;&#x62F;&#x629; &#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; Firestore - &#x62A;&#x639;&#x62F;&#x64A;&#x644; &#x627;&#x644;&#x62A;&#x62F;&#x648;&#x64A;&#x646;&#x627;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/angular/%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D8%AF%D9%88%D9%86%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-%D8%AA%D8%B9%D8%AF%D9%8A%D9%84-%D8%A7%D9%84%D8%AA%D8%AF%D9%88%D9%8A%D9%86%D8%A7%D8%AA-r1479/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_02/620a9af7b11c5_-----Angular---Firestore-------Angular-firestore-firebase-blog-.png.18fb0d00aab61467e09930d9a767d888.png" /></p>

<p>
	تعلمنا في الجزء الأول من هذه السلسلة كيفية بناء مدونة كتطبيق وحيد الصفحة باستخدام إطار العمل Angular للواجهة الأمامية، وقاعدة بيانات Firestore، وإضافة محرر للمدونة ثم إضافة تدوينات جديدة وعرضها على الصفحة الرئيسية، أما في هذا الجزء فسنتعلم كيفية التعديل على التدوينات وإضافة الترقيم في نهاية الصفحة الرئيسية لعرض عدد محدد من التدوينات فيها والتنقل بين صفحات التدوينات الأخرى.
</p>

<p>
	هذا المقال جزء من سلسلة عن بناء مدونة باستخدام <a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-angular-r1393/" rel="">إطار العمل Angular</a> وقاعدة بيانات Firestore
</p>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-r1409/" rel="">مقدمة في بناء تطبيقات الويب باستخدام إطار العمل Angular وقاعدة بيانات Firestore</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D8%AF%D9%88%D9%86%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%AA%D8%AF%D9%88%D9%8A%D9%86%D8%A7%D8%AA-%D9%88%D8%B9%D8%B1%D8%B6%D9%87%D8%A7-r1478/#" rel="">بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore - إضافة التدوينات وعرضها</a>
	</li>
	<li>
		بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore - تعديل التدوينات
	</li>
	<li>
		<a href="#" rel="">بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore - إضافة الاستثيثاق</a>
	</li>
	<li>
		<a href="#" rel="">نشر مدونة مبنية عبر Angular على Firebase</a>
	</li>
</ul>
<h2>
	حذف تدوينة
</h2>

<p>
	سننشئ الآن خدمة Snackbar، وهي خدمة تُظهر شريطًا يعرض رسائل للمستخدم نعرض عبرها رسالة بعد حذف التدوينة، وننشئها بكتابة الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_9" style="">
<span class="pln">ng g s services</span><span class="pun">/</span><span class="typ">Snackbar</span></pre>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/services/snackbar.service.ts" rel="external nofollow">snackbar.service.ts</a> وضع الشيفرة التالية فيه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_11" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Injectable</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">MatSnackBar</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/material/snack-bar'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@Injectable</span><span class="pun">({</span><span class="pln">
providedIn</span><span class="pun">:</span><span class="pln"> </span><span class="str">'root'</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">SnackbarService</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

 constructor</span><span class="pun">(</span><span class="kwd">private</span><span class="pln"> snackBar</span><span class="pun">:</span><span class="pln"> </span><span class="typ">MatSnackBar</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span><span class="pln">

 showSnackBar</span><span class="pun">(</span><span class="pln">message</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">snackBar</span><span class="pun">.</span><span class="pln">open</span><span class="pun">(</span><span class="pln">message</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Close'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   duration</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2000</span><span class="pun">,</span><span class="pln">
   panelClass</span><span class="pun">:</span><span class="pln"> </span><span class="str">'snackbar-ribon'</span><span class="pun">,</span><span class="pln">
   verticalPosition</span><span class="pun">:</span><span class="pln"> </span><span class="str">'top'</span><span class="pun">,</span><span class="pln">
   horizontalPosition</span><span class="pun">:</span><span class="pln"> </span><span class="str">'center'</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لقد أنشأنا الآن التابع <code>showSnackBar</code> الذي يقبل معامِلًا يكون هو الرسالة التي نريد عرضها، والحد الأقصى لعرض هذه الرسالة هو 2000 مللي ثانية، وقد عرَّفنا موضع الشريط لينتصف أعلى الصفحة.
</p>

<p>
	أضف الآن التنسيق التالي لشريط Snackbar في الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/styles.scss" rel="external nofollow">styles.scss</a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_13" style="">
<span class="pun">.</span><span class="pln">snackbar</span><span class="pun">-</span><span class="pln">ribon </span><span class="pun">{</span><span class="pln">
 color</span><span class="pun">:</span><span class="pln"> </span><span class="pun">#</span><span class="pln">FFFFFF</span><span class="pun">;</span><span class="pln">
 background</span><span class="pun">:</span><span class="pln"> </span><span class="pun">#</span><span class="lit">17a2b8</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنضيف الآن مزية حذف إحدى التدوينات الموجودة، وسنحذف التدوينة من تجميعة <code>blogs</code> وفقًا لمعرِّف التدوينة <code>postID</code>.
</p>

<p>
	أضف الشيفرة التالية في الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/services/blog.service.ts" rel="external nofollow">blog.service.ts</a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_15" style="">
<span class="pln">deletePost</span><span class="pun">(</span><span class="pln">postId</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">db</span><span class="pun">.</span><span class="pln">doc</span><span class="pun">(</span><span class="str">'blogs/'</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> postId</span><span class="pun">).</span><span class="kwd">delete</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogsite/blob/master/src/app/components/blog-card/blog-card.component.ts#L50-L58" rel="external nofollow">blog-card.comonent.ts</a> واستورد <code>SnackbarService</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_17" style="">
<span class="pln">deletePost</span><span class="pun">(</span><span class="pln">postId</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">db</span><span class="pun">.</span><span class="pln">doc</span><span class="pun">(</span><span class="str">'blogs/'</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> postId</span><span class="pun">).</span><span class="kwd">delete</span><span class="pun">();}</span></pre>

<p>
	ثم احقن خدمة <code>SnackbarService</code> في المنشئ كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_19" style="">
<span class="pln">constructor</span><span class="pun">(</span><span class="pln">
 </span><span class="com">// other service injection</span><span class="pln">
 </span><span class="kwd">private</span><span class="pln"> snackBarService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">SnackbarService</span><span class="pln">
 </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	كما سنحدّث تابع الحذف في الملف <a href="https://github.com/AnkitSharma-007/blogsite/blob/master/src/app/components/blog-card/blog-card.component.ts#L50-L58" rel="external nofollow">blog-card.comonent.ts</a>، بحيث يكون تعريفه كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_21" style="">
<span class="kwd">delete</span><span class="pun">(</span><span class="pln">postId</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">confirm</span><span class="pun">(</span><span class="str">'Are you sure'</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">blogService</span><span class="pun">.</span><span class="pln">deletePost</span><span class="pun">(</span><span class="pln">postId</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">
   </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">snackBarService</span><span class="pun">.</span><span class="pln">showSnackBar</span><span class="pun">(</span><span class="str">'</span><span class="typ">Blog</span><span class="pln"> post deleted
successfully</span><span class="str">');</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سيقبل تابع الحذف <code>postID</code> كمعامِل وسيعرض رسالة تحذير تطلب تأكيد الحذف، وعند ضغط المستخدم على OK، يُستدعَى تابع <code>deletePost</code> الخاص بالخدمة <code>BlogService</code>، ثم نعرض رسالة تظهر نجاح عملية الحذف باستخدام شريط snackbar.
</p>

<p>
	افتح المتصفح واضغط على زر Delete في بطاقة التدوينة، سيظهر صندوق جافاسكربت يطلب تأكيد عملية حذف التدوينة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91943" href="https://academy.hsoub.com/uploads/monthly_2022_02/delete-confirm.png.0faa87ee38b59958228cccd0fca41cfe.png" rel=""><img alt="delete-confirm.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91943" data-unique="4tj82uxm4" src="https://academy.hsoub.com/uploads/monthly_2022_02/delete-confirm.thumb.png.c9aa0ab69cfcd1c5a032e1362d8ff34a.png" style="width: 600px; height: auto;"></a>
</p>

<p>
	إذا ضغط المستخدم على OK ستُحذف التدوينة ونحصل على رسالة توكيد في شريط Snackbar:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91944" href="https://academy.hsoub.com/uploads/monthly_2022_02/deleted-post.png.28e1686112ad6e28501f4554beea6980.png" rel=""><img alt="deleted-post.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91944" data-unique="ofmihrbi7" src="https://academy.hsoub.com/uploads/monthly_2022_02/deleted-post.thumb.png.f2d855600911cdb3a641f6e83121d1ed.png" style="width: 600px; height: auto;"></a>
</p>

<h2>
	التعديل على تدوينة موجودة
</h2>

<p>
	سننفذ الآن خاصية التعديل على تدوينة قائمة وموجودة، من خلال إضافة التعريف التالي إلى الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/services/blog.service.ts" rel="external nofollow">blog.service.ts</a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_26" style="">
<span class="pln">updatePost</span><span class="pun">(</span><span class="pln">postId</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">,</span><span class="pln"> post</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Post</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> putData </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">post</span><span class="pun">));</span><span class="pln">
 </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">db</span><span class="pun">.</span><span class="pln">doc</span><span class="pun">(</span><span class="str">'blogs/'</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> postId</span><span class="pun">).</span><span class="pln">update</span><span class="pun">(</span><span class="pln">putData</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سيقبل التابع <code>updatePost</code> كلا من <code>postID</code> وكائنًا من النوع Post كمعاملات، وسنحلل كائن Post إلى كائن JSON ثم نحدّث الكائن في تجميعة <code>blogs</code>.
</p>

<p>
	أضف توجيه وظيفة التعديل في الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/app.module.ts" rel="external nofollow">app.module.ts</a> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_28" style="">
<span class="typ">RouterModule</span><span class="pun">.</span><span class="pln">forRoot</span><span class="pun">([</span><span class="pln">
 </span><span class="pun">...</span><span class="pln">
 </span><span class="pun">{</span><span class="pln"> path</span><span class="pun">:</span><span class="pln"> </span><span class="str">'editpost/:id'</span><span class="pun">,</span><span class="pln"> component</span><span class="pun">:</span><span class="pln"> </span><span class="typ">BlogEditorComponent</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
 </span><span class="pun">...</span><span class="pln">
</span><span class="pun">])</span></pre>

<p>
	ثم أضف تعريف الاستيراد التالي في الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/blog-editor/blog-editor.component.ts" rel="external nofollow">blog-editor.component.ts</a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_30" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Subject</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'rxjs'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> takeUntil </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'rxjs/operators'</span><span class="pun">;</span></pre>

<p>
	صرِّح الآن عن متغير <code>Subject</code> جديد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_32" style="">
<span class="kwd">private</span><span class="pln"> unsubscribe$ </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Subject</span><span class="pun">&lt;</span><span class="kwd">void</span><span class="pun">&gt;();</span></pre>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/blog-editor/blog-editor.component.ts" rel="external nofollow">blog-editor.component.ts</a> وأضف الشيفرة التالية في المنشئ:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_35" style="">
<span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">route</span><span class="pun">.</span><span class="pln">snapshot</span><span class="pun">.</span><span class="pln">params</span><span class="pun">[</span><span class="str">'id'</span><span class="pun">])</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">postId </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">route</span><span class="pun">.</span><span class="pln">snapshot</span><span class="pun">.</span><span class="pln">paramMap</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'id'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	في الشيفرة أعلاه، نستخدم الصنف <code>ActivatedRoute</code> لجلب معرِّف التدوينة من الرابط.
</p>

<p>
	سنضيف الآن تابعًا لإعداد استمارة التعديل عند النقر على زر Edit في بطاقة التدوينة في الصفحة الرئيسية، ويكون تعريف التابع كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_37" style="">
<span class="pln">setPostFormData</span><span class="pun">(</span><span class="pln">postFormData</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">postData</span><span class="pun">.</span><span class="pln">title </span><span class="pun">=</span><span class="pln"> postFormData</span><span class="pun">.</span><span class="pln">title</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">postData</span><span class="pun">.</span><span class="pln">content </span><span class="pun">=</span><span class="pln"> postFormData</span><span class="pun">.</span><span class="pln">content</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	حدِّث الآن التابع <code>ngOnInit</code> داخل الصنف <code>BlogEditorComponent</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_40" style="">
<span class="pln">ngOnInit</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">setEditorConfig</span><span class="pun">();</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">postId</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">formTitle </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Edit'</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">blogService</span><span class="pun">.</span><span class="pln">getPostbyId</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">postId</span><span class="pun">)</span><span class="pln">
   </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">takeUntil</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">unsubscribe$</span><span class="pun">))</span><span class="pln">
   </span><span class="pun">.</span><span class="pln">subscribe</span><span class="pun">(</span><span class="pln">
    result </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
     </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">setPostFormData</span><span class="pun">(</span><span class="pln">result</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
   </span><span class="pun">);</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إذا كان <code>postID</code> معيَّنًا set فهذا يعني أن هذا طلب تحرير Edit، وسنجعل عنوان الاستمارة Edit، ونستدعي التابع <code>getPostbyID</code> من <code>BlogService</code> لجلب تفاصيل التدوينة المتوافقة مع <code>postID</code>.
</p>

<p>
	عند النقر على Save، نحتاج إلى معالجة كلا من حالة إنشاء تدوينة جديدة وتحرير تدوينة موجودة من قبل، وعليه سنحدِّث <code>saveBlogPost</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_42" style="">
<span class="pln">saveBlogPost</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">postId</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">blogService</span><span class="pun">.</span><span class="pln">updatePost</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">postId</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">postData</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">
  </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">router</span><span class="pun">.</span><span class="pln">navigate</span><span class="pun">([</span><span class="str">'/'</span><span class="pun">]);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
 </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">postData</span><span class="pun">.</span><span class="pln">createdDate </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">datePipe</span><span class="pun">.</span><span class="pln">transform</span><span class="pun">(</span><span class="typ">Date</span><span class="pun">.</span><span class="pln">now</span><span class="pun">(),</span><span class="pln"> </span><span class="str">'MMdd-yyyy HH:mm'</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">blogService</span><span class="pun">.</span><span class="pln">createPost</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">postData</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">
   </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">router</span><span class="pun">.</span><span class="pln">navigate</span><span class="pun">([</span><span class="str">'/'</span><span class="pun">]);</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سننفذ الواجهة <code>OnDestroy</code> على الصنف <code>BlogEditorComponent</code>، وسنكمل اشتراك <code>unsubscribe$‎</code> داخل التابع <code>ngOnDestroy</code>، انظر الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_44" style="">
<span class="pln">ngOnDestroy</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">unsubscribe$</span><span class="pun">.</span><span class="pln">next</span><span class="pun">();</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">unsubscribe$</span><span class="pun">.</span><span class="pln">complete</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	افتح المتصفح وانقر على زر Edit في بطاقة التدوينة في الصفحة الرئيسية، ستنتقل الصفحة إلى صفحة Edit Post. تستطيع الآن أن ترى محرر التدوينة وفيه محتوى التدوينة جاهزًا بداخله، وإذا نظرت إلى الرابط ستجد أنه يحتوي على postID الخاص بهذه التدوينة. انظر الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91945" href="https://academy.hsoub.com/uploads/monthly_2022_02/editpost.png.ac586a4470a3e1b809acb5137d528cd3.png" rel=""><img alt="editpost.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91945" data-unique="yyrml7c0q" src="https://academy.hsoub.com/uploads/monthly_2022_02/editpost.png.ac586a4470a3e1b809acb5137d528cd3.png" style="width: 600px; height: auto;"></a>
</p>

<p>
	يمكن الآن تعديل محتوى التدوينة داخل المحرر، وبعد تمام التعديل ننقر على زر الحفظ لتحديث التدوينة بالمحتوى الجديد والعودة إلى الصفحة الرئيسية لنرى المحتوى الجديد في ملخص التدوينة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91947" href="https://academy.hsoub.com/uploads/monthly_2022_02/updated-post.png.ad804d2028055ab231f85301c671d4c8.png" rel=""><img alt="updated-post.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91947" data-unique="pkzwwrspo" src="https://academy.hsoub.com/uploads/monthly_2022_02/updated-post.thumb.png.48203d64bc30ec7ac261f7a1cdbbe8d3.png" style="width: 600px; height: auto;"></a>
</p>

<h2>
	إضافة شريط تنقل بين صفحات التدوينات
</h2>

<p>
	سنضيف خاصية الترقيم pagination في الصفحة الرئيسية وذلك للتنقل بين صفحات بطاقات التدوينات، مستخدمين ngx-pagination لهذا الغرض، وهو مكون مفتوح المصدر يوفر خاصية ترقيم بسيطة وسهلة الاستخدام لتطبيقات <a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D8%A7-%D9%87%D9%8A-angular%D8%9F-r1379/" rel="">Angular</a>.
</p>

<p>
	نفِّذ الأمر التالي لتثبيت المكون <code>ngx-pagination</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_48" style="">
<span class="pln">npm i ngx</span><span class="pun">-</span><span class="pln">pagination </span><span class="pun">--</span><span class="pln">save</span></pre>

<p>
	استورد <code>NgxPaginationModule</code> في الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/app.module.ts" rel="external nofollow">app.module.ts</a> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_50" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">NgxPaginationModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'ngx-pagination'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@NgModule</span><span class="pun">(</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 imports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
  </span><span class="typ">NgxPaginationModule</span><span class="pun">,</span><span class="pln">
 </span><span class="pun">],</span><span class="pln">
</span><span class="pun">})</span></pre>

<h3>
	إنشاء مكون المرقم PaginatorComponent
</h3>

<p>
	نفِّذ الأمر التالي في الطرفية الأصلية لتوليد مكون المرقِّم PaginatorComponent:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_52" style="">
<span class="pln">ng g c components</span><span class="pun">/</span><span class="pln">paginator</span></pre>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/paginator/paginator.component.ts" rel="external nofollow">paginator.component.ts</a> وأضف تعريفات الاستيراد التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_54" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Input</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Router</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/router'</span><span class="pun">;</span></pre>

<p>
	سنضيف خاصيتي إدخال لهذا المكون، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_56" style="">
<span class="lit">@Input</span><span class="pun">()</span><span class="pln">
 pageSizeOptions</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[];</span><span class="pln"> 

</span><span class="lit">@Input</span><span class="pun">()</span><span class="pln">
 config</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">;</span></pre>

<p>
	احقن صنف الموجِّه router داخل المنشئ، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_58" style="">
<span class="pln">constructor</span><span class="pun">(</span><span class="kwd">private</span><span class="pln"> router</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Router</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	سنضيف تابعًا يعالج حدث <code>pageChange</code> للمرقِّم الخاص بنا، وسيكون تعريف ذلك التابع كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_60" style="">
<span class="pln">pageChange</span><span class="pun">(</span><span class="pln">newPage</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">router</span><span class="pun">.</span><span class="pln">navigate</span><span class="pun">([</span><span class="str">'/page/'</span><span class="pun">,</span><span class="pln"> newPage</span><span class="pun">]);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	كذلك، نضيف التابع <code>changePageItemCount</code> في الصنف <code>PaginatorComponent</code>، ويُستخدم هذا التابع لإعداد ترقيم ديناميكي للمرقِّم، حيث يعين عدد العناصر التي يظهرها على كل صفحة وفقًا لاختيار من قائمة منسدلة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_62" style="">
<span class="pln">changePageItemCount</span><span class="pun">(</span><span class="pln">selectedItem</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 localStorage</span><span class="pun">.</span><span class="pln">setItem</span><span class="pun">(</span><span class="str">'pageSize'</span><span class="pun">,</span><span class="pln"> selectedItem</span><span class="pun">.</span><span class="pln">value</span><span class="pun">);</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">config</span><span class="pun">.</span><span class="pln">itemsPerPage </span><span class="pun">=</span><span class="pln"> selectedItem</span><span class="pun">.</span><span class="pln">value</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنخزِّن القيمة التي يختارها المستخدم في الذاكرة المحلية، وذلك لضمان أن القيمة لا تُفقد عند تحديث الصفحة، وكذلك لضمان جودة <a href="https://academy.hsoub.com/design/user-experience/%d9%85%d8%af%d8%ae%d9%84-%d8%a5%d9%84%d9%89-%d8%aa%d8%ac%d8%b1%d8%a8%d8%a9-%d8%a7%d9%84%d9%85%d8%b3%d8%aa%d8%ae%d8%af%d9%85-user-experience-r149/" rel="">تجربة المستخدم</a>.
</p>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/paginator/paginator.component.html" rel="external nofollow">paginator.component.html</a> واستبدل الشيفرة التالية بالموجود فيه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_64" style="">
<span class="pun">&lt;</span><span class="pln">div </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"paginator-controls"</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">pagination</span><span class="pun">-</span><span class="pln">controls </span><span class="pun">(</span><span class="pln">pageChange</span><span class="pun">)=</span><span class="str">"pageChange($event)"</span><span class="pln"> </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"mypagination"</span><span class="pun">&gt;&lt;/</span><span class="pln">pagination</span><span class="pun">-</span><span class="pln">controls</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">form</span><span class="pun">-</span><span class="pln">field</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">label</span><span class="pun">&gt;</span><span class="typ">Items</span><span class="pln"> per page</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">label</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">select </span><span class="pun">[(</span><span class="pln">ngModel</span><span class="pun">)]=</span><span class="str">"config.itemsPerPage"</span><span class="pln">
</span><span class="pun">(</span><span class="pln">selectionChange</span><span class="pun">)=</span><span class="str">"changePageItemCount($event)"</span><span class="pun">&gt;</span><span class="pln">
   </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">option </span><span class="pun">*</span><span class="pln">ngFor</span><span class="pun">=</span><span class="str">"let page of pageSizeOptions"</span><span class="pln"> </span><span class="pun">[</span><span class="pln">value</span><span class="pun">]=</span><span class="str">"page"</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">{{</span><span class="pln"> page </span><span class="pun">}}</span><span class="pln">
   </span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">option</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">select</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">form</span><span class="pun">-</span><span class="pln">field</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span></pre>

<p>
	أخيرًا، سنضيف التنسيق الخاص بالمكون <code>PaginatorComponent</code>، فافتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/paginator/paginator.component.scss" rel="external nofollow">paginator.component.scss</a> واستبدل الشيفرة أدناه بالموجود فيه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_66" style="">
<span class="pun">.</span><span class="pln">my</span><span class="pun">-</span><span class="pln">pagination </span><span class="pun">::</span><span class="pln">ng</span><span class="pun">-</span><span class="pln">deep </span><span class="pun">.</span><span class="pln">ngx</span><span class="pun">-</span><span class="pln">pagination </span><span class="pun">{</span><span class="pln">
 margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10px</span><span class="pln"> </span><span class="lit">0px</span><span class="pln"> </span><span class="lit">10px</span><span class="pln"> </span><span class="lit">0px</span><span class="pun">;</span><span class="pln">
 padding</span><span class="pun">-</span><span class="kwd">inline</span><span class="pun">-</span><span class="pln">start</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">paginator</span><span class="pun">-</span><span class="pln">controls </span><span class="pun">{</span><span class="pln">
 display</span><span class="pun">:</span><span class="pln"> flex</span><span class="pun">;</span><span class="pln">
 justify</span><span class="pun">-</span><span class="pln">content</span><span class="pun">:</span><span class="pln"> space</span><span class="pun">-</span><span class="pln">between</span><span class="pun">;</span><span class="pln">
 padding</span><span class="pun">-</span><span class="pln">top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="lit">@media</span><span class="pln"> screen and </span><span class="pun">(</span><span class="pln">min</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">320px</span><span class="pun">)</span><span class="pln"> and </span><span class="pun">(</span><span class="pln">max</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">420px</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="pun">.</span><span class="pln">paginator</span><span class="pun">-</span><span class="pln">controls </span><span class="pun">{</span><span class="pln">
  flex</span><span class="pun">-</span><span class="pln">direction</span><span class="pun">:</span><span class="pln"> column</span><span class="pun">-</span><span class="pln">reverse</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أما الآن، فسنضيف رابط موجِّهٍ في الملف <code>app.module.ts</code> لدعم الترقيم كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_68" style="">
<span class="pln"> </span><span class="pun">{</span><span class="pln"> path</span><span class="pun">:</span><span class="pln"> </span><span class="str">'page/:pagenum'</span><span class="pun">,</span><span class="pln"> component</span><span class="pun">:</span><span class="pln"> </span><span class="typ">HomeComponent</span><span class="pln"> </span><span class="pun">},</span></pre>

<h3>
	إضافة المكون PaginatorComponent إلى قائمة التدوينات
</h3>

<p>
	يجب أن نضيف المكون <code>PaginatorComponent</code> إلى مكون قائمة التدوينات<code>BlogCardComponent</code> من أجل تفعيل الترقيم فيها، لنصرح عن خاصيتين، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_70" style="">
<span class="pln">config</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">;</span><span class="pln">
pageSizeOptions </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span></pre>

<p>
	أضف الاستيراد للصنف <code>ActivatedRoute</code> في المكون، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_72" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">ActivatedRoute</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/router'</span><span class="pun">;</span></pre>

<p>
	احقن الصنف <code>ActivatedRoute</code> في المنشئ، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_74" style="">
<span class="pln">constructor</span><span class="pun">(</span><span class="pln">
 </span><span class="com">// other services</span><span class="pln">
 </span><span class="kwd">private</span><span class="pln"> route</span><span class="pun">:</span><span class="pln"> </span><span class="typ">ActivatedRoute</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	أضف الشيفرة التالية داخل منشئ الصنف <code>BlogCardComponent</code> لبدء الخصائص المصرَّح عنها للتو:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_76" style="">
<span class="kwd">this</span><span class="pun">.</span><span class="pln">pageSizeOptions </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">];</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> pageSize </span><span class="pun">=</span><span class="pln"> localStorage</span><span class="pun">.</span><span class="pln">getItem</span><span class="pun">(</span><span class="str">'pageSize'</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">config </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 currentPage</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
 itemsPerPage</span><span class="pun">:</span><span class="pln"> pageSize </span><span class="pun">?</span><span class="pln"> </span><span class="pun">+</span><span class="pln">pageSize </span><span class="pun">:</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">pageSizeOptions</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	والآن، حدِّث التابع <code>ngOnInit</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_78" style="">
<span class="pln">ngOnInit</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">route</span><span class="pun">.</span><span class="pln">params</span><span class="pun">.</span><span class="pln">subscribe</span><span class="pun">(</span><span class="pln">
  params </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">config</span><span class="pun">.</span><span class="pln">currentPage </span><span class="pun">=</span><span class="pln"> </span><span class="pun">+</span><span class="pln">params</span><span class="pun">[</span><span class="str">'pagenum'</span><span class="pun">];</span><span class="pln">
   </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">getBlogPosts</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
 </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	تحديث قالب BlogCardComponent
</h3>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/blog-card/blog-card.component.html" rel="external nofollow">blog-card.component.html</a> وأضف مكون المرقِّم كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_80" style="">
<span class="pun">&lt;</span><span class="pln">app</span><span class="pun">-</span><span class="pln">paginator </span><span class="pun">[</span><span class="pln">pageSizeOptions</span><span class="pun">]=</span><span class="str">"pageSizeOptions"</span><span class="pln">
</span><span class="pun">[</span><span class="pln">config</span><span class="pun">]=</span><span class="str">"config"</span><span class="pun">&gt;&lt;/</span><span class="pln">app</span><span class="pun">-</span><span class="pln">paginator</span><span class="pun">&gt;</span></pre>

<p>
	سنضيف أنبوب ترقيم أيضًا في التوجيه <code>ngFor</code> أثناء التكرار على قائمة التدوينات، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7288_82" style="">
<span class="pun">&lt;</span><span class="pln">div </span><span class="pun">*</span><span class="pln">ngFor</span><span class="pun">=</span><span class="str">"let post of blogPost | paginate: config"</span><span class="pun">&gt;</span></pre>

<p>
	افتح المتصفح الآن، يجب أن ترى المرقِّم في الصفحة الرئيسية، كما تستطيع التنقل بين الصفحات كما تشاء، ويتغير الرابط برقم الصفحة التي انتقلت إليها، كما يمكن رؤية القائمة المنسدلة بجانب المرقِّم، والتي تسمح باختيار عدد العناصر التي ينبغي إظهارها في كل صفحة، انظر الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91946" href="https://academy.hsoub.com/uploads/monthly_2022_02/chkpt6.png.2fe8e64de8c260951965451bd2793a80.png" rel=""><img alt="chkpt6.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91946" data-unique="0anurchpi" src="https://academy.hsoub.com/uploads/monthly_2022_02/chkpt6.thumb.png.5fac31be71e6210e90e7c503bf7537ec.png" style="width: 600px; height: auto;"></a>
</p>

<h2>
	خاتمة
</h2>

<p>
	تعلمنا في هذا الجزء من سلسلة بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore كيفية حذف التدوينات والتعديل عليها وإضافة الترقيم للصفحات، وسنتعلم في المقال التالي كيفية إضافة استيثاق جوجل للوصول إلى التطبيق وإضافة التدوينات والتعديل عليها.
</p>

<p>
	ترجمة -وبتصرف- لفصول من كتاب Build a full stack web application using angular and firebase لصاحبه Ankit Sharma.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D8%AF%D9%88%D9%86%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%AA%D8%AF%D9%88%D9%8A%D9%86%D8%A7%D8%AA-%D9%88%D8%B9%D8%B1%D8%B6%D9%87%D8%A7-r1478/" rel="">بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore - إضافة التدوينات وعرضها</a> 
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%88%D9%8A%D8%A8-%D9%83%D8%A7%D9%85%D9%84-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-angular-%D9%88%D9%85%D9%86%D8%B5%D8%A9-firebase-r1394/" rel="">بناء تطبيق ويب كامل باستخدام Angular ومنصة Firebase</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-angular-%D9%88%D9%86%D8%B4%D8%B1%D9%87%D8%A7-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1388/" rel="">تهيئة بيئة تطبيقات Angular ونشرها على الويب</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1479</guid><pubDate>Fri, 25 Feb 2022 09:14:33 +0000</pubDate></item><item><title>&#x628;&#x646;&#x627;&#x621; &#x645;&#x62F;&#x648;&#x646;&#x629; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x625;&#x637;&#x627;&#x631; &#x627;&#x644;&#x639;&#x645;&#x644; Angular &#x648;&#x642;&#x627;&#x639;&#x62F;&#x629; &#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; Firestore - &#x625;&#x636;&#x627;&#x641;&#x629; &#x627;&#x644;&#x62A;&#x62F;&#x648;&#x64A;&#x646;&#x627;&#x62A; &#x648;&#x639;&#x631;&#x636;&#x647;&#x627;</title><link>https://academy.hsoub.com/programming/javascript/angular/%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D8%AF%D9%88%D9%86%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%AA%D8%AF%D9%88%D9%8A%D9%86%D8%A7%D8%AA-%D9%88%D8%B9%D8%B1%D8%B6%D9%87%D8%A7-r1478/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_02/620a8fa2c3a7d_-----Angular---Firestore--------Angular-firestore-firebase-blog-.png.de78d3719c54a9a854265d08ca26c49a.png" /></p>

<p>
	تعرفنا في <a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-r1409/" rel="">المقال السابق</a> على إطار العمل Angular وإمكانياته المختلفة، وكذلك على قاعدة البيانات Firestore المقدمة من جوجل، وكذلك على منصة Firebase التي تدعمها جوجل أيضًا والتي سننشر عليها تطبيقنا وهيأنا مشروعنا وأنشأنا ما يلزمه لبدء العمل عليه، أما في هذا الفصل فسنكمل العمل وسنتعلم كيفية بناء مدونة كتطبيق على استخدام إطار عمل Angular في إنشاء تطبيقات وحيدة الصفحة وتخصيصها وإضافة الوظائف اللازمة لها، وكذلك الصلاحيات التي تكون للمستخدمين للتعامل مع تلك الوظائف من خلال أزرار أو غيرها، ثم نشر تطبيق المدونة على منصة Firebase.
</p>

<p>
	هذا المقال جزء من سلسلة عن بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore:
</p>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-r1409/" rel="">مقدمة في بناء تطبيقات الويب باستخدام إطار العمل Angular وقاعدة بيانات Firestore</a>
	</li>
	<li>
		<a href="#" rel="">بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore - إضافة التدوينات وعرضها</a>
	</li>
	<li>
		<a href="#" rel="">بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore - تعديل التدوينات</a>
	</li>
	<li>
		<a href="#" rel="">بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore - إضافة الاستثيثاق</a>
	</li>
	<li>
		<a href="#" rel="">نشر مدونة مبنية عبر Angular على Firebase</a>
	</li>
</ul>
<h2>
	إنشاء الصفحة الرئيسية
</h2>

<p>
	سنبني الهيكل الأساسي للصفحة الرئيسية والتي فيها شريط للتنقل يحوي الروابط الأساسية في التطبيق.
</p>

<p>
	نشغِّل الأمر التالي في الطرفية لتوليد مكوِّن شريط التنقل navigation bar:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_10" style="">
<span class="pln">ng g c components</span><span class="pun">/</span><span class="pln">nav</span><span class="pun">-</span><span class="pln">bar</span></pre>

<p>
	ثم نفتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/nav-bar/nav-bar.component.html" rel="external nofollow">nav-bar.component.html</a> ونستبدل الشيفرة التالية بالموجودة هناك:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_12" style="">
<span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">toolbar </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"nav-bar mat-elevation-z2"</span><span class="pun">&gt;&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">toolbar</span><span class="pun">&gt;</span></pre>

<p>
	كذلك، نضيف تنسيق شريط التنقل في الملف <code>src/app/components/nav-bar/nav-bar.component.scss</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_14" style="">
<span class="pun">.</span><span class="pln">nav</span><span class="pun">-</span><span class="pln">bar </span><span class="pun">{</span><span class="pln">
 background</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> </span><span class="pun">#</span><span class="lit">1565C0</span><span class="pun">;</span><span class="pln">
 color</span><span class="pun">:</span><span class="pln"> </span><span class="pun">#</span><span class="pln">FFFFFF</span><span class="pun">;</span><span class="pln">
 position</span><span class="pun">:</span><span class="pln"> fixed</span><span class="pun">;</span><span class="pln">
 top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
 z</span><span class="pun">-</span><span class="pln">index</span><span class="pun">:</span><span class="pln"> </span><span class="lit">99</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

button</span><span class="pun">:</span><span class="pln">focus </span><span class="pun">{</span><span class="pln">
 outline</span><span class="pun">:</span><span class="pln"> none</span><span class="pun">;</span><span class="pln">
 border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لإنشاء مكون <code>HomeComponent</code> ليمثل الصفحة الرئيسية، نشغِّل الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_18" style="">
<span class="pln">ng g c components</span><span class="pun">/</span><span class="pln">home</span></pre>

<p>
	لن نضيف أي شيفرة أخرى هنا إلى <code>HomeComponent</code>، لكن سنعود إليها في الجزء الثالث والأخير من السلسلة.
</p>

<h2>
	إضافة وحدة التوجيه Router Module
</h2>

<p>
	سنضيف وحدة التوجيه RouterModule في الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/app.module.ts" rel="external nofollow">app.module.ts</a> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_20" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">RouterModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/router'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@NgModule</span><span class="pun">({</span><span class="pln">
 </span><span class="pun">...</span><span class="pln">
 imports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
  </span><span class="typ">RouterModule</span><span class="pun">.</span><span class="pln">forRoot</span><span class="pun">([</span><span class="pln">
   </span><span class="pun">{</span><span class="pln"> path</span><span class="pun">:</span><span class="pln"> </span><span class="str">''</span><span class="pun">,</span><span class="pln"> component</span><span class="pun">:</span><span class="pln"> </span><span class="typ">HomeComponent</span><span class="pun">,</span><span class="pln"> pathMatch</span><span class="pun">:</span><span class="pln"> </span><span class="str">'full'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
   </span><span class="pun">{</span><span class="pln"> path</span><span class="pun">:</span><span class="pln"> </span><span class="str">'**'</span><span class="pun">,</span><span class="pln"> component</span><span class="pun">:</span><span class="pln"> </span><span class="typ">HomeComponent</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">]),</span><span class="pln">
 </span><span class="pun">],</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	المسار الفارغ هنا يمثل المسار الافتراضي للتطبيق، فإذا كان المسار في الرابط فارغًا سيعرض التطبيق صفحة HomeComponent، أما المسار فهو محرف بدل wildcard، بمعنى أن الموجِّه سيختار هذا الاتجاه إذا كان الرابط المطلوب لا يطابق أي اتجاه route معرَّف في الإعدادات من قبل.
</p>

<p>
	ومن المهم الانتباه إلى الترتيب الذي نعرِّف به الاتجاهات للتطبيق، فالموجِّه يستخدم سياسة أول تطابق ناجح عند مطابقة الاتجاهات، ولهذا يجب أن توضع الاتجاهات الأكثر دقة وتخصصًا فوق العامة منها والأقل تخصصًا، أما الاتجاه البدل wildcade route فيجب وضعه فوق النوع الثاني، أي الاتجاهات العامة، لأنه يطابق كل رابط، ويجب ألا يتم اختياره إلا إذا لم تطابَق اتجاهات أخرى قبله.
</p>

<p>
	يجب الآن تحديث AppComponent لإضافة مكون الموجه، لذا افتح <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/app.component.html" rel="external nofollow">الملف app.component.html</a> واستبدل بمحتوياته الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_22" style="">
<span class="pun">&lt;</span><span class="pln">app</span><span class="pun">-</span><span class="pln">nav</span><span class="pun">-</span><span class="pln">bar</span><span class="pun">&gt;&lt;/</span><span class="pln">app</span><span class="pun">-</span><span class="pln">nav</span><span class="pun">-</span><span class="pln">bar</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">div </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"container"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">router</span><span class="pun">-</span><span class="pln">outlet</span><span class="pun">&gt;&lt;/</span><span class="pln">router</span><span class="pun">-</span><span class="pln">outlet</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span></pre>

<p>
	الوسم <code>&lt;router-outlet&gt;</code> في الشيفرة أعلاه هو موضع مؤقت placeholder يملؤه <a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-angular-r1393/" rel="">Angular</a> ديناميكيًا بالمكون وفقًا للحالة الراهنة للموجِّه.
</p>

<p>
	أضف التنسيقات التالية إلى الملف src/styles.scss:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_24" style="">
<span class="pln">body </span><span class="pun">{</span><span class="pln">
 background</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> </span><span class="pun">#</span><span class="pln">fafafa</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">container </span><span class="pun">{</span><span class="pln">
 padding</span><span class="pun">-</span><span class="pln">top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">60px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	إضافة وحدة الاستمارات
</h2>

<p>
	سنضيف وحدة الاستمارات forms module في الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/app.module.ts" rel="external nofollow">app.module.ts</a> كما هو موضح أدناه، كي نستطيع استخدام استمارات قالبية template-driven في تطبيقنا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_26" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">FormsModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/forms'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@NgModule</span><span class="pun">({</span><span class="pln">
 </span><span class="pun">...</span><span class="pln">
 imports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
  </span><span class="typ">FormsModule</span><span class="pun">,</span><span class="pln">
 </span><span class="pun">],</span><span class="pln">
</span><span class="pun">})</span></pre>

<h2>
	إنشاء نموذج البيانات وخدمة المدونة
</h2>

<p>
	لكل تدوينة هيكل أساسي فيه بيانات التدوينة، ويعتمد التطبيق على هذا الهيكل بما أنه التطبيق هو مدونة بالأصل، لذا سننشئ نموذج بيانات التدوينة ثم سننشئ خدمة المدونة لمعالجة العمليات المطبقة على التدوينة في قاعدة البيانات من إنشاء وتعديل وحذف.
</p>

<p>
	أنشئ مجلدًا جديدًا باسم models داخل المجلد src/app، ثم أنشئ ملفًا باسم <code>post.ts</code> داخل هذا المجلد الجديد، والصق فيه الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_28" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Post</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 postId</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">;</span><span class="pln">
 title</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">;</span><span class="pln">
 content</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">;</span><span class="pln">
 author</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">;</span><span class="pln">
 createdDate</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">;</span><span class="pln">

  constructor</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">content </span><span class="pun">=</span><span class="pln"> </span><span class="str">''</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لقد هيأنا بهذا خاصية المحتوى لتكون سلسلة نصية فارغة في منشئ الصنف، وذلك لضمان أن المحتوى لن يظهر في صورة غير معرّفة undefined عند ربطه بحقل من حقول الاستمارة.
</p>

<p>
	أنشئ خدمة جديدة لمعالجة عمليات <a href="http://xn--%20https-ojibaajjs7dviwfrb0b0e/academy.hsoub.com/devops/servers/databases/%D8%A7%D9%84%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-%D9%81%D9%8A-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%88%D8%AA%D8%B5%D9%85%D9%8A%D9%85%D9%87%D8%A7-r519/" rel="external nofollow">قاعدة البيانات</a>، باستخدام الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_30" style="">
<span class="pln">ng g s services</span><span class="pun">/</span><span class="pln">blog</span></pre>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/services/blog.service.ts" rel="external nofollow">blog.service.ts</a> وأضف تعريفات الاستيراد التالية في أعلاه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_32" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Post</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'../models/post'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AngularFirestore</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/fire/firestore'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> map </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'rxjs/operators'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Observable</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'rxjs'</span><span class="pun">;</span></pre>

<p>
	ثم احقن AngularFirestore في المنشئ كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_38" style="">
<span class="pln">constructor</span><span class="pun">(</span><span class="kwd">private</span><span class="pln"> db</span><span class="pun">:</span><span class="pln"> </span><span class="typ">AngularFirestore</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	سنضيف الآن التابع من أجل إنشاء تدوينة جديدة، من خلال وضع تعريف التابع في ملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/services/blog.service.ts" rel="external nofollow">blog.service.ts</a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_40" style="">
<span class="pln">createPost</span><span class="pun">(</span><span class="pln">post</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Post</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> postData </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">post</span><span class="pun">));</span><span class="pln">
 </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">db</span><span class="pun">.</span><span class="pln">collection</span><span class="pun">(</span><span class="str">'blogs'</span><span class="pun">).</span><span class="pln">add</span><span class="pun">(</span><span class="pln">postData</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سيقبل هذا التابع معامِلًا من النوع Post، وسنحلل المعامِل إلى كائن <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-json-r604/" rel="">JSON</a>، ونضيفه إلى تجميعة <code>blogs</code> في قاعدة بياناتنا وفقًا لحالة التجميعة، فيضاف إليها إذا كانت التجميعة موجودة مسبقًا، أما إذا لم تكن موجودة فسينشئها تابع الإضافة ثم يضيف الكائن الجديد إليها.
</p>

<h2>
	إضافة محرر المدونة
</h2>

<p>
	سيعتمد محرر المدونة على حزمة CKEditor لإضافة التدوينة الجديدة وتعديلها، وهذه الحزمة عبارة عن محرر نصوص غنية rich text مكتوب بجافاسكربت وذي معمارية مبنية على الوحدات، وتوفر خصائصه وواجهته الواضحة تجربة استخدام ممتازة لإنشاء محتوى دلالي semantic بأسلوب "ما تراه هو ما تحصل عليه WYSIWYG"، وهو متوافق مع جميع متصفحات الويب الحديثة.
</p>

<p>
	نفذ الأمر التالي لتثبيت مكون محرر CKEditor لإطار Angular:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_43" style="">
<span class="pln">npm install </span><span class="pun">--</span><span class="pln">save </span><span class="lit">@ckeditor</span><span class="pun">/</span><span class="pln">ckeditor5</span><span class="pun">-</span><span class="pln">angular</span></pre>

<p>
	ثم شغِّل الأمر التالي لتثبيت إحدى البنيات الرسمية، وهي المحرر التقليدي هنا classic editor:
</p>

<pre class="ipsCode">
npm install --save @ckeditor/ckeditor5-build-classic
</pre>

<p>
	واستورد CKEditorModule إلى الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/app.module.ts" rel="external nofollow">app.module.ts</a> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_45" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">CKEditorModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@ckeditor/ckeditor5-angular'</span><span class="pun">;</span><span class="pln">
</span><span class="lit">@NgModule</span><span class="pun">(</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 imports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
  </span><span class="typ">CKEditorModule</span><span class="pun">,</span><span class="pln">
 </span><span class="pun">],</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	سننشئ مكونًا جديدًا لإضافة التدوينة وتحريرها، نفِّذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_47" style="">
<span class="pln">ng g c components</span><span class="pun">/</span><span class="pln">blog</span><span class="pun">-</span><span class="pln">editor</span></pre>

<h3>
	إضافة وجهة لصفحة إنشاء التدوينة
</h3>

<p>
	أضف وجهة route للمكون BlogEditor في الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/app.module.ts" rel="external nofollow">app.module.ts</a> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_49" style="">
<span class="typ">RouterModule</span><span class="pun">.</span><span class="pln">forRoot</span><span class="pun">([</span><span class="pln">
 </span><span class="pun">...</span><span class="pln">
 </span><span class="pun">{</span><span class="pln"> path</span><span class="pun">:</span><span class="pln"> </span><span class="str">'addpost'</span><span class="pun">,</span><span class="pln"> component</span><span class="pun">:</span><span class="pln"> </span><span class="typ">BlogEditorComponent</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
 </span><span class="pun">...</span><span class="pln">
</span><span class="pun">])</span></pre>

<h3>
	إضافة المحرر إلى التدوينة
</h3>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/blog-editor/blog-editor.component.ts" rel="external nofollow">blog-editor.component.ts</a> وأضف تعريفات الاستيراد التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_51" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> as </span><span class="typ">ClassicEditor</span><span class="pln"> from </span><span class="str">'@ckeditor/ckeditor5-build-classic'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Post</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'src/app/models/post'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">DatePipe</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/common'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">BlogService</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'src/app/services/blog.service'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Router</span><span class="pun">,</span><span class="pln"> </span><span class="typ">ActivatedRoute</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/router'</span><span class="pun">;</span></pre>

<p>
	كذلك، سنهييء بعض الخصائص لهذا المكون، فأضف الشيفرة التالية إلى صنف <code>BlogEditorComponent</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_53" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="typ">Editor</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">ClassicEditor</span><span class="pun">;</span><span class="pln">
ckeConfig</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">;</span><span class="pln">
postData </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Post</span><span class="pun">();</span><span class="pln">
formTitle </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Add'</span><span class="pun">;</span><span class="pln">
postId </span><span class="pun">=</span><span class="pln"> </span><span class="str">''</span><span class="pun">;</span></pre>

<p>
	سننشئ تابعًا لتعريف الإعدادات الخاصة بمحرر التدوينات، فأضف تعريف التابع <code>setEditorConfig</code> في الصنف <code>BlogEditorComponent</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_55" style="">
<span class="pln">setEditorConfig</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">ckeConfig </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 removePlugins</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'ImageUpload'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'MediaEmbed'</span><span class="pun">],</span><span class="pln">
 heading</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   options</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> model</span><span class="pun">:</span><span class="pln"> </span><span class="str">'paragraph'</span><span class="pun">,</span><span class="pln"> title</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Paragraph'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">class</span><span class="pun">:</span><span class="pln"> </span><span class="str">'ckheading_paragraph'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> model</span><span class="pun">:</span><span class="pln"> </span><span class="str">'heading1'</span><span class="pun">,</span><span class="pln"> view</span><span class="pun">:</span><span class="pln"> </span><span class="str">'h1'</span><span class="pun">,</span><span class="pln"> title</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Heading 1'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">class</span><span class="pun">:</span><span class="pln"> </span><span class="str">'ckheading_heading1'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> model</span><span class="pun">:</span><span class="pln"> </span><span class="str">'heading2'</span><span class="pun">,</span><span class="pln"> view</span><span class="pun">:</span><span class="pln"> </span><span class="str">'h2'</span><span class="pun">,</span><span class="pln"> title</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Heading 2'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">class</span><span class="pun">:</span><span class="pln"> </span><span class="str">'ckheading_heading2'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> model</span><span class="pun">:</span><span class="pln"> </span><span class="str">'heading3'</span><span class="pun">,</span><span class="pln"> view</span><span class="pun">:</span><span class="pln"> </span><span class="str">'h3'</span><span class="pun">,</span><span class="pln"> title</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Heading 3'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">class</span><span class="pun">:</span><span class="pln"> </span><span class="str">'ckheading_heading3'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> model</span><span class="pun">:</span><span class="pln"> </span><span class="str">'heading4'</span><span class="pun">,</span><span class="pln"> view</span><span class="pun">:</span><span class="pln"> </span><span class="str">'h4'</span><span class="pun">,</span><span class="pln"> title</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Heading 4'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">class</span><span class="pun">:</span><span class="pln"> </span><span class="str">'ckheading_heading4'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> model</span><span class="pun">:</span><span class="pln"> </span><span class="str">'heading5'</span><span class="pun">,</span><span class="pln"> view</span><span class="pun">:</span><span class="pln"> </span><span class="str">'h5'</span><span class="pun">,</span><span class="pln"> title</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Heading 5'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">class</span><span class="pun">:</span><span class="pln"> </span><span class="str">'ckheading_heading5'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> model</span><span class="pun">:</span><span class="pln"> </span><span class="str">'heading6'</span><span class="pun">,</span><span class="pln"> view</span><span class="pun">:</span><span class="pln"> </span><span class="str">'h6'</span><span class="pun">,</span><span class="pln"> title</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Heading 6'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">class</span><span class="pun">:</span><span class="pln"> </span><span class="str">'ckheading_heading6'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> model</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Formatted'</span><span class="pun">,</span><span class="pln"> view</span><span class="pun">:</span><span class="pln"> </span><span class="str">'pre'</span><span class="pun">,</span><span class="pln"> title</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Formatted'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
   </span><span class="pun">]</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
 </span><span class="pun">};</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يًستخدم التابع لضبط إعدادات المحرر التقليدي، وسنزيل إضافتي ImageUpload و MediaEmbed من المحرر بما أننا لن نستخدم وظائفهما في التطبيق، وسنضبط خيارات التنسيق في المحرر لتشمل الترويسات والفقرات والنصوص المنسَّقة formatted text.
</p>

<p>
	نستدعي هذا التابع داخل تابع <code>ngOnInit</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_57" style="">
<span class="pln">ngOnInit</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">setEditorConfig</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	تحديث قالب التدوينة
</h3>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/blog-editor/blog-editor.component.html" rel="external nofollow">blog-editor.component.html</a> وضع الشيفرة التالية فيه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_60" style="">
<span class="pun">&lt;</span><span class="pln">h1</span><span class="pun">&gt;{{</span><span class="pln">formTitle</span><span class="pun">}}</span><span class="pln"> </span><span class="typ">Post</span><span class="pun">&lt;/</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">hr </span><span class="pun">/&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">form </span><span class="pun">#</span><span class="pln">myForm</span><span class="pun">=</span><span class="str">"ngForm"</span><span class="pln"> </span><span class="pun">(</span><span class="pln">ngSubmit</span><span class="pun">)=</span><span class="str">"myForm.form.valid &amp;&amp; saveBlogPost()"</span><span class="pln">
accept</span><span class="pun">-</span><span class="pln">charset</span><span class="pun">=</span><span class="str">"UTF-8"</span><span class="pln"> novalidate</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">input type</span><span class="pun">=</span><span class="str">"text"</span><span class="pln"> </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"blogHeader"</span><span class="pln"> placeholder</span><span class="pun">=</span><span class="str">"Add title..."</span><span class="pln">
</span><span class="kwd">class</span><span class="pun">=</span><span class="str">"form-control"</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"postTitle"</span><span class="pln">
 </span><span class="pun">[(</span><span class="pln">ngModel</span><span class="pun">)]=</span><span class="str">"postData.title"</span><span class="pln"> </span><span class="pun">#</span><span class="pln">postTitle</span><span class="pun">=</span><span class="str">"ngModel"</span><span class="pln"> required </span><span class="pun">/&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">span </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"text-danger"</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ngIf</span><span class="pun">=</span><span class="str">"</span><span class="pln">myForm</span><span class="pun">.</span><span class="pln">submitted </span><span class="pun">&amp;&amp;</span><span class="pln">
postTitle</span><span class="pun">.</span><span class="pln">errors</span><span class="pun">?.</span><span class="pln">required</span><span class="str">"&gt;</span><span class="pln">
 </span><span class="typ">Title</span><span class="pln"> is required
  </span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">br </span><span class="pun">/&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">div </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"form-group"</span><span class="pun">&gt;</span><span class="pln">
   </span><span class="pun">&lt;</span><span class="pln">ckeditor name</span><span class="pun">=</span><span class="str">"myckeditor"</span><span class="pln"> </span><span class="pun">[</span><span class="pln">config</span><span class="pun">]=</span><span class="str">"ckeConfig"</span><span class="pln">
</span><span class="pun">[(</span><span class="pln">ngModel</span><span class="pun">)]=</span><span class="str">"postData.content"</span><span class="pln"> </span><span class="pun">#</span><span class="pln">myckeditor</span><span class="pun">=</span><span class="str">"ngModel"</span><span class="pln">
 debounce</span><span class="pun">=</span><span class="str">"300"</span><span class="pln"> </span><span class="pun">[</span><span class="pln">editor</span><span class="pun">]=</span><span class="str">"Editor"</span><span class="pun">&gt;&lt;/</span><span class="pln">ckeditor</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">div </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"form-group"</span><span class="pun">&gt;</span><span class="pln">
   </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"submit"</span><span class="pln"> mat</span><span class="pun">-</span><span class="pln">raised</span><span class="pun">-</span><span class="pln">button
color</span><span class="pun">=</span><span class="str">"primary"</span><span class="pun">&gt;</span><span class="typ">Save</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
   </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">" button"</span><span class="pln"> mat</span><span class="pun">-</span><span class="pln">raised</span><span class="pun">-</span><span class="pln">button color</span><span class="pun">=</span><span class="str">"warn"</span><span class="pln">
</span><span class="pun">(</span><span class="pln">click</span><span class="pun">)=</span><span class="str">"cancel()"</span><span class="pun">&gt;</span><span class="pln">CANCEL</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">form</span><span class="pun">&gt;</span></pre>

<p>
	لقد عرّفنا هنا استمارة قالبية نستدعي منها التابع <code>saveBlogPost</code> على عملية الإرسال الناجحة لبيانات الاستمارة، وتحتوي الاستمارة على حقلين، أحدهما يقبل عنوان التدوينة وهو حقل إجباري، وأما الآخر فيقبل محتواها، ويحتوي على مكون محرر CKEditor، حيث تُستخدم خاصية المحرر لتعيين نوعه.
</p>

<p>
	ونستخدم هنا النسخة التقليدية من محرر <code>CKEditor</code>، وسنربط خاصية الإعداد <code>config</code> لمكون <code>ckeditor</code> مع المتغير <code>ckeConfig</code> الذي أعددناه في القسم السابق. كذلك سنربط محتوى المكون <code>ckeditor</code> مع خاصية المحتوى للكائن <code>postData</code> الذي يحمل النوع <code>Post</code>.
</p>

<p>
	كذلك، سيعيد المكون <code>ckeditor</code> المحتوى بتنسيق <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> وليس بتنسيق نص عادي، وسنحفظ كل تدوينة في صورة HTML في قاعدة البيانات، لنضمن عدم فقد تنسيق المحتوى أثناء الحفظ، وكذلك أثناء جلبه من قاعدة البيانات.
</p>

<p>
	أخيرًا، أضف التنسيق لمحرر التدوينة في الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/styles.scss" rel="external nofollow">styles.scss</a> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_65" style="">
<span class="pun">.</span><span class="pln">ck</span><span class="pun">-</span><span class="pln">editor__editable </span><span class="pun">{</span><span class="pln">
 max</span><span class="pun">-</span><span class="pln">height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">350px</span><span class="pun">;</span><span class="pln">
 min</span><span class="pun">-</span><span class="pln">height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">350px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

pre </span><span class="pun">{</span><span class="pln">
 display</span><span class="pun">:</span><span class="pln"> block</span><span class="pun">;</span><span class="pln">
 padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">9.5px</span><span class="pun">;</span><span class="pln">
 margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">10px</span><span class="pun">;</span><span class="pln">
 font</span><span class="pun">-</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">13px</span><span class="pun">;</span><span class="pln">
 line</span><span class="pun">-</span><span class="pln">height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.42857143</span><span class="pun">;</span><span class="pln">
 color</span><span class="pun">:</span><span class="pln"> </span><span class="pun">#</span><span class="lit">333</span><span class="pun">;</span><span class="pln">
 word</span><span class="pun">-</span><span class="kwd">break</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">break</span><span class="pun">-</span><span class="pln">all</span><span class="pun">;</span><span class="pln">
 word</span><span class="pun">-</span><span class="pln">wrap</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">break</span><span class="pun">-</span><span class="pln">word</span><span class="pun">;</span><span class="pln">
 background</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> </span><span class="pun">#</span><span class="pln">f5f5f5</span><span class="pun">;</span><span class="pln">
 border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1px</span><span class="pln"> solid </span><span class="pun">#</span><span class="pln">ccc</span><span class="pun">;</span><span class="pln">
 border</span><span class="pun">-</span><span class="pln">radius</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

blockquote </span><span class="pun">{</span><span class="pln">
 display</span><span class="pun">:</span><span class="pln"> block</span><span class="pun">;</span><span class="pln">
 padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10px</span><span class="pln"> </span><span class="lit">20px</span><span class="pun">;</span><span class="pln">
 margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">20px</span><span class="pun">;</span><span class="pln">
 font</span><span class="pun">-</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">17.5px</span><span class="pun">;</span><span class="pln">
 border</span><span class="pun">-</span><span class="pln">left</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5px</span><span class="pln"> solid </span><span class="pun">#</span><span class="pln">eee</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">spacer </span><span class="pun">{</span><span class="pln">
 flex</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="kwd">auto</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

img</span><span class="pun">{</span><span class="pln">
 max</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	إضافة تدوينة جديدة
</h2>

<p>
	سننفذ الآن خاصية إضافة التدوينة الجديدة في تطبيقنا، فافتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/blog-editor/blog-editor.component.ts" rel="external nofollow">blog-editor.component.ts</a> وأضف تعريفات الخدمات التالية إلى المنشئ:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_67" style="">
<span class="pln">constructor</span><span class="pun">(</span><span class="kwd">private</span><span class="pln"> route</span><span class="pun">:</span><span class="pln"> </span><span class="typ">ActivatedRoute</span><span class="pun">,</span><span class="pln">
 </span><span class="kwd">private</span><span class="pln"> datePipe</span><span class="pun">:</span><span class="pln"> </span><span class="typ">DatePipe</span><span class="pun">,</span><span class="pln">
 </span><span class="kwd">private</span><span class="pln"> blogService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">BlogService</span><span class="pun">,</span><span class="pln">
 </span><span class="kwd">private</span><span class="pln"> router</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Router</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	ثم أضف مزود <code>DataPipe</code> في قسم المزخرِف <code>‎@Component</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_69" style="">
<span class="lit">@Component</span><span class="pun">({</span><span class="pln">
 </span><span class="pun">...</span><span class="pln"> providers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">DatePipe</span><span class="pun">]</span><span class="pln"> 
</span><span class="pun">}</span></pre>

<p>
	وكذلك تعريف التابع <code>saveBlogPost</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_71" style="">
<span class="pln">saveBlogPost</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">postData</span><span class="pun">.</span><span class="pln">createdDate </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">datePipe</span><span class="pun">.</span><span class="pln">transform</span><span class="pun">(</span><span class="typ">Date</span><span class="pun">.</span><span class="pln">now</span><span class="pun">(),</span><span class="pln"> </span><span class="str">'MMdd-yyyy HH:mm'</span><span class="pun">);</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">blogService</span><span class="pun">.</span><span class="pln">createPost</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">postData</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">
  </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">router</span><span class="pun">.</span><span class="pln">navigate</span><span class="pun">([</span><span class="str">'/'</span><span class="pun">]);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
 </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أضف الآن التاريخ الحالي في كائن <code>postData</code> على أنه تاريخ إنشاء التدوينة، ثم استدع التابع <code>createPost</code> من <code>BlogService</code> لإضافة تدوينة جديدة إلى قاعدة بياناتنا، وسيُستدعى هذا التابع عند النقر على زر Save. سنضيف أيضًا تعريف التابع التالي لتابع cancel الذي يُستدعى عند النقر على زر Cancel:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_73" style="">
<span class="pln">cancel</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">router</span><span class="pun">.</span><span class="pln">navigate</span><span class="pun">([</span><span class="str">'/'</span><span class="pun">]);</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	إضافة أزرار إلى شريط التنقل
</h2>

<p>
	سنضيف في شريط التنقل زرًا للانتقال إلى محرر التدوينة وكذلك إلى الصفحة الرئيسية، من خلال وضع الشيفرة التالية في العنصر <code>&lt;mat-toolbar&gt;</code> داخل الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/nav-bar/nav-bar.component.html" rel="external nofollow">nav-bar.component.html</a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_75" style="">
<span class="pun">&lt;</span><span class="pln">button mat</span><span class="pun">-</span><span class="pln">button </span><span class="pun">[</span><span class="pln">routerLink</span><span class="pun">]=</span><span class="str">'[""]'</span><span class="pun">&gt;</span><span class="pln"> </span><span class="typ">My</span><span class="pln"> blog </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">button mat</span><span class="pun">-</span><span class="pln">button </span><span class="pun">[</span><span class="pln">routerLinkActive</span><span class="pun">]=</span><span class="str">'["link-active"]'</span><span class="pln">
</span><span class="pun">[</span><span class="pln">routerLink</span><span class="pun">]=</span><span class="str">'["/addpost"]'</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="typ">Add</span><span class="pln"> </span><span class="typ">Post</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">span </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"spacer"</span><span class="pun">&gt;&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span></pre>

<p>
	بما أن الخادم يعمل فستحدِّث الصفحة نفسها كلما حدث تغيير جديد، فافتح المتصفح وانقر على زر AddPost في شريط التنقل لينتقل التطبيق إلى صفحة إضافة التدوينة، ثم أضف تدوينة جديدة وانقر زر الحفظ لحفظ التدوينة في قاعدة البيانات، انظر الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91939" href="https://academy.hsoub.com/uploads/monthly_2022_02/addpost.png.e6f74ef529f36d5010ba0a9f603dcfdf.png" rel=""><img alt="addpost.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91939" data-unique="pa889iukl" src="https://academy.hsoub.com/uploads/monthly_2022_02/addpost.thumb.png.3281735ce3d7d7025ef8cae102001fe1.png" style="width: 600px; height: auto;"></a>
</p>

<p>
	ستضاف التدوينة إلى قاعدة بيانات Firestore الخاصة بنا بمجرد النقر على زر Save الموضح في الصورة أعلاه، ثم تنتقل الصفحة إلى الصفحة الرئيسية للتطبيق.
</p>

<p>
	فإذا أردنا هنا أن نتحقق من إضافة البيانات بنجاح إلى قاعدة البيانات، فإننا نفتح طرفية Firebase وننتقل إلى صفحة اللوحة العامة للمشروع ثم اضغط على الرابط Database في القائمة اليسرى، لنرى حينئذ سجلًا يُظهر التدوينة التي نشرناها، وسيحتوي حقل content على محتوى التدوينة بتنسيق HTML. انظر الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91940" href="https://academy.hsoub.com/uploads/monthly_2022_02/dbcheck.png.bc0477796fbb53938b9843a14b900577.png" rel=""><img alt="dbcheck.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91940" data-unique="9o5aic3y3" src="https://academy.hsoub.com/uploads/monthly_2022_02/dbcheck.thumb.png.c928f444708bc7a007039f38b869d111.png" style="width: 800px; height: auto;"></a>
</p>

<h2>
	إنشاء أنابيب مخصصة لمعالجة البيانات
</h2>

<p>
	سنضيف نوعين من الأنابيب المخصصة custom pipes في تطبيقنا:
</p>

<ul>
<li>
		Excerpt: يعرض هذا النوع ملخصًا للمقالة على بطاقة التدوينة.
	</li>
	<li>
		Slug: يعرض هذا النوع الاسم اللطيف slug الموجود في رابط التدوينة.
	</li>
</ul>
<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		الاسم اللطيف هو الاسم المناسب ليكون رابطًا صالحًا مثلًا عنوان "تعلم البرمجة بجافاسكربت" غير مناسب ليكون رابطًا لأنه يحوي فراغات بينما "تعلم-البرمجة-بجافاسكربت" هو slug لأنه مناسب لأن يكون رابطًا ويضاف في نهاية العنوان مثل ‪academy.hsoub.com/programming/تعلم-البرمجة-بجافاسكربت
	</p>
</blockquote>

<p>
	شغِّل الأمر التالي لتوليد أنبوب الملخص excerpt pipe:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_77" style="">
<span class="pln">ng g p customPipes</span><span class="pun">/</span><span class="pln">excerpt</span></pre>

<p>
	ثم افتح <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/0b3d8d33891e2929e9fa4ef581387e60718103ee/src/app/customPipes/excerpt.pipe.ts#L8-L15" rel="external nofollow">excerpt.pipe.ts</a> واستبدل الشيفرة التالية بتابع التحويل transform الذي في الملف:
</p>

<pre class="ipsCode">
transform(content: string) {
 const postSummary = content.replace(/(&lt;([^&gt;]+)&gt;)/ig, '');
 if (postSummary.length &gt; 300) {
  return postSummary.substr(0, 300) + ' [...]';
 } else {
  return postSummary;
 }
}
</pre>

<p>
	سيقبل هذا الأنبوب محتويات التدوينة كسلسلة نصية ويعيد أول 300 محرف لتشكل ملخص تلك التدوينة، وبما أن محتوى التدوينة يكون بتنسيق HTML، فسنزيل كل وسوم HTML قبل استخراج الملخص.
</p>

<p>
	شغٍّل الأمر التالي لتوليد أنبوب Slug:
</p>

<pre class="ipsCode">
ng g p customPipes/slug
</pre>

<p>
	ثم افتح ملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/0b3d8d33891e2929e9fa4ef581387e60718103ee/src/app/customPipes/slug.pipe.ts#L6-L11" rel="external nofollow">slug.pipe.ts</a> واستبدل الشيفرة التالية بتابع التحويل الذي فيه:
</p>

<pre class="ipsCode">
transform(title: string) {
 const urlSlug = title.trim().toLowerCase().replace(/ /g, '-');
 return urlSlug;
}
</pre>

<p>
	سيقبل هذا الأنبوب عنوان التدوينة ويعيده ليكون هو الاسم اللطيف slug في الرابط، وسنستبدل محرف المسافة البيضاء التي بين الكلمات في العنوان بمحرف <code>-</code> لإنشاء الاسم اللطيف للرابط.
</p>

<h2>
	عرض التدوينات
</h2>

<p>
	سنحتاج بعد إضافة كل تدوينة على حدى إلى عرض هذه التدوينة في مكان ما، لذا سنعرض كل التدوينات في الصفحة الرئيسية على شكل بطاقات يمكن الضغط عليها لعرض التدوينة في صفحة منفصلة. سنجلب أولًا التدوينات من قاعدة الباينات ونعرضها في الصفحة الرئيسية في مكون خاص يدعى <code>BlogCardComponent</code>.
</p>

<h3>
	جلب التدوينات من قاعدة البيانات
</h3>

<p>
	يُستخدم التابع <code>getAllPosts</code> لجلب جميع التدوينات الخاصة بالمدونة من قاعدة البيانات، وسنضيف ذلك التابع في الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/services/blog.service.ts" rel="external nofollow">blog.service.ts</a>.
</p>

<p>
	فيما يلي التعريف الخاص بتابع <code>getAllPosts</code>:
</p>

<pre class="ipsCode">
getAllPosts(): Observable&lt;Post[]&gt; { const blogs = this.db.collection&lt;Post&gt;('blogs', refgetAllPosts(): Observable&lt;Post[]&gt; {
 const blogs = this.db.collection&lt;Post&gt;('blogs', ref =&gt;
ref.orderBy('createdDate', 'desc'))
 .snapshotChanges().pipe(
  map(actions =&gt; {
   return actions.map(
    c =&gt; ({
     postId: c.payload.doc.id,
     ...c.payload.doc.data()
   }));
  }));
 return blogs;
}
</pre>

<p>
	سنجلب التدوينة وفق تاريخ إنشائها تنازليًا، لنضمن أن التدوينة الأخيرة تكون في الأعلى دومًا.
</p>

<h3>
	إنشاء مكون قائمة التدوينات BlogCardComponent
</h3>

<p>
	نفِّذ الأمر التالي لإنشاء مكون بطاقة التدوينة Blog Card Component:
</p>

<pre class="ipsCode">
ng g c components/blog-card
</pre>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/blog-card.component.ts" rel="external nofollow">blog-card.component.ts</a> وأضف تعريفات الاستيراد التالية:
</p>

<pre class="ipsCode">
import { OnDestroy } from '@angular/core';
import { BlogService } from 'src/app/services/blog.service';
import { Post } from 'src/app/models/post';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
</pre>

<p>
	ثم احقن خدمة المدونة blog service في منشئ الصنف <code>BlogCardComponent</code> كما يلي:
</p>

<pre class="ipsCode">
constructor(private blogService: BlogService) { }
</pre>

<p>
	وأضف خاصية تحتفظ بالتدوينة الحالية:
</p>

<pre class="ipsCode">
blogPost: Post[] = [];
private unsubscribe$ = new Subject&lt;void&gt;();
</pre>

<p>
	سننشئ الآن تابعًا ليجلب التدوينة ويستدعيها داخل <code>ngOnInit</code> الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/blog-card.component.ts" rel="external nofollow">blog-card.component.ts</a>:
</p>

<pre class="ipsCode">
ngOnInit() {
 this.getBlogPosts();
}

getBlogPosts() {
 this.blogService.getAllPosts()
  .pipe(takeUntil(this.unsubscribe$))
  .subscribe(result =&gt; {
   this.blogPost = result;
  });
}
</pre>

<p>
	بعد ذلك نطبق الواجهة <code>OnDestroy</code> على الصنف <code>BlogCardComponent</code>، ثم نكمل تنفيذ الاشتراك <code>unsubscribe$‎</code> داخل التابع <code>ngOnDestroy</code>، كما هو موضح في الشيفرة أدناه:
</p>

<pre class="ipsCode">
export class BlogCardComponent implements OnInit, OnDestroy {
...

 ngOnDestroy() {
  this.unsubscribe$.next();
  this.unsubscribe$.complete();
 }
}
</pre>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/blog-card.component.html" rel="external nofollow">blog-card.component.html</a> واستبدل الشيفرة التالية بالشيفرة الموجودة في الملف:
</p>

<pre class="ipsCode">
&lt;ng-template #emptyblog&gt;
 &lt;div class="spinner-container"&gt;
  &lt;mat-spinner&gt;&lt;/mat-spinner&gt;
 &lt;/div&gt;
&lt;/ng-template&gt;
&lt;ng-container *ngIf="blogPost &amp;&amp; blogPost.length&gt;0; else emptyblog"&gt;
 &lt;div *ngFor="let post of blogPost"&gt;
  &lt;mat-card class="blog-card mat-elevation-z2"&gt;
   &lt;mat-card-content&gt;
    &lt;a class="blog-title" [routerLink]="['/blog/',
post.postId, post.title | slug]"&gt;
     &lt;h2&gt;{{ post.title}} &lt;/h2&gt;
    &lt;/a&gt;
   &lt;/mat-card-content&gt;
   &lt;mat-card-content&gt;
    &lt;div [innerHTML]="post.content | excerpt"&gt;&lt;/div&gt;
   &lt;/mat-card-content&gt;
   &lt;mat-divider&gt;&lt;/mat-divider&gt;
   &lt;mat-card-actions align="end"&gt;
    &lt;ng-container&gt;
     &lt;button mat-raised-button color="accent"
[routerLink]="['/editpost',post.postId]"&gt;Edit&lt;/button&gt;
     &lt;button mat-raised-button color="warn"
(click)="delete(post.postId)"&gt;Delete&lt;/button&gt;
    &lt;/ng-container&gt;
    &lt;span class="spacer"&gt;&lt;/span&gt;
    &lt;button mat-raised-button color="primary"
[routerLink]="['/blog/', post.postId, post.title | slug]"&gt;Read
 More&lt;/button&gt;
   &lt;/mat-card-actions&gt;
  &lt;/mat-card&gt;
 &lt;/div&gt;
 &lt;mat-divider&gt;&lt;/mat-divider&gt;
&lt;/ng-container&gt;
</pre>

<p>
	سنعرض عنوان التدوينة والملخص في المكوِّن mat-card، وهو مكون في Angular Material يُستخدم لعرض المحتوى في هيئة بطاقات، ونذكِّر هنا أننا استخدمنا أنبوب الاسم اللطيف slug لإنشاء رابط التدوينة، وأنبوب الملخص excerpt للحصول على ملخص لكل تدوينة.
</p>

<p>
	وستحتوي بطاقة كل تدوينة على أزرار لتحريرها وحذفها، ويستطيع أي مستخدم عند هذه النقطة أن يصل إلى هذه الأزرار، بيد أن هذا سلوك خاطئ وسنغيره لاحقًا بإضافة أذونات authorizations إلى التطبيق كي لا يستطيع أحد أن يعدّل أو يحذف من التدوينات إلا من لديه صلاحيات المدير admin فقط.
</p>

<p>
	جدير بالذكر هنا أننا قد نحصل على خطأ في هذا الملف، ذلك أننا أضفنا زر الحذف Delete وعرَّفنا تابعًا للحذف عند وقوع حدث النقر على الزر، غير أننا لم نضف أي تابع حذف بعد، وعليه فسنحصل على خطأ وقت التصريف compile-time error. ولحل هذه المشكلة، نضيف الشيفرة التالية في الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/blog-card.component.ts" rel="external nofollow">blog-card.component.ts</a>:
</p>

<pre class="ipsCode">
delete(postId: string) { 
 // هنا يوضع تعريف التابع، وسنكتبه لاحقًا
}
</pre>

<p>
	عرَّفنا في هذه الشيفرة تابعًا فارغًا، وسنضيف منطق الحذف إليه في الجزء الأخير من الكتاب.
</p>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/blog-card.component.scss" rel="external nofollow">blog-card.component.scss</a> واستبدل الشيفرة التالية بالموجودة في الملف:
</p>

<pre class="ipsCode">
.blog-card {
 margin-bottom: 15px;
}

.blog-title {
 text-decoration: none;
}

a:hover {
 color: indianred;
}

.spinner-container{
 display: flex;
 align-items: center;
 justify-content: center;
 height: 100%;
}
</pre>

<h3>
	إضافة قائمة التدوينات إلى الصفحة الرئيسية
</h3>

<p>
	بما أننا سنعرض بطاقات التدوينات على الصفحة الرئيسية، فسنحتاج إلى إضافة مكون بطاقة التدوينة إلى مكون الصفحة الرئيسية home، فافتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/home.component.html" rel="external nofollow">home.component.html</a> واستبدل شيفرة HTML التالية بالموجودة في الملف:
</p>

<pre class="ipsCode">
&lt;div class="row left-panel"&gt;
 &lt;div class="col-md-9"&gt;
  &lt;app-blog-card&gt;&lt;/app-blog-card&gt;
 &lt;/div&gt;
&lt;/div&gt;
</pre>

<p>
	ثم افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/home.component.scss" rel="external nofollow">home.component.scss</a> وأضف تعريف التنسيق التالي داخله:
</p>

<pre class="ipsCode">
.left-panel {
 margin-top: 15px;
}
</pre>

<p>
	هنا نكون قد أضفنا بطاقة التدوينة إلى الصفحة الرئيسية، فافتح المتصفح لترى التدوينة معروضة على الصفحة الرئيسية، وهي نفس التدوينة التي أضفناها في نقطة التحقق الأولى من قبل، وستحتوي البطاقة أيضًا على أزرار التحرير Edit والحذف Delete وقراءة المزيد Read More، غير أنها جميعًا لا تعمل إلى الآن، وإنما سنضيف منطق هذه الأزرار في الجزء الأخير من الكتاب، انظر الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91941" href="https://academy.hsoub.com/uploads/monthly_2022_02/chkpt2.png.b7e47d58395c6990848136045a57c43f.png" rel=""><img alt="chkpt2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91941" data-unique="40sto19s4" src="https://academy.hsoub.com/uploads/monthly_2022_02/chkpt2.thumb.png.e7209d23952545f4d6b3a5ddfc99594c.png" style="width: 650px; height: auto;"></a>
</p>

<h2>
	عرض تدوينة واحدة
</h2>

<p>
	بعد أن عرضنا قائمة بكل التدوينات في الصفحة الرئيسية، نحتاج إلى عرض كل تدوينة على حدى في صفحة منفصلة وذلك لمن يريد قراءة التدوينة، وهنا سننشئ المكون <code>BlogComponent</code> لعرض كل تدوينة معتمدين على مكتبات خطوط وأيقونات لتنسيق المكون.
</p>

<h3>
	إضافة مكتبة الخط Awesome
</h3>

<p>
	سنضيف مكتبات الخط Awesome وأيقونات السمة Material إلى التطبيق، ونستخدم تجميعات الأيقونات التي توفرها تلك المكتبة في تطبيقنا، فأضف الأسطر التالية في الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/index.html" rel="external nofollow">index.html</a>:
</p>

<pre class="ipsCode">
&lt;link href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet"&gt;
&lt;link rel="stylesheet" type="text/css"
 href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/fontawesome.min.css" /&gt;
</pre>

<h3>
	قراءة التدوينة
</h3>

<p>
	شغِّل الأمر التالي لإنشاء مكون تدوينة، من أجل إضافة خاصية قراءة التدوينات للتطبيق:
</p>

<pre class="ipsCode">
ng g c components/blog
</pre>

<p>
	أضف رابط الموجِّه router لهذا المكون في ملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/app.module.ts" rel="external nofollow">app.module.ts</a> كما يلي:
</p>

<pre class="ipsCode">
{ path: 'blog/:id/:slug', component: BlogComponent },
</pre>

<p>
	لقد عرَّفنا معامليْن في هذا الاتجاه، الأول هو <code>id</code>، وهو المعرِّف الفريد لكل تدوينة، أما الآخر فهو الاسم اللطيف slug الذي يُنشأ من عنوان التدوينة نفسها.
</p>

<p>
	أضف تعريف التابع التالي في ملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/services/blog.service.ts" rel="external nofollow">blog.services.ts</a>، سيجلب هذا التابع تفاصيل التدوينة بناءً على المعرِّف الموجود في تجميعات <code>blogs</code>:
</p>

<pre class="ipsCode">
getPostbyId(id: string): Observable&lt;Post&gt; {
 const blogDetails = this.db.doc&lt;Post&gt;('blogs/' + id).valueChanges();
 return blogDetails;
}
</pre>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/blog/blog.component.ts" rel="external nofollow">blog.component.ts</a> واستورد التعريفات كما هو موضح أدناه:
</p>

<pre class="ipsCode">
import { OnDestroy } from '@angular/core';
import { Post } from 'src/app/models/post';
import { ActivatedRoute } from '@angular/router';
import { BlogService } from 'src/app/services/blog.service';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
</pre>

<p>
	سنحدِّث الصنف <code>BlogComponent</code> كما يلي:
</p>

<pre class="ipsCode">
export class BlogComponent implements OnInit, OnDestroy {

 postData: Post = new Post();
 postId;
 private unsubscribe$ = new Subject&lt;void&gt;();

 constructor(private route: ActivatedRoute,
  private blogService: BlogService) {
  if (this.route.snapshot.params['id']) {
   this.postId = this.route.snapshot.paramMap.get('id');
  }
 }

ngOnInit() {
 this.blogService.getPostbyId(this.postId)
  .pipe(takeUntil(this.unsubscribe$))
  .subscribe(
   (result: Post) =&gt; {
    this.postData = result;
   }
  );
}

 ngOnDestroy() {
  this.unsubscribe$.next();
  this.unsubscribe$.complete();
 }
}
</pre>

<p>
	داخل تابع الخطاف <code>ngOnInit</code>، سنستدعي التابع <code>getPostbyID</code> الخاص بـ <code>BlogService</code>، ونمرر <code>postID</code> كمعامِل لجلب تفاصيل تدوينة ما، وسنحصل على المعرِّف من الرابط في منشئ الصنف، أما داخل التابع <code>ngOnDestroy</code> فسنكمل اشتراك <code>unsubscribe$‎</code>.
</p>

<p>
	افتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/home.component.html" rel="external nofollow">blog.component.html</a> واستبدل الشيفرة التالية بالموجودة فيه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_80" style="">
<span class="pun">&lt;</span><span class="pln">div </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"docs-example-viewer-wrapper"</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">h1 </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"entry-title"</span><span class="pun">&gt;{{</span><span class="pln">postData</span><span class="pun">.</span><span class="pln">title</span><span class="pun">}}&lt;/</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">card</span><span class="pun">-</span><span class="pln">subtitle </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"blog-info"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">i </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"fa fa-calendar"</span><span class="pln"> aria</span><span class="pun">-</span><span class="pln">hidden</span><span class="pun">=</span><span class="str">"true"</span><span class="pun">&gt;&lt;/</span><span class="pln">i</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">{{</span><span class="pln">postData</span><span class="pun">.</span><span class="pln">createdDate </span><span class="pun">|</span><span class="pln"> date</span><span class="pun">:</span><span class="str">'longDate'</span><span class="pun">}}</span><span class="pln">
 </span><span class="pun">&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">card</span><span class="pun">-</span><span class="pln">subtitle</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">divider</span><span class="pun">&gt;&lt;/</span><span class="pln">mat</span><span class="pun">-</span><span class="pln">divider</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;</span><span class="pln">div </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"docs-example-viewer-body"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">div </span><span class="pun">[</span><span class="pln">innerHTML</span><span class="pun">]=</span><span class="str">"postData.content"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span></pre>

<p>
	يُستخدم هذا المكون لعرض التدوينة كاملة، وسنعرض تاريخ إنشاء التدوينة وأيقونة التقويم calendar باستخدام الخط awesome، وسنربط محتوى التدوينة بالخاصية <code>innerHTML</code> للعنصر <code>&lt;div&gt;</code> بما أن محتوى التدوينة يخزَّن ويُجلب بتنسيق HTML، وذلك لضمان أن التدوينة ستُعرض كنص عادي على الشاشة.
</p>

<p>
	أخيرًا، سنضيف التنسيق إلى <code>BlogComponent</code>، فافتح الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/components/home.component.scss" rel="external nofollow">blog.component.scss</a> وضع تعريفات التنسيق التالية فيه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8183_83" style="">
<span class="pun">.</span><span class="pln">docs</span><span class="pun">-</span><span class="pln">example</span><span class="pun">-</span><span class="pln">viewer</span><span class="pun">-</span><span class="pln">body </span><span class="pun">{</span><span class="pln">
 padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">20px</span><span class="pun">;</span><span class="pln">
 align</span><span class="pun">-</span><span class="pln">content</span><span class="pun">:</span><span class="pln"> center</span><span class="pun">;</span><span class="pln">
 align</span><span class="pun">-</span><span class="pln">items</span><span class="pun">:</span><span class="pln"> center</span><span class="pun">;</span><span class="pln">
 font</span><span class="pun">-</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">14px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">docs</span><span class="pun">-</span><span class="pln">example</span><span class="pun">-</span><span class="pln">viewer</span><span class="pun">-</span><span class="pln">wrapper </span><span class="pun">{</span><span class="pln">
 border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1px</span><span class="pln"> solid rgba</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="pun">.</span><span class="lit">03</span><span class="pun">);</span><span class="pln">
 box</span><span class="pun">-</span><span class="pln">shadow</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">1px</span><span class="pln"> </span><span class="lit">1px</span><span class="pln"> rgba</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="pun">.</span><span class="lit">24</span><span class="pun">),</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">2px</span><span class="pln"> rgba</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="pun">.</span><span class="lit">12</span><span class="pun">);</span><span class="pln">
 border</span><span class="pun">-</span><span class="pln">radius</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4px</span><span class="pun">;</span><span class="pln">
 margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1em</span><span class="pln"> </span><span class="kwd">auto</span><span class="pun">;</span><span class="pln">
 background</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> </span><span class="pun">#</span><span class="pln">FFFFFF</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">entry</span><span class="pun">-</span><span class="pln">title </span><span class="pun">{</span><span class="pln">
 margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">20px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">blog</span><span class="pun">-</span><span class="pln">info </span><span class="pun">{</span><span class="pln">
 margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">15px</span><span class="pln"> </span><span class="lit">20px</span><span class="pln"> </span><span class="lit">10px</span><span class="pun">;</span><span class="pln">
 align</span><span class="pun">-</span><span class="pln">content</span><span class="pun">:</span><span class="pln"> center</span><span class="pun">;</span><span class="pln">
 align</span><span class="pun">-</span><span class="pln">items</span><span class="pun">:</span><span class="pln"> center</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">.</span><span class="pln">fa</span><span class="pun">-</span><span class="pln">user </span><span class="pun">{</span><span class="pln">
 margin</span><span class="pun">-</span><span class="pln">left</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10px</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	تجربة عرض التدوينة
</h3>

<p>
	افتح المتصفح واضغط على زر Read More في بطاقة التدوينة لتنتقل إلى صفحة جديدة تعرض التدوينة كاملة، ويكون تاريخ إنشاؤها معروضًا تحت عنوانها مباشرة. كذلك، إذا نظرنا إلى رابط الصفحة سنجد أنه يحتوي على معرِّف التدوينة وعنوانها في صورة اسم لطيف slug، انظر الصورة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91942" href="https://academy.hsoub.com/uploads/monthly_2022_02/firstpost.png.3d42fabca4969e0a12fbc51ee6efb261.png" rel=""><img alt="firstpost.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91942" data-unique="ph8qfv3nv" src="https://academy.hsoub.com/uploads/monthly_2022_02/firstpost.thumb.png.3aa405e687059f2f2324e4cc01f301b3.png" style="width: 600px; height: auto;"></a>
</p>

<h2>
	خاتمة
</h2>

<p>
	تعلمنا في هذا الجزء من مشروع إنشاء مدونة في Angular وقاعدة بيانات Firsstore كيفية إضافة محرر للمدونة ثم إضافة تدوينات جديدة وعرضها وعرض كل التدوينات في الواجهة الرئيسية ثم عرض كل تدوينة على حدة، أما في الجزء الثاني فسنتعلم كيفية التعديل على التدوينات وحذفها.
</p>

<p>
	ترجمة -وبتصرف- لفصول من كتاب Build a full stack web application using angular and firebase لصاحبه Ankit Sharma.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-r1409/" rel="">مقدمة في بناء تطبيقات الويب باستخدام إطار العمل Angular وقاعدة بيانات Firestore</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%88%D9%8A%D8%A8-%D9%83%D8%A7%D9%85%D9%84-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-angular-%D9%88%D9%85%D9%86%D8%B5%D8%A9-firebase-r1394/" rel="">بناء تطبيق ويب كامل باستخدام Angular ومنصة Firebase</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-angular-%D9%88%D9%86%D8%B4%D8%B1%D9%87%D8%A7-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1388/" rel="">تهيئة بيئة تطبيقات Angular ونشرها على الويب</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1478</guid><pubDate>Wed, 23 Feb 2022 16:02:00 +0000</pubDate></item><item><title>&#x645;&#x642;&#x62F;&#x645;&#x629; &#x641;&#x64A; &#x628;&#x646;&#x627;&#x621; &#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A; &#x627;&#x644;&#x648;&#x64A;&#x628; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x625;&#x637;&#x627;&#x631; &#x627;&#x644;&#x639;&#x645;&#x644; Angular &#x648;&#x642;&#x627;&#x639;&#x62F;&#x629; &#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; Firestore</title><link>https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-r1409/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_12/61cadc064c9ea_---------Angular----Firestore------angular-web-apps-bootstra.png.19910d8c609a2ea57d1ca519e6df57b7.png" /></p>
<p>
	إطار Angular هو <a href="https://academy.hsoub.com/programming/general/%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-framework/" rel="">إطار عمل</a> مفتوح المصدر يُستخدم في <a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%88%D9%8A%D8%A8-%D9%83%D8%A7%D9%85%D9%84-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-angular-%D9%88%D9%85%D9%86%D8%B5%D8%A9-firebase-r1394/" rel="">إنشاء تطبيقات </a>لمنصات متعددة مثل الويب وويب الأجهزة المحمولة وكذلك تطبيقات سطح المكتب، وهو أحد أشهر أطر العمل في مجال تطبيقات وحيدة الصفحة.
</p>

<p>
	سننشئ في هذه السلسلة تطبيقًا للتدوين باستخدام إطار أنجولَر في الواجهة الأمامية، ونستخدم Google cloud Firestore كقاعدة بيانات، كما سنتعلم كيفية نقل التطبيق إلى منصة Firebase وتشغيله عليها.
</p>

<p>
	سيحتوي التطبيق على المزايا التالية:
</p>

<ul>
	<li>
		لغة التصميم Material Design.
	</li>
	<li>
		إضافة تدوينة جديدة.
	</li>
	<li>
		تعديل تدوينة حالية.
	</li>
	<li>
		حذف تدوينة حالية.
	</li>
	<li>
		المصادقة مع حساب جوجل.
	</li>
	<li>
		المصادقة المبنية على الدور role-based.
	</li>
	<li>
		ترقيم التدوينات.
	</li>
	<li>
		التعليقات على التدوينات.
	</li>
	<li>
		خيار مشاركة التدوينة على الشبكات الاجتماعية.
	</li>
</ul>

<p>
	وسنتعلم المفاهيم التالية حول أنجولَر:
</p>

<ul>
	<li>
		استخدام Cloud Firestore مع تطبيق Angular.
	</li>
	<li>
		مكتبة Angular Material وإطار عمل Bootstrap.
	</li>
	<li>
		الاستمارات المبنية على القوالب template-driven.
	</li>
	<li>
		التحقق من الاستمارات.
	</li>
	<li>
		الأنابيب المخصصة Custom pipes.
	</li>
	<li>
		الصنف Auth-guards.
	</li>
	<li>
		الاستيثاق Authentication والتصريح Authorization.
	</li>
	<li>
		تسجيل الدخول بجوجل باستخدام Firebase.
	</li>
	<li>
		ترقيم الصفحات من جانب العميل Client-Side pagination باستخدام ترقيم ngx.
	</li>
</ul>

<p>
	يمكن الاطلاع على نسخة عاملة من التطبيق <a href="https://blogsite-30c69.firebaseapp.com/" rel="external nofollow">في firebaseapp</a>.
</p>

<p>
	ينبغي أن تكون في نهاية السلسلة قد أتقنت <a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D8%A7-%D9%87%D9%8A-angular%D8%9F-r1379/" rel="">مفاهيم إطار عمل Angular</a> المتقدمة، واستطعت إنشاء تطبيقات ويب تفاعلية باستخدام Angular وقاعدة بيانات Firebase.
</p>

<p>
	هذا المقال جزء من سلسلة عن بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore
</p>

<ul>
	<li>
		مقدمة في بناء تطبيقات الويب باستخدام إطار العمل Angular وقاعدة بيانات Firestore.
	</li>
	<li>
		بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore - إضافة التدوينات وعرضها.
	</li>
	<li>
		بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore - تعديل التدوينات.
	</li>
	<li>
		بناء مدونة باستخدام إطار العمل Angular وقاعدة بيانات Firestore - إضافة الاستثيثاق.
	</li>
	<li>
		نشر مدونة مبنية عبر Angular على Firebase.
	</li>
</ul>

<h2>
	مصطلحات أساسية
</h2>

<p>
	لابد التطرق إلى المصطلحات التي سنتعرض لها خلال هذه السلسلة والمرور عليها سريعًا، إذ لن نتعمق فيها ونشرحها أثناء العمل على بناء التطبيق لذلك ننصحك بالقراءة عنها ما أمكن ليسهل عليك فهم عملية بناء المدونة.
</p>

<h3>
	التطبيقات وحيدة الصفحة
</h3>

<p>
	التطبيقات وحيدة الصفحة Single Page Application هي تطبيقات ويب تحتوي على صفحة <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> واحدة، تُحمَّل من الخادم عند إطلاق التطبيق لأول مرة عند، ثم يتولى المتصفح كل شيء بعدها، ولا يرسل الخادم أي HTML بعد التحميل الأول للصفحة، بل يطلب المتصفح بيانات من الخادم ويرسلها الخادم إليها، فتُعاد كتابة البيانات دون إعادة تحميل الصفحة.
</p>

<p>
	ولا تُحدَّث الصفحة في التطبيقات وحيدة الصفحات إلا عند طلب بيانات جديدة من الخادم -انتبه من أن التحديث refresh يختلف عن إعادة التحميل reload- وذلك لنحصل على تجربة مستخدم أفضل.
</p>

<h3>
	ما هي Typescript؟
</h3>

<p>
	وفقًا <a href="https://wiki.hsoub.com/TypeScript" rel="external">لتعريفها في موسوعة حسوب</a>، فهي لغة برمجة <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D8%A7%D9%84%D9%85%D9%82%D8%B5%D9%88%D8%AF-%D8%A8%D9%85%D8%B5%D8%B7%D9%84%D8%AD-%D9%85%D9%81%D8%AA%D9%88%D8%AD-%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1-open-source%D8%9F-r885/" rel="">مفتوحة المصدر</a> من تطوير شركة Microsoft، تُعَد امتدادًا وتوسعةً للغة JavaScript، حيث أضافت العديد من المزايا إليها، خاصّةً دعم الأنواع types الذي يُساعد على تجنّب الأخطاء والعلل البرمجيّة وتوفير شيفرة برمجية نقية قابلة للقراءة أكثر من شيفرة <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-javascript-r664/" rel="">JavaScript</a> العادية.
</p>

<h3>
	ما هو Angular؟
</h3>

<p>
	هو إطار عمل مفتوح المصدر للغة جافاسكربت تقوم جوجل على تطويره ومتابعته. يسمح لنا Angular بإنشاء تطبيقات ويب من جانب العميل client-side باستخدام لغة Typescript، ويُستخدم في إنشاء التطبيقات وحيدة الصفحة، كما يسمح ببناء تطبيقات لمنصات التشغيل المختلفة مثل الويب وويب الأجهزة المحمولة mobile web، وتطبيقات الويب الأصلية native web apps، و<a href="https://academy.hsoub.com/programming/general/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%B3%D8%B7%D8%AD-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D8%A8/" rel="">تطبيقات سطح المكتب</a> الأصلية كذلك.
</p>

<p>
	يحتوي إطار عمل Angular على العديد من المزايا، من أهمها ما يلي:
</p>

<ul>
	<li>
		الكفاءة العالية في التنفيذ.
	</li>
	<li>
		قابلية التوسع.
	</li>
	<li>
		معماريته قائمة على المكونات components.
	</li>
	<li>
		فيه دعم مضمَّن للاستمارات forms وعمليات التحقق validation فيها.
	</li>
	<li>
		فيه دعم متعدد المنصات للتطوير.
	</li>
	<li>
		مفتوح المصدر ومدعوم من قِبل جوجل.
	</li>
</ul>

<p>
	تُبنى معمارية إطار Angular على المكونات، أي أنه component-based، وسيمر كل مكون خلال سلسلة من الأحداث من إنشائه حتى تدميره، وتلك الأحداث المحورية في حياة المكونات تعرِّفها خطافات دورة الحياة lifecycle hooks، وتوفر مكتبة Angular الأساسية مجموعةً من واجهات خطاطيف دورات الحياة التي تسمح لنا بالاستفادة من تلك اللحظات الرئيسية في دورة حياة كل مكون.
</p>

<p>
	تحتوي كل واجهة من تلك الواجهات على تابع خطاف hook method وحيد يكون اسمه هو اسم الواجهة مسبوقًا بـ <code>ng</code>، ويوفر Angular ثمانية توابع لخطاطيف دورات الحياة، كما هو موضح في الجدول أدناه، وهو جدول مستوحى من <a href="https://angular.io/guide/lifecycle-hooks." ipsnoembed="false" rel="external nofollow">https://angular.io/guide/lifecycle-hooks.</a>
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
	<thead>
		<tr>
			<th style="text-align:right">
				الخطاف
			</th>
			<th style="text-align:right">
				الغرض والتوقيت
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td style="text-align:right">
				<code>ngOnChanges()‎</code>
			</td>
			<td style="text-align:right">
				الاستجابة عند ضبط Angular لخصائص دخل البيانات المربوطة data-bound أو إعادة ضبطها. يستقبل التابع كائن SimpleChanges من قيم الخاصية الحالية والسابقة، ويُستدعى قبل <code>ngOnInit()‎</code> وعند تغير واحد أو أكثر من خصائص دخل data-bound.
			</td>
		</tr>
		<tr>
			<td style="text-align:right">
				<code>ngOnInit()‎</code>
			</td>
			<td style="text-align:right">
				تهيئة الموجِّه directive أو المكون بعد عرض Angular لخصائص data-bound لأول مرة، وضبطها لخصائص دخل المكوِّن. يُستدعى مرة واحدة، بعد أول <code>ngOnChanges()‎</code>.
			</td>
		</tr>
		<tr>
			<td style="text-align:right">
				<code>ngDoCheck()‎</code>
			</td>
			<td style="text-align:right">
				اكتشاف التغييرات التي لا يستطيع Angular أن يكتشفها بنفسه، واتخاذ إجراء بشأنها. يُستدعى في كل مرة يُلتقط فيها تغير ما، مباشرة بعد <code>ngOnChanges()‎</code> و <code>ngOnInit()‎</code>.
			</td>
		</tr>
		<tr>
			<td style="text-align:right">
				<code>ngAfterContentInit()‎</code>
			</td>
			<td style="text-align:right">
				الاستجابة بعد أن يرسل Angular المحتوى الخارجي ليُعرض في المكون أو العرض الذي يوجد فيه الموجِّه. يُستدعى مرة واحدة بعد أول <code>ngDoCheck()‎</code>.
			</td>
		</tr>
		<tr>
			<td style="text-align:right">
				<code>ngAfterContentChecked()‎</code>
			</td>
			<td style="text-align:right">
				الاستجابة بعد تحقق Angular أن المحتوى تم عرضه في الموجِّه أو المكون. يُستدعى بعد <code>ngAfterContentInit()‎</code> وفي كل استدعاء <code>ngDoCheck()‎</code> يتبعه.
			</td>
		</tr>
		<tr>
			<td style="text-align:right">
				<code>ngAfterViewInit()‎</code>
			</td>
			<td style="text-align:right">
				الرد بعد تهيئة Angular لعروض المكون والعروض الفرعية أو العروض التي فيها الموجِّه. يُستدعى مرة واحدة بعد أول <code>ngAfterContentChecked()‎</code>.
			</td>
		</tr>
		<tr>
			<td style="text-align:right">
				<code>ngAfterViewChecked()‎</code>
			</td>
			<td style="text-align:right">
				الرد بعد تحقق Angular من عروض المكون والعروض الفرعية أو التي يوجد فيها الموجِّه. يُستدعى بعد <code>ngAfterViewInit()‎</code> وفي كل استدعاء <code>ngAfterViewChecked()‎</code> يتبعه.
			</td>
		</tr>
		<tr>
			<td style="text-align:right">
				<code>ngOnDestroy()‎</code>
			</td>
			<td style="text-align:right">
				التنظيف قبل تدمير Angular للموجِّه/المكون. ألغ الاشتراك في العناصر المرئية observables وافصل معالجات الأحداث event handlers لتجنب حدوث تسريب في الذاكرة. يُستدعى قبل تدمير Angular للموجِّه/المكون.
			</td>
		</tr>
	</tbody>
</table>

<p>
	يحتوي الموجِّه على نفس مجموعة خطاطيف دورات الحياة التي للمكوِّن، ولا ينفِّذ أي منهما كل الخطاطيف التي لديه، إذ لا يُستدعى التابع الخطاف إلا عند تعريفه.
</p>

<p>
	ينبغي أن تكون لديك معرفة أساسية بإطار عمل Angular كما ذكرنا في مقدمة المقالة، وإلا سيستلزم الأمر أن تُراجع <a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-angular-r1393/" rel="">سلسلة أساسيات Angular.js في أكاديمية حسوب أولًا</a>.
</p>

<h3>
	ما هي Firebase؟
</h3>

<p>
	هي منصة تطوير لتطبيقات الويب التي تُستخدم على الحواسيب الشخصية والأجهزة المحمولة، وقد طورتها شركة Firebase في عام 2011، ثم استحوذت عليها شركة جوجل في 2014، وتُعَد هذه المنصة من فئة تطبيقات الواجهة الخلفية التي تُقدَّم كخدمة Backend as a Service، واختصارها Baas.
</p>

<p>
	توفر منصة Firebase عدة مزايا لتطوير التطبيقات، أهمها ما يلي:
</p>

<ul>
	<li>
		سرعة بناء التطبيقات دون الحاجة لإدارة البنية التحتية لها، إذ توفر مزايا مثل التحليلات وقواعد البيانات والرسائل وتقارير التعطل crash reports.
	</li>
	<li>
		مدعومة من قِبل جوجل: بما أن جوجل استحوذت على المنصة، فهي توفر لها بنيةً تحتيةً قويةً وقابلةً للزيادة حتى في حالة التطبيقات الكبيرة.
	</li>
	<li>
		منصة واحدة لعدة منتجات تعمل معًا بشكل أفضل: تعمل منتجات Firebase بكفاءة وهي مستقلة عن بعضها، لكن تستطيع مشاركة البيانات فيما بينها، وهذا يعطيها مزية عن غيرها من التطبيقات التي لا تجمعها منصة واحدة.
	</li>
</ul>

<p>
	تدعم Firebase عدة منصات تشغيل مثل <a href="https://academy.hsoub.com/apps/ios/" rel="">iOS</a> وأندرويد والويب ويونِيتي Unity وكذلك <a href="https://academy.hsoub.com/programming/cpp/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-c-r802/" rel="">C++‎</a>، وهي منصة شاملة تحتوي على ثمانية عشر منتجًا، مقسمة إلى ثلاثة تصانيف:
</p>

<ul>
	<li>
		بناء تطبيقات أفضل.
	</li>
	<li>
		تطوير جودة التطبيق.
	</li>
	<li>
		نمو الشركة القائمة على ذلك التطبيق.
	</li>
</ul>

<p>
	يوضح الجدول التالي بعض المنتجات من هذه التصانيف، ويُرجع إلى <a href="https://firebase.google.com/products-build" ipsnoembed="false" rel="external nofollow">https://firebase.google.com/products-build</a> للحصول على التفاصيل الكاملة لكل منتج منها:
</p>

<table>
	<thead>
		<tr>
			<th style="text-align:center">
				بناء تطبيقات أفضل
			</th>
			<th style="text-align:center">
				تطوير جودة التطبيق
			</th>
			<th style="text-align:center">
				نمو الشركة
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td style="text-align:center">
				Cloud Firestore
			</td>
			<td style="text-align:center">
				Crashlytics
			</td>
			<td style="text-align:center">
				Google Analytics
			</td>
		</tr>
		<tr>
			<td style="text-align:center">
				Cloud Storage
			</td>
			<td style="text-align:center">
				Performance Monitoring
			</td>
			<td style="text-align:center">
				Predictions
			</td>
		</tr>
		<tr>
			<td style="text-align:center">
				Authentication
			</td>
			<td style="text-align:center">
				Test Lab
			</td>
			<td style="text-align:center">
				Cloud Messaging
			</td>
		</tr>
		<tr>
			<td style="text-align:center">
				Hosting
			</td>
			<td style="text-align:center">
				App Distribution
			</td>
			<td style="text-align:center">
				Remote Config
			</td>
		</tr>
	</tbody>
</table>

<p>
	أما خطط الأسعار في منصة Firebase فهي كما يلي:
</p>

<ul>
	<li>
		خطة Spark: هذه الخطة مجانية ومناسبة للشركات الصغيرة والتطبيقات المخصصة للعرض فقط demo apps.
	</li>
	<li>
		خطة Blaze: هذه الخطة يزيد الاشتراك فيها مع زيادة الاستهلاك، وهي مناسبة للشركات الكبيرة.
	</li>
</ul>

<p>
	يُرجع إلى <a href="https://firebase.google.com/pricing" rel="external nofollow">معلومات التسعير المفصلة</a> في موقع firebase لمزيد من التوضيح.
</p>

<h3>
	ما هي Angular Material؟
</h3>

<p>
	تُعَد Angular Material مكتبة مكونات لواجهة المستخدم UI Components لإطار عمل Angular، ومبنية على مواصفات لغة التصميم المادي material design الخاصة بجوجل، وتوفر لنا مكونات حديثة لواجهة المستخدم للعمل على عدة منصات، حيث تم تحسينها لأجل Angular، ويمكن إدراجها في تطبيقاته بسهولة.
</p>

<p>
	كذلك تدعم هذه المكتبة جميع المتصفحات الحديثة، وتوفر سمات themes مضمنة فيها لتحسين مظهر التطبيقات، مع توفيرها لسمات مخصصة أيضًا.
</p>

<h2>
	إعداد بيئة تطوير Angular
</h2>

<p>
	يجب تثبيت البرامج التالية لنستطيع التطوير باستخدام إطار Angular:
</p>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/express/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-nodejs-%D9%88express-r1099/" rel="">Node.js</a>.
	</li>
	<li>
		Angular CLI.
	</li>
	<li>
		<a href="https://academy.hsoub.com/apps/productivity/%D8%A3%D9%81%D8%B6%D9%84-%D8%A7%D9%84%D8%A5%D8%B6%D8%A7%D9%81%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%AC%D8%A7%D9%86%D9%8A%D8%A9-%D9%84%D9%84%D9%85%D8%AD%D8%B1%D8%B1-vs-code-r349/" rel="">Visual Studio Code</a>.
	</li>
</ul>

<h3>
	Node.js
</h3>

<p>
	تتصرف Node مثل خادم للتطوير، حيث تسمح لتطبيق Angular بالعمل على الحاسوب المحلي. ثبِّت النسخة طويلة الدعم من Node -النسخة الحالية وقت كتابة هذه الكلمات هي 14.18.0- والتي تناسب نظام تشغيلك <a href="https://nodejs.org/en/download/" rel="external nofollow">من موقعها</a>، وذلك التثبيت سيثبّت بالتبعية <a href="https://academy.hsoub.com/programming/javascript/%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-r1225/" rel="">مدير الحزم NPM</a>.
</p>

<p>
	افتح الطرفية وشغِّل الأمر التالي لمعرفة إصدار node:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4091_8" style=""><span class="pln">node </span><span class="pun">-</span><span class="pln">v</span></pre>

<p>
	وهذا الأمر أيضًا لنعرف إصدار NPM:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4091_10" style=""><span class="pln">npm </span><span class="pun">-</span><span class="pln">v</span></pre>

<p>
	انظر لقطة الشاشة أدناه للتوضيح:
</p>

<p style="text-align: center;">
	<img alt="node-version.png" class="ipsImage ipsImage_thumbnailed" data-fileid="87278" data-unique="91zw7s2v8" src="https://academy.hsoub.com/uploads/monthly_2021_12/node-version.png.9b40658156b065204bbb3b515bf8db6c.png">
</p>

<p>
	نلاحظ أن إصدار NPM هنا هو 6.14.15.
</p>

<h3>
	Angular CLI
</h3>

<p>
	أداة Angular CLI هي واجهة غير رسومية -تعمل من سطر الأوامر- تسمح لنا بتطوير تطبيقات Angular ووضع هياكلها البنيوية وتهيئتها، فهي توفر الأدوات والأوامر التي نحتاج إليها من أجل تسهيل تطوير تطبيقات Angular.
</p>

<p>
	افتح نافذة الطرفية وشغّل الأمر التالي لتثبيت Angular CLI:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4091_12" style=""><span class="pln">npm install </span><span class="pun">-</span><span class="pln">g </span><span class="lit">@angular</span><span class="pun">/</span><span class="pln">cli</span></pre>

<p>
	يُستخدم الأمر أدناه لمعرفة إصدار Angular CLI:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4091_14" style=""><span class="pln">ng version</span></pre>

<p>
	الإصدار الحالي لها وقت كتابة هذه الكلمات هو الإصدار 12.2.7، انظر إلى الصورة أدناه:
</p>

<p style="text-align: center;">
	<img alt="ngcli-version.png" class="ipsImage ipsImage_thumbnailed" data-fileid="87277" data-unique="i9xm73zbv" style="width: 500px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2021_12/ngcli-version.png.be49e57a40966343b073de8d08a299c3.png">
</p>

<h3>
	Visual Studio Code
</h3>

<p>
	بيئة التطوير Visual Studio Code هي بيئة تطوير مجانية ومفتوحة المصدر طورتها مايكروسوفت، ويمكن استخدامها كمحرر برمجي خفيف، وتدعم تطوير البرمجيات بلغات C++‎ و C#‎ وجافا و PHP وبايثون و Typescript وغيرها، وهي متاحة لأنظمة التشغيل الثلاثة المشهورة: ويندوز وماك ولينكس.
</p>

<p>
	سنستخدم هذه البيئة في تطوير تطبيقات Angular في هذه السلسلة، إذ أنها من أشهر بيئات التطوير المستخدمة في كتابة برامج Angular، رغم دعم بيئات تطوير أخرى له، مثل Sublime Text و Atom و Webstorm وغيرها.
</p>

<p>
	والآن، ثبِّت النسخة الأخيرة من بيئة Visual Studio Code التي تناسب نظام تشغيلك <a href="https://code.visualstudio.com/" rel="external nofollow">من موقعه</a>.
</p>

<h2>
	إنشاء تطبيق Angular جديد
</h2>

<p>
	ستجد الشيفرة المصدرية للتطبيق التالي متاحة في <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-Cl%20oudFirestore" rel="external nofollow">هذا المستودع في github</a>، فتستطيع نسخ المستودع والاسترشاد بالشيفرة أثناء إنشائنا للتطبيق. كذلك، ستحتاج إلى حساب Gmail لتستطيع الدخول إلى Firebase، وذلك إضافة إلى إعداد بيئة تطوير Angular.
</p>

<p>
	انتقل الآن إلى المجلد الذي تريد إنشاء المشروع الجديد فيه، وافتح نافذة الطرفية -أو command prompt على ويندوز- وشغّل الأمر التالي لإنشاء تطبيق Angular جديد باسم blogsite:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_4091_19" style=""><span class="pln">ng </span><span class="kwd">new</span><span class="pln"> blogsite </span><span class="pun">--</span><span class="pln">routing</span><span class="pun">=</span><span class="kwd">false</span><span class="pln"> </span><span class="pun">--</span><span class="pln">style</span><span class="pun">=</span><span class="pln">scss</span></pre>

<p>
	غيّر المجلد وانتقل منه إلى مجلد الجذر root للمشروع، وهو blog site في حالتنا، وافتح المشروع في VS code من خلال تشغيل مجموعة الأوامر التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4091_21" style=""><span class="pln">cd blogsite
code </span><span class="pun">.</span></pre>

<h2>
	إعداد Firebase
</h2>

<p>
	سننشئ مشروعًا على firebase، ونعِدّ قاعدة بيانات Google cloud firebase لها، وسنستخدم قاعدة البيانات تلك لتطبيق Angular، كما هو مبين في الخطوات التالية:
</p>

<h3>
	إنشاء مشروع على Firebase
</h3>

<p>
	اتبع الخطوات التالية لإنشاء مشروع جديد على Firebase:
</p>

<ol>
	<li>
		اذهب إلى <a href="https://console.firebase.google.com/%E2%80%8E" ipsnoembed="false" rel="external nofollow">https://console.firebase.google.com/‎</a> وسجل الدخول باستخدام حساب gmail.
	</li>
	<li>
		اضغط على زر Create a Project.
	</li>
	<li>
		أدخل اسم المشروع، يمكنك إعطاؤه أي اسم تريد، وسنستخدم الاسم blogsite في حالتنا. اضغط بعدها على Continue. انظر الصورة أدناه:
	</li>
</ol>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="87274" href="https://academy.hsoub.com/uploads/monthly_2021_12/firebase1.png.377752c00b1a94f002e7bcf2885d27d7.png" rel=""><img alt="firebase1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="87274" data-unique="30oxtujqz" style="width: 500px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2021_12/firebase1.png.377752c00b1a94f002e7bcf2885d27d7.png"></a>
</p>

<ol start="4">
	<li>
		عطِّل زر Enable Google Analytics for this project الذي في الصفحة التالية، ثم اضغط على Create Project. انظر الصورة:
	</li>
</ol>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="87275" href="https://academy.hsoub.com/uploads/monthly_2021_12/firebase2.png.0812eabbe8cafac1cbc3cc6473223368.png" rel=""><img alt="firebase2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="87275" data-unique="n1am303dv" style="width: 500px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2021_12/firebase2.thumb.png.e31647121467a0760f857e55370d37b3.png"></a>
</p>

<h3>
	إضافة إعدادات Firebase إلى التطبيق
</h3>

<p>
	سننشئ تطبيق ويب لمشروع Firebase، وسنضيف بيانات الإعدادات لتطبيق Firebase إلى تطبيق Angular، هذا يسمح له بالاتصال بتطبيق Firebase.
</p>

<p>
	في صفحة overview للمشروع، اضغط على أيقونة الويب التي تحمل رمز وسم إغلاق في <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a>، كما هو موضح في الصورة أدناه:
</p>

<p style="text-align: center;">
	<img alt="webicon" class="ipsImage ipsImage_thumbnailed" data-fileid="87281" data-unique="uzdvabm3e" style="width: 500px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2021_12/webicon.png.49451b4869c5a3a1aec91cc7f71923ac.png">
</p>

<p>
	ثم في الصفحة التالية، أدخل اسمًا مستعارًا nickname، وتستطيع هنا اختيار أي اسم تريد، وقد استخدمنا blogsite، وهو نفس اسم مشروعنا.
</p>

<p>
	اضغط الآن على زر Register app كما هو موضح في الصورة أدناه:
</p>

<p style="text-align: center;">
	<img alt="register" class="ipsImage ipsImage_thumbnailed" data-fileid="87279" data-unique="rifqyljn1" style="width: 649px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2021_12/register.png.4473c81725bbceab9e7506a97395b8dc.png">
</p>

<p>
	انسخ الكائن <code>firebaseConfig</code> من وسم <a href="https://wiki.hsoub.com/HTML/script" rel="external"><code>&lt;script&gt;</code></a>، والصق الشيفرة المنسوخة إلى <code>src/environments/environment.ts</code> كما هو موضح في الشيفرة أدناه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4091_28" style=""><span class="pln">firebaseConfig</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 apiKey</span><span class="pun">:</span><span class="pln"> </span><span class="str">"AIzaSyCxqWK4SVAAJkozEkURuteREIW9197z6-s"</span><span class="pun">,</span><span class="pln">
 authDomain</span><span class="pun">:</span><span class="pln"> </span><span class="str">"blogsite-b165e.firebaseapp.com"</span><span class="pun">,</span><span class="pln">
 databaseURL</span><span class="pun">:</span><span class="pln"> </span><span class="str">"https://blogsite-b165e.firebaseio.com"</span><span class="pun">,</span><span class="pln">
 projectId</span><span class="pun">:</span><span class="pln"> </span><span class="str">"blogsite-b165e"</span><span class="pun">,</span><span class="pln">
 storageBucket</span><span class="pun">:</span><span class="pln"> </span><span class="str">"blogsite-b165e.appspot.com"</span><span class="pun">,</span><span class="pln">
 messagingSenderId</span><span class="pun">:</span><span class="pln"> </span><span class="str">"1057108181105"</span><span class="pun">,</span><span class="pln">
 appId</span><span class="pun">:</span><span class="pln"> </span><span class="str">"1:1057108181105:web:ac5bbb18e5f34c7e575bd0"</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	بالمثل، الصق الشيفرة إلى <code>src/environments/environment.prod.ts</code>، واستورد ثابت البيئة environment constant إلى <code>src/app/app.module.ts</code> كما هو موضح في الشيفرة أدناه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4091_30" style=""><span class="pun">...</span><span class="pln">
 </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AppComponent</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./app.component'</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> environment </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'src/environments/environment'</span><span class="pun">;</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	والآن، اضغط على Continue to the console في صفحة Firebase.
</p>

<h3>
	إنشاء قاعدة بيانات Firebase السحابية
</h3>

<p>
	انتقل إلى صفحة Project Overview لمشروع Firebase الخاص بك، واختر Database من قسم Develop الموجود في القائمة على اليسار، ثم اضغط على زر Create database.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="87273" href="https://academy.hsoub.com/uploads/monthly_2021_12/create-database.png.6af3ea8a056f06e85ebc31cd8d65c9ac.png" rel=""><img alt="create-database.png" class="ipsImage ipsImage_thumbnailed" data-fileid="87273" data-unique="tq1ag04bl" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2021_12/create-database.thumb.png.7460e91dbe381611e82764624872b5c4.png"></a>
</p>

<p>
	في نافذة إنشاء قاعدة البيانات Create database المنبثقة، اختر "البدء في وضع الاختبار" Start in test mode واضغط Next، ثم اترك القيمة الافتراضية لموقع Cloud Firestore كما هي واضغط Done. هكذا نكون قد أعددنا <a href="https://academy.hsoub.com/programming/sql/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%B9%D9%86-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-r584/" rel="">قاعدة بيانات</a> Cloud Firestore لمشروع Firebase، انظر الصورة أدناه:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="87280" href="https://academy.hsoub.com/uploads/monthly_2021_12/testmode.png.799e77db49aabbb2850c942a51527401.png" rel=""><img alt="testmode.png" class="ipsImage ipsImage_thumbnailed" data-fileid="87280" data-unique="3uy0iujxk" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2021_12/testmode.thumb.png.acaa710e6a582506ac8502f1c3313812.png"></a>
</p>

<p>
	يُنصح بإعداد قاعدة البيانات في وضع الاختبار test mode لعينات التطبيقات رغم أن هذا سلوك غير صحيح من الناحية الأمنية، حيث نرى من الرسالة المعروضة أنه بإمكان أي أحد يملك مرجعًا إلى قاعدة البيانات أن يقرأها ويعدّل فيها لمدة ثلاثين يومًا. سنغير هذه القاعدة في الجزء الأخير من الكتاب لنسمح بتعديل قاعدة البيانات من قِبل المستخدمين المصرح لهم فقط.
</p>

<h3>
	ربط Firebase مع تطبيق Angular
</h3>

<p>
	افتح نافذةً طرفيةً جديدةً في المجلد الجذر للمشروع لنستخدمها في تنفيذ جميع أوامر Angular، ونفِّذ الأمر التالي لتثبيت حزم Firebase الخاصة به:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_4091_35" style=""><span class="pln"> npm install firebase </span><span class="lit">@angular</span><span class="pun">/</span><span class="pln">fire </span><span class="pun">--</span><span class="pln">save</span></pre>

<p>
	استورد المكتبات الموجودة في <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/app.module.ts#L9-L10" rel="external nofollow">AnkitSharma</a>، كما هو موضح أدناه.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4091_39" style=""><span class="pln"> </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AngularFireModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/fire'</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AngularFirestoreModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/fire/firestore'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@NgModule</span><span class="pun">({</span><span class="pln">
 </span><span class="pun">...</span><span class="pln">
 imports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="com">// other imports</span><span class="pln">
  </span><span class="typ">AngularFireModule</span><span class="pun">.</span><span class="pln">initializeApp</span><span class="pun">(</span><span class="pln">environment</span><span class="pun">.</span><span class="pln">firebaseConfig</span><span class="pun">),</span><span class="pln">
  </span><span class="typ">AngularFirestoreModule</span><span class="pun">,</span><span class="pln">
 </span><span class="pun">],</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">})</span></pre>

<h2>
	تهيئة مكتبة التنسيق لتطبيق Angular
</h2>

<p>
	يمكن الاعتماد على مكتبة Material Design أو مكتبة Bootstrap وذلك لإضافة التنسيقات لتطبيقات جاهزة بسهولة للتطبيق وسنشرح كيفية إضافة هاتين المكتبتين إلى تطبيقنا.
</p>

<h3>
	إضافة مكتبة Angular Material
</h3>

<p>
	نفِّذ الأمر التالي في الطرفية لتثبيت حزم Angular Material وحزمة تطوير المكونات Component Dev Kit CDK، ومكتبات تحريك Angular -أو Angular animations libraries-.
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_4091_41" style=""><span class="pln">npm install </span><span class="pun">--</span><span class="pln">save </span><span class="lit">@angular</span><span class="pun">/</span><span class="pln">material </span><span class="lit">@angular</span><span class="pun">/</span><span class="pln">cdk </span><span class="lit">@angular</span><span class="pun">/</span><span class="pln">animations</span></pre>

<p>
	بعد نجاح تثبيت هذه الحزم، استورد المكتبات إلى ملف <code>src/app/app.module.ts</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_4091_43" style=""><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="typ">BrowserAnimationsModule</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/platformbrowser/animations'</span><span class="pun">;</span><span class="pln">
</span><span class="lit">@NgModule</span><span class="pun">({</span><span class="pln">
 </span><span class="pun">...</span><span class="pln">
 imports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
  </span><span class="typ">BrowserAnimationsModule</span><span class="pun">,</span><span class="pln">
 </span><span class="pun">],</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	توفر حزمة Angular Material أربعة سمات themes افتراضيًا، هي ما يلي:
</p>

<ul>
	<li>
		deeppurple-amber.css.
	</li>
	<li>
		indigo-pink.css.
	</li>
	<li>
		pink-bluegrey.css.
	</li>
	<li>
		purple-green.css.
	</li>
</ul>

<p>
	لإدراج سمة في تطبيق Angular، نحتاج إلى إضافة مرجع في الملف <code>style.scss</code>، فإذا أردنا إضافة السمة الثانية من القائمة أعلاه -indigo-pink.css- على التطبيق كله، فإننا سنضيف السطر التالي في ملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/styles.scss#L3" rel="external nofollow">styles.scss</a>:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_4091_45" style=""><span class="pln"> </span><span class="lit">@import</span><span class="pln"> </span><span class="str">"~@angular/material/prebuilt-themes/indigo-pink.css"</span><span class="pun">;</span></pre>

<p>
	أما لتعلم المزيد حول سمات material فيُنظر في <a href="https://material.angular.io/guide/theming" rel="external nofollow">دليل موقع Angular</a>.
</p>

<p>
	سننشئ بعد ذلك وحدةً جديدةً نضع فيها المكونات المتعلقة بحزمة Angular Material، وذلك بكتابة الأمر التالي في الطرفية:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_4091_47" style=""><span class="pln">ng g m ng</span><span class="pun">-</span><span class="pln">material</span></pre>

<p>
	افتح ملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/ng-material/ng-material.module.ts" rel="external nofollow">ng-material.module.ts</a>، واستبدل بالشيفرة الموجودة فيه الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4091_49" style=""><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">NgModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">CommonModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/common'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">MatButtonModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/material/button'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">MatCardModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/material/card'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">MatDividerModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/material/divider'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">MatIconModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/material/icon'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">MatInputModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/material/input'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">MatMenuModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/material/menu'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">MatProgressSpinnerModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/material/progressspinner'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">MatSelectModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/material/select'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">MatSnackBarModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/material/snack-bar'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">MatToolbarModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/material/toolbar'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">MatTooltipModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/material/tooltip'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@NgModule</span><span class="pun">({</span><span class="pln">
 declarations</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[],</span><span class="pln">
 imports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="typ">CommonModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">MatToolbarModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">MatButtonModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">MatCardModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">MatInputModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">MatIconModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">MatDividerModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">MatMenuModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">MatSelectModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">MatSnackBarModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">MatProgressSpinnerModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">MatTooltipModule</span><span class="pun">,</span><span class="pln">
 </span><span class="pun">],</span><span class="pln">
 exports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="typ">CommonModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">MatToolbarModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">MatButtonModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">MatCardModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">MatInputModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">MatIconModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">MatDividerModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">MatMenuModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">MatSelectModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">MatSnackBarModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">MatProgressSpinnerModule</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">MatTooltipModule</span><span class="pun">,</span><span class="pln">
 </span><span class="pun">]</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">NgMaterialModule</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	استوردنا في الشيفرة السابقة جميع الوحدات الخاصة بمكونات Angular Material التي سنستخدمها في هذا التطبيق، وستعمل وحدة أخرى مستقلة بذاتها على جعل التطبيق سهل الصيانة.
</p>

<p>
	استورد الوحدة <code>NgMaterialModule</code> إلى الملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/app/app.module.ts" rel="external nofollow">app.module.ts</a> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4091_51" style=""><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">NgMaterialModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./ng-material/ng-material.module'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@NgModule</span><span class="pun">({</span><span class="pln">
 </span><span class="pun">...</span><span class="pln">
 imports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
  </span><span class="typ">NgMaterialModule</span><span class="pun">,</span><span class="pln">
 </span><span class="pun">],</span><span class="pln">
</span><span class="pun">})</span></pre>

<h3>
	إضافة إطار Bootstrap
</h3>

<p>
	<a href="https://academy.hsoub.com/programming/html/bootstrap/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D8%A8%D9%88%D8%AA%D8%B3%D8%AA%D8%B1%D8%A7%D8%A8-5-r1315/" rel="">إطار العمل Bootstrap</a> هو إطار CSS يُستخدم في بناء تطبيقات ويب متجاوبة responsive وموجهة للأجهزة المحمولة أولًا mobile-first، وهو إطار عمل مفتوح المصدر يستخدمه مطورو الويب بكثرة، ويُعد لهذا أشهر إطار <a href="https://academy.hsoub.com/programming/css/%d8%aa%d8%b9%d8%b1%d9%91%d9%81-%d8%b9%d9%84%d9%89-%d8%a3%d8%b3%d8%a7%d8%b3%d9%8a%d8%a7%d8%aa-css-r70/" rel="">CSS</a> لتطوير الويب، وآخر إصدار منه وقت كتابة هذه الكلمات هو الإصدار 5.1، وهو يدعم آخر الإصدارات المستقرة من متصفحات الويب المشهورة، بما في ذلك إنترنت إكسبلورر 10-11، ومتصفح Edge.
</p>

<p>
	لتثبيت إطار bootstrap في تطبيقنا، نشغِّل الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_4091_53" style=""><span class="pln">npm install bootstrap </span><span class="pun">--</span><span class="pln">save</span></pre>

<p>
	كذلك، نضيف مرجعًا عامًا global إلى إطار bootstrap في ملف <a href="https://github.com/AnkitSharma-007/blogging-app-with-Angular-CloudFirestore/blob/master/src/styles.scss" rel="external nofollow">src/styles.scss</a> كما يلي:
</p>

<pre class="ipsCode">@import "~bootstrap/dist/css/bootstrap.css";
</pre>

<h2>
	تشغيل التطبيق من الخادم
</h2>

<p>
	افتح نافذةً طرفيةً جديدةً وشغِّل الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_4091_56" style=""><span class="pln">ng serve </span><span class="pun">-</span><span class="pln">o</span></pre>

<p>
	ستوفر حزمة Angular CLI الآن التطبيق على العنوان <code>localhost:4200</code>، والذي سيُفتح بواسطة المتصفح الافتراضي لديك، كما يظهر في الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="87276" href="https://academy.hsoub.com/uploads/monthly_2021_12/local4200.png.34843fd87d28aa0397924d06e8f3c64a.png" rel=""><img alt="local4200.png" class="ipsImage ipsImage_thumbnailed" data-fileid="87276" data-unique="7tqex32cw" style="width: 650px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2021_12/local4200.thumb.png.0e5b71c7db766bc32a581cf0fe7053a2.png"></a>
</p>

<p>
	سيعيد التطبيق الآن عملية التصريف compiling وإعادة التحميل كلما تغير الملف، وسنترك الخادم يعمل ونعود لمتابعة إنشاء بقية المكونات.
</p>

<h2>
	خاتمة
</h2>

<p>
	تعرفنا في هذا الفصل إلى إطار العمل Angular وإمكانية استخدامه في إنشاء تطبيقات ويب وحيدة الصفحة، وكيفية إعداد بيئة العمل المناسبة اللازمة لإنشاء مثل تلك التطبيقات؛ أما في المقال التالي فسنستخدمه لبناء مدونة على أساس تطبيق عملي عليه، وكيفية تخصيصها وإضافة الوظائف اللازمة لها من إنشاء للتدوينات ونشرٍ لها، ثم تحريرها وحذفها، والصلاحيات اللازمة لذلك لأنواع المستخدمين.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-angular-%D9%88%D9%86%D8%B4%D8%B1%D9%87%D8%A7-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1388/" rel="">تهيئة بيئة تطبيقات Angular ونشرها على الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%AA%D9%86%D9%82%D9%84-%D9%88%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-angular-r1382/" rel="">إضافة التنقل وإدارة البيانات في تطبيق Angular</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-angular-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1380/" rel="">كيفية استعمال Angular في بناء تطبيقات الويب</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1409</guid><pubDate>Tue, 28 Dec 2021 16:00:00 +0000</pubDate></item><item><title>&#x628;&#x646;&#x627;&#x621; &#x62A;&#x637;&#x628;&#x64A;&#x642; &#x648;&#x64A;&#x628; &#x643;&#x627;&#x645;&#x644; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; Angular &#x648;&#x645;&#x646;&#x635;&#x629; Firebase</title><link>https://academy.hsoub.com/programming/javascript/angular/%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%88%D9%8A%D8%A8-%D9%83%D8%A7%D9%85%D9%84-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-angular-%D9%88%D9%85%D9%86%D8%B5%D8%A9-firebase-r1394/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_12/61aa6ed67888c_----Angular-Firebase---Component-Angular-HTML-TypeScript-CSS----Firebase-Firestone-Deployment-Angular-CDK.png.b2a189dfb33cb820ce01b2b33c08df8b.png" /></p>

<p>
	سنبني في هذا المقال لوحة كانبان kanban على الإنترنت باستخدام <a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D8%A7-%D9%87%D9%8A-angular%D8%9F-r1379/" rel="">Angular</a> ومنصة Firebase، حيث سيحتوي تطبيقنا النهائي على ثلاثة فئات من المهام وهي الأعمال المتأخرة والأعمال قيد الإنجاز والأعمال المنجزة، كما سنكون قادرين على إنشاء مهام وحذفها ونقلها من فئة إلى أخرى باستخدام عمليتي السحب والإفلات، وسنطوِّر <a href="https://academy.hsoub.com/learn/front-end-web-development/" rel="">واجهة المستخدِم</a> باستخدام Angular وسنستخدِم قاعدة بيانات Firestore على أساس مخزن دائم لبيانات التطبيق، كما سننشر التطبيق في نهاية المقال على استضافة Firebase باستخدام واجهة سطر أوامر Angular CLI.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="84248" href="https://academy.hsoub.com/uploads/monthly_2021_12/001.png.d9cba6f35c5a6625208e894fae1ffbe1.png" rel=""><img alt="001.png" class="ipsImage ipsImage_thumbnailed" data-fileid="84248" data-unique="6xtumyycl" src="https://academy.hsoub.com/uploads/monthly_2021_12/001.thumb.png.f997ea877f9c1495fd5180f49b456f56.png" style="width: 700px; height: auto;"></a>
</p>

<p>
	ستتعلم في هذا المقال ما يلي:
</p>

<ul>
<li>
		كيفية استخدام Angular material وعدة تطوير المكونات CDK.
	</li>
	<li>
		كيفية دمج Firebase مع تطبيق Angular الخاص بك.
	</li>
	<li>
		كيفية الحفاظ على بياناتك الدائمة في قاعدة بيانات Firestone.
	</li>
	<li>
		كيفية نشر تطبيقك إلى استضافة Firebase باستخدام Angular CLI وبأمر واحد فقط.
	</li>
</ul>
<p>
	يفترض هذا المقال بأنه لديك حساب جوجل Google وأساسيات Angular، كما ننصحك بالرجوع إلى المقالين <a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-angular-r1393/" rel="">مقدمة في مفاهيم Angular</a> و<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D8%A7-%D9%87%D9%8A-angular%D8%9F-r1379/" rel="">ما هي Angular؟</a> للتعرُّف على مفاهيم Angular الأساسية.
</p>

<h2>
	1. إنشاء مشروع جديد
</h2>

<p>
	لننشئ مساحة عمل جديدة لإطار عمل Angular أولًا:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6880_14" style="">
<span class="pln">ng </span><span class="kwd">new</span><span class="pln"> kanban</span><span class="pun">-</span><span class="pln">fire
</span><span class="pun">?</span><span class="pln"> </span><span class="typ">Would</span><span class="pln"> you like to add </span><span class="typ">Angular</span><span class="pln"> routing</span><span class="pun">?</span><span class="pln"> </span><span class="typ">No</span><span class="pln">
</span><span class="pun">?</span><span class="pln"> </span><span class="typ">Which</span><span class="pln"> stylesheet format would you like to </span><span class="kwd">use</span><span class="pun">?</span><span class="pln"> CSS</span></pre>

<p>
	قد تستغرق هذه الخطوة بضع دقائق، حيث سينشئ Angular CLI بنية مشروعك ويُثبِّت جميع التبعيات المتعلقة به، كما عليك الانتقال إلى المجلد <strong><code>kanban-fire</code></strong> وتشغيل خادم تطوير عبر <a href="https://academy.hsoub.com/programming/php/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-cli-%D9%81%D9%8A-php-r1176/" rel="">واجهة سطر الأوامر</a> Angular CLI عندما تكتمل عملية التثبيت بتنفيذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6880_16" style="">
<span class="pln">ng serve</span></pre>

<p>
	افتح العنوان <a href="http://localhost:4200" rel="external nofollow">http://localhost:4200</a> لكي تشاهد صفحةً مشابهةً لهذه الصورة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="84249" href="https://academy.hsoub.com/uploads/monthly_2021_12/002.png.0c9013b402b3e5f8c88c38fe66db0c95.png" rel=""><img alt="002.png" class="ipsImage ipsImage_thumbnailed" data-fileid="84249" data-unique="8b4tcpl99" src="https://academy.hsoub.com/uploads/monthly_2021_12/002.thumb.png.1abac6433bf0cc31b5c1eb4953447774.png" style="width: 700px; height: auto;"></a>
</p>

<p>
	افتح ملف <code>src/app/app.component.html</code> في المحرر الخاص بك واحذف كامل محتواه، إذ ستشاهد صفحة فارغة فقط عندما تعود مرةً ثانيةً إلى العنوان <a href="http://localhost:4200" rel="external nofollow">http://localhost:4200</a>.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		توضيح: يمكنك العثور على ملفات المشروع من مستودع <a href="https://github.com/FirebaseExtended/codelab-kanban-fire/tree/create-a-project" rel="external nofollow">codelab-kanban-fire</a> على GitHub.
	</p>
</blockquote>

<h2>
	2. إضافة مكتبة Material وعدة CDK
</h2>

<p>
	تتضمن Angular افتراضيًا مكونات واجهة المستخدِم المتوافقة مع مكتبة Material Design على أساس جزء من حزمة <code>‎@angular/material</code>، حيث تكون إحدى التبعيات الخاصة بحزمة <code>‎@angular/material</code> هي مجموعة تطوير المكونات Component Development Kit -أو CDK اختصارًا-، إذ تُوفِّر CDK الأوليات مثل أدوات a11y المساعدة والسحب والإفلات والتراكب، كما يمكننا الوصول إلى CDK عن طريق حزمة <code>‎@angular/cdk</code>.
</p>

<p>
	نفِّذ الأمر التالي لإضافة مكتبة Material Design إلى التطبيق الخاص بك:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6880_12" style="">
<span class="pln">ng add </span><span class="lit">@angular</span><span class="pun">/</span><span class="pln">material</span></pre>

<p>
	يطلب منك الأمر السابق اختيار سمة لتطبيقك في حال كنت ترغب في استخدام تنسيقات الطباعة للمواد العالمية وإعداد تحريكات المتصفح للمواد Angular، وبعدها اختر "Indigo/Pink" للحصول على النتيجة نفسها الموجودة ضمن المقال، وأجب "نعم Yes" من أجل السؤالين الأخيرين، كما يثبِّت الأمر <code>ng add</code> الحزمة <code>‎@angular/material</code> وتبعياته، ويستورد <code>BrowserAnimationsModule</code> في <code>AppModule</code>، ويمكننا استخدام المكونات التي تقدمها هذه الوحدة في الخطوة التالية.
</p>

<p>
	أضف أولًا شريط أدوات وأيقونة إلى <code>AppComponent</code> من خلال فتح ملف <code>app.component.html</code> وإضافة الشيفرة التالية:
</p>

<ul>
<li>
		الملف src/app/app.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6880_18" style="">
<span class="tag">&lt;mat-toolbar</span><span class="pln"> </span><span class="atn">color</span><span class="pun">=</span><span class="atv">"primary"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;mat-icon&gt;</span><span class="pln">local_fire_department</span><span class="tag">&lt;/mat-icon&gt;</span><span class="pln">
  </span><span class="tag">&lt;span&gt;</span><span class="pln">Kanban Fire</span><span class="tag">&lt;/span&gt;</span><span class="pln">
</span><span class="tag">&lt;/mat-toolbar&gt;</span></pre>

<p>
	أضفنا هنا شريط أدوات باستخدام اللون الأساسي لسمة Material Design واستخدمنا داخله أيقونة <code>local_fire_depeartment</code> بجانب العنوان Kanban Fire، فإذا نظرت إلى الطرفية الخاصة بك، فسترى أنَّ Angular يرمي لك بعض الأخطاء، ومن أجل إصلاحها تأكَّد من إضافة الاستيرادات التالية ضمن ملف <code>AppModule</code>:
</p>

<ul>
<li>
		الملف src/app/app.module.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6880_20" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">MatToolbarModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/material/toolbar'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">MatIconModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/material/icon'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@NgModule</span><span class="pun">({</span><span class="pln">
  declarations</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="typ">AppComponent</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
  imports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="pun">...</span><span class="pln">
    </span><span class="typ">MatToolbarModule</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">MatIconModule</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
  providers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[],</span><span class="pln">
  bootstrap</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">AppComponent</span><span class="pun">]</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AppModule</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	نحتاج إلى استيراد الوحدات المقابلة لها في ملف <code>AppModule</code> نظرًا لأننا استخدمنا شريط أدوات وأيقونة عن طريق Angular material، فيجب عليك رؤية النتيجة التالية على الشاشة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="84250" href="https://academy.hsoub.com/uploads/monthly_2021_12/003.png.a991eccb50f42101628b2b6110303b62.png" rel=""><img alt="003.png" class="ipsImage ipsImage_thumbnailed" data-fileid="84250" data-unique="jrfbezae7" src="https://academy.hsoub.com/uploads/monthly_2021_12/003.thumb.png.86138293d21fb254ead24ace46b59df6.png" style="width: 650px; height: auto;"></a>
</p>

<p>
	ليست نتيجةً سيئةً بما أنك لم تكتب سوى أربعة سطور من <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> واستوردت مرتين فقط.
</p>

<h2>
	3. عرض المهام
</h2>

<p>
	سننشئ الآن مكونًا نستطيع استخدامه لتمثيل المهام وعرضها في لوحة kanban، لذا انتقل إلى مجلد <code>src/app</code> ونفِّذ أمر CLI التالي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6880_25" style="">
<span class="pln">ng generate component task</span></pre>

<p>
	ينشئ هذا الأمر <code>TaskComponent</code> ويضيف التعريف الخاص به إلى ملف <code>AppModule</code>، وبعدها أنشئ ملف يسمى <code>task.ts</code> ضمن دليل <code>task</code>، حيث سنستخدم هذا الملف لتعريف واجهة المهام في لوحة kanban، حيث سيكون لكل مهمة ثلاثة حقول اختيارية هي <code>id</code> و<code>title</code> و<code>description</code> وجميعها من نوع سلسلة نصية:
</p>

<ul>
<li>
		الملف src/app/task/task.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6880_27" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">interface</span><span class="pln"> </span><span class="typ">Task</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  id</span><span class="pun">?:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">;</span><span class="pln">
  title</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">;</span><span class="pln">
  description</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لنحدِّث الآن ملف <code>task.component.ts</code> لكي يقبل مكون <code>TaskComponent</code> إدخالات من نوع كائن من النوع <code>task</code> ولكي يكون قادرًا على إصدار مخرجات "<code>edit</code>":
</p>

<ul>
<li>
		الملف src/app/task/task.component.ts:
	</li>
</ul>
<pre class="ipsCode">
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { Task } from './task';

@Component({
  selector: 'app-task',
  templateUrl: './task.component.html',
  styleUrls: ['./task.component.css']
})
export class TaskComponent {
  @Input() task: Task | null = null;
  @Output() edit = new EventEmitter&lt;Task&gt;();
}
</pre>

<p>
	عدِّل قالب <code>TaskComponent</code> عن طريق استبدال شيفرة HTML التالية بمحتوى الملف task.component.html:
</p>

<ul>
<li>
		الملف src/app/task/task.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6880_29" style="">
<span class="tag">&lt;mat-card</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"item"</span><span class="pln"> *</span><span class="atn">ngIf</span><span class="pun">=</span><span class="atv">"task"</span><span class="pln"> (</span><span class="atn">dblclick</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"edit.emit(task)"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;h2&gt;</span><span class="pln">{{ task.title }}</span><span class="tag">&lt;/h2&gt;</span><span class="pln">
  </span><span class="tag">&lt;p&gt;</span><span class="pln">
    {{ task.description }}
  </span><span class="tag">&lt;/p&gt;</span><span class="pln">
</span><span class="tag">&lt;/mat-card&gt;</span></pre>

<p>
	لاحظ أنه توجد لدينا أخطاء تظهر في الطرفية:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6880_31" style="">
<span class="pln">'mat-card' is not a known element:
1. If 'mat-card' is an Angular component, then verify that it is part of this module.
2. If 'mat-card' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.ng</span></pre>

<p>
	نستخدِم مكون <a href="https://material.angular.io/components/card/overview" rel="external nofollow"><code>mat-card</code></a> في القالب السابق الموجود في حزمة <code>‎@angular/material</code>، ولكنا لم تستورد الوحدة الخاصة به في التطبيق، ولإصلاح هذا الخطأ نحتاج إلى استيراد الوحدة <code>MatCardModule</code> في ملف <code>AppModule</code>:
</p>

<ul>
<li>
		الملف src/app/app.module.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6880_33" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">MatCardModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/material/card'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@NgModule</span><span class="pun">({</span><span class="pln">
  declarations</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="typ">AppComponent</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
  imports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="pun">...</span><span class="pln">
    </span><span class="typ">MatCardModule</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
  providers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[],</span><span class="pln">
  bootstrap</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">AppComponent</span><span class="pun">]</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AppModule</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	ستنشئ الآن بعض المهام في <code>AppComponent</code> ونتصوَّرها باستخدام <code>TaskComponent</code>، لذا عرِّف مصفوفةً باسم <code>todo</code> في <code>AppComponent</code> وأضف مهمتين داخلها:
</p>

<ul>
<li>
		الملف src/app/app.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6880_35" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Task</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'./task/task'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@Component</span><span class="pun">(...)</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AppComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  todo</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Task</span><span class="pun">[]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
      title</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Buy milk'</span><span class="pun">,</span><span class="pln">
      description</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Go to the store and buy milk'</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
      title</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Create a Kanban app'</span><span class="pun">,</span><span class="pln">
      description</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Using Firebase and Angular create a Kanban app!'</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">];</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أضف موجِّه <code>‎*ngFor</code> في نهاية ملف <code>app.component.html</code> كما يلي:
</p>

<ul>
<li>
		الملف src/app/app.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6880_37" style="">
<span class="tag">&lt;app-task</span><span class="pln"> *</span><span class="atn">ngFor</span><span class="pun">=</span><span class="atv">"let task of todo"</span><span class="pln"> [</span><span class="atn">task</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"task"</span><span class="tag">&gt;&lt;/app-task&gt;</span></pre>

<p>
	ستشاهد ما يلي عندما تفتح نافذة المتصفح:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="84251" href="https://academy.hsoub.com/uploads/monthly_2021_12/004.png.046ca509b397683f2a03d81eb305ff89.png" rel=""><img alt="004.png" class="ipsImage ipsImage_thumbnailed" data-fileid="84251" data-unique="snpxzqcn4" src="https://academy.hsoub.com/uploads/monthly_2021_12/004.thumb.png.67bca4a6baa3e9172f9a5c3c64ace083.png" style="width: 650px; height: auto;"></a>
</p>

<h2>
	4. تضمين السحب والإفلات للمهام
</h2>

<p>
	نحن جاهزون الآن لتنفيذ الجزء الممتع من التطبيق، لذا دعنا ننشئ ثلاث حاويات من أجل الحالات الثلاث المختلفة للمهام وضمّن وظيفة السحب والإفلات في التطبيق باستخدام Angular CDK.
</p>

<p>
	احذف المكوِّن <code>app-task</code> مع الموجِّه <code>‎*ngFor</code> الخاص به في أعلى الملف app.component.html واستبدل الشيفرة التالية به:
</p>

<ul>
<li>
		الملف<code>src/app/app.component.html</code>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6880_39" style="">
<span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"container-wrapper"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"container"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;h2&gt;</span><span class="pln">Backlog</span><span class="tag">&lt;/h2&gt;</span><span class="pln">

    </span><span class="tag">&lt;mat-card</span><span class="pln">
      </span><span class="atn">cdkDropList</span><span class="pln">
      </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"todo"</span><span class="pln">
      #</span><span class="atn">todoList</span><span class="pun">=</span><span class="atv">"cdkDropList"</span><span class="pln">
      [</span><span class="atn">cdkDropListData</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"todo"</span><span class="pln">
      [</span><span class="atn">cdkDropListConnectedTo</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"[doneList, inProgressList]"</span><span class="pln">
      (</span><span class="atn">cdkDropListDropped</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"drop($event)"</span><span class="pln">
      </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"list"</span><span class="tag">&gt;</span><span class="pln">
      </span><span class="tag">&lt;p</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"empty-label"</span><span class="pln"> *</span><span class="atn">ngIf</span><span class="pun">=</span><span class="atv">"todo.length === 0"</span><span class="tag">&gt;</span><span class="pln">Empty list</span><span class="tag">&lt;/p&gt;</span><span class="pln">
      </span><span class="tag">&lt;app-task</span><span class="pln"> (</span><span class="atn">edit</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"editTask('todo', $event)"</span><span class="pln"> *</span><span class="atn">ngFor</span><span class="pun">=</span><span class="atv">"let task of todo"</span><span class="pln"> </span><span class="atn">cdkDrag</span><span class="pln"> [</span><span class="atn">task</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"task"</span><span class="tag">&gt;&lt;/app-task&gt;</span><span class="pln">
    </span><span class="tag">&lt;/mat-card&gt;</span><span class="pln">
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">

  </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"container"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;h2&gt;</span><span class="pln">In progress</span><span class="tag">&lt;/h2&gt;</span><span class="pln">

    </span><span class="tag">&lt;mat-card</span><span class="pln">
      </span><span class="atn">cdkDropList</span><span class="pln">
      </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"inProgress"</span><span class="pln">
      #</span><span class="atn">inProgressList</span><span class="pun">=</span><span class="atv">"cdkDropList"</span><span class="pln">
      [</span><span class="atn">cdkDropListData</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"inProgress"</span><span class="pln">
      [</span><span class="atn">cdkDropListConnectedTo</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"[todoList, doneList]"</span><span class="pln">
      (</span><span class="atn">cdkDropListDropped</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"drop($event)"</span><span class="pln">
      </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"list"</span><span class="tag">&gt;</span><span class="pln">
      </span><span class="tag">&lt;p</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"empty-label"</span><span class="pln"> *</span><span class="atn">ngIf</span><span class="pun">=</span><span class="atv">"inProgress.length === 0"</span><span class="tag">&gt;</span><span class="pln">Empty list</span><span class="tag">&lt;/p&gt;</span><span class="pln">
      </span><span class="tag">&lt;app-task</span><span class="pln"> (</span><span class="atn">edit</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"editTask('inProgress', $event)"</span><span class="pln"> *</span><span class="atn">ngFor</span><span class="pun">=</span><span class="atv">"let task of inProgress"</span><span class="pln"> </span><span class="atn">cdkDrag</span><span class="pln"> [</span><span class="atn">task</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"task"</span><span class="tag">&gt;&lt;/app-task&gt;</span><span class="pln">
    </span><span class="tag">&lt;/mat-card&gt;</span><span class="pln">
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">

  </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"container"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;h2&gt;</span><span class="pln">Done</span><span class="tag">&lt;/h2&gt;</span><span class="pln">

    </span><span class="tag">&lt;mat-card</span><span class="pln">
      </span><span class="atn">cdkDropList</span><span class="pln">
      </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"done"</span><span class="pln">
      #</span><span class="atn">doneList</span><span class="pun">=</span><span class="atv">"cdkDropList"</span><span class="pln">
      [</span><span class="atn">cdkDropListData</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"done"</span><span class="pln">
      [</span><span class="atn">cdkDropListConnectedTo</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"[todoList, inProgressList]"</span><span class="pln">
      (</span><span class="atn">cdkDropListDropped</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"drop($event)"</span><span class="pln">
      </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"list"</span><span class="tag">&gt;</span><span class="pln">
      </span><span class="tag">&lt;p</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"empty-label"</span><span class="pln"> *</span><span class="atn">ngIf</span><span class="pun">=</span><span class="atv">"done.length === 0"</span><span class="tag">&gt;</span><span class="pln">Empty list</span><span class="tag">&lt;/p&gt;</span><span class="pln">
      </span><span class="tag">&lt;app-task</span><span class="pln"> (</span><span class="atn">edit</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"editTask('done', $event)"</span><span class="pln"> *</span><span class="atn">ngFor</span><span class="pun">=</span><span class="atv">"let task of done"</span><span class="pln"> </span><span class="atn">cdkDrag</span><span class="pln"> [</span><span class="atn">task</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"task"</span><span class="tag">&gt;&lt;/app-task&gt;</span><span class="pln">
    </span><span class="tag">&lt;/mat-card&gt;</span><span class="pln">
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	يبدو أنَّ هناك الكثير من الأمور التي تحدث في الشيفرة السابقة، لذلك سنتناول كل قسم منها على حدة، حيث تُعَدّ الشيفرة التالية بنية المستوى الأعلى للقالب:
</p>

<ul>
<li>
		الملف src/app/app.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6880_41" style="">
<span class="pln">...
</span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"container-wrapper"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"container"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;h2&gt;</span><span class="pln">Backlog</span><span class="tag">&lt;/h2&gt;</span><span class="pln">
    ...
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">

  </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"container"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;h2&gt;</span><span class="pln">In progress</span><span class="tag">&lt;/h2&gt;</span><span class="pln">
    ...
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">

  </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"container"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;h2&gt;</span><span class="pln">Done</span><span class="tag">&lt;/h2&gt;</span><span class="pln">
    ...
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	أنشأنا هنا وسم <code>div</code> يحيط بجميع الحاويات الثلاث مع صنف باسم "<code>container-wrapper</code>" ويوجد لكل حاوية صنف باسم "<code>container</code>" كما يوجد عنوان لها ضمن وسم <code>h2</code>، فانظر الآن إلى بنية أول حاوية:
</p>

<ul>
<li>
		الملف src/app/app.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6880_43" style="">
<span class="pln">...
    </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"container"</span><span class="tag">&gt;</span><span class="pln">
      </span><span class="tag">&lt;h2&gt;</span><span class="pln">Backlog</span><span class="tag">&lt;/h2&gt;</span><span class="pln">

      </span><span class="tag">&lt;mat-card</span><span class="pln">
        </span><span class="atn">cdkDropList</span><span class="pln">
        </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"todo"</span><span class="pln">
        #</span><span class="atn">todoList</span><span class="pun">=</span><span class="atv">"cdkDropList"</span><span class="pln">
        [</span><span class="atn">cdkDropListData</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"todo"</span><span class="pln">
        [</span><span class="atn">cdkDropListConnectedTo</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"[doneList, inProgressList]"</span><span class="pln">
        (</span><span class="atn">cdkDropListDropped</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"drop($event)"</span><span class="pln">
        </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"list"</span><span class="pln">
      </span><span class="tag">&gt;</span><span class="pln">
        </span><span class="tag">&lt;p</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"empty-label"</span><span class="pln"> *</span><span class="atn">ngIf</span><span class="pun">=</span><span class="atv">"todo.length === 0"</span><span class="tag">&gt;</span><span class="pln">Empty list</span><span class="tag">&lt;/p&gt;</span><span class="pln">
        </span><span class="tag">&lt;app-task</span><span class="pln"> (</span><span class="atn">edit</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"editTask('todo', $event)"</span><span class="pln"> *</span><span class="atn">ngFor</span><span class="pun">=</span><span class="atv">"let task of todo"</span><span class="pln"> </span><span class="atn">cdkDrag</span><span class="pln"> [</span><span class="atn">task</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"task"</span><span class="tag">&gt;&lt;/app-task&gt;</span><span class="pln">
      </span><span class="tag">&lt;/mat-card&gt;</span><span class="pln">
    </span><span class="tag">&lt;/div&gt;</span><span class="pln">
...</span></pre>

<p>
	عرَّفنا أولًا <a href="https://academy.hsoub.com/devops/cloud-computing/%D8%A3%D8%A8%D8%B1%D8%B2-%D8%A7%D9%84%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-%D8%A7%D9%84%D8%AA%D9%8A-%D9%8A%D8%AC%D8%A8-%D8%B9%D9%84%D9%8A%D9%83-%D8%A7%D9%84%D8%A5%D9%84%D9%85%D8%A7%D9%85-%D8%A8%D9%87%D8%A7-%D8%B9%D9%86-%D8%A7%D9%84%D8%AD%D8%A7%D9%88%D9%8A%D8%A7%D8%AA-r561/" rel="">الحاوية</a> على أنها <code>mat-card</code> والتي تستخدِم موجِّه <code>cdkDropList</code>، كما استخدمنا هنا <code>mat-card</code> بسبب التنسيقات التي يوفرها هذا المكوِّن، وسيسمح لنا <code>cdkDropList</code> لاحقًا بإفلات المهام داخل العنصر، كما عيَّنا اثنين من المدخلات كما يلي:
</p>

<ul>
<li>
		<code>cdkDropListData</code>: تمثِّل إدخالًا للقائمة المنسدلة التي تسمح لنا بتحديد مصفوفة البيانات.
	</li>
	<li>
		<code>cdkDropListConnectedTo</code>: هو مرجع إلى <code>cdkDropLists</code> الأخرى والتي يتصل بها <code>cdkDropList</code> الحالي، حيث سنحدد أي القوائم الأخرى التي يمكننا إسقاط العناصر ضمنها من خلال تعيين هذا الإدخال.
	</li>
</ul>
<p>
	نريد بالإضافة إلى ذلك التعامل مع حدث الإفلات عن طريق استخدام خرج <code>cdkDropListDropped</code>، حيث سنستدعي تابع <code>drop</code> المعرَّف ضمن <code>AppComponent</code> كما نمرر له الحدث الحالي على أساس وسيط بمجرد أن يُصدر <code>cdkDropList</code> الخرج، ولاحظ أنه استخدمنا <code>id</code> أيضًا على أساس معرِّف لهذه الحاوية واسم <code>class</code> حتى تتمكن من تنسيقه، كما سننلق نظرةً على محتوى أبناء <code>mat-card</code> وسنجد العنصرَين التاليين ضمنها وهما:
</p>

<ul>
<li>
		فقرة تستخدمها لإظهار نص بأنَّ "القائمة فارغة" عندما لا توجد أية عناصر في قائمة <code>todo</code>.
	</li>
	<li>
		مكوِّن <code>app-task</code>، ولاحظ هنا بأننا تتعامل مع خرج <code>edit</code> الذي عرّفناه بالأصل عن طريق استدعاء تابع <code>editTask</code> وتمرير اسم القائمة وكائن <code>‎$event</code> لها، حيث سيساعدنا ذلك في استبدال المهمة المحرَّرة من القائمة الصحيحة، ثم نكرِّر الخطوات السابقة على قائمة <code>todo</code> ونمرِّر دخل <code>task</code> له، كما نضيف في هذه المرة أيضًا موجِّه <code>cdkDrag</code> الذي يجعل المهام الفردية قابلةً للسحب.
	</li>
</ul>
<p>
	نحتاج إلى تحديث ملف <code>app.module.ts</code> وتضمين استيراد وحدة <code>DragDropModule</code> لكي تعمل الشيفرة السابقة:
</p>

<ul>
<li>
		الملف src/app/app.module.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6880_45" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">DragDropModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/cdk/drag-drop'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@NgModule</span><span class="pun">({</span><span class="pln">
  declarations</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="typ">AppComponent</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
  imports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="pun">...</span><span class="pln">
    </span><span class="typ">DragDropModule</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
  providers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[],</span><span class="pln">
  bootstrap</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">AppComponent</span><span class="pun">]</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AppModule</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	نحتاج أيضًا إلى تعريف مصفوفات <code>inProgress</code> و<code>done</code> في الملف نفسه الذي عرَّفت فيه كل من توابع <code>editTask</code> و<code>drop</code>.
</p>

<ul>
<li>
		الملف src/app/app.component.ts:
	</li>
</ul>
<pre class="ipsCode">
...
import { CdkDragDrop, transferArrayItem } from '@angular/cdk/drag-drop';

@Component(...)
export class AppComponent {
  todo: Task[] = [...];
  inProgress: Task[] = [];
  done: Task[] = [];

  editTask(list: string, task: Task): void {}

  drop(event: CdkDragDrop&lt;Task[]|null&gt;): void {
    if (event.previousContainer === event.container) {
      return;
    }
    if (!event.container.data || !event.previousContainer.data) {
      return;
    }
    transferArrayItem(
      event.previousContainer.data,
      event.container.data,
      event.previousIndex,
      event.currentIndex
    );
  }
}
</pre>

<p>
	لاحظ أنه تحققنا أولًا في تابع <code>drop</code> من حالة إفلات المهمة في القائمة نفسها التي كانت موجودةً فيها أصلًا وفي هذه الحالة لا نفعل أي شيء؛ أما في حال كان الإفلات في قائمة جديدة، فسننقل المهمة الحالية إلى الحاوية الوجهة.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		<strong>تنبيه</strong>: لا نعالج هنا حالة إعادة ترتيب المهام في الحاوية نفسها، حيث سنحذف هذه الوظيفة من المقال لتبسيط العملية، ولكن لا تتردد في تنفيذها بنفسك بالاستعانة <a href="https://material.angular.io/cdk/drag-drop/overview" rel="external nofollow">بتوثيق CDK</a>.
	</p>
</blockquote>

<p>
	يجب أن تكون النتيجة كما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="84252" href="https://academy.hsoub.com/uploads/monthly_2021_12/005.png.795dfac92c83c8f298901f119a6ae30d.png" rel=""><img alt="005.png" class="ipsImage ipsImage_thumbnailed" data-fileid="84252" data-unique="6xif0dfta" src="https://academy.hsoub.com/uploads/monthly_2021_12/005.thumb.png.380e160c37d78f34fac5a20619d15fc4.png"></a>
</p>

<p>
	ستكون قادرًا على نقل العناصر بين قائمتين عندما تصل لهذه النقطة.
</p>

<h2>
	5. إنشاء مهام جديدة
</h2>

<p>
	يجب علينا تحديث قالب <code>AppComponent</code> كما يلي لتضمين الوظيفة المسؤولة عن إنشاء مهام جديدة:
</p>

<ul>
<li>
		الملف src/app/app.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6880_47" style="">
<span class="tag">&lt;mat-toolbar</span><span class="pln"> </span><span class="atn">color</span><span class="pun">=</span><span class="atv">"primary"</span><span class="tag">&gt;</span><span class="pln">
...
</span><span class="tag">&lt;/mat-toolbar&gt;</span><span class="pln">

</span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"content-wrapper"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;button</span><span class="pln"> (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"newTask()"</span><span class="pln"> </span><span class="atn">mat-button</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;mat-icon&gt;</span><span class="pln">add</span><span class="tag">&lt;/mat-icon&gt;</span><span class="pln"> Add Task
  </span><span class="tag">&lt;/button&gt;</span><span class="pln">

  </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"container-wrapper"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"container"</span><span class="tag">&gt;</span><span class="pln">
      ...
    </span><span class="tag">&lt;/div&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	ننشئ عنصر <code>div</code> في المستوى الأعلى حول <code>container-wrapper</code> وتضيف أيضًا زر بأيقونة "<code>add</code>" من نوع Material بجانب عنوان "إضافة مهمة Add Task"، حيث نحتاج إلى مغلِّف إضافي لوضع الزر في أعلى القائمة الخاصة بكل حاوية والتي سنضعها لاحقًا بجانب بعضها بعضًا باستخدام flexbox، كما نحتاج إلى استيراد الوحدة المرتبطة به ضمن ملف <code>AppModule</code> لأن هذا الزر يستخدم مكوِّن الزر من نوع Material:
</p>

<ul>
<li>
		الملف src/app/app.module.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6880_49" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">MatButtonModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/material/button'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@NgModule</span><span class="pun">({</span><span class="pln">
  declarations</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="typ">AppComponent</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
  imports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="pun">...</span><span class="pln">
    </span><span class="typ">MatButtonModule</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
  providers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[],</span><span class="pln">
  bootstrap</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">AppComponent</span><span class="pun">]</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AppModule</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	سنضمِّن الوظيفة المسؤولة عن إضافة المهام في <code>AppComponent</code> عن طريق استخدام مربع حوار من نوع Material، حيث سيكون لدينا ضمن مربع الحوار استمارةً بحقلين هما العنوان والوصف، وبالتالي عندما ينقر المستخدِم على زر "إضافة مهمة Add Task"، فسنفتح له مربع الحوار، وعندما يرسل المستخدِم هذه الاستمارة، فسنضف مهمةً جديدةً إلى قائمة <code>todo</code>، كما سنلقِ نظرةً على التضمين عالي المستوى لهذه الوظيفة في <code>AppComponent</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6880_52" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">MatDialog</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/material/dialog'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@Component</span><span class="pun">(...)</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AppComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">

  constructor</span><span class="pun">(</span><span class="kwd">private</span><span class="pln"> dialog</span><span class="pun">:</span><span class="pln"> </span><span class="typ">MatDialog</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

  newTask</span><span class="pun">():</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> dialogRef </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">dialog</span><span class="pun">.</span><span class="pln">open</span><span class="pun">(</span><span class="typ">TaskDialogComponent</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      width</span><span class="pun">:</span><span class="pln"> </span><span class="str">'270px'</span><span class="pun">,</span><span class="pln">
      data</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        task</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{},</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
    dialogRef
      </span><span class="pun">.</span><span class="pln">afterClosed</span><span class="pun">()</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">subscribe</span><span class="pun">((</span><span class="pln">result</span><span class="pun">:</span><span class="pln"> </span><span class="typ">TaskDialogResult</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todo</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">task</span><span class="pun">));</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عرَّفنا بانيًا للمكوِّن في الشفرة السابقة وحقنّا صنف <code>MatDialog</code> ضمنه، وسنفعل ضمن دالة <code>newTask</code> ما يلي:
</p>

<ul>
<li>
		نفتح مربع حوار جديد باستخدام <code>TaskDialogComponent</code> الذي سنعرِّفه لاحقًا.
	</li>
	<li>
		نحدِّد عرض مربع الحوار ليكون <code>270px</code>.
	</li>
	<li>
		نمرِّر مهمةً فارغةً إلى مربع الحوار على أساس بيانات، حيث سنكون قادرين على الحصول على مرجع إلى الكائن الحاوي لهذه البيانات في <code>TaskDialogComponent</code>.
	</li>
	<li>
		نشترك بحدث الإغلاق <code>close</code> ونضيف المهمة من الكائن <code>result</code> إلى المصفوفة <code>todo</code>.
	</li>
</ul>
<p>
	يجب أولًا استيراد وحدة <code>MatDialogModule</code> في ملف <code>AppModule</code> لتتأكد من أنّ كل شيء يعمل بصورة صحيحة :
</p>

<ul>
<li>
		الملف src/app/app.module.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6880_54" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">MatDialogModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/material/dialog'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@NgModule</span><span class="pun">({</span><span class="pln">
  declarations</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="typ">AppComponent</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
  imports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="pun">...</span><span class="pln">
    </span><span class="typ">MatDialogModule</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
  providers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[],</span><span class="pln">
  bootstrap</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">AppComponent</span><span class="pun">]</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AppModule</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	لننشئ الآن مكوِّن <code>TaskDialogComponent</code>عن طريق الانتقال إلى الدليل <code>src/app</code> وتنفيذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6880_57" style="">
<span class="pln">ng generate component task</span><span class="pun">-</span><span class="pln">dialog</span></pre>

<p>
	افتح ملف الآتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1555_16" style="">
<span class="pln"> src</span><span class="pun">/</span><span class="pln">app</span><span class="pun">/</span><span class="pln">task</span><span class="pun">-</span><span class="pln">dialog</span><span class="pun">/</span><span class="pln">task</span><span class="pun">-</span><span class="pln">dialog</span><span class="pun">.</span><span class="pln">component</span><span class="pun">.</span><span class="pln">html </span></pre>

<p>
	من أجل تضمين الوظائف الخاصة به واستبدال ما يلي بمحتواه:
</p>

<ul>
<li>
		الملف src/app/task-dialog/task-dialog.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6880_61" style="">
<span class="tag">&lt;mat-form-field&gt;</span><span class="pln">
  </span><span class="tag">&lt;mat-label&gt;</span><span class="pln">Title</span><span class="tag">&lt;/mat-label&gt;</span><span class="pln">
  </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">matInput</span><span class="pln"> </span><span class="atn">cdkFocusInitial</span><span class="pln"> [(</span><span class="atn">ngModel</span><span class="pln">)]</span><span class="pun">=</span><span class="atv">"data.task.title"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
</span><span class="tag">&lt;/mat-form-field&gt;</span><span class="pln">

</span><span class="tag">&lt;mat-form-field&gt;</span><span class="pln">
  </span><span class="tag">&lt;mat-label&gt;</span><span class="pln">Description</span><span class="tag">&lt;/mat-label&gt;</span><span class="pln">
  </span><span class="tag">&lt;textarea</span><span class="pln"> </span><span class="atn">matInput</span><span class="pln"> [(</span><span class="atn">ngModel</span><span class="pln">)]</span><span class="pun">=</span><span class="atv">"data.task.description"</span><span class="tag">&gt;&lt;/textarea&gt;</span><span class="pln">
</span><span class="tag">&lt;/mat-form-field&gt;</span><span class="pln">

</span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">mat-dialog-actions</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">mat-button</span><span class="pln"> [</span><span class="atn">mat-dialog-close</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"{ task: data.task }"</span><span class="tag">&gt;</span><span class="pln">OK</span><span class="tag">&lt;/button&gt;</span><span class="pln">
  </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">mat-button</span><span class="pln"> (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"cancel()"</span><span class="tag">&gt;</span><span class="pln">Cancel</span><span class="tag">&lt;/button&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	ننشئ في القالب أعلاه استمارةً بحقلين هما <code>title</code> و<code>description</code>، كما نستخدِم الموجِّه <code>cdkFocusInput</code> لجعل التركيز على حقل الإدخال <code>title</code> تلقائيًا عندما يفتح المستخدِم مربع الحوار، ولاحظ كيف نستخدِم خاصية <code>data</code> للمكوِّن على أساس مرجع داخل القالب، حيث ستشبه هذه تمامًا <code>data</code> التي نمررها للتابع <code>open</code> في <code>AppComponent</code>.
</p>

<p>
	كما نستخدِم تربيط البيانات ثنائي الاتجاه باستخدام <code>ngModel</code> من أجل تحديث العنوان ووصف المهمة عندما يغيِّر المستخدِم محتوى هذه الحقول، وعندما ينقر المستخدِم على زر موافق، فسنُعيد النتيجة <code>{ task: data.task }</code> تلقائيًا وهي المهمة التي بدّلناها باستخدام حقول الاستمارة في القالب أعلاه، كما سنُضمِّن الآن <a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A7%D9%84%D9%85%D8%AA%D8%AD%D9%83%D9%85%D8%A7%D8%AA-controllers-%D9%81%D9%8A-angularjs-r186/" rel="">المتحكم </a>الخاص بالمكوِّن كما يلي:
</p>

<ul>
<li>
		الملف src/app/task-dialog/task-dialog.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6880_63" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Component</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Inject</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> MAT_DIALOG_DATA</span><span class="pun">,</span><span class="pln"> </span><span class="typ">MatDialogRef</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/material/dialog'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Task</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'../task/task'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@Component</span><span class="pun">({</span><span class="pln">
  selector</span><span class="pun">:</span><span class="pln"> </span><span class="str">'app-task-dialog'</span><span class="pun">,</span><span class="pln">
  templateUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./task-dialog.component.html'</span><span class="pun">,</span><span class="pln">
  styleUrls</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'./task-dialog.component.css'</span><span class="pun">],</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">TaskDialogComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">private</span><span class="pln"> backupTask</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Partial</span><span class="pun">&lt;</span><span class="typ">Task</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">...</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">task </span><span class="pun">};</span><span class="pln">

  constructor</span><span class="pun">(</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> dialogRef</span><span class="pun">:</span><span class="pln"> </span><span class="typ">MatDialogRef</span><span class="pun">&lt;</span><span class="typ">TaskDialogComponent</span><span class="pun">&gt;,</span><span class="pln">
    </span><span class="lit">@Inject</span><span class="pun">(</span><span class="pln">MAT_DIALOG_DATA</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> data</span><span class="pun">:</span><span class="pln"> </span><span class="typ">TaskDialogData</span><span class="pln">
  </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

  cancel</span><span class="pun">():</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">task</span><span class="pun">.</span><span class="pln">title </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">backupTask</span><span class="pun">.</span><span class="pln">title</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">task</span><span class="pun">.</span><span class="pln">description </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">backupTask</span><span class="pun">.</span><span class="pln">description</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">dialogRef</span><span class="pun">.</span><span class="pln">close</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">data</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نحقن في الشيفرة السابقة مرجعًا في مربع الحوار ضمن <code>TaskDialogComponent</code> حتى نتمكن من إغلاقه، كما نحقن أيضًا القيمة الخاصة بالمزود المرتبطة مع مفتاح <code>MAT_DIALOG_DATA</code> والذي يمثل كائن البيانات الذي مرَّرناه إلى التابع المفتوح في <code>AppComponent</code>، ونعرِّف أيضًا الخاصية الخاصة <code>backupTask</code> والتي هي نسخة من المهمة التي مررناها مع كائن البيانات.
</p>

<p>
	سنستعيد خصائص <code>this.data.task</code> التي ربما تكون قد تغيّرت ونغلق مربع الحوار ونمرِّر <code>this.data</code> على أساس نتيجة عندما يضغط المستخدِم على زر "إلغاء"، وهناك نوعان من المراجع التي استخدمناها ولكن لم تُعرَّف بعد وهما:
</p>

<ul>
<li>
		<code>TaskDialogData</code>
	</li>
	<li>
		<code>TaskDialogResult</code>
	</li>
</ul>
<p>
	وأضف الآن التعريفات التالية إلى أسفل الملف:
</p>

<ul>
<li>
		الملف src/app/task-dialog/task-dialog.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6880_69" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> interface </span><span class="typ">TaskDialogData</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  task</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Partial</span><span class="pun">&lt;</span><span class="typ">Task</span><span class="pun">&gt;;</span><span class="pln">
  enableDelete</span><span class="pun">:</span><span class="pln"> boolean</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> interface </span><span class="typ">TaskDialogResult</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  task</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Task</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">delete</span><span class="pun">?:</span><span class="pln"> boolean</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الشيء الأخير الذي نحتاج إلى فعله قبل جهوزية الوظيفة هو استيراد بعض <a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%81%D9%8A-angularjs-r196/" rel="">الوحدات</a> في ملف <code>AppModule</code>.
</p>

<ul>
<li>
		الملف src/app/app.module.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6880_71" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">MatInputModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/material/input'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">FormsModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/forms'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@NgModule</span><span class="pun">({</span><span class="pln">
  declarations</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="typ">AppComponent</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
  imports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="pun">...</span><span class="pln">
    </span><span class="typ">MatInputModule</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">FormsModule</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
  providers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[],</span><span class="pln">
  bootstrap</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">AppComponent</span><span class="pun">]</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AppModule</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span></pre>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		<strong>تنبيه</strong>: تأكد أيضًا من إضافة الاستيرادات الخاصة بكل من <code>TaskDialogComponent</code> و<code>TaskDialogResult</code> في ملف <code>app.component.ts</code>.
	</p>
</blockquote>

<p>
	ستشاهد الآن واجهة المستخدِم التالية عندما تنقر على زر "إضافة مهمة Add Task":
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="84253" href="https://academy.hsoub.com/uploads/monthly_2021_12/006.png.f4a8d3a79cfd84ba8bb8c62fcb88117d.png" rel=""><img alt="006.png" class="ipsImage ipsImage_thumbnailed" data-fileid="84253" data-unique="r4i7fjklh" src="https://academy.hsoub.com/uploads/monthly_2021_12/006.thumb.png.a48f686868161854533c08647002d27d.png" style="width: 650px; height: auto;"></a>
</p>

<h2>
	6. تحسين تنسيقات التطبيق
</h2>

<p>
	سنحسِّن التخطيط الخاص بالتطبيق بحيث يتحسّن مظهر التطبيق وذلك عن طريق تغيير وتبديل تنسيقاته بصورة بسيطة، حيث ستضع الحاويات بجانب بعضها البعض كما سنصيف بعض التعديلات الطفيفة على زر "إضافة مهمة Add Task" وعنوان القائمة فارغة، لذا افتح ملف <code>src/app/app.component.css</code> وأضف التنسيقات التالية إلى أسفله:
</p>

<ul>
<li>
		الملف src/app/app.component.css:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6880_75" style="">
<span class="pln">mat</span><span class="pun">-</span><span class="pln">toolbar </span><span class="pun">{</span><span class="pln">
  margin</span><span class="pun">-</span><span class="pln">bottom</span><span class="pun">:</span><span class="pln"> </span><span class="lit">20px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

mat</span><span class="pun">-</span><span class="pln">toolbar </span><span class="pun">&gt;</span><span class="pln"> span </span><span class="pun">{</span><span class="pln">
  margin</span><span class="pun">-</span><span class="pln">left</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">content</span><span class="pun">-</span><span class="pln">wrapper </span><span class="pun">{</span><span class="pln">
  max</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1400px</span><span class="pun">;</span><span class="pln">
  margin</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">auto</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">container</span><span class="pun">-</span><span class="pln">wrapper </span><span class="pun">{</span><span class="pln">
  display</span><span class="pun">:</span><span class="pln"> flex</span><span class="pun">;</span><span class="pln">
  justify</span><span class="pun">-</span><span class="pln">content</span><span class="pun">:</span><span class="pln"> space</span><span class="pun">-</span><span class="pln">around</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">container </span><span class="pun">{</span><span class="pln">
  width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">400px</span><span class="pun">;</span><span class="pln">
  margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">25px</span><span class="pln"> </span><span class="lit">25px</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">list </span><span class="pun">{</span><span class="pln">
  border</span><span class="pun">:</span><span class="pln"> solid </span><span class="lit">1px</span><span class="pln"> </span><span class="com">#ccc;</span><span class="pln">
  min</span><span class="pun">-</span><span class="pln">height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">60px</span><span class="pun">;</span><span class="pln">
  border</span><span class="pun">-</span><span class="pln">radius</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

app</span><span class="pun">-</span><span class="kwd">new</span><span class="pun">-</span><span class="pln">task </span><span class="pun">{</span><span class="pln">
  margin</span><span class="pun">-</span><span class="pln">bottom</span><span class="pun">:</span><span class="pln"> </span><span class="lit">30px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">empty</span><span class="pun">-</span><span class="pln">label </span><span class="pun">{</span><span class="pln">
  font</span><span class="pun">-</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2em</span><span class="pun">;</span><span class="pln">
  padding</span><span class="pun">-</span><span class="pln">top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10px</span><span class="pun">;</span><span class="pln">
  text</span><span class="pun">-</span><span class="pln">align</span><span class="pun">:</span><span class="pln"> center</span><span class="pun">;</span><span class="pln">
  opacity</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.2</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ضبطنا في الشيفرة أعلاه التخطيط الخاص بشريط الأدوات وعنوانه، كما تأكدنا من محاذاة المحتوى أفقيًا عن طريق تعيين عرضه إلى <code>1400px</code> وهامشه إلى <code>auto</code>، ثم وضعنا الحاويات بجانب بعضها بعضًا باستخدام flexbox، وأخيرًا أجرينا بعض التعديلات في كيفية تصوّر المهام والقوائم الفارغة، ويجب عليك مشاهدة واجهة المستخدِم التالية عند إعادة تحميل تطبيقك:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="84254" href="https://academy.hsoub.com/uploads/monthly_2021_12/007.png.9e1842debe5d08de3bd6ef5bc9485c96.png" rel=""><img alt="007.png" class="ipsImage ipsImage_thumbnailed" data-fileid="84254" data-unique="5miedwm5g" src="https://academy.hsoub.com/uploads/monthly_2021_12/007.thumb.png.9f6d70c5210a29ffd3ca43b23c8660fc.png" style="width: 650px; height: auto;"></a>
</p>

<p>
	لا يزال لدينا مشكلةً مزعجةً تحدث عندما ننقل المهام بين الحاويات على الرغم من تحسين تنسيقات تطبيقنا بصورة كبيرة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="84255" href="https://academy.hsoub.com/uploads/monthly_2021_12/008.png.6d277350f5b1097569c912616687b50d.png" rel=""><img alt="008.png" class="ipsImage ipsImage_thumbnailed" data-fileid="84255" data-unique="8kpm7us8n" src="https://academy.hsoub.com/uploads/monthly_2021_12/008.thumb.png.bed5f08655519579976a54fae21571a0.png" style="width: 300px; height: auto;"></a>
</p>

<p>
	نرى بطاقتين للمهمة نفسها -أي واحدة للمهمة التي نسحبها وأخرى للمهمة الموجودة في الحاوية نفسها- عندما نبدأ في سحب مهمة "شراء الحليب Buy milk"، في حين توفِّر لنا عدة التطوير CDK في Angular أسماء أصناف <a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> التي تمكِّننا من إصلاح هذه المشكلة، والآن أضف التنسيق التالي في نهاية ملف <code>src/app/app.component.css</code>:
</p>

<ul>
<li>
		الملف src/app/app.component.css:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6880_80" style="">
<span class="pun">.</span><span class="pln">cdk</span><span class="pun">-</span><span class="pln">drag</span><span class="pun">-</span><span class="pln">animating </span><span class="pun">{</span><span class="pln">
  transition</span><span class="pun">:</span><span class="pln"> transform </span><span class="lit">250ms</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">cdk</span><span class="pun">-</span><span class="pln">drag</span><span class="pun">-</span><span class="pln">placeholder </span><span class="pun">{</span><span class="pln">
  opacity</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	بينما نسحب عنصر ما، سيستنسخه سحب وإفلات Angular CDK ويدرجه في الموضع الذي سنُفلِت الأصل فيه، كما عيَّنا خاصية الشفافية في صنف <code>cdk-drag-placeholder</code> للتأكد من عدم ظهور هذا العنصر عند عملية النقل والتي سيضيفها CDK إلى العنصر النائب.
</p>

<p>
	بالإضافة إلى ذلك فعندما تفلت عنصرًا فسيضيف CDK صنف <code>cdk-drag-animating</code>، ولإضافة تحريك سلس للعنصر بدلًا من نقل العنصر مباشرةً فإننا نعرِّف انتقالًا بمدة <code>250ms</code>؛ أما لإجراء بعض التعديلات الطفيفة على التنسيقات الخاصة بالمهام، فسنذهب إلى ملف <code>task.component.css</code> ونعيّن طريقة عرض العنصر إلى <code>block</code> ونضبط بعض الهوامش كما يلي:
</p>

<ul>
<li>
		الملف src/app/task/task.component.css:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6880_82" style="">
<span class="pun">:</span><span class="pln">host </span><span class="pun">{</span><span class="pln">
  display</span><span class="pun">:</span><span class="pln"> block</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">item </span><span class="pun">{</span><span class="pln">
  margin</span><span class="pun">-</span><span class="pln">bottom</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10px</span><span class="pun">;</span><span class="pln">
  cursor</span><span class="pun">:</span><span class="pln"> pointer</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	7. تحرير وحذف المهام الحالية
</h2>

<p>
	سنعيد استخدام معظم الوظائف التي ضمّنَّاها مسبقًا لتحرير المهام الحالية وإزالتها، فعندما ينقر المستخدِم نقرًا مزدوجًا على مهمة ما، فسنفتح <code>TaskDialogComponent</code> ونملأ الحقلين الموجودين في الاستمارة بقيمتي <code>title</code> و<code>description</code> الخاصَين بالمهمة.
</p>

<p>
	كما سنضيف في مكون <code>TaskDialogComponent</code> أيضًا زر الحذف، فعندما ينقر المستخدِم عليه سنمرِّر تعليمة الحذف والتي ستنتهي في ملف <code>AppComponent</code>، ويكون التغيير الوحيد الذي نحتاج إلى فعله في ملف <code>TaskDialogComponent</code> هو في القالب الخاص به:
</p>

<ul>
<li>
		الملف src/app/task-dialog/task-dialog.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6880_85" style="">
<span class="tag">&lt;mat-form-field&gt;</span><span class="pln">
 ...
</span><span class="tag">&lt;/mat-form-field&gt;</span><span class="pln">

</span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">mat-dialog-actions</span><span class="tag">&gt;</span><span class="pln">
  ...
  </span><span class="tag">&lt;button</span><span class="pln">
    *</span><span class="atn">ngIf</span><span class="pun">=</span><span class="atv">"data.enableDelete"</span><span class="pln">
    </span><span class="atn">mat-fab</span><span class="pln">
    </span><span class="atn">color</span><span class="pun">=</span><span class="atv">"primary"</span><span class="pln">
    </span><span class="atn">aria-label</span><span class="pun">=</span><span class="atv">"Delete"</span><span class="pln">
    [</span><span class="atn">mat-dialog-close</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"{ task: data.task, delete: true }"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;mat-icon&gt;</span><span class="pln">delete</span><span class="tag">&lt;/mat-icon&gt;</span><span class="pln">
  </span><span class="tag">&lt;/button&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	يعرض هذا الزر أيقونة الحذف المنسقة بتنسيق مكتبة Material الافتراضي، فعندما ينقر المستخدِم عليه، فسنغلق مربع الحوار ونمرِّر الكائن الآتي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1555_22" style="">
<span class="pln"> </span><span class="pun">{</span><span class="pln"> task</span><span class="pun">:</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">task</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">delete</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span></pre>

<p>
	على أساس نتيجة لذلك، ولاحظ أيضًا أننا جعلنا الزر دائريًا عن طريق استخدام <code>mat-fab</code>، كما جعلنا لونه مثل اللون الأساسي المستخدَم في التطبيق، وحدَّدنا ظهوره فقط عندما يمكن حذف بيانات مربع الحوار؛ أما القسم المتبقي من عملية التضمين لكل من وظيفتي التحرير والحذف للمهام فهو موجود في ملف <code>AppComponent</code> بحيث كل ما يجب عليك القيام به هو استبدل تابع <code>editTask</code> بما يلي:
</p>

<ul>
<li>
		الملف src/app/app.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6880_89" style="">
<span class="lit">@Component</span><span class="pun">({</span><span class="pln"> </span><span class="pun">...</span><span class="pln"> </span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AppComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
  editTask</span><span class="pun">(</span><span class="pln">list</span><span class="pun">:</span><span class="pln"> </span><span class="str">'done'</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="str">'todo'</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="str">'inProgress'</span><span class="pun">,</span><span class="pln"> task</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Task</span><span class="pun">):</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> dialogRef </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">dialog</span><span class="pun">.</span><span class="pln">open</span><span class="pun">(</span><span class="typ">TaskDialogComponent</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      width</span><span class="pun">:</span><span class="pln"> </span><span class="str">'270px'</span><span class="pun">,</span><span class="pln">
      data</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        task</span><span class="pun">,</span><span class="pln">
        enableDelete</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
    dialogRef</span><span class="pun">.</span><span class="pln">afterClosed</span><span class="pun">().</span><span class="pln">subscribe</span><span class="pun">((</span><span class="pln">result</span><span class="pun">:</span><span class="pln"> </span><span class="typ">TaskDialogResult</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">const</span><span class="pln"> dataList </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">[</span><span class="pln">list</span><span class="pun">];</span><span class="pln">
      </span><span class="kwd">const</span><span class="pln"> taskIndex </span><span class="pun">=</span><span class="pln"> dataList</span><span class="pun">.</span><span class="pln">indexOf</span><span class="pun">(</span><span class="pln">task</span><span class="pun">);</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="kwd">delete</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        dataList</span><span class="pun">.</span><span class="pln">splice</span><span class="pun">(</span><span class="pln">taskIndex</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        dataList</span><span class="pun">[</span><span class="pln">taskIndex</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> task</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إذا نظرت إلى وسائط تابع <code>editTask</code> فستجد:
</p>

<ul>
<li>
		قائمةً تحوي أحد الأنواع <code>'done' | 'todo' | 'inProgress'</code> والتي تمثِّل نوعًا موحَّدًا من سلسلة نصية مع قيم مقابلة للخصائص المرتبطة بكل حاوية.
	</li>
	<li>
		المهمة الحالية التي تريد تحريرها.
	</li>
</ul>
<p>
	أنشئ في جسم التابع نسخةً من <code>TaskDialogComponent</code> لأن <code>data</code> الخاصة به تُمرَّر على أساس كائن وهو الذي يحدِّد المهمة التي نريد تحريرها ويمكِّن زر التحرير في الاستمارة أيضًا عن طريق إسناد الخاصية <code>enableDelete</code> إلى القيمة <code>true</code>، وعندما نحصل على النتيجة المُعادة من مربع الحوار فسنتعامل مع حالتين:
</p>

<ul>
<li>
		عند إسناد راية <code>delete</code> إلى <code>true</code> -أي عندما يضغط المستخدِم على زر الحذف- فسنزيل المهمة من القائمة الموجودة ضمنها.
	</li>
	<li>
		وإلا فسنستبدل فقط المهمة ذات الدليل المعطى بالمهمة التي حصلنا عليها من نتيجة مربع الحوار.
	</li>
</ul>
<h2>
	8. إنشاء مشروع Firebase وربطه بالمشروع
</h2>

<ul>
<li>
		لتنشأ مشروع Firebase جديد، انتقل إلى <a href="https://console.firebase.google.com/" rel="external nofollow">Firebase Console</a>.
	</li>
	<li>
		أنشئ مشروعًا جديدًا باسم "KanbanFire".
	</li>
</ul>
<p>
	سنربط الآن مشروعنا مع Firebase، حيث يقدِّم فريق Firebase حزمة <code>‎@angular/fire</code> والتي توفر عملية التكامل بين التقنيتين، ولإضافة دعم Firebase إلى تطبيقك افتح الدليل الجذر لمساحة العمل الخاصة بتطبيقك ونفِّذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6880_91" style="">
<span class="pln">ng add </span><span class="lit">@angular</span><span class="pun">/</span><span class="pln">fire</span></pre>

<p>
	يثبِّت هذا الأمر حزمة <code>‎@angular/fire</code> ويسألك بعض الأسئلة، ويجب عليك رؤية مثل هذه الصورة في الطرفية الخاصة بك:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="84256" href="https://academy.hsoub.com/uploads/monthly_2021_12/009.png.f8b35082ea9bb4cda3fbec93351a1661.png" rel=""><img alt="009.png" class="ipsImage ipsImage_thumbnailed" data-fileid="84256" data-unique="q8agfkid3" src="https://academy.hsoub.com/uploads/monthly_2021_12/009.thumb.png.185d66fa5b2dce8ae41bc989496af0b5.png" style="width: 700px; height: auto;"></a>
</p>

<p>
	تفتح عملية التثبيت في غضون ذلك نافذة المتصفح حتى تتمكن من المصادقة باستخدام حساب Firebase الخاص بك، ويطلب منك أخيرًا اختيار مشروع Firebase وينشئ بعض الملفات على القرص الخاص بك، وبعد ذلك نحتاج إلى إنشاء قاعدة بيانات Firestore، إذ يمكنك فعل ذلك من خلال التوجه إلى "Cloud Firestore" ثم النقر فوق "Create Database".
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="84257" href="https://academy.hsoub.com/uploads/monthly_2021_12/010.png.d7633963daef98fb147acdc285b6a6d4.png" rel=""><img alt="010.png" class="ipsImage ipsImage_thumbnailed" data-fileid="84257" data-unique="z9ze2fq29" src="https://academy.hsoub.com/uploads/monthly_2021_12/010.thumb.png.80a01c0855efe281555673c3bd3070f7.png" style="width: 300px; height: auto;"></a>
</p>

<p>
	أنشئ بعد ذلك قاعدة بيانات في وضع الاختبار:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="84258" href="https://academy.hsoub.com/uploads/monthly_2021_12/011.png.e5356144605dced2b39939bf143953f2.png" rel=""><img alt="011.png" class="ipsImage ipsImage_thumbnailed" data-fileid="84258" data-unique="aejow5zb8" src="https://academy.hsoub.com/uploads/monthly_2021_12/011.thumb.png.013e6c756483980bad33c9e910e3da73.png" style="width: 700px; height: auto;"></a>
</p>

<p>
	أخيرًا، حدِّد المنطقة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="84259" href="https://academy.hsoub.com/uploads/monthly_2021_12/012.png.b35d167454615d0a1449d2098ff12f52.png" rel=""><img alt="012.png" class="ipsImage ipsImage_thumbnailed" data-fileid="84259" data-unique="pqxtqjh7a" src="https://academy.hsoub.com/uploads/monthly_2021_12/012.thumb.png.9bca6b1592b0a50870e57f2be8bde6ed.png" style="width: 700px; height: auto;"></a>
</p>

<p>
	الشيء الأخير الذي يجب عليك فعله هو إضافة تهيئة Firebase إلى بيئتك، حيث يمكنك العثور على تهيئة المشروع الخاص بك في Firebase Console.
</p>

<ul>
<li>
		انقر على أيقونة الإعدادات بجوار نظرة عامة عن المشروع Project Overview.
	</li>
	<li>
		اختر إعدادات المشروع Project Settings.
	</li>
</ul>
<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="84260" href="https://academy.hsoub.com/uploads/monthly_2021_12/013.png.159c8e40e24a9437586de864f0d556b6.png" rel=""><img alt="013.png" class="ipsImage ipsImage_thumbnailed" data-fileid="84260" data-unique="xushybsri" src="https://academy.hsoub.com/uploads/monthly_2021_12/013.thumb.png.22e1bce984c80315c9559d0dd1b7be30.png" style="width: 450px; height: auto;"></a>
</p>

<p>
	حدِّد "تطبيق ويب Web app" ضمن "تطبيقاتك Your apps":
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="84261" href="https://academy.hsoub.com/uploads/monthly_2021_12/014.png.c56311470ab31d48097c127561925925.png" rel=""><img alt="014.png" class="ipsImage ipsImage_thumbnailed" data-fileid="84261" data-unique="30mkg48ok" src="https://academy.hsoub.com/uploads/monthly_2021_12/014.thumb.png.8a12807723976d68c97344a90226af29.png" style="width: 500px; height: auto;"></a>
</p>

<p>
	سجّل تطبيقك بعد ذلك و<strong>تأكد من نفعيل "Firebase Hosting"</strong>:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="84262" href="https://academy.hsoub.com/uploads/monthly_2021_12/015.png.d91f738853207a9c9638f0a20ff1a88a.png" rel=""><img alt="015.png" class="ipsImage ipsImage_thumbnailed" data-fileid="84262" data-unique="ebw6ea8oj" src="https://academy.hsoub.com/uploads/monthly_2021_12/015.thumb.png.9b80f8bc9cdc7f446e44bfb3ba0b9098.png" style="width: 500px; height: auto;"></a>
</p>

<p>
	يمكنك نسخ التهيئة الخاصة بك إلى ملف <code>src/environments/environment.ts</code> بعد أن تنقر على "تسجيل التطبيق Register app":
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="84263" href="https://academy.hsoub.com/uploads/monthly_2021_12/016.png.e47ee83b070fb2882f7eb4ae428dcd96.png" rel=""><img alt="016.png" class="ipsImage ipsImage_thumbnailed" data-fileid="84263" data-unique="i4tqubvx2" src="https://academy.hsoub.com/uploads/monthly_2021_12/016.thumb.png.0d39e69ca907e60e6d093cc8a8a37467.png" style="width: 500px; height: auto;"></a>
</p>

<p>
	في النهاية، يجب أن يبدو ملف التكوين الخاص بك كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6880_108" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> environment </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  production</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
  firebase</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    apiKey</span><span class="pun">:</span><span class="pln"> </span><span class="str">'&lt;your-key&gt;'</span><span class="pun">,</span><span class="pln">
    authDomain</span><span class="pun">:</span><span class="pln"> </span><span class="str">'&lt;your-project-authdomain&gt;'</span><span class="pun">,</span><span class="pln">
    databaseURL</span><span class="pun">:</span><span class="pln"> </span><span class="str">'&lt;your-database-URL&gt;'</span><span class="pun">,</span><span class="pln">
    projectId</span><span class="pun">:</span><span class="pln"> </span><span class="str">'&lt;your-project-id&gt;'</span><span class="pun">,</span><span class="pln">
    storageBucket</span><span class="pun">:</span><span class="pln"> </span><span class="str">'&lt;your-storage-bucket&gt;'</span><span class="pun">,</span><span class="pln">
    messagingSenderId</span><span class="pun">:</span><span class="pln"> </span><span class="str">'&lt;your-messaging-sender-id&gt;'</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span></pre>

<h2>
	9. نقل البيانات إلى Firestore
</h2>

<p>
	استخدم الآن <code>‎@angular/fire</code> لنقل بياناتك إلى Firestore بعد أن أعددت Firebase SDK، لذا سنستورد أولًا الوحدات التي سنحتاجها في ملف <code>AppModule</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6880_110" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> environment </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'src/environments/environment'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AngularFireModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/fire'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AngularFirestoreModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/fire/firestore'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@NgModule</span><span class="pun">({</span><span class="pln">
  declarations</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">AppComponent</span><span class="pun">,</span><span class="pln"> </span><span class="typ">TaskDialogComponent</span><span class="pun">,</span><span class="pln"> </span><span class="typ">TaskComponent</span><span class="pun">],</span><span class="pln">
  imports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="pun">...</span><span class="pln">
    </span><span class="typ">AngularFireModule</span><span class="pun">.</span><span class="pln">initializeApp</span><span class="pun">(</span><span class="pln">environment</span><span class="pun">.</span><span class="pln">firebase</span><span class="pun">),</span><span class="pln">
    </span><span class="typ">AngularFirestoreModule</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
  providers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[],</span><span class="pln">
  bootstrap</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">AppComponent</span><span class="pun">],</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AppModule</span><span class="pln"> </span><span class="pun">{}</span></pre>

<p>
	يجب علينا حقن <code>AngularFirestore</code> في باني <code>AppComponent</code> بما أن التطبيق الخاص بنا يستخدِم Firestone:
</p>

<ul>
<li>
		الملف src/app/app.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6880_112" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AngularFirestore</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/fire/firestore'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@Component</span><span class="pun">({...})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AppComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="kwd">private</span><span class="pln"> dialog</span><span class="pun">:</span><span class="pln"> </span><span class="typ">MatDialog</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">private</span><span class="pln"> store</span><span class="pun">:</span><span class="pln"> </span><span class="typ">AngularFirestore</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نحدِّث بعد ذلك الطريقة التي نهيئ بها مصفوفات الحاويات:
</p>

<ul>
<li>
		الملف src/app/app.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6880_114" style="">
<span class="pun">...</span><span class="pln">

</span><span class="lit">@Component</span><span class="pun">({...})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AppComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  todo </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">store</span><span class="pun">.</span><span class="pln">collection</span><span class="pun">(</span><span class="str">'todo'</span><span class="pun">).</span><span class="pln">valueChanges</span><span class="pun">({</span><span class="pln"> idField</span><span class="pun">:</span><span class="pln"> </span><span class="str">'id'</span><span class="pln"> </span><span class="pun">})</span><span class="pln"> </span><span class="kwd">as</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Task</span><span class="pun">[]&gt;;</span><span class="pln">
  inProgress </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">store</span><span class="pun">.</span><span class="pln">collection</span><span class="pun">(</span><span class="str">'inProgress'</span><span class="pun">).</span><span class="pln">valueChanges</span><span class="pun">({</span><span class="pln"> idField</span><span class="pun">:</span><span class="pln"> </span><span class="str">'id'</span><span class="pln"> </span><span class="pun">})</span><span class="pln"> </span><span class="kwd">as</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Task</span><span class="pun">[]&gt;;</span><span class="pln">
  </span><span class="kwd">done</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">store</span><span class="pun">.</span><span class="pln">collection</span><span class="pun">(</span><span class="str">'done'</span><span class="pun">).</span><span class="pln">valueChanges</span><span class="pun">({</span><span class="pln"> idField</span><span class="pun">:</span><span class="pln"> </span><span class="str">'id'</span><span class="pln"> </span><span class="pun">})</span><span class="pln"> </span><span class="kwd">as</span><span class="pln"> </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Task</span><span class="pun">[]&gt;;</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	استخدمنا هنا <code>AngularFirestore</code> للحصول على محتوى التجميعة من قاعدة البيانات مباشرةً، ولاحظ أنّ <code>valueChanges</code> يُعيد <a href="https://rxjs.dev/guide/observable" rel="external nofollow"><code>observables</code></a> عوضًا عن مصفوفة، ولاحظ أيضًا أننا حدَّدنا أن حقل <code>id</code> للمستندات في هذه التجميعة يجب أن يُسمى أيضًا <code>id</code> ليطابق الاسم الذي استخدمناه في الواجهة <code>Task</code>؛ أما <code>observables</code> التي أُعيدت عن طريق <code>valueChanges</code> فستُصدر تجميعةً من المهام في أي وقت تتغير فيه.
</p>

<p>
	نظرًا لأننا نتعامل مع المرصودات observables بدلًا من المصفوفات فأنت بحاجة إلى تحديث طريقة إضافة المهام وإزالتها وتعديلها، وأيضًا تحديث الوظيفة المسؤولة عن نقل المهام بين الحاويات وعوضًا عن تغيير المصفوفات في الذاكرة ستستخدم Firebase SDK لتحديث البيانات في قاعدة البيانات، وسنرى كيف ستبدو الشيفرة بعد إعادة ترتيبها، لذا استبدل التابع <code>drop</code> الموجود في ملف <code>src/app/app.component.ts</code> بدايةً:
</p>

<ul>
<li>
		الملف src/app/app.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6880_117" style="">
<span class="pln">drop</span><span class="pun">(</span><span class="pln">event</span><span class="pun">:</span><span class="pln"> </span><span class="typ">CdkDragDrop</span><span class="pun">&lt;</span><span class="typ">Task</span><span class="pun">[]&gt;):</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">event</span><span class="pun">.</span><span class="pln">previousContainer </span><span class="pun">===</span><span class="pln"> event</span><span class="pun">.</span><span class="pln">container</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> item </span><span class="pun">=</span><span class="pln"> event</span><span class="pun">.</span><span class="pln">previousContainer</span><span class="pun">.</span><span class="pln">data</span><span class="pun">[</span><span class="pln">event</span><span class="pun">.</span><span class="pln">previousIndex</span><span class="pun">];</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">store</span><span class="pun">.</span><span class="pln">firestore</span><span class="pun">.</span><span class="pln">runTransaction</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> promise </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">all</span><span class="pun">([</span><span class="pln">
      </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">store</span><span class="pun">.</span><span class="pln">collection</span><span class="pun">(</span><span class="pln">event</span><span class="pun">.</span><span class="pln">previousContainer</span><span class="pun">.</span><span class="pln">id</span><span class="pun">).</span><span class="pln">doc</span><span class="pun">(</span><span class="pln">item</span><span class="pun">.</span><span class="pln">id</span><span class="pun">).</span><span class="kwd">delete</span><span class="pun">(),</span><span class="pln">
      </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">store</span><span class="pun">.</span><span class="pln">collection</span><span class="pun">(</span><span class="pln">event</span><span class="pun">.</span><span class="pln">container</span><span class="pun">.</span><span class="pln">id</span><span class="pun">).</span><span class="pln">add</span><span class="pun">(</span><span class="pln">item</span><span class="pun">),</span><span class="pln">
    </span><span class="pun">]);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> promise</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
  transferArrayItem</span><span class="pun">(</span><span class="pln">
    event</span><span class="pun">.</span><span class="pln">previousContainer</span><span class="pun">.</span><span class="pln">data</span><span class="pun">,</span><span class="pln">
    event</span><span class="pun">.</span><span class="pln">container</span><span class="pun">.</span><span class="pln">data</span><span class="pun">,</span><span class="pln">
    event</span><span class="pun">.</span><span class="pln">previousIndex</span><span class="pun">,</span><span class="pln">
    event</span><span class="pun">.</span><span class="pln">currentIndex
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنزيل المهمة من التجميعة الأولى ونضيفها إلى التجميعة الثانية وذلك من أجل نقل مهمة من الحاوية الحالية إلى الحاوية الهدف، وبما أننا نؤدي هاتين العمليتين ونريد أن تظهرا على أساس عملية واحدة -أي جعل العملية ذَرية-، فسننفِّذها عن طريق معاملة Firestore، كما سنحدِّث تابع <code>editTask</code> حتى نتمكن من استخدام <code>Firestore</code>، لذا سنحتاج إلى تغيير الأسطر التالية من الشيفرة بداخل المعالج الخاص بمربع الإغلاق::
</p>

<ul>
<li>
		الملف src/app/app.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6880_119" style="">
<span class="pun">...</span><span class="pln">
dialogRef</span><span class="pun">.</span><span class="pln">afterClosed</span><span class="pun">().</span><span class="pln">subscribe</span><span class="pun">((</span><span class="pln">result</span><span class="pun">:</span><span class="pln"> </span><span class="typ">TaskDialogResult</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="kwd">delete</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">store</span><span class="pun">.</span><span class="pln">collection</span><span class="pun">(</span><span class="pln">list</span><span class="pun">).</span><span class="pln">doc</span><span class="pun">(</span><span class="pln">task</span><span class="pun">.</span><span class="pln">id</span><span class="pun">).</span><span class="kwd">delete</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">store</span><span class="pun">.</span><span class="pln">collection</span><span class="pun">(</span><span class="pln">list</span><span class="pun">).</span><span class="pln">doc</span><span class="pun">(</span><span class="pln">task</span><span class="pun">.</span><span class="pln">id</span><span class="pun">).</span><span class="pln">update</span><span class="pun">(</span><span class="pln">task</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	نستطيع الوصول إلى المستند المستهدَف والمقابل للمهمة التي نريد التعديل عليها باستخدام Firestore SDK ويمكنك حذفه أو تحديثه، ونحتاج أخيرًا إلى تحديث التابع المسؤول عن إنشاء مهام جديدة من خلال استبدال السطر <code>this.todo.push('task')‎</code> بالسطر <code>this.store.collection('todo').add(result.task)‎</code>.
</p>

<p>
	لاحظ أن التجميعات الآن ليست مصفوفات وإنما مرصودات observables، ونحتاج إلى تحديث قالب <code>AppComponent</code> فقط لكي نتمكَّن من تصوّرهم عن طريق استبدال كل وصول إلى خصائص <code>todo</code> و<code>inProgress</code> و<code>done</code> بالخصائص التالية <code>todo | async</code> و<code>inProgress | async</code> و<code>done | async</code> على التتالي، كما يشترك أنبوب async تلقائيًا في المرصودات observables المرتبطة بالتجميعات، وتُشغِّل Angular تلقائيًا الكشف عن حدوث التغييرات ومعالجة المصفوفة المصدَرة عندما تُصدِر المرصودات قيمةً جديدةً، وفيما يلي التغييرات التي ستجريها في حاوية <code>todo</code> على سبيل المثال:
</p>

<ul>
<li>
		الملف src/app/app.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6880_121" style="">
<span class="tag">&lt;mat-card</span><span class="pln">
  </span><span class="atn">cdkDropList</span><span class="pln">
  </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"todo"</span><span class="pln">
  #</span><span class="atn">todoList</span><span class="pun">=</span><span class="atv">"cdkDropList"</span><span class="pln">
  [</span><span class="atn">cdkDropListData</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"todo | async"</span><span class="pln">
  [</span><span class="atn">cdkDropListConnectedTo</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"[doneList, inProgressList]"</span><span class="pln">
  (</span><span class="atn">cdkDropListDropped</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"drop($event)"</span><span class="pln">
  </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"list"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;p</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"empty-label"</span><span class="pln"> *</span><span class="atn">ngIf</span><span class="pun">=</span><span class="atv">"(todo | async)?.length === 0"</span><span class="tag">&gt;</span><span class="pln">Empty list</span><span class="tag">&lt;/p&gt;</span><span class="pln">
  </span><span class="tag">&lt;app-task</span><span class="pln"> (</span><span class="atn">edit</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"editTask('todo', $event)"</span><span class="pln"> *</span><span class="atn">ngFor</span><span class="pun">=</span><span class="atv">"let task of todo | async"</span><span class="pln"> </span><span class="atn">cdkDrag</span><span class="pln"> [</span><span class="atn">task</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"task"</span><span class="tag">&gt;&lt;/app-task&gt;</span><span class="pln">
</span><span class="tag">&lt;/mat-card&gt;</span></pre>

<p>
	نطبِّق أنبوب async عندما نمرِّر البيانات إلى <a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%8A%D9%87-routing-%D9%81%D9%8A-angularjs-r225/" rel="">الموجّه</a> <code>cdkDropList</code>، حيث تفعل الشيء ذاته أيضًا داخل الموجِّه <code>‎*ngIf</code> ولكن لاحظ أننا نستخدِم أيضًا تسلسلًا اختياريًا -والذي يُعرّف أيضًا باسم معامِل التنقل الآمن في Angular- عندما نحاول الوصول إلى خاصية <code>length</code> لضمان عدم حصولنا على خطأ وقت التشغيل runtime error في حال لم تكن قيمة <code>todo | async</code> إما <code>null</code> أو <code>undefined</code>، والآن يجب أن تشاهد شيئًا يشبه الصورة التالية عندما تنشئ مهمةً جديدةً في واجهة المستخدِم وتفتح Firestore:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="84264" href="https://academy.hsoub.com/uploads/monthly_2021_12/017.png.b276a607c9a2ea7564fe5dd7826bd932.png" rel=""><img alt="017.png" class="ipsImage ipsImage_thumbnailed" data-fileid="84264" data-unique="kh5nm4g3r" src="https://academy.hsoub.com/uploads/monthly_2021_12/017.thumb.png.bb4136f1bd5fcae2348d7a91044de8b4.png" style="width: 750px; height: auto;"></a>
</p>

<h2>
	10. تحسين عمليات التحديث بشكل أفضل
</h2>

<p>
	سنؤدي حاليًا عمليات التحديث في التطبيق الخاص بك بصورة أفضل، حيث يوجد لدينا مصدر خدمة في Firestore، ولكن في الوقت نفسه لدينا نسخًا محليةً من المهام، وبالتالي عندما تُصدر أي مرصودات مرتبطة بالتجميعات أيَّ تغيير، فسنحصل على مصفوفة من المهام، وعندما يُغير إجراء المستخدِم من الحالة، فسنحدِّث القيم المحلية أولًا ثم نرسِل عملية التغيير إلى Firestore؛ أما عندما ننقل مهمةً من حاوية إلى أخرى، فسنستدعي <code>transferArrayItem</code> التي تعمل على النسخ المحلية من المصفوفات والتي تمثِّل المهام الموجودة في كل حاوية.
</p>

<p>
	يعامل Firebase SDK هذه المصفوفات على أنها غير قابلة للتغيير، مما يعني أنه سنحصل على نسخ جديدة منها أيضًا والتي ستعيد الحالة السابقة قبل نقل المهمة في المرة القادمة التي تُشغّل فيها Angular الكاشف عن حدوث تغييرات، والوقت نفسه نشغِّل التحديث في Firestore ويشغِّل Firebase SDK عملية التحديث بالقيم الصحيحة، وبالتالي ستُعرَض واجهة المستخدِم بالصورة الصحيحة، وبهذه الطريقة تُنقَل المهمة من القائمة الأولى إلى القائمة الثانية كما توضحه الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="84265" href="https://academy.hsoub.com/uploads/monthly_2021_12/018.gif.abf3d0774c7241e2a844d09a92e8c5fe.gif" rel=""><img alt="018.gif" class="ipsImage ipsImage_thumbnailed" data-fileid="84265" data-unique="gyrrv4njx" src="https://academy.hsoub.com/uploads/monthly_2021_12/018.thumb.gif.48053b688afb703780c57721f1fe5a62.gif" style="width: 500px; height: auto;"></a>
</p>

<p>
	تختلف الطريقة الصحيحة لحل هذه المشكلة من تطبيق لآخر، ولكن نحتاج في جميع الحالات إلى الحفاظ على حالة ثابتة ريثما تُحدَّث البيانات، كما يمكننا الاستفادة من المزايا التي يقدمها <a href="http://rxjs.dev/api/index/class/BehaviorSubject" rel="external nofollow"><code>BehaviorSubject</code></a> والذي يغلِّف observer الأصلي الذي نتلقاه من <code>valueChanges</code>، ولكن ما يحدث في الحقيقة هو أنَّ <code>BehaviorSubject</code> يحتفظ بمصفوفة قابلة للتغيير والتي تتلقى عمليات التحديث من <code>transferArrayItem</code>، فكل ما عليك فعله لإصلاح هذه المشكلة هو تحديث ملف <code>AppComponent</code>:
</p>

<ul>
<li>
		الملف src/app/app.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6880_127" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">AngularFirestore</span><span class="pun">,</span><span class="pln"> </span><span class="typ">AngularFirestoreCollection</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/fire/firestore'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">BehaviorSubject</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'rxjs'</span><span class="pun">;</span><span class="pln">


</span><span class="kwd">const</span><span class="pln"> getObservable </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">collection</span><span class="pun">:</span><span class="pln"> </span><span class="typ">AngularFirestoreCollection</span><span class="pun">&lt;</span><span class="typ">Task</span><span class="pun">&gt;)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> subject </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">BehaviorSubject</span><span class="pun">&lt;</span><span class="typ">Task</span><span class="pun">[]&gt;([]);</span><span class="pln">
  collection</span><span class="pun">.</span><span class="pln">valueChanges</span><span class="pun">({</span><span class="pln"> idField</span><span class="pun">:</span><span class="pln"> </span><span class="str">'id'</span><span class="pln"> </span><span class="pun">}).</span><span class="pln">subscribe</span><span class="pun">((</span><span class="pln">val</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Task</span><span class="pun">[])</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    subject</span><span class="pun">.</span><span class="pln">next</span><span class="pun">(</span><span class="pln">val</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> subject</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="lit">@Component</span><span class="pun">(...)</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AppComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  todo </span><span class="pun">=</span><span class="pln"> getObservable</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">store</span><span class="pun">.</span><span class="pln">collection</span><span class="pun">(</span><span class="str">'todo'</span><span class="pun">))</span><span class="pln"> as </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Task</span><span class="pun">[]&gt;;</span><span class="pln">
  inProgress </span><span class="pun">=</span><span class="pln"> getObservable</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">store</span><span class="pun">.</span><span class="pln">collection</span><span class="pun">(</span><span class="str">'inProgress'</span><span class="pun">))</span><span class="pln"> as </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Task</span><span class="pun">[]&gt;;</span><span class="pln">
  done </span><span class="pun">=</span><span class="pln"> getObservable</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">store</span><span class="pun">.</span><span class="pln">collection</span><span class="pun">(</span><span class="str">'done'</span><span class="pun">))</span><span class="pln"> as </span><span class="typ">Observable</span><span class="pun">&lt;</span><span class="typ">Task</span><span class="pun">[]&gt;;</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	كل ما نفعله في مقتطف الشيفرة أعلاه هو إنشاء <code>BehaviorSubject</code> يُصدر قيمةً في كل مرة تتغير قيمة observable المرتبطة مع التجميعة، فالآن أصبح كل شيء يعمل كما يجب لأن <code>BehaviorSubject</code> يعيد استخدام المصفوفة خلال استدعاءات الكشف عن حدوث تغييرات ويحدِّث فقط عندما نحصل على قيمة جديدة من Firestore.
</p>

<h2>
	11. نشر التطبيق
</h2>

<p>
	كل ما علينا فعله لنشر التطبيق الخاص بنا هو تنفيذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6880_129" style="">
<span class="pln">ng deploy</span></pre>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		<strong>ملاحظة</strong>: يجب عليك توفير تكوين Firebase في بيئة الإنتاج الخاصة بك وذلك ضمن ملف <code>src/environment/environment.prod.ts</code>.
	</p>
</blockquote>

<p>
	سوف يفعل هذا الأمر ما يلي:
</p>

<ul>
<li>
		يبني التطبيق الخاص بك باستخدام تكوين الإنتاج وبتطبيق التحسينات خلال عملية الترجمة.
	</li>
	<li>
		ينشر التطبيق الخاص بك إلى استضافة Firebase.
	</li>
	<li>
		يعطيك عنوان URL حتى تتمكن من معاينة النتيجة النهائية.
	</li>
</ul>
<h2>
	نهاية المشروع
</h2>

<p>
	تهانينا لقد نجحت في بناء لوحة kanban باستخدام Angular وFirebase.
</p>

<p>
	لقد أنشأت واجهة مستخدِم بثلاثة أعمدة تمثِّل حالات المهام المختلفة، كما نفَّذت عمليتي السحب والإفلات للمهام بين الأعمدة باستخدام Angular CDK، ثمَّ بنيت استمارةً لإنشاء مهام جديدة وحرَّرت المهام الموجودة باستخدام Angular Material، وبعد ذلك تعلّمت كيفية استخدام <code>‎@angular/fire</code> ونقلت حالة التطبيق الخاص بك جميعها إلى Firestore، وأخيرًا نشرت تطبيقك إلى استضافة Firebase.
</p>

<p>
	تذكَّر أنك نشرت التطبيق باستخدام تكوينات الاختبار ولذلك تأكد من ضبط السماحيات الصحيحة قبل نشر التطبيق الخاص بك للإنتاج، حيث يمكنك معرفة كيفية القيام بذلك من <a href="https://firebase.google.com/docs/firestore/security/get-started" rel="external nofollow">هنا</a>، كما أنك لم تأخذ بالحسبان ترتيب المهام الفردية في الحاوية نفسها في التطبيق الذي أنشأته، حيث يمكنك استخدام حقل مخصص للترتيب في المستند الخاص بالمهمة لإضافة ذلك وترتيب المهام بناءً عليه، وبالإضافة إلى ذلك بنيت لوحة kanban من أجل مستخدِم واحد فقط، مما يعني أنه لدينا لوحة kanban واحدةً لأي شخص يستخدِم التطبيق، كما ستحتاج إلى تغيير هيكل قاعدة البيانات الخاصة بك من أجل تضمين لوحات منفصلة لمستخدِمِين آخرِين.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://developers.google.com/codelabs/building-a-web-app-with-angular-and-firebase" rel="external nofollow">Building a web application with Angular and Firebase</a> من موقع <a href="https://developers.google.com/" rel="external nofollow">developers.google.com</a> الرسمي.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-angular-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1380/" rel="">كيفية استعمال Angular في بناء تطبيقات الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-angular-%D9%88%D9%86%D8%B4%D8%B1%D9%87%D8%A7-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1388/" rel="">تهيئة بيئة تطبيقات Angular ونشرها على الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%8A%D9%87%D8%A7%D8%AA-directives-%D9%81%D9%8A-angularjs-r202/" rel="">التوجيهات (Directives) في AngularJS</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1394</guid><pubDate>Wed, 15 Dec 2021 06:35:21 +0000</pubDate></item><item><title>&#x645;&#x642;&#x62F;&#x645;&#x629; &#x641;&#x64A; &#x645;&#x641;&#x627;&#x647;&#x64A;&#x645; Angular</title><link>https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-angular-r1393/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_12/61aa5d6653962_-----Angular-----Angular-HTML-TypeScript-----Angular-----De.png.947a03cebc83900c4264b7fee35d74c8.png" /></p>

<p>
	تُعَدّ <a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D8%A7-%D9%87%D9%8A-angular%D8%9F-r1379/" rel="">Angular</a> منصةً وإطارًا لبناء تطبيقات الصفحة الواحدة من جهة العميل عن طريق استخدام لغة <a href="http://wiki.hsoub.com/HTML" rel="external">HTML</a> ولغة <a href="https://wiki.hsoub.com/TypeScript" rel="external">TypeScript</a>، حيث أنَّ Angular مكتوبة باستخدام لغة TypeScript، كما تُضمِّن Angular الوظائف الأساسية والاختيارية على أساس مجموعة من مكتبات <a href="https://academy.hsoub.com/programming/javascript/typescript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-typescript-r1214/" rel="">TypeScript</a> التي تستوردها بعد ذلك إلى تطبيقاتك.
</p>

<p>
	تعتمد بنية تطبيق Angular على بعض المفاهيم الأساسية، حيث أنَّ اللبنات الأساسية لإطار Angular هي المكونات التي تُنظَّم ضمن <code>NgModule</code> في التطبيق، إذ يُعرَّف تطبيق Angular على أنه مجموعة من الوحدات المعرفة ضمن المزخرف <code>‎@NgModule</code> (المزخرف هو الذي يأتي المحرف @ قبله)، كما يحتوي التطبيق دائمًا على وحدة جذر تسمى root module واحدة على الأقل تمكّن من إجراء عملية تشغيل تمهيدية للتطبيق وتدير التطبيق ككل وعادًة ما تحتوي على الكثير من الوحدات التي تقدم ميزات للتطبيق تسمى feature modules.
</p>

<p>
	تُعرِّف المكونات طرق العرض والتي تُعَدّ مجموعات من عناصر الشاشة التي تستطيع Angular الاختيار بينها وتعديلها وفقًا لمنطق البرنامج والبيانات الخاصة بك، وتستخدِم المكونات الخدمات التي توفر وظائفًا محددةً لا ترتبط مباشرًة بطرق العرض، حيث يمكن حقن مزودِي الخدمات في المكونات على أساس تبعيات، وهذا يجعل الشيفرة الخاصة بك معياريةً وفعالةً وقابلةً لإعادة الاستخدام.
</p>

<p>
	تُعَدّ الوحدات modules والمكونات conponents والخدمات services أصنافًا تستخدِم المزخرفات decorators، حيث تحدِّد هذه المزخرفات نوعها وتوفّر بيانات وصفية تخبر Angular عن كيفية استخدامها.
</p>

<p>
	تربط <a href="https://academy.hsoub.com/programming/html/html5/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D8%B5%D9%81%D9%8A%D8%A9-microdata-%D9%81%D9%8A-html5-r375/" rel="">البيانات الوصفية</a> لصنف مكوِّن هذا المكون مع قالب template يُعرِّف قسم العرض الخاص به، حيث يجمع القالب بين لغة HTML والتوجيهات في Angular والإضافات المسؤولة عن عملية التربيط التي تساعد Angular في تصيير وإخراج صفحة HTML من أجل عرضها، كما توفر البيانات الوصفية لصنف الخدمة المعلومات التي تحتاج إليها Angular لجعلها متاحةً للمكونات الأخرى عن طريق حقن التبعية dependency injection -أو DI اختصارًا.
</p>

<p>
	تعرّف مكونات التطبيق العديد من أقسام العرض والتي تكون مرتبةً هرميًا، ولذلك توفِّر Angular خدمة التوجيه عبر الموجه Router لمساعدتك على تحديد مسارات التنقل بين هذه الأقسام، حيث يوفر الموجه إمكانيات تنقل متطورة في المتصفح.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		تستطيع الاطلاع على <a href="https://angular.io/generated/live-examples/architecture/stackblitz.html" rel="external nofollow">المثال في بيئة تجريبية</a> / <a href="https://angular.io/generated/zips/architecture/architecture.zip" rel="external nofollow">تنزيل المثال</a> لتحصل على عينة التطبيق الذي يشرحه هذا المقال.
	</p>
</blockquote>

<h2>
	الوحدات Modules
</h2>

<p>
	تُعَدّ وحدات NgModules الموجودة في Angular تتمةً لوحدات ES2015 في <a href="https://wiki.hsoub.com/JavaScript" rel="external">JavaScript</a>، إلا أنها تختلف عنها، حيث يصرح المزخرف <code>NgModule</code> عن سياق تجميع لمجموعة من المكونات المخصصة لمجال معين أو لسير عمل ما أو مجموعة من الإمكانيات المرتبطة ببعضها ارتباطًا وثيقًا، كما يستطيع NgModule ربط المكونات المعرفة فيه بشيفرات ذات صلة مثل الخدمات بهدف تشكيل وحدات وظيفية.
</p>

<p>
	يحتوي كل تطبيق Angular على وحدة جذر تسمى <code>AppModule</code> والتي توفر آلية التكوين المسؤولة عن تشغيل التطبيق الذي يحتوي على العديد من الوحدات الوظيفية عادًة، ويمكن أن تستورد وحدات NgModule وظائفًا من وحدات NgModule أخرى كما في وحدات JavaScript، كما تسمح بتصدير الوظائف الخاصة بها واستخدامها في وحدات أخرى، إذ يجب عليك مثلًا استيراد خدمة <code>Router</code> من أجل استخدام خدمة التوجيه في التطبيق الخاص بك.
</p>

<p>
	يساعد تنظيم الشيفرة الخاصة بك ضمن وحدات وظيفية مميزة في عملية إدارة تطوير التطبيقات المعقدة وفي تسهيل عملية التصميم من أجل إعادة الاستخدام، كما تتيح لك هذه التقنية الاستفادة من عملية التحميل الكسول lazy-loading، أي تحميل الوحدات عند الطلب لتقليل كمية الشيفرات البرمجية التي يجب تحميلها عند بدء التشغيل.
</p>

<h2>
	المكونات components
</h2>

<p>
	يحتوي كل تطبيق Angular على مكوِّن واحد على الأقل وهو المكوِّن الجذر الذي يربط التسلسل الهرمي للمكون بنموذج كائن المستند DOM، حيث يُعرِّف كل مكون صنفًا يحتوي على بيانات التطبيق والمنطق ويرتبط بقالب HTML يحدِّد طريقة العرض في البيئة المستهدفة، كما يحدِّد المزخرف <a href="https://angular.io/api/core/Component" rel="external nofollow"><code>‎@Component()‎</code></a> الصنف الموجود أسفله مباشرًة على أساس مكوِّن ويوفِّر القالب والبيانات الوصفية الخاصة به.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		توضيح: تُعَدّ المزخرفات دوالًا تُعدّل أصناف JavaScript، وتُعرِّف Angular عددًا من المزخرفات التي تربط أنواعًا محددةً من البيانات الوصفية بالأصناف، وبالتالي يعرف النظام ما الذي تعنيه هذه الأصناف وكيف يجب أن تعمل، وعادة ما يسبق المزخرف المحرف @.
	</p>
</blockquote>

<h3>
	القوالب والتوجيهات وربط البيانات
</h3>

<p>
	يجمع القالب بين عناصر HTML وصياغة Angular التي تستطيع تعديل عناصر HTML قبل عرضها، كما تزودنا التوجيهات الخاصة بالقالب بمنطق عمل البرنامج، في حين يربط ترميز التربيط بيانات التطبيق الخاص بك مع <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-dom-r644/" rel="">DOM</a>.
</p>

<p>
	يوجد نوعان من صياغة الربط:
</p>

<ul>
<li>
		يتيح لك التربيط عبر الحدث الاستجابة لمدخلات المستخدِم في البيئة المستهدَفة عن طريق تحديث بيانات التطبيق الخاص بك.
	</li>
	<li>
		يتيح لك التربيط عبر الخاصية إدخال القيم المحسوبة من بيانات التطبيق الخاص بك إلى عناصر HTML.
	</li>
</ul>
<p>
	تقيّم Angular التوجيهات وتحل السياق المرتبط بالقالب لتعديل عناصر HTML وDOM وفقًا لبيانات برنامجك والمنطق قبل إظهار قسم العرض، كما تدعم Angular تربيط البيانات باتجاهَين، مما يعني انعكاس التغييرات التي تحدث في DOM مثل خيارات المستخدِم في بيانات البرنامج الخاص بك أيضًا، كما يمكن للقوالب الخاصة بك استخدام الأنابيب من أجل تحسين تجربة المستخدِم عن طريق تحويل القيم قبل عرضها، حيث يمكنك مثلًا استخدام الأنابيب لعرض التواريخ وقيم العملات عرضًا يناسب البيئة المحلية للمستخدِم، إذ توفِّر Angular أنابيبًا pipes معرّفةً مسبقًا من أجل إجراء تحويلات عليها ويمكنك تعريف الأنابيب الخاصة بك أيضًا.
</p>

<h2>
	الخدمات وحقن التبعية
</h2>

<p>
	إذا أدرت مشاركة بيانات أو منطق ما غير مرتبط بقسم عرض معين عبر المكونات فبإمكانك إنشاء صنف خدمة بحيث يُسبق التعريف الخاص بصنف الخدمة بالمزخرف ‎<a href="https://angular.io/api/core/Injectable" rel="external nofollow"><code>@Injectable()‎</code></a>، حيث يوفِّر هذا المزخرِف البيانات الوصفية التي تمكّن مزودي الخدمة الآخرين من حقنهم على أساس تبعيات في الصنف الخاص بك، ويتيح لك حقن التبعية DI الحفاظ على أصناف المكونات الخاصة بك بصورة بسيطة وفعالة، حيث أنَّ المكونات لا تجلب البيانات من الخادم أو تتحقق من صحة إدخال المستخدِم أو تطبع على الطرفية مباشرةً، ولإنما تفوِّض هذه المهام إلى الخدمات.
</p>

<h3>
	التوجيه
</h3>

<p>
	يوفِّر لك وحدات Angular الجاهزة خدمة التوجيه عبر الوحدة <a href="https://angular.io/api/router/Router" rel="external nofollow"><code>Router</code></a> التي تتيح لك تعريف وجهات محددة في تطبيقك والتنقل في مختلف مساراتها مهما كانت متداخلة وعرض القوالب المعرَّفة في تلك المسارات حيث نمذجت واجهة التنقل Router لتعمل بآلية عمل التنقل نفسه والخاص بالمتصفِّحات المعروفة:
</p>

<ul>
<li>
		أدخِل عنوان URL في شريط العنوان وسينقلك المتصفِّح إلى الصفحة التابعة لهذا العنوان.
	</li>
	<li>
		انقر على الروابط في الصفحة وسينقلك المتصفِّح إلى صفحة أخرى جديدة.
	</li>
	<li>
		انقر على أزرار العودة إلى الخلف أو الانتقال إلى الأمام الخاصة بالمتصفح وسينقلك المتصفِّح إلى الوراء أو إلى الأمام اعتمادًا على تاريخ الصفحات التي زرتها.
	</li>
</ul>
<p>
	يعيِّن جهاز التوجيه مسارات تشبه عناوين URL إلى أقسام العرض بدلًا من الصفحات وعندما يجري المستخدِم إجراءً معيَّنًا مثل النقر على رابط ما والذي يحمِّل صفحةً جديدةً في المتصفح، فيعترض هنا جهاز التوجيه سلوك المتصفح ويعرض له أو يخفي عنه التسلسل الهرمي للعرض، فإذا حدَّد جهاز التوجيه أن حالة التطبيق الحالية تتطلب وظائفًا معينةً ولم تُحمَّل الوحدة التي تحدِّد هذه الوظائف، فيمكن لجهاز التوجيه تحميل الوحدة تحميلًا بطيئًا عند الطلب.
</p>

<p>
	يعترض جهاز التوجيه عنوان URL للرابط وفقًا لقواعد التنقل التي حددتها لأقسام العرض الخاصة بتطبيقك وحالة البيانات، حيث يمكنك الانتقال إلى طرق عرض جديدة عندما ينقر المستخدِم على زر أو يختار أحد الخيارات الموجودة في قائمة منسدلة أو في استجابة لبعض التنبيهات القادمة من أي مصدر آخر، حيث يسجِّل جهاز التوجيه النشاط في تاريخ المتصفِّح، وبالتالي تعمل أزرار العودة إلى الخلف أو الانتقال إلى الأمام خلال عملية التنقل أيضًا.
</p>

<p>
	يجب عليك ربط مسارات التنقل بمكوناتك من أجل تعريف قواعد التنقل، حيث يستخدِم المسار بنيةً تشبه عنوان URL، وقد يدمج به بيانات التطبيق الخاصة بك بالطريقة نفسها التي تدمج بها بنية القالب أقسام العرض الخاصة بك مع بيانات تطبيقك، كما يمكنك بعد ذلك تطبيق منطق معين في البرنامج لاختيار أي من أقسام العرض يجب إظهارها أو إخفاؤها على أساس استجابة لإدخال المستخدِم وقواعد الوصول التي عرَّفتها.
</p>

<h2>
	الخاتمة
</h2>

<p>
	تعلمت الأساسيات حول اللبنات الأساسية التي يتكون منها تطبيق Angular، ويوضِّح الرسم البياني التالي كيف ترتبط هذه المكونات الأساسية مع بعضها.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="84246" href="https://academy.hsoub.com/uploads/monthly_2021_12/001.png.7026a120959570e03e302e107c874b45.png" rel=""><img alt="001.png" class="ipsImage ipsImage_thumbnailed" data-fileid="84246" data-unique="tib9ktbyd" src="https://academy.hsoub.com/uploads/monthly_2021_12/001.png.7026a120959570e03e302e107c874b45.png"></a>
</p>

<ul>
<li>
		يعرِّف كل من المكوِّن والقالب قسم العرض في Angular.
	</li>
	<li>
		يضيف المزخرِف إلى صنف المكوِّن بيانات وصفيةً تتضمن مؤشرًا إلى القالب المرتبط بهذا المكوِّن.
	</li>
	<li>
		تعدِّل التوجيهات وصياغة التربيط في قالب المكون قسم العرض استنادًا إلى بيانات التطبيق والمنطق.
	</li>
	<li>
		يوفِّر حاقن التبعية الخدمات لمكوِّن ما مثل خدمة الموجِّه التي تتيح لك تعريف التنقل بين أقسام العرض.
	</li>
</ul>
<p>
	أصبحت الآن جاهزًا لتتعلم المزيد عن الأدوات والتقنيات المتاحة لمساعدتك في إنشاء تطبيقات Angular ونشرها.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://angular.io/guide/architecture" rel="external nofollow">Introduction to Angular concepts</a> من موقع <a href="angular.io" rel="">angular.io</a> الرسمي.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-angular-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1380/" rel="">كيفية استعمال Angular في بناء تطبيقات الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%AA%D9%86%D9%82%D9%84-%D9%88%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-angular-r1382/" rel="">إضافة التنقل وإدارة البيانات في تطبيق Angular</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D8%A8%D8%A7%D8%AF%D8%A6-angularjs-r176/" rel="">مبادئ AngularJS</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1393</guid><pubDate>Wed, 08 Dec 2021 16:01:00 +0000</pubDate></item><item><title>&#x62A;&#x647;&#x64A;&#x626;&#x629; &#x628;&#x64A;&#x626;&#x629; &#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A; Angular &#x648;&#x646;&#x634;&#x631;&#x647;&#x627; &#x639;&#x644;&#x649; &#x627;&#x644;&#x648;&#x64A;&#x628;</title><link>https://academy.hsoub.com/programming/javascript/angular/%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-angular-%D9%88%D9%86%D8%B4%D8%B1%D9%87%D8%A7-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1388/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_12/61a74149571f0_---Angular-------Angular-HTML-TypeScript----------.png.5d2a686317d43b9d8070e53cd8d09a27.png" /></p>

<p>
	سنتعلم في هذا المقال كيفية تهيئة البيئة الأولية لتطبيقات Angular وإعدادها وتوليد ملفات التطبيق الأساسية عبر واجهة سطر أوامر Angular المعروفة بالاسم Angular CLI ثم سنشرح بعدها كيفية تهيئة تطبيق Angular للنشر على إحدى الاستضافات أو الخوادم.
</p>

<p>
	سنستفيد من هذا المقال في تعلم كيفية نشر تطبيق التجارة الإلكترونية الذي بنيناه في المقالات السابقة، وسنبدأ في الخطوة الأولى في تعلم كيفية إنشاء بيئة محلية وتوليد الملفات الأولية الأساسية لأي تطبيق Angular. إن لم تكون تابعت تلك المقالات، فننصح بالرجوع إليها وقراءتها وتطبيقها أولًا ثم إكمال قراءة هذا المقال الذي يعتمد عليها.
</p>

<p>
	يُعَدّ هذا المقال جزءًا من سلسلة مقالات حول بناء تطبيق تجارة إلكترونية عبر Angular:
</p>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D8%A7-%D9%87%D9%8A-angular%D8%9F-r1379/" rel="">ما هي Angular؟</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-angular-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1380/" rel="">كيفية استعمال Angular في بناء تطبيقات الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%AA%D9%86%D9%82%D9%84-%D9%88%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-angular-r1382/" rel="">إضافة التنقل وإدارة البيانات في تطبيق Angular</a>
	</li>
	<li>
		تهيئة بيئة تطبيقات Angular ونشرها على الويب
	</li>
</ul>
<h2>
	إعداد البيئة المحلية ومساحة العمل
</h2>

<p>
	يشرح هذا القسم كيفية إعداد بيئتك الخاصة بالتطوير باستخدام Angular عن طريق أداة <a href="https://angular.io/cli" rel="external nofollow">Angular CLI</a> والتي تتضمن معلومات حول المتطلبات الأساسية وطريقة تثبيت CLI وإنشاء مساحة عمل أولية وتطبيق أولي وتشغيل هذا التطبيق محليًا للتحقق من صحة إعداد البيئة.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		ملاحظة: إذا كنت وافدًا جديدًا على Angular للمرة الأولى، فسننصحك بقراءة مقال كيفية استعمال Angular في بناء تطبيقات الويب المشار إليه أعلاه، حيث يقدِّم لك هذا المقال أساسيات Angular من خلال إنشاء تطبيق تجارة إلكترونية، والذي يعتمد على بيئة StackBlitz التفاعلية المستخدَمة في التطوير عن طريق الإنترنت بحيث أنك لن تحتاج إلى إعداد البيئة المحلية الخاصة بك حتى تكون مستعدًا لذلك.
	</p>
</blockquote>

<h3>
	المتطلبات الأساسية
</h3>

<p>
	يجب أن تكون على معرفة بما يلي لكي تتمكن من استخدام إطار Angular:
</p>

<ul>
<li>
		<a href="https://wiki.hsoub.com/JavaScript" rel="external">JavaScript</a>
	</li>
	<li>
		<a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a>
	</li>
	<li>
		<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a>
	</li>
</ul>
<p>
	تُعَدّ معرفة <a href="https://wiki.hsoub.com/TypeScript/" rel="external">TypeScript</a> مفيدةً أيضًا ولكنها غير مطلوبة، وتحتاج إلى المتطلبات التالية لتثبيت Angular على نظامك المحلي:
</p>

<h4>
	Node.js
</h4>

<p>
	يتطلب Angular نسخةً طويلة الدعم ومستقرةً من <a href="https://wiki.hsoub.com/Node.js/synopsis/" rel="external">Node.js</a>.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		راجع الكلمة المفتاحية <code>engines</code> في ملف <a href="https://unpkg.com/browse/@angular/core/package.json" rel="external nofollow">package.json</a> لمعلومات أكثر حول متطلبات الإصدار المحددة.
	</p>
</blockquote>

<p>
	يمكنك الاطلاع على <a href="https://nodejs.org/" rel="external nofollow">nodejs.org</a> لمزيد من المعلومات حول تثبيت Node.js، وإذا كنت غير متأكد من إصدار Node.js الذي يعمل على النظام لديك، فشغِّل الأمر <code>node -v</code> في نافذة الطرفية فقط.
</p>

<h4>
	مدير حزم npm
</h4>

<p>
	تعتمد كل من Angular CLI وتطبيقات Angular على <a href="https://academy.hsoub.com/programming/javascript/%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-r1225/" rel="">مدير الحزم npm</a> من أجل استخدام العديد من الميزات والوظائف، وتحتاج أنت إلى مدير حزمة npm لتحميل وتثبيت حزم npm، كما يستخدم هذا الدليل واجهة سطر أوامر لعميل npm والتي تُثَبَّت مع Node.js افتراضيًا، ويمكنك تشغيل الأمر <code>npm -v</code> في نافذة طرفية لتتحقق من تثبيت عميل npm على جهازك.
</p>

<h3>
	تثبيت واجهة سطر الأوامر Angular CLI
</h3>

<p>
	يمكنك استخدام Angular CLI لإنشاء المشاريع وتوليد شيفرات التطبيق واستيراد المكتبات، وتنفيذ مجموعة متنوعة من المهام المتعلقة بعملية التطوير، مثل الاختبار والتجميع والنشر، لذا افتح نافذةً طرفيةً ما ونفِّذ الأمر التالي لتثبيت Angular CLI:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_5542_14" style="">
<span class="pln">npm install </span><span class="pun">-</span><span class="pln">g </span><span class="lit">@angular</span><span class="pun">/</span><span class="pln">cli</span></pre>

<h3>
	إنشاء مساحة عمل وتوليد الملفات الأولية لتطبيق Angular
</h3>

<p>
	يمكنك تطوير التطبيقات الخاصة بك ضمن مساحة عمل Angular، ولإنشاء مساحة عمل جديدة وتطبيق أولي اتبع ما يلي:
</p>

<p>
	أولًا، نفِّذ الأمر التالي <code>ng new</code> واختر الاسم <code>my-app</code> على أساس اسم للمشروع كما هو موضَّح:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_5542_16" style="">
<span class="pln">   ng </span><span class="kwd">new</span><span class="pln"> </span><span class="kwd">my</span><span class="pun">-</span><span class="pln">app</span></pre>

<p>
	ثانيًا، يطالبك الأمر <code>ng new</code> بمعلومات حول الميزات التي تريد إضافتها للتطبيق الأولي، كما اقبل بالإعدادات الافتراضية عن طريق الضغط على مفتاح Enter أو Return.
</p>

<p>
	يثبِّت سطر أوامر Angular حزم npm الضرورية لعمل Angular والتبعيات الأخرى حيث من الممكن أن تستغرق هذه العملية بضع دقائق، وتنشئ واجهة سطر الأوامر CLI مساحة عمل جديدة وتطبيق ترحيب بسيط جاهز للعمل.
</p>

<h3>
	تشغيل تطبيق Angular
</h3>

<p>
	يتضمن Angular CLI خادم لتتمكن من بناء وتخديم التطبيق الخاص بك محليًا.
</p>

<ol>
<li>
		انتقل إلى مجلد مساحة العمل مثل my-app.
	</li>
	<li>
		نفِّذ الأمر التالي:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_5542_18" style="">
<span class="pln">cd </span><span class="kwd">my</span><span class="pun">-</span><span class="pln">app
ng serve </span><span class="pun">--</span><span class="pln">open</span></pre>

<p>
	يُشغِّل الأمر <code>ng serve</code> الخادم ومراقبة ملفاتك وإعادة بناء تطبيقك في حال حدوث تغييرات على هذه الملفات، كما يفتح <code>‎--open</code> (أو فقط <code>‎-o</code>) متصفحك تلقائيًا ويوجّهك إلى العنوان <code><a href="http://localhost:4200/%E2%80%8E" ipsnoembed="true" rel="external nofollow">http://localhost:4200/‎</a></code>، وإذا قمت بعملية التثبيت والإعداد بصورة صحيحة، فستشاهد صفحةً مشابهةً للصفحة التالية على شاشة المتصفح الخاص بك.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="83779" href="https://academy.hsoub.com/uploads/monthly_2021_12/001.png.ef9ffc2ae4094c3f863785865c97c2de.png" rel=""><img alt="001.png" class="ipsImage ipsImage_thumbnailed" data-fileid="83779" data-unique="fki3t1hum" src="https://academy.hsoub.com/uploads/monthly_2021_12/001.thumb.png.b5906d37bda793c6adc5080a2dc1cb2d.png" style="width: 500px; height: auto;"></a>
</p>

<p>
	لنأخذ الآن مثالًا عمليًا ونشغل تطبيق <a href="https://academy.hsoub.com/design/general/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%85%D8%AA%D8%AC%D8%B1-%D9%88%D9%85%D9%88%D9%82%D8%B9-%D8%AA%D8%AC%D8%A7%D8%B1%D8%A9-%D8%A5%D9%84%D9%83%D8%AA%D8%B1%D9%88%D9%86%D9%8A%D8%A9-%D8%B9%D9%84%D9%89-%D8%B4%D8%A8%D9%83%D8%A9-%D8%A7%D9%84%D8%A5%D9%86%D8%AA%D8%B1%D9%86%D8%AA-r439/" rel="">التجارة الإلكترونية</a> الذي بنيناه في مقالات سابقة، إذ بنينا التطبيق على منصة StackBlitz من <a href="https://stackblitz.com/angular/xamroalrvyvx?file=src%2Fapp%2Fapp.component.ts" rel="external nofollow">هذه الملفات الأولية</a>.
</p>

<p>
	لنبدأ أولًا بتشغيل التطبيق الخاص بك محليًا باتباع الخطوات التالية:
</p>

<p>
	انقر على أيقونة تنزيل المشروع الموجودة في القائمة اليسارية مقابل كلمة مشروع Project لتنزيل شيفرة المصدر من مشروع StackBlitz الخاص بك الذي عملت عليه في المقالات السابقة.
</p>

<p>
	أنشأ مساحة عمل جديدة عن طريق الأمر <code>ng new</code> كما تعلمت سابقًا، حيث أنَّ <code>my-project-name</code> هو الاسم الذي تريد تسمية مشروعك به:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_5542_21" style="">
<span class="pln">ng </span><span class="kwd">new</span><span class="pln"> </span><span class="kwd">my</span><span class="pun">-</span><span class="pln">project</span><span class="pun">-</span><span class="pln">name</span></pre>

<p>
	يعرض هذا الأمر مجموعة من الأسئلة التي تدخل في عملية ضبط التطبيق وسنقوم في هذا المقال بالإبقاء على الإعدادات الافتراضية لكل سؤال.
</p>

<p>
	استبدل مجلد <code>‎/src</code> في تطبيقك الذي قمت بإنشائه للتو بمجلد <code>‎/src</code> الذي قمت بتنزيله من StackBlitz.
</p>

<p>
	ثانيًا، استخدم أمر CLI التالي لتشغيل التطبيق محليًا:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_5542_23" style="">
<span class="pln">ng serve</span></pre>

<p>
	ثالثًا، انتقل إلى العنوان <a href="http://localhost:4200" ipsnoembed="true" rel="external nofollow">http://localhost:4200</a> لاستعراض التطبيق الخاص بك في المتصفح، فإذا لم يكن المنفذ الافتراضي 4200 متوفرًا، فيمكنك تحديد منفذ آخر عن طريق استخدام راية المنفذ كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_5542_25" style="">
<span class="pln">ng serve </span><span class="pun">--</span><span class="pln">port </span><span class="lit">4201</span></pre>

<p>
	يمكنك تحرير الشيفرة ورؤية التغييرات أثناء تخديم التطبيق الخاص بك عن طريق عملية التحديث التلقائي التي يقوم بها المتصفح، كما يمكنك إيقاف أمر <code>ng serve</code> فقط بالضغط على الاختصار <code>Ctrl</code>+ <code>c</code>.
</p>

<h2>
	بناء تطبيق Angular ونشره على الويب
</h2>

<p>
	وصلنا إلى المرحلة الأخيرة وهي بناء تطبيق Angular ونشره على الإنترنت.
</p>

<p>
	أولًا، استخدم الأمر <code>build</code> لبناء التطبيق الخاص بك لعملية الإنتاج، حيث يستخدِم هذا الأمر إعدادات البناء الخاصة ببيئة الإنتاج <code>production</code>.
</p>

<pre class="ipsCode">
ng build
</pre>

<p>
	يُنشئ هذا الأمر مجلد <code>dist</code> في الدليل الجذر للتطبيق، حيث يحوي جميع الملفات التي تحتاجها <a href="https://academy.hsoub.com/devops/linux/%D9%85%D9%82%D8%AF%D9%91%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D8%AE%D8%AA%D8%B5%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B7%D8%B1%D9%81%D9%8A%D8%A9-aliases-%D9%88%D8%AF%D9%88%D8%A7%D9%84%D9%87%D8%A7-%D8%A3%D9%86%D8%AC%D8%B2-%D9%85%D9%87%D8%A7%D9%85%D9%83-%D8%A8%D8%B3%D8%B1%D8%B9%D8%A9-r65/" rel="">خدمة الاستضافة</a> لتخديم التطبيق الخاص بك.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		إذا رمى الأمر <code>ng build</code> خطأً حول حزم مفقودة، فألحِق التبعيات المفقودة في ملف <code>package.json</code> الخاص بمشروعك المحلي بحيث تتلاءم مع التبعيات الموجودة في المشروع الذي حُمِّل من موقع StackBlitz.
	</p>
</blockquote>

<p>
	ثانيًا، انسخ محتويات المجلد <code>dist/my-project-name</code> إلى خادم الويب الخاص بك، ونظرًا لأن هذه الملفات ثابتة، فيمكنك استضافتها على أي خادم ويب قادر على تخديم مثل هذا النوع من الملفات مثل <code>Node.js</code> أو Java أو ‎.NET أو أي واجهة خلفية مثل <a href="https://firebase.google.com/docs/hosting" rel="external nofollow">Firebase</a> أو <a href="https://cloud.google.com/solutions/web-hosting" rel="external nofollow">Google Cloud</a> أو <a href="https://cloud.google.com/appengine/docs/standard/python/getting-started/hosting-a-static-website" rel="external nofollow">App Engine</a>.
</p>

<h2>
	الخطوة التالية
</h2>

<p>
	أعددنا في هذا المقال بيئةً محليةً لتطبيق Angular، وتعلّمنا كيفية تشغيل التطبيق لبدء عملية تطويره، ثم تعلمنا في النهائية كيفية بناء التطبيق وتهيئته للنشر، كما طبَّقنا ذلك على تطبيق التجارة الإلكترونية الذي بنيناه سابقًا، وأصبحت الآن تملك فكرةً عامةً عن Angular وأصبحت جاهزًا لبدء احتراف برمجة تطبيقات Angular.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://angular.io/start/start-deployment#deploying-an-application" rel="external nofollow">Deploying an application</a> والمقال <a href="https://angular.io/guide/setup-local#setting-up-the-local-environment-and-workspace" rel="external nofollow">Setting up the local environment and workspace</a> من موقع <a href="angular.io" rel="">angular.io</a> الرسمي.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-angular-r1393/" rel="">مقدمة في مفاهيم Angular</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%AA%D9%86%D9%82%D9%84-%D9%88%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-angular-r1382/" rel="">إضافة التنقل وإدارة البيانات في تطبيق Angular</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-angular-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1380/" rel="">كيفية استعمال Angular في بناء تطبيقات الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r662/" rel="">تعلم البرمجة</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1388</guid><pubDate>Thu, 02 Dec 2021 16:03:00 +0000</pubDate></item><item><title>&#x625;&#x636;&#x627;&#x641;&#x629; &#x627;&#x644;&#x62A;&#x646;&#x642;&#x644; &#x648;&#x625;&#x62F;&#x627;&#x631;&#x629; &#x627;&#x644;&#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; &#x641;&#x64A; &#x62A;&#x637;&#x628;&#x64A;&#x642; Angular</title><link>https://academy.hsoub.com/programming/javascript/angular/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%AA%D9%86%D9%82%D9%84-%D9%88%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-angular-r1382/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_11/61a1432389105_------Angular-----Angular-HTML-TypeScript---------A.png.59bdec26e178d1a37bfffed198a39b36.png" /></p>

<p>
	بُني هذا المقال على الخطوة الأولى من سلسلة المقالات التعليمية لتعلم استعمال Angular عبر بناء تطبيق تجارة إلكترونية، حيث يحتوي تطبيق المتجر عبر الانترنت في هذه المرحلة من التطوير على قائمة المنتجات الأساسية، كما ستضيف في الأقسام التالية الميزات التالية إلى التطبيق:
</p>

<ul>
<li>
		اكتب <a href="https://academy.hsoub.com/marketing/performance-marketing/analytics/%D8%A8%D9%86%D9%8A%D8%A9-%D9%85%D8%B3%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-url-%D9%88%D8%A3%D9%87%D9%85%D9%8A%D8%AA%D9%87%D8%A7-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%B2%D9%8A%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D9%88%D9%82%D8%B9-r159/" rel="">عنوان URL</a> في شريط العنوان للانتقال إلى صفحة المنتج المقابلة له.
	</li>
	<li>
		انقر فوق الروابط الموجودة في الصفحة للتنقل داخل تطبيق الصفحة الواحدة الخاص بك.
	</li>
	<li>
		انقر على زرَّي الانتقال للخلف والأمام للمتصفح للتنقل في سجل المتصفح تلقائيًا.
	</li>
	<li>
		تحديث قسم العرض الخاص بتفاصيل المنتج ليتضمن زر <strong>الشراء</strong> الذي يضيف المنتج الحالي إلى قائمة منتجات تديرها خدمة سلة التسوق.
	</li>
	<li>
		إضافة مكوِّن السلة الذي يعرض العناصر الموجودة في السلة.
	</li>
	<li>
		إضافة مكوِّن الشحن الذي يعيد أسعار الشحن للعناصر الموجودة في السلة باستخدام <a href="https://angular.io/api/common/http/HttpClient" rel="external nofollow"><code>HttpClient</code></a> الخاص بـ Angular لاستعادة بيانات الشحن من ملف <code>json.</code>.
	</li>
</ul>
<p>
	هذه المقال جزءٌ من سلسلة مقالات حول بناء تطبيق تجارة إلكترونية عبر Angular:
</p>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D8%A7-%D9%87%D9%8A-angular%D8%9F-r1379/" rel="">ما هي Angular؟</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-angular-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1380/" rel="">كيفية استعمال Angular في بناء تطبيقات الويب</a>
	</li>
	<li>
		إضافة التنقل وإدارة البيانات في تطبيق Angular
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-angular-%D9%88%D9%86%D8%B4%D8%B1%D9%87%D8%A7-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1388/" rel="">تهيئة بيئة تطبيقات Angular ونشرها على الويب</a>
	</li>
</ul>
<h2>
	ربط مسار URL مع مكون
</h2>

<p>
	يستخدم التطبيق الموجِّه Router من Angular للانتقال إلى المكوِّن <code>ProductListComponent</code> افتراضيًا، حيث يوضِّح هذا القسم كيفية تعريف الوجهة لعرض تفاصيل المنتجات بصورة فردية.
</p>

<p>
	نبدأ أولًا بتوليد مكون جديد باسم <code>product-details</code> لعرض تفاصيل المنتج وذلك بتنفيذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_918_9" style="">
<span class="pln">ng generate component product</span><span class="pun">-</span><span class="pln">details</span></pre>

<p>
	بعد ذلك أضف وجهة لتفاصيل المنتج في <code>app.module.ts</code> من خلال استخدام <code>path</code> مع القيمة <code>products/:productId</code> وضبط <code>component</code> إلى المكون <code>ProductDetailsComponent</code>.
</p>

<ul>
<li>
		الملف src/app/app.module.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_18" style="">
<span class="lit">@NgModule</span><span class="pun">({</span><span class="pln">
  imports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="typ">BrowserModule</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">ReactiveFormsModule</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">RouterModule</span><span class="pun">.</span><span class="pln">forRoot</span><span class="pun">([</span><span class="pln">
      </span><span class="pun">{</span><span class="pln"> path</span><span class="pun">:</span><span class="pln"> </span><span class="str">''</span><span class="pun">,</span><span class="pln"> component</span><span class="pun">:</span><span class="pln"> </span><span class="typ">ProductListComponent</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln"> path</span><span class="pun">:</span><span class="pln"> </span><span class="str">'products/:productId'</span><span class="pun">,</span><span class="pln"> component</span><span class="pun">:</span><span class="pln"> </span><span class="typ">ProductDetailsComponent</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">])</span><span class="pln">
  </span><span class="pun">],</span></pre>

<p>
	افتح <code>product-list.component.html</code>، وعدِّل المِربط الخاص باسم المنتج ليتضمن <a href="https://angular.io/api/router/RouterLink" rel="external nofollow"><code>routerLink</code></a> مع استخدام <code>product.id</code> على أساس معامِل.
</p>

<ul>
<li>
		الملف src/app/product-list/product-list.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_918_20" style="">
<span class="tag">&lt;div</span><span class="pln"> *</span><span class="atn">ngFor</span><span class="pun">=</span><span class="atv">"let product of products"</span><span class="tag">&gt;</span><span class="pln">

  </span><span class="tag">&lt;h3&gt;</span><span class="pln">
    </span><span class="tag">&lt;a</span><span class="pln"> [</span><span class="atn">title</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"product.name + ' details'"</span><span class="pln"> [</span><span class="atn">routerLink</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"['/products', product.id]"</span><span class="tag">&gt;</span><span class="pln">
      {{ product.name }}
    </span><span class="tag">&lt;/a&gt;</span><span class="pln">
  </span><span class="tag">&lt;/h3&gt;</span><span class="pln">

</span><span class="com">&lt;!-- . . . --&gt;</span><span class="pln">

</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	يساعدك الموجِّه <code>RouterLink</code> على تخصيص الرابط، حيث يحتوي الموجه أو العنوان في هذه الحالة على جزء واحد ثابت هو <code>‎/products</code>، ويكون الجزء الأخير متغيرًا يتمثَّل في إدراج الخاصية <code>id</code> للمنتج الحالي، حيث سيكون العنوان URL مثلًا لمنتج يملك المُعرِّف 1 مشابهًا للعنوان <code><a href="https://getting-started-myfork.stackblitz.io/products/1" ipsnoembed="false" rel="external nofollow">https://getting-started-myfork.stackblitz.io/products/1</a></code>
</p>

<p>
	أخيرًا، تحقّق من عمل الموجِّه على النحو المطلوب من خلال النقر على اسم المنتج، حيث يجب أن يعرض التطبيق المكوِّن <code>ProductDetailsComponent</code> والذي سيعرض رسالة "product-details works" أي صفحة تفاصيل المنتج تعمل!، ولاحظ أنَّ العنوان في نافذة المعاينة يتغير، كما يكون الجزء الأخير هو <code>products/#‎</code> حيث تمثّل <code>#</code> رقم المسار الذي نقرت عليه.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="83450" href="https://academy.hsoub.com/uploads/monthly_2021_11/007.png.db2bc54f8f0b94f8543ce1cdadd992ae.png" rel=""><img alt="007.png" class="ipsImage ipsImage_thumbnailed" data-fileid="83450" data-unique="5dv5i8uns" src="https://academy.hsoub.com/uploads/monthly_2021_11/007.png.db2bc54f8f0b94f8543ce1cdadd992ae.png"></a>
</p>

<h2>
	عرض تفاصيل المنتج
</h2>

<p>
	يعالِج <code>ProductDetailsComponent</code> طريقة عرض كل منتج، يعرض موجِّه Angular المكوِّنات اعتمادًا على عنوان المتصفح والمسارات التي حددتها، كما ستستخدِم في هذا القسم موجِّه Angular لدمج بيانات المنتجات <code>products</code> ومعلومات المسار لعرض التفاصيل الخاصة لكل منتج.
</p>

<p>
	بدايةً، استورد <a href="https://angular.io/api/router/ActivatedRoute" rel="external nofollow"><code>ActivatedRoute</code></a> من <code>‎@angular/router</code> ومصفوفة المنتجات <code>products</code> من <code>‎../products</code> في ملف <code>product-details.component.ts</code>.
</p>

<ul>
<li>
		الملف src/app/product-details/product-details.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_22" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Component</span><span class="pun">,</span><span class="pln"> </span><span class="typ">OnInit</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">ActivatedRoute</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/router'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Product</span><span class="pun">,</span><span class="pln"> products </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'../products'</span><span class="pun">;</span></pre>

<p>
	حدِّد خاصية <code>product</code>.
</p>

<ul>
<li>
		الملف src/app/product-details/product-details.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_918_30" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ProductDetailsComponent</span><span class="pln"> </span><span class="kwd">implements</span><span class="pln"> </span><span class="typ">OnInit</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  product</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Product</span><span class="pun">|</span><span class="kwd">undefined</span><span class="pun">;</span><span class="pln">
  </span><span class="com">/* ... */</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	احقن <code>ActivatedRoute</code> في الباني <code>constructor()‎</code> عن طريق إضافة <code>private route: ActivatedRoute</code> على أساس وسيط ضمن أقواس الباني.
</p>

<ul>
<li>
		الملف src/app/product-details/product-details.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_918_28" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ProductDetailsComponent</span><span class="pln"> </span><span class="kwd">implements</span><span class="pln"> </span><span class="typ">OnInit</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  product</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Product</span><span class="pun">|</span><span class="kwd">undefined</span><span class="pun">;</span><span class="pln">

  constructor</span><span class="pun">(</span><span class="kwd">private</span><span class="pln"> route</span><span class="pun">:</span><span class="pln"> </span><span class="typ">ActivatedRoute</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span></pre>

<p>
	يوجد صنف يدعى <code>ActivatedRoute</code> لكل مكوِّن يحمِّله موجّه Angular بحيث يحتوي على معلومات حول الموجه route ومعامِلاته، كما تُهيّئ المكوِّن ليستخدِم خدمة عن طريق حقن <code>ActivatedRoute</code>، وسنغطي ذلك في خطوة إدارة البيانات بتفصيل أكثر.
</p>

<p>
	استخرج <code>productId</code> من معامِلات المسار في دالة <code>ngOnInit()‎</code> واعثر على المنتج المقابل له ضمن مصفوفة <code>products</code>.
</p>

<ul>
<li>
		الملف src/app/product-details/product-details.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_32" style="">
<span class="pln">ngOnInit</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// First get the product id from the current route.</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> routeParams </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">route</span><span class="pun">.</span><span class="pln">snapshot</span><span class="pun">.</span><span class="pln">paramMap</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> productIdFromRoute </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Number</span><span class="pun">(</span><span class="pln">routeParams</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'productId'</span><span class="pun">));</span><span class="pln">

  </span><span class="com">// Find the product that correspond with the id provided in route.</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">product </span><span class="pun">=</span><span class="pln"> products</span><span class="pun">.</span><span class="pln">find</span><span class="pun">(</span><span class="pln">product </span><span class="pun">=&gt;</span><span class="pln"> product</span><span class="pun">.</span><span class="pln">id </span><span class="pun">===</span><span class="pln"> productIdFromRoute</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تقابل معامِلات المسار متغيرات المسار التي حدَّدتها في المسار، حيث نستخدِم <code>route.snapshot</code> للوصول إلى معاملات الموجه والذي يُعَدّ <a href="https://angular.io/api/router/ActivatedRouteSnapshot" rel="external nofollow"><code>ActivatedRouteSnapshot</code></a> ويحوي معلومات حول الموجه النشط في لحظة معينة من الزمن، كما يوفِّر العنوان الذي يطابق موجه <code>productId</code>، حيث يستخدِم Angular معرف المنتج <code>productId</code> لعرض تفاصيل كل منتج فريد.
</p>

<p>
	حدِّث قالب <code>ProductDetailsComponent</code> لعرض تفاصيل المنتج باستخدام <a href="https://angular.io/api/common/NgIf" rel="external nofollow">‎*ngIf</a>، فإذا كان المنتج منتج موجودًا، فسيعرض محتوى <a href="https://wiki.hsoub.com/HTML/div" rel="external"><code>&lt;div&gt;</code></a> مع اسم وسعر وتوصيف المنتج.
</p>

<ul>
<li>
		الملف src/app/product-details/product-details.component.html
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_918_40" style="">
<span class="tag">&lt;h2&gt;</span><span class="pln">Product Details</span><span class="tag">&lt;/h2&gt;</span><span class="pln">

</span><span class="tag">&lt;div</span><span class="pln"> *</span><span class="atn">ngIf</span><span class="pun">=</span><span class="atv">"product"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;h3&gt;</span><span class="pln">{{ product.name }}</span><span class="tag">&lt;/h3&gt;</span><span class="pln">
  </span><span class="tag">&lt;h4&gt;</span><span class="pln">{{ product.price | currency }}</span><span class="tag">&lt;/h4&gt;</span><span class="pln">
  </span><span class="tag">&lt;p&gt;</span><span class="pln">{{ product.description }}</span><span class="tag">&lt;/p&gt;</span><span class="pln">

</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	يستخدِم السطر <code>&lt;h4&gt;{{ product.price | currency }}&lt;/h4&gt;</code> <a href="https://angular.io/guide/pipes" rel="external nofollow">الأنابيب</a> العملة <code>currency</code> لتحويل قيمة <code>product.price</code> من عدد إلى سلسلة نصية بصيغة عملة، حيث يُعَدّ الأنبوب طريقةً يمكنك من خلالها تحويل البيانات الموجودة في قالب <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> الخاص بك.
</p>

<p>
	عندما ينقر المستخدمون على اسم في قائمة المنتجات، ينقلهم الموجِّه إلى العنوان المميز للمنتج ويعرِض <code>ProductDetailsComponent</code> ويعرض تفاصيل المنتج.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="83451" href="https://academy.hsoub.com/uploads/monthly_2021_11/008.png.71bb2e3a2b48a22ae014a4b3cc0b441b.png" rel=""><img alt="008.png" class="ipsImage ipsImage_thumbnailed" data-fileid="83451" data-unique="fmtdxh0ra" src="https://academy.hsoub.com/uploads/monthly_2021_11/008.png.71bb2e3a2b48a22ae014a4b3cc0b441b.png"></a>
</p>

<p>
	يحتوي تطبيق المتجر في هذه المرحلة من التطوير على فئات المنتجات مع قسمي عرض هما قائمة المنتجات وتفاصيل المنتج، حيث يمكن للمستخدِمين النقر على اسم المنتج من قائمة المنتجات لمشاهدة تفاصيله في قسم عرض جديد مع رابط مميز أو مسار، وسنكمل إضافة ميزة سلة التسوق وإدارة بياناتها للتطبيق.
</p>

<h2>
	إنشاء خدمة سلة التسوق
</h2>

<p>
	تُعَدّ الخدمة service في Angular نسخةً من صنف تجعله متاحًا لأي جزء من أجزاء التطبيق الخاص بك باستخدام نظام حقن التبعية في Angular، كما يمكن للمستخدمين حاليًا عرض معلومات المنتج، ويمكن للتطبيق أن يحاكي المشاركة والإشعارات في حال حدوث تغييرات في المنتج، وتكون الخطوة التالية هي بناء طريقة تمكِّن المستخدِمين من إضافة منتجات إلى السلة، حيث يرشدك هذا المقال من خلال عملية إضافة زر <strong>الشراء</strong> وإعداد خدمة السلة لتخزين المعلومات حول المنتجات ضمن السلة.
</p>

<h3>
	تعريف خدمة السلة
</h3>

<p>
	لإنشاء خدمة السلة، استعمل الطرفية ونفذ الأمر التالي لتوليد خدمة باسم <code>cart</code>:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_918_38" style="">
<span class="pln">ng generate service cart</span></pre>

<ul>
<li>
		الملف src/app/cart.service.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_52" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Injectable</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@Injectable</span><span class="pun">({</span><span class="pln">
  providedIn</span><span class="pun">:</span><span class="pln"> </span><span class="str">'root'</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CartService</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  constructor</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

</span><span class="pun">}</span></pre>

<p>
	استورد واجهة <code>Product</code> من <code>‎./products.ts</code> ضمن <code>cart.service.ts</code>، وعرّف خاصيةً باسم <code>items</code> في صنف <code>CartService</code> لتخزين مصفوفة المنتجات الحالية الموجودة في سلة التسوق.
</p>

<ul>
<li>
		الملف src/app/cart.service.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_918_50" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Product</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'./products'</span><span class="pun">;</span><span class="pln">
</span><span class="pun">/*</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">*/</span><span class="pln">
export </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CartService</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  items</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Product</span><span class="pun">[]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
</span><span class="pun">/*</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">*/</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عرِّف التوابع المسؤولة عن إضافة عناصر إلى سلة التسوق وإعادة عناصر سلة التسوق ومسح عناصر سلة التسوق.
</p>

<ul>
<li>
		الملف src/app/cart.service.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_55" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CartService</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  items</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Product</span><span class="pun">[]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
</span><span class="com">/* . . . */</span><span class="pln">

  addToCart</span><span class="pun">(</span><span class="pln">product</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Product</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">items</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">product</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  getItems</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">items</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  clearCart</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">items </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">items</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="com">/* . . . */</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	شرح التوابع السابقة المعرفة:
</p>

<ul>
<li>
		<code>addToCart()‎</code>: يضيف منتجًا إلى مصفوفة <code>items</code>.
	</li>
	<li>
		<code>getItems()‎</code>: يجمِّع العناصر التي يضيفها المستخدمون إلى سلة التسوق ويُعيد كل عنصر مع الكمية المرتبطة به.
	</li>
	<li>
		<code>clearCart()‎</code>: يعيد مصفوفةً فارغةً من العناصر، أي أنه يُفرِّغ السلة من العناصر الموجودة فيها.
	</li>
</ul>
<h3>
	استخدم خدمة السلة
</h3>

<p>
	سنبرمج الآن عملية إضافة منتج إلى السلة عن طريق استخدام الخدمة <code>CartService</code>.
</p>

<p>
	أولًا، استورد خدمة السلة في ملف <code>product-details.component.ts</code>.
</p>

<ul>
<li>
		الملف src/app/product-details/product-details.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_57" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Component</span><span class="pun">,</span><span class="pln"> </span><span class="typ">OnInit</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">ActivatedRoute</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/router'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Product</span><span class="pun">,</span><span class="pln"> products </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'../products'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">CartService</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'../cart.service'</span><span class="pun">;</span></pre>

<p>
	ثانيًا، احقن خدمة السلة عن طريق إضافتها إلى الباني <code>constructor()‎</code>.
</p>

<ul>
<li>
		الملف src/app/product-details/product-details.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_59" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ProductDetailsComponent</span><span class="pln"> implements </span><span class="typ">OnInit</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> route</span><span class="pun">:</span><span class="pln"> </span><span class="typ">ActivatedRoute</span><span class="pun">,</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> cartService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">CartService</span><span class="pln">
  </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ثالثًا، عرِّف التابع <code>addToCart()‎</code> الذي تضيف المنتج الحالي إلى سلة التسوق.
</p>

<ul>
<li>
		الملف src/app/product-details/product-details.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_61" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ProductDetailsComponent</span><span class="pln"> implements </span><span class="typ">OnInit</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  addToCart</span><span class="pun">(</span><span class="pln">product</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Product</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">cartService</span><span class="pun">.</span><span class="pln">addToCart</span><span class="pun">(</span><span class="pln">product</span><span class="pun">);</span><span class="pln">
    window</span><span class="pun">.</span><span class="pln">alert</span><span class="pun">(</span><span class="str">'Your product has been added to the cart!'</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يفعل التابع <code>addToCart()‎</code> ما يلي:
</p>

<ul>
<li>
		يأخذ المنتج الحالي <code>product</code> على أساس وسيط.
	</li>
	<li>
		يستخدِم التابع <code>addToCart()‎</code> الموجود ضمن <code>CartService</code> لإضافة المنتج إلى السلة.
	</li>
	<li>
		يعرض رسالةً تخبرنا بأنه قد أُضيف منتج إلى السلة.
	</li>
</ul>
<p>
	رابعًا، أضف زر <strong>الشراء</strong> في ملف <code>product-details.component.html</code> واربط الحدث <code>click()‎</code> إلى التابع <code>addToCart()‎</code>، إذ تحدِّث هذه الشيفرة قالب تفاصيل المنتج بإضافة زر <strong>الشراء</strong> المسؤول عن إضافة المنتج الحالي إلى سلة التسوق.
</p>

<ul>
<li>
		الملف src/app/product-details/product-details.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_918_63" style="">
<span class="tag">&lt;h2&gt;</span><span class="pln">Product Details</span><span class="tag">&lt;/h2&gt;</span><span class="pln">

</span><span class="tag">&lt;div</span><span class="pln"> *</span><span class="atn">ngIf</span><span class="pun">=</span><span class="atv">"product"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;h3&gt;</span><span class="pln">{{ product.name }}</span><span class="tag">&lt;/h3&gt;</span><span class="pln">
  </span><span class="tag">&lt;h4&gt;</span><span class="pln">{{ product.price | currency }}</span><span class="tag">&lt;/h4&gt;</span><span class="pln">
  </span><span class="tag">&lt;p&gt;</span><span class="pln">{{ product.description }}</span><span class="tag">&lt;/p&gt;</span><span class="pln">

  </span><span class="tag">&lt;button</span><span class="pln"> (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"addToCart(product)"</span><span class="tag">&gt;</span><span class="pln">Buy</span><span class="tag">&lt;/button&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	خامسًا، تحقّق من ظهور زر <strong>الشراء</strong> الجديد كما هو متوقَّع من خلال تحديث التطبيق والنقر على اسم المنتج لعرض تفاصيله.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="83444" href="https://academy.hsoub.com/uploads/monthly_2021_11/001.png.2bb30c9cb063403112321231a02c9484.png" rel=""><img alt="001.png" class="ipsImage ipsImage_thumbnailed" data-fileid="83444" data-unique="lhtkz9n98" src="https://academy.hsoub.com/uploads/monthly_2021_11/001.png.2bb30c9cb063403112321231a02c9484.png"></a>
</p>

<p>
	أخيرًا، انقر على زر <strong>الشراء</strong> لإضافة المنتج إلى قائمة العناصر المخزنة في سلة التسوق وعرض رسالة تأكيد.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="83445" href="https://academy.hsoub.com/uploads/monthly_2021_11/002.png.0daa476d807140f3b1f059c12b8cf775.png" rel=""><img alt="002.png" class="ipsImage ipsImage_thumbnailed" data-fileid="83445" data-unique="5zq654ghl" src="https://academy.hsoub.com/uploads/monthly_2021_11/002.png.0daa476d807140f3b1f059c12b8cf775.png"></a>
</p>

<h2>
	إنشاء قسم العرض الخاص بالسلة
</h2>

<p>
	يمكنك إنشاء قسم العرض الخاص بسلة التسوق لكي يتمكن العملاء من رؤية سلة التسوق الخاصة بهم وذلك بخطوتين:
</p>

<ul>
<li>
		أنشئ مكوِّن السلة وهيّئ التوجيه إلى هذا المكوِّن الجديد.
	</li>
	<li>
		اعرض العناصر الموجودة في السلة.
	</li>
</ul>
<h3>
	إعداد مكون السلة
</h3>

<p>
	اتبع الخطوات نفسها التي استخدمتها في إنشاء <code>ProductDetailsComponent</code> من أجل إنشاء قسم العرض الخاص بالسلة وهيِّئ التوجيه لهذا المكون الجديد.
</p>

<p>
	بدايةً، أنشئ مكونًا جديدًا باسم <code>cart</code> يمثل السلة بتنفيذ الأمر التالي في الطرفية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_65" style="">
<span class="pln">ng generate component cart</span></pre>

<p>
	سيولد هذا الأمر الملف cart.component.ts الخاص بالمكون وقالبه وملفات تنسيقاته.
</p>

<ul>
<li>
		الملف src/app/cart/cart.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_67" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Component</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@Component</span><span class="pun">({</span><span class="pln">
  selector</span><span class="pun">:</span><span class="pln"> </span><span class="str">'app-cart'</span><span class="pun">,</span><span class="pln">
  templateUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./cart.component.html'</span><span class="pun">,</span><span class="pln">
  styleUrls</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'./cart.component.css'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CartComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  constructor</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span></pre>

<p>
	ينشئ StackBlitz -الذي ضبطناه في المقال الأول ببداية المشروع- أيضًا تابع <code>ngOnInit()‎</code> في المكونات افتراضيًا، ويمكنك تجاهل <code>ngOnInit()‎</code> الخاصة بالمكون <code>CartComponent</code> في هذا المقال.
</p>

<p>
	لاحظ أن المكون <code>CartComponent</code> المولد حديثًا يضاف إلى قسم التصريحات <code>declarations</code> في الملف app.module.ts.
</p>

<ul>
<li>
		الملف src/app/app.module.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_69" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">CartComponent</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./cart/cart.component'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@NgModule</span><span class="pun">({</span><span class="pln">
  declarations</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="typ">AppComponent</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">TopBarComponent</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">ProductListComponent</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">ProductAlertsComponent</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">ProductDetailsComponent</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">CartComponent</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">],</span></pre>

<p>
	أبقَ في الملف <code>app.module.ts</code> وأضف فيه وجهة لمكون <code>CartComponent</code>، مع تعيين قيمة <code>path</code> إلى القيمة <code>cart</code>.
</p>

<ul>
<li>
		الملف src/app/app.module.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_71" style="">
<span class="lit">@NgModule</span><span class="pun">({</span><span class="pln">
  imports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="typ">BrowserModule</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">ReactiveFormsModule</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">RouterModule</span><span class="pun">.</span><span class="pln">forRoot</span><span class="pun">([</span><span class="pln">
      </span><span class="pun">{</span><span class="pln"> path</span><span class="pun">:</span><span class="pln"> </span><span class="str">''</span><span class="pun">,</span><span class="pln"> component</span><span class="pun">:</span><span class="pln"> </span><span class="typ">ProductListComponent</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln"> path</span><span class="pun">:</span><span class="pln"> </span><span class="str">'products/:productId'</span><span class="pun">,</span><span class="pln"> component</span><span class="pun">:</span><span class="pln"> </span><span class="typ">ProductDetailsComponent</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln"> path</span><span class="pun">:</span><span class="pln"> </span><span class="str">'cart'</span><span class="pun">,</span><span class="pln"> component</span><span class="pun">:</span><span class="pln"> </span><span class="typ">CartComponent</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">])</span><span class="pln">
  </span><span class="pun">],</span></pre>

<p>
	حدِّث زر <strong>الدفع</strong> بحيث يوجِّه إلى الرابط <code>‎/‎‎cart</code>، وأضف موجِّه routerLink يشير إلى <code>‎/cart</code> في ملف <code>top-bar.component.html</code>.
</p>

<ul>
<li>
		الملف src/app/top-bar/top-bar.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_918_75" style="">
<span class="tag">&lt;a</span><span class="pln"> </span><span class="atn">routerLink</span><span class="pun">=</span><span class="atv">"/cart"</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"button fancy-button"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;i</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"material-icons"</span><span class="tag">&gt;</span><span class="pln">shopping_cart</span><span class="tag">&lt;/i&gt;</span><span class="pln">Checkout
</span><span class="tag">&lt;/a&gt;</span></pre>

<p>
	تحقّق من عمل <code>CartComponent</code> كما هو متوقَّع من خلال النقر على زر <strong>الدفع</strong>، حيث يمكنك رؤية النص الافتراضي الذي وضعناه "السلة تعمل!"، ويملك الرابط النمط <code><a href="https://getting-started.stackblitz.io/cart" ipsnoembed="false" rel="external nofollow">https://getting-started.stackblitz.io/cart</a></code>، حيث أنَّ <code>getting-started.stackblitz.io</code> قد تكون مختلفةً في مشروع StackBlitz الخاص بك.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="83446" href="https://academy.hsoub.com/uploads/monthly_2021_11/003.png.538161b9dc81b03aa42d0b67ccba2346.png" rel=""><img alt="003.png" class="ipsImage ipsImage_thumbnailed" data-fileid="83446" data-unique="6senyik85" src="https://academy.hsoub.com/uploads/monthly_2021_11/003.png.538161b9dc81b03aa42d0b67ccba2346.png"></a>
</p>

<h3>
	عرض عناصر السلة
</h3>

<p>
	سنعمل في هذا القسم على عرض المنتجات في السلة عن طريق استخدام خدمة السلة.
</p>

<p>
	بدايةً، استورد <code>CartService</code> في ملف <code>cart.component.ts</code> من ملف <code>cart.service.ts</code>.
</p>

<ul>
<li>
		الملف src/app/cart/cart.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_77" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Component</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">CartService</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'../cart.service'</span><span class="pun">;</span></pre>

<p>
	احقن الخدمة <code>CartService</code> بحيث يتمكن المكون <code>CartComponent</code> من استخدامه عن طريق إضافته إلى الباني <code>constructor()‎</code>.
</p>

<ul>
<li>
		الملف src/app/cart/cart.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_79" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CartComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  constructor</span><span class="pun">(</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> cartService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">CartService</span><span class="pln">
  </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عرِّف الخاصية <code>items</code> لتخزين المنتجات في السلة.
</p>

<ul>
<li>
		الملف src/app/cart/cart.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_82" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CartComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  items </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">cartService</span><span class="pun">.</span><span class="pln">getItems</span><span class="pun">();</span><span class="pln">

  constructor</span><span class="pun">(</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> cartService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">CartService</span><span class="pln">
  </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تحدد هذه الشيفرة العناصر عن طريق تابع <code>getItems()‎</code> الموجودة ضمن <code>CartService</code>، ولقد عرَّفت هذا التابع عندما أنشأت <code>cart.service.ts</code>.
</p>

<p>
	حدِّث قالب السلة مع إضافة ترويسة، واستخدم <code>&lt;div&gt;</code> مع <code>‎*ngFor</code> لعرض كل عنصر من عناصر السلة مع اسمه وسعره، وستكون شيفرة قالب المكون <code>CartComponent</code> كما يلي:
</p>

<ul>
<li>
		الملف src/app/cart/cart.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_918_86" style="">
<span class="tag">&lt;h3&gt;</span><span class="pln">Cart</span><span class="tag">&lt;/h3&gt;</span><span class="pln">

</span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"cart-item"</span><span class="pln"> *</span><span class="atn">ngFor</span><span class="pun">=</span><span class="atv">"let item of items"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;span&gt;</span><span class="pln">{{ item.name }}</span><span class="tag">&lt;/span&gt;</span><span class="pln">
  </span><span class="tag">&lt;span&gt;</span><span class="pln">{{ item.price | currency }}</span><span class="tag">&lt;/span&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	تحقّق من عمل السلة كما يجب:
</p>

<ul>
<li>
		انقر على <strong>My Store.</strong>
	</li>
	<li>
		انقر على اسم المنتج لعرض تفاصيله.
	</li>
	<li>
		انقر زر <strong>الشراء</strong> لإضافة المنتج إلى السلة.
	</li>
	<li>
		انقر فوق زر <strong>الدفع</strong> لرؤية السلة.
	</li>
</ul>
<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="83447" href="https://academy.hsoub.com/uploads/monthly_2021_11/004.png.665276d0b3d52c872edf4f16e222f9f3.png" rel=""><img alt="004.png" class="ipsImage ipsImage_thumbnailed" data-fileid="83447" data-unique="hhsrvizlp" src="https://academy.hsoub.com/uploads/monthly_2021_11/004.png.665276d0b3d52c872edf4f16e222f9f3.png"></a>
</p>

<h2>
	جلب أسعار الشحن
</h2>

<p>
	تُعيد الخوادم البيانات على هيئة مجرى stream غالبًا والتي تُعَدّ مفيدةً لتسهيلها من عملية نقل البيانات المعادة وإجراء تعديلات على الطريقة التي تُطلَب فيها تلك البيانات، كما تعد الوحدة <a href="https://angular.io/api/common/http/HttpClient" rel="external nofollow"><code>HttpClient</code></a> في Angular طريقة أساسية لجلب البيانات من واجهات برمجة تطبيقات خارجية وتوفيرها للتطبيق الخاص بك على أساس مجرى، حيث يُوضِّح هذا القسم لك كيفية استخدام الوحدة <code>HttpClient</code> لاسترجاع أسعار الشحن من ملف خارجي.
</p>

<p>
	يأتي التطبيق الذي ولَّده StackBlitz مع بيانات الشحن بشكل مسبق ضمن ملف <code>assets/shipping.json</code>، كما يمكنك استخدام هذه البيانات لإضافة أسعار الشحن للعناصر الموجودة في السلة.
</p>

<ul>
<li>
		الملف src/assets/shipping.json:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_918_89" style="">
<span class="pun">[</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Overnight"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"price"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">25.99</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"2-Day"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"price"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">9.99</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Postal"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"price"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2.99</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">]</span></pre>

<h3>
	تهيئة AppModule لاستخدام HttpClient
</h3>

<p>
	يجب عليك ضبط التطبيق الخاص بك لاستخدام الوحدة <a href="https://angular.io/api/common/http/HttpClientModule" rel="external nofollow"><code>HttpClientModule</code></a> من أجل استخدام خدمة <code>HttpClient</code> من Angular، حيث يُسجِّل <code>HttpClientModule</code> المزوِّدين الذين يحتاجهم التطبيق الخاص بك لكي يستخدم خدمة <code>HttpClient</code> في جميع ملفات التطبيق.
</p>

<p>
	لنبدأ، أولًا استورد الوحدة <code>HttpClientModule</code> في ملف <code>app.module.ts</code> عن طريق حزمة <code>‎@angular/common/http</code> في الجزء العلوي من الملف مع باقي الاستيرادات، ونظرًا لوجود عدد من الاستيرادات الأخرى، فإن هذه الشيفرة تحذفها للإيجاز، ولذلك تأكد من وضع الاستيرادات الموجودة في مكانها الصحيح.
</p>

<ul>
<li>
		الملف src/app/app.module.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_91" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">HttpClientModule</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/common/http'</span><span class="pun">;</span></pre>

<p>
	ثانيًا، لتسجيل مزوِّد <code>HttpClient</code> ليكون متاحًا في جميع ملفات التطبيق، أضف <code>HttpClientModule</code> إلى ملف <code>AppModule</code> ضمن مصفوفة الاستيرادات <code>imports</code> في <code>‎@NgModule</code>.
</p>

<ul>
<li>
		الملف src/app/app.module.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_93" style="">
<span class="lit">@NgModule</span><span class="pun">({</span><span class="pln">
  imports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="typ">BrowserModule</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">HttpClientModule</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">ReactiveFormsModule</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">RouterModule</span><span class="pun">.</span><span class="pln">forRoot</span><span class="pun">([</span><span class="pln">
      </span><span class="pun">{</span><span class="pln"> path</span><span class="pun">:</span><span class="pln"> </span><span class="str">''</span><span class="pun">,</span><span class="pln"> component</span><span class="pun">:</span><span class="pln"> </span><span class="typ">ProductListComponent</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln"> path</span><span class="pun">:</span><span class="pln"> </span><span class="str">'products/:productId'</span><span class="pun">,</span><span class="pln"> component</span><span class="pun">:</span><span class="pln"> </span><span class="typ">ProductDetailsComponent</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln"> path</span><span class="pun">:</span><span class="pln"> </span><span class="str">'cart'</span><span class="pun">,</span><span class="pln"> component</span><span class="pun">:</span><span class="pln"> </span><span class="typ">CartComponent</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">])</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
  declarations</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="typ">AppComponent</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">TopBarComponent</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">ProductListComponent</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">ProductAlertsComponent</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">ProductDetailsComponent</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">CartComponent</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
  bootstrap</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="typ">AppComponent</span><span class="pln">
  </span><span class="pun">]</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AppModule</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span></pre>

<h3>
	تهيئة الخدمة CartService لاستخدام HttpClient
</h3>

<p>
	تتمثَّل الخطوة التالية في حقن خدمة <code>HttpClient</code> ضمن خدمتك حتى يتمكن تطبيقك من جلب البيانات والتفاعل مع الموارد وواجهات برمجة التطبيقات الخارجية.
</p>

<p>
	استورد خدمة <code>HttpClient</code> من حزمة <code>‎@angular/common/http</code> ضمن ملف <code>cart.service.ts</code>.
</p>

<ul>
<li>
		الملف src/app/cart.service.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_95" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Injectable</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">HttpClient</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/common/http'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Product</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./products'</span><span class="pun">;</span></pre>

<p>
	احقن <code>HttpClient</code> في الباني <code>constructor()‎</code> الخاص بالخدمة <code>CartService</code>.
</p>

<ul>
<li>
		الملف src/app/cart.service.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_97" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CartService</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  items</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Product</span><span class="pun">[]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">

  constructor</span><span class="pun">(</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> http</span><span class="pun">:</span><span class="pln"> </span><span class="typ">HttpClient</span><span class="pln">
  </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
</span><span class="com">/* . . . */</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	تهيئة الخدمة CartService لجلب أسعار الشحن
</h3>

<p>
	يمكنك استخدام تابع <code>get()‎</code> من <code>HttpClient</code> للحصول على بيانات الشحن من ملف <code>shipping.json</code>.
</p>

<p>
	عرِّف تابعًا جديدًا <code>getShippingPrices()‎</code> يستخدِم تابع <code>get()‎</code> من <code>HttpClient</code> ضمن ملف <code>cart.service.ts</code> أسفل تابع <code>clearCart()‎</code>.
</p>

<ul>
<li>
		الملف src/app/cart.service.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_99" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CartService</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="com">/* . . . */</span><span class="pln">
  getShippingPrices</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">http</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">&lt;{</span><span class="pln">type</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">}[]&gt;(</span><span class="str">'/assets/shipping.json'</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	انتهينا من عملية الضبط والتهيئة لجلب أسعار الشحن.
</p>

<h2>
	إنشاء مكون الشحن
</h2>

<p>
	الآن بعدما هيّئت تطبيقك لاسترجاع بيانات الشحن، يمكنك إنشاء مكان لعرض تلك البيانات.
</p>

<p>
	بدايةً، ولِّد مكوِّنًا باسم <code>shipping</code> يمثل عملية الشحن بتنفيذ الأمر التالي في الطرفية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_101" style="">
<span class="pln">ng generate component shipping</span></pre>

<p>
	سيولد هذا الأمر الملف shipping.component.ts الخاص بالمكون وقالبه وملفات تنسيقاته.
</p>

<ul>
<li>
		الملف src/app/shipping/shipping.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_103" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Component</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@Component</span><span class="pun">({</span><span class="pln">
  selector</span><span class="pun">:</span><span class="pln"> </span><span class="str">'app-shipping'</span><span class="pun">,</span><span class="pln">
  templateUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./shipping.component.html'</span><span class="pun">,</span><span class="pln">
  styleUrls</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'./shipping.component.css'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ShippingComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  constructor</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span></pre>

<p>
	أضف مسار للشحن في ملف <code>app.module.ts</code>، واضبط الحقل <code>pah</code> إلى القيمة <code>shipping</code> والحقل <code>component</code> إلى المكون <code>ShippingComponent</code>.
</p>

<ul>
<li>
		الملف src/app/app.module.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_105" style="">
<span class="lit">@NgModule</span><span class="pun">({</span><span class="pln">
  imports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="typ">BrowserModule</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">HttpClientModule</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">ReactiveFormsModule</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">RouterModule</span><span class="pun">.</span><span class="pln">forRoot</span><span class="pun">([</span><span class="pln">
      </span><span class="pun">{</span><span class="pln"> path</span><span class="pun">:</span><span class="pln"> </span><span class="str">''</span><span class="pun">,</span><span class="pln"> component</span><span class="pun">:</span><span class="pln"> </span><span class="typ">ProductListComponent</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln"> path</span><span class="pun">:</span><span class="pln"> </span><span class="str">'products/:productId'</span><span class="pun">,</span><span class="pln"> component</span><span class="pun">:</span><span class="pln"> </span><span class="typ">ProductDetailsComponent</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln"> path</span><span class="pun">:</span><span class="pln"> </span><span class="str">'cart'</span><span class="pun">,</span><span class="pln"> component</span><span class="pun">:</span><span class="pln"> </span><span class="typ">CartComponent</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln"> path</span><span class="pun">:</span><span class="pln"> </span><span class="str">'shipping'</span><span class="pun">,</span><span class="pln"> component</span><span class="pun">:</span><span class="pln"> </span><span class="typ">ShippingComponent</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">])</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
  declarations</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="typ">AppComponent</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">TopBarComponent</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">ProductListComponent</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">ProductAlertsComponent</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">ProductDetailsComponent</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">CartComponent</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">ShippingComponent</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
  bootstrap</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="typ">AppComponent</span><span class="pln">
  </span><span class="pun">]</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AppModule</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	لا يوجد رابط لمكون الشحن الجديد حتى الآن، ولكن يمكنك رؤية قالبه في جزء المعاينة عن طريق إدخال عنوان URL الذي يحدده مساره، حيث يحتوي عنوان URL على النمط الآتي: 
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2038_7" style="">
<span class="pln"> https</span><span class="pun">:</span><span class="com">//angular-ynqttp--4200.local.webcontainer.io/shipping</span><span class="pln">
</span></pre>

<p>
	حيث أنَّ جزء <code>angular-ynqttp--4200.local.webcontainer.io</code> قد يكون مختلفًا في مشروع StackBlitz الخاص بك.
</p>

<h3>
	تهيئة المكون ShippingComponent لاستخدام الخدمة CartService
</h3>

<p>
	ينضبط المكون <code>ShippingComponent</code> لجلب بيانات الشحن عبر استخدام طلب <a href="https://academy.hsoub.com/programming/general/%d9%85%d8%af%d8%ae%d9%84-%d8%a5%d9%84%d9%89-http-r73/" rel="">HTTP</a> من ملف <code>shipping.json</code>.
</p>

<p>
	أولًا، استورد <code>CartService</code> في ملف <code>shipping.component.ts</code>.
</p>

<ul>
<li>
		الملف src/app/shipping/shipping.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_107" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Component</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">CartService</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'../cart.service'</span><span class="pun">;</span></pre>

<p>
	ثانيًا، احقن خدمة السلة في الباني <code>constructor()‎</code> الخاص بالمكون <code>ShippingComponent</code>.
</p>

<ul>
<li>
		الملف src/app/shipping/shipping.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_109" style="">
<span class="pln">constructor</span><span class="pun">(</span><span class="kwd">private</span><span class="pln"> cartService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">CartService</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ثالثًا، عرِّف الخاصية <code>shippingCosts</code> التي تُضبط باستخدام التابع <code>getShippingPrices()‎</code> من الخدمة <code>CartService</code>.
</p>

<ul>
<li>
		الملف src/app/shipping/shipping.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_918_111" style="">
<span class="pln">export </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ShippingComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  shippingCosts </span><span class="pun">=</span><span class="pln"> this</span><span class="pun">.</span><span class="pln">cartService</span><span class="pun">.</span><span class="pln">getShippingPrices</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	رابعًا، حدِّث قالب المكون <code>ShippingComponent</code> لكي يعرض أنواع وأسعار الشحن باستخدام أنبوب <a href="https://angular.io/api/common/AsyncPipe" rel="external nofollow"><code>async</code></a>.
</p>

<ul>
<li>
		الملف src/app/shipping/shipping.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_918_113" style="">
<span class="tag">&lt;h3&gt;</span><span class="pln">Shipping Prices</span><span class="tag">&lt;/h3&gt;</span><span class="pln">

</span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"shipping-item"</span><span class="pln"> *</span><span class="atn">ngFor</span><span class="pun">=</span><span class="atv">"let shipping of shippingCosts | async"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;span&gt;</span><span class="pln">{{ shipping.type }}</span><span class="tag">&lt;/span&gt;</span><span class="pln">
  </span><span class="tag">&lt;span&gt;</span><span class="pln">{{ shipping.price | currency }}</span><span class="tag">&lt;/span&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	يُعيد أنبوب <code>async</code> أحدث قيمة من مجرى البيانات ويتابع تحميل مكوِّن معيَّن، حيث يتوقف أنبوب <code>async</code> تلقائيًا عندما تدمر Angular هذا المكوِّن.
</p>

<p>
	خامسًا، أضف رابطًا من قسم العرض الخاص بالمكون <code>CartComponent</code> إلى قسم العرض الخاص بالمكون <code>ShippingComponent</code>.
</p>

<ul>
<li>
		الملف src/app/cart/cart.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_918_115" style="">
<span class="tag">&lt;h3&gt;</span><span class="pln">Cart</span><span class="tag">&lt;/h3&gt;</span><span class="pln">

</span><span class="tag">&lt;p&gt;</span><span class="pln">
  </span><span class="tag">&lt;a</span><span class="pln"> </span><span class="atn">routerLink</span><span class="pun">=</span><span class="atv">"/shipping"</span><span class="tag">&gt;</span><span class="pln">Shipping Prices</span><span class="tag">&lt;/a&gt;</span><span class="pln">
</span><span class="tag">&lt;/p&gt;</span><span class="pln">

</span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"cart-item"</span><span class="pln"> *</span><span class="atn">ngFor</span><span class="pun">=</span><span class="atv">"let item of items"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;span&gt;</span><span class="pln">{{ item.name }}</span><span class="tag">&lt;/span&gt;</span><span class="pln">
  </span><span class="tag">&lt;span&gt;</span><span class="pln">{{ item.price | currency }}</span><span class="tag">&lt;/span&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	سادسًا، انقر على زر <strong>الدفع Checkout</strong> لرؤية السلة المحدَّثة، وتذكَّر أنَّ أي تعديلات على التطبيق ستؤدي إلى تحديث المعاينة، وبالتالي إفراغ السلة.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="83448" href="https://academy.hsoub.com/uploads/monthly_2021_11/005.png.20585a07c2dbb060c0735e91c271600b.png" rel=""><img alt="005.png" class="ipsImage ipsImage_thumbnailed" data-fileid="83448" data-unique="en9ncmlw5" src="https://academy.hsoub.com/uploads/monthly_2021_11/005.png.20585a07c2dbb060c0735e91c271600b.png"></a>
</p>

<p>
	انقر على الرابط للانتقال إلى أسعار الشحن.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="83449" href="https://academy.hsoub.com/uploads/monthly_2021_11/006.png.5944f1a97cd3e8afc8f15bcd4bdaa6d6.png" rel=""><img alt="006.png" class="ipsImage ipsImage_thumbnailed" data-fileid="83449" data-unique="b0q6dljex" src="https://academy.hsoub.com/uploads/monthly_2021_11/006.png.5944f1a97cd3e8afc8f15bcd4bdaa6d6.png"></a>
</p>

<p>
	أصبح تطبيقنا الآن يعرض قائمة بالمنتجات وفيه سلة تسوق مع توفير إمكانية عرض الأسعار، وسنعمل في الخطوة التالية على إضافة ميزة الدفع عن طريق تعبئة استمارة تجمع المعلومات المتعلقة بالمستخدم والتي تعد جزءًا من عملية التسوق من المتجر.
</p>

<h2>
	تعريف نموذج استمارة الدفع
</h2>

<p>
	توضِّح لك هذه الخطوة كيفية إعداد نموذج استمارة الدفع في صنف المكون حيث يحدّد هذا النموذج حالة الاستمارة.
</p>

<p>
	افتح الملف <code>cart.component.ts</code> واستورد خدمة <a href="https://angular.io/api/forms/FormBuilder" rel="external nofollow"><code>FormBuilder</code></a> من حزمة <code>‎@angular/forms</code>، حيث توفِّر هذه الخدمة توابعًا ملائمةً لتوليد عناصر التحكم.
</p>

<ul>
<li>
		الملف src/app/cart/cart.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_117" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Component</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">FormBuilder</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/forms'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">CartService</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'../cart.service'</span><span class="pun">;</span></pre>

<p>
	احقن خدمة <code>FormBuilder</code> في الباني <code>constructor()‎</code> الخاص بالمكون <code>CartComponent</code>، حيث أنَّ هذه الخدمة هي جزء من وحدة <a href="https://angular.io/api/forms/ReactiveFormsModule" rel="external nofollow"><code>ReactiveFormsModule</code></a> التي استوردتها للتو.
</p>

<ul>
<li>
		الملف src/app/cart/cart.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_119" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CartComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> cartService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">CartService</span><span class="pun">,</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> formBuilder</span><span class="pun">:</span><span class="pln"> </span><span class="typ">FormBuilder</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	استخدم تابع <code>group()‎</code> من <code>FormBuilder</code> من أجل الحصول على اسم المستخدِم وعنوانه وذلك من أجل ضبط خاصية <code>checkoutForm</code> إلى نموذج استمارة يحوي حقلَي <code>name</code> و<code>address</code>.
</p>

<ul>
<li>
		الملف src/app/cart/cart.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_121" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CartComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  items </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">cartService</span><span class="pun">.</span><span class="pln">getItems</span><span class="pun">();</span><span class="pln">
  checkoutForm </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">formBuilder</span><span class="pun">.</span><span class="pln">group</span><span class="pun">({</span><span class="pln">
    name</span><span class="pun">:</span><span class="pln"> </span><span class="str">''</span><span class="pun">,</span><span class="pln">
    address</span><span class="pun">:</span><span class="pln"> </span><span class="str">''</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> cartService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">CartService</span><span class="pun">,</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> formBuilder</span><span class="pun">:</span><span class="pln"> </span><span class="typ">FormBuilder</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عرِّف التابع <code>onSubmit()‎</code> المسؤول عن معالجة الاستمارة، حيث يسمح هذا التابع للمستخدمين بإرسال أسمائهم وعناوينهم، كما أنَّ هذا التابع يستخدِم تابع <code>clearCart()‎</code> الخاص بخدمة <code>CartService</code> لإعادة ضبط الاستمارة وتفريغ السلة، ويكون صنف المكوِّن الخاص بالسلة كاملًا كما يلي:
</p>

<ul>
<li>
		الملف src/app/cart/cart.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_45" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Component</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">FormBuilder</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/forms'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">CartService</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'../cart.service'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@Component</span><span class="pun">({</span><span class="pln">
  selector</span><span class="pun">:</span><span class="pln"> </span><span class="str">'app-cart'</span><span class="pun">,</span><span class="pln">
  templateUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./cart.component.html'</span><span class="pun">,</span><span class="pln">
  styleUrls</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'./cart.component.css'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CartComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  items </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">cartService</span><span class="pun">.</span><span class="pln">getItems</span><span class="pun">();</span><span class="pln">
  checkoutForm </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">formBuilder</span><span class="pun">.</span><span class="pln">group</span><span class="pun">({</span><span class="pln">
    name</span><span class="pun">:</span><span class="pln"> </span><span class="str">''</span><span class="pun">,</span><span class="pln">
    address</span><span class="pun">:</span><span class="pln"> </span><span class="str">''</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> cartService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">CartService</span><span class="pun">,</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> formBuilder</span><span class="pun">:</span><span class="pln"> </span><span class="typ">FormBuilder</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

  onSubmit</span><span class="pun">():</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// Process checkout data here</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">items </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">cartService</span><span class="pun">.</span><span class="pln">clearCart</span><span class="pun">();</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">warn</span><span class="pun">(</span><span class="str">'Your order has been submitted'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">checkoutForm</span><span class="pun">.</span><span class="pln">value</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">checkoutForm</span><span class="pun">.</span><span class="pln">reset</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	إنشاء استمارة الدفع
</h3>

<p>
	اتبع الخطوات التالية لإضافة استمارة الدفع في أسفل قسم العرض الخاص بسلة التسوق.
</p>

<ul>
<li>
		أولًا، أضف وسم <a href="https://wiki.hsoub.com/HTML/form" rel="external"><code>&lt;form&gt;</code></a> وزر <strong>الشراء Purchase</strong> في نهاية ملف <code>cart.component.html. </code>
	</li>
	<li>
		ثانيًا، استخدِم تربيط الخاصيات لإضافة الخاصية <code>formGroup</code> إلى وسم <code>&lt;form&gt;</code> وربط <code>checkoutForm</code> بها.
	</li>
	<li>
		الملف src/app/cart/cart.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_48" style="">
<span class="pun">&lt;</span><span class="pln">form </span><span class="pun">[</span><span class="pln">formGroup</span><span class="pun">]=</span><span class="str">"checkoutForm"</span><span class="pun">&gt;</span><span class="pln">

  </span><span class="pun">&lt;</span><span class="pln">button </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"button"</span><span class="pln"> type</span><span class="pun">=</span><span class="str">"submit"</span><span class="pun">&gt;</span><span class="typ">Purchase</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;/</span><span class="pln">form</span><span class="pun">&gt;</span></pre>

<ul>
<li>
		<p>
			ثالثًا، استخدم التربيط عن طريق الحدث <code>ngSubmit</code> ضمن وسم <code>form</code> للاستماع إلى عملية إرسال النموذج واستدعاء تابع <code>onSubmit()‎</code> مع قيمة <code>checkoutForm</code>.
		</p>
	</li>
	<li>
		<p>
			الملف src/app/cart/cart.component.html (تفاصيل قالب مكون السلة):
		</p>
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_124" style="">
<span class="pun">&lt;</span><span class="pln">form </span><span class="pun">[</span><span class="pln">formGroup</span><span class="pun">]=</span><span class="str">"checkoutForm"</span><span class="pln"> </span><span class="pun">(</span><span class="pln">ngSubmit</span><span class="pun">)=</span><span class="str">"onSubmit()"</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">form</span><span class="pun">&gt;</span></pre>

<p>
	رابعًا، أضف حقول <a href="https://wiki.hsoub.com/HTML/input" rel="external"><code>&lt;input&gt;</code></a> لكل من الاسم <code>name</code> والعنوان <code>address</code>، وأضف لكل منها سمة <a href="https://angular.io/api/forms/FormControlName" rel="external nofollow"><code>formControlName</code></a> التي تربط عناصر تحكم <code>name</code> و<code>address</code> الموجودة في استمارة <code>checkoutForm</code> بحقول <code>&lt;input&gt;</code> الخاصة بهم، ويكون المكوِّن كاملًا كما يلي:
</p>

<ul>
<li>
		الملف src/app/cart/cart.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_918_126" style="">
<span class="tag">&lt;h3&gt;</span><span class="pln">Cart</span><span class="tag">&lt;/h3&gt;</span><span class="pln">

</span><span class="tag">&lt;p&gt;</span><span class="pln">
  </span><span class="tag">&lt;a</span><span class="pln"> </span><span class="atn">routerLink</span><span class="pun">=</span><span class="atv">"/shipping"</span><span class="tag">&gt;</span><span class="pln">Shipping Prices</span><span class="tag">&lt;/a&gt;</span><span class="pln">
</span><span class="tag">&lt;/p&gt;</span><span class="pln">

</span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"cart-item"</span><span class="pln"> *</span><span class="atn">ngFor</span><span class="pun">=</span><span class="atv">"let item of items"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;span&gt;</span><span class="pln">{{ item.name }} </span><span class="tag">&lt;/span&gt;</span><span class="pln">
  </span><span class="tag">&lt;span&gt;</span><span class="pln">{{ item.price | currency }}</span><span class="tag">&lt;/span&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span><span class="pln">

</span><span class="tag">&lt;form</span><span class="pln"> [</span><span class="atn">formGroup</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"checkoutForm"</span><span class="pln"> (</span><span class="atn">ngSubmit</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"onSubmit()"</span><span class="tag">&gt;</span><span class="pln">

  </span><span class="tag">&lt;div&gt;</span><span class="pln">
    </span><span class="tag">&lt;label</span><span class="pln"> </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"name"</span><span class="tag">&gt;</span><span class="pln">
      Name
    </span><span class="tag">&lt;/label&gt;</span><span class="pln">
    </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"name"</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text"</span><span class="pln"> </span><span class="atn">formControlName</span><span class="pun">=</span><span class="atv">"name"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">

  </span><span class="tag">&lt;div&gt;</span><span class="pln">
    </span><span class="tag">&lt;label</span><span class="pln"> </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"address"</span><span class="tag">&gt;</span><span class="pln">
      Address
    </span><span class="tag">&lt;/label&gt;</span><span class="pln">
    </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"address"</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text"</span><span class="pln"> </span><span class="atn">formControlName</span><span class="pun">=</span><span class="atv">"address"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">

  </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"button"</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"submit"</span><span class="tag">&gt;</span><span class="pln">Purchase</span><span class="tag">&lt;/button&gt;</span><span class="pln">

</span><span class="tag">&lt;/form&gt;</span></pre>

<p>
	يستطيع المستخدِمين بعد وضع عناصر قليلة في السلة معاينة تلك العناصر ومن ثم إدخال أسمائهم وعناوينهم وتأكيد عملية الشراء.
</p>

<p style="text-align: center;">
	<code><code><code class="typescript language-typescript"><a class="ipsAttachLink ipsAttachLink_image" data-fileid="83452" href="https://academy.hsoub.com/uploads/monthly_2021_11/009.png.737ccec1547ac144e16ce4b7c4fb9f7a.png" rel=""><img alt="009.png" class="ipsImage ipsImage_thumbnailed" data-fileid="83452" data-unique="ceq0aa7vb" src="https://academy.hsoub.com/uploads/monthly_2021_11/009.png.737ccec1547ac144e16ce4b7c4fb9f7a.png"></a></code> </code> </code>
</p>

<p>
	افتح الطرفية لتأكيد عملية الإرسال، حيث ستجد كائنًا يحتوي على الاسم والعنوان اللذَين أرسلتهما.
</p>

<h2>
	الخطوة التالية
</h2>

<p>
	ضبطت في هذا المثال التطبيق الخاص بك حتى تتمكن من عرض تفاصيل كل منتج في عنوان متميز، ولديك الآن تطبيق متجر يحتوي على قائمة بالمنتجات وسلة تسوق ونموذج دفع، وبإمكانك إلقاء نظرة على أسعار الشحن أيضًا.
</p>

<p>
	أكمل إلى <a href="https://angular.io/start/start-deployment" rel="external nofollow">قسم النشر</a> لتنتقل إلى التطوير المحلي أو تنشر التطبيق الخاص بك إلى Firebase أو الخادم الخاص بك وذلك لمتابعة استكشاف Angular.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://angular.io/start/start-routing#adding-navigation" rel="external nofollow">Adding navigation</a> والمقال <a href="https://angular.io/start/start-data#managing-data" rel="external nofollow">Managing data</a> والمقال <a href="https://angular.io/start/start-forms#using-forms-for-user-input" rel="external nofollow">Using forms for user input</a> من موقع <a href="angular.io" rel="">angular.io</a> الرسمي.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A7%D9%84%D9%85%D8%AA%D8%AD%D9%83%D9%85%D8%A7%D8%AA-controllers-%D9%81%D9%8A-angularjs-r186/" rel="">المتحكمات (Controllers) في AngularJS</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r662/" rel="">تعلم البرمجة</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1382</guid><pubDate>Fri, 26 Nov 2021 16:00:00 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x627;&#x633;&#x62A;&#x639;&#x645;&#x627;&#x644; Angular &#x641;&#x64A; &#x628;&#x646;&#x627;&#x621; &#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A; &#x627;&#x644;&#x648;&#x64A;&#x628;</title><link>https://academy.hsoub.com/programming/javascript/angular/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-angular-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1380/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_11/61a0beabd69a5_----Angular------Angular-HTML-JavaScript-TypeScript--------Output-Inpu.png.7b7c8ea9a65a4ccd69ae36fa4d86b34a.png" /></p>

<p>
	يقدِّم لك هذا المقال التعليمي الأساسيات الموجودة في Angular من خلال السير معك في عملية بناء موقع تجارة إلكترونية مؤلف من قائمة من المنتجات، وسلة التسوق، ونموذج الدفع، كما يستخدِم هذا المقال تطبيقًا جاهزًا يمكنك معاينته وتعديله بصورة تفاعلية عن طريق <a href="https://stackblitz.com/" rel="external nofollow">StackBlitz</a> دون الحاجة إلى إعداد بيئة عمل محلية، حيث تُعَدّ StackBlitz بيئة تطوير تعتمد على المتصفح وتمكِّنك من إنشاء وحفظ ومشاركة المشاريع باستخدام مجموعة متنوعة من التقنيات.
</p>

<p>
	هذه المقال جزءٌ من سلسلة مقالات حول بناء تطبيق تجارة إلكترونية عبر Angular:
</p>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D8%A7-%D9%87%D9%8A-angular%D8%9F-r1379/" rel="">ما هي Angular؟</a>
	</li>
	<li>
		<a href="%D8%B1%D8%A7%D8%A8%D8%B7" rel="">كيفية استعمال Angular في بناء تطبيقات الويب</a>
	</li>
	<li>
		<a href="%D8%B1%D8%A7%D8%A8%D8%B7" rel="">إضافة التنقل وإدارة البيانات في تطبيق Angular</a>
	</li>
	<li>
		<a href="%D8%B1%D8%A7%D8%A8%D8%B7" rel="">تهيئة بيئة تطبيقات Angular ونشرها على الويب</a>
	</li>
</ul>
<h2>
	المتطلبات الأساسية
</h2>

<p>
	يجب أن يكون لديك فهم أساسي لما يلي لتحقيق أعلى إفادة من هذا المقال التعليمي:
</p>

<ul>
<li>
		<a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a>
	</li>
	<li>
		<a href="https://wiki.hsoub.com/JavaScript" rel="external">JavaScript</a>
	</li>
	<li>
		<a href="https://wiki.hsoub.com/TypeScript/" rel="external">TypeScript</a>
	</li>
</ul>
<h2>
	تفاصيل التطبيق الذي سنبنيه
</h2>

<p>
	تُبنَى تطبيقات Angular بالاعتماد على المكوِّنات، إذ تمثِِّل هذه المكوِّنات اللبنات الأساسية لبناء واجهة المستخدِم والتي تتيح لك إعادة استخدام مجموعات كبيرة من وظائف واجهة المستخدِم، ويتألف المكوِّن من ثلاثة أشياء:
</p>

<ul>
<li>
		صنف المكوِّن الذي يعالِج البيانات والوظائف.
	</li>
	<li>
		قالب HTML الذي يحدِّد واجهة المستخدِم.
	</li>
	<li>
		التنسيقات الخاصة بالمكوِّن التي تحدِّد الشكل والمظهر.
	</li>
</ul>
<p>
	يوضح هذا الدليل كيفية بناء تطبيق باستخدام المكوِّنات التالية:
</p>

<ul>
<li>
		<code>&lt;app-root&gt;</code>: المكوِّن الجذر الذي يُحمَّل أولًا والذي يمثِّل الحاوية للمكوِّنات الأخرى.
	</li>
	<li>
		<code>&lt;app-top-bar&gt;</code>: اسم المتجر وزر الدفع.
	</li>
	<li>
		<code>&lt;app-product-list&gt;</code>: قائمة المنتجات.
	</li>
	<li>
		<code>&lt;app-product-alerts&gt;</code> - مكوِّن يحتوي على تنبيهات التطبيق.
	</li>
</ul>
<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="83397" href="https://academy.hsoub.com/uploads/monthly_2021_11/001.png.dd55be7b6880f5f349d976bbd690bf06.png" rel=""><img alt="001.png" class="ipsImage ipsImage_thumbnailed" data-fileid="83397" data-unique="qq268mcfc" src="https://academy.hsoub.com/uploads/monthly_2021_11/001.png.dd55be7b6880f5f349d976bbd690bf06.png"></a>
</p>

<h2>
	البدء مع الملفات الأساسية للمشروع
</h2>

<p>
	ولِّد <a href="https://angular.io/generated/live-examples/getting-started-v0/stackblitz.html" rel="external nofollow">مشروع العينة الجاهزة في StackBlitz</a> لإنشاء الملفات الأساسية التي سنتعامل معها، واتبع ما يلي لحفظ عملك:
</p>

<ol>
<li>
		سجّل الدخول إلى StackBlitz.
	</li>
	<li>
		انسخ المشروع الذي ولَّدته.
	</li>
	<li>
		احفظ المشروع بصورة دورية.
	</li>
</ol>
<p>
	يعرض جزء المعاينة الموجود على اليمين في StackBlitz حالة بدء تطبيق المثال، كما تعرض المعاينة منطقتين هما:
</p>

<ul>
<li>
		My Store شريط علوي يحوي اسم المتجر وزر الدفع.
	</li>
	<li>
		Products ترويسة من أجل قائمة المنتجات.
	</li>
</ul>
<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="83398" href="https://academy.hsoub.com/uploads/monthly_2021_11/002.gif.81d5af80d7362e8bc7fc1f445d987490.gif" rel=""><img alt="002.gif" class="ipsImage ipsImage_thumbnailed" data-fileid="83398" data-unique="4wwtvz2yz" src="https://academy.hsoub.com/uploads/monthly_2021_11/002.gif.81d5af80d7362e8bc7fc1f445d987490.gif"></a>
</p>

<p>
	يعرض جزء المشروع الموجود على اليسار الملفات المصدرية التي يتكون منها التطبيق، كما تتضمن ملفات البنية التحتية وملفات الضبط، في حين ينشئ StackBlitz ملفات أساسية وبيانات وهمية لك عندما تولِّد تطبيقات المثال في StackBlitz والتي تأتي مترافقة مع المقالات التعليمية، كما توجد جميع الملفات التي ستستخدمها خلال المقال التعليمي ضمن مجلد <code>src</code>، ويمكنك الاطلاع على <a href="https://developer.stackblitz.com/docs/platform/" rel="external nofollow">التوثيق الخاص بـ StackBlitz</a> لمزيد من المعلومات حول استخدام StackBlitz.
</p>

<h2>
	إنشاء قائمة المنتجات
</h2>

<p>
	سنعمل في هذا القسم على تحديث التطبيق لعرض قائمة المنتجات، حيث سنستخدم بيانات المنتج المعرَّفة مسبقًا في ملف <code>products.ts</code> والتوابع الموجودة في ملف <code>product-list.component.ts</code>، كما سيرشدك هذا القسم خلال عملية تحرير ملف HTML والمعروف باسم القالب أيضًا.
</p>

<p>
	بدايةً، افتح ملف القالب <code>product-list.component.html</code> في مجلد <code>product-list</code> وأضف موجِّهًا هيكليًا <a href="https://angular.io/api/common/NgForOf*" rel="external nofollow"><code>ngFor</code></a> للعنصر <code>&lt;div&gt;</code>، كما يلي:
</p>

<ul>
<li>
		الملف src/app/product-list/product-list.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2901_6" style="">
<span class="tag">&lt;h2&gt;</span><span class="pln">Products</span><span class="tag">&lt;/h2&gt;</span><span class="pln">

</span><span class="tag">&lt;div</span><span class="pln"> *</span><span class="atn">ngFor</span><span class="pun">=</span><span class="atv">"let product of products"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	سيتكرر العنصر <a href="https://wiki.hsoub.com/HTML/div" rel="external"><code>&lt;div&gt;</code></a> من أجل كل منتج <code>product</code> في قائمة المنتجات <code>products</code> باستخدام <code>ngFor*</code>، كما تشكّل الموجّهات الهيكلية Structural directives، أو تعيد تشكيل بنية DOM عن طريق إضافة العناصر وإزالتها ومعالجتها.
</p>

<p>
	بعد ذلك أضف داخل العنصر <code>&lt;div&gt;</code> العنوان <code>&lt;h3&gt;</code> وضع فيه <code>{{ product.name }}</code> الذي يمثل اسم المنتج وهو مثال عن سياق الإدراج في Angular، حيث يتيح لك الإدراج <code>{{ }}</code> تصيير قيمة الخاصية على أساس نص.
</p>

<ul>
<li>
		الملف src/app/product-list/product-list.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2901_8" style="">
<span class="tag">&lt;h2&gt;</span><span class="pln">Products</span><span class="tag">&lt;/h2&gt;</span><span class="pln">

</span><span class="tag">&lt;div</span><span class="pln"> *</span><span class="atn">ngFor</span><span class="pun">=</span><span class="atv">"let product of products"</span><span class="tag">&gt;</span><span class="pln">

  </span><span class="tag">&lt;h3&gt;</span><span class="pln">
      {{ product.name }}
  </span><span class="tag">&lt;/h3&gt;</span><span class="pln">

</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	يُحدَّث جزء المعاينة لعرض اسم كل منتج في القائمة.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="83399" href="https://academy.hsoub.com/uploads/monthly_2021_11/003.png.8603ea85b05a9b4e962aa314b2bb1832.png" rel=""><img alt="003.png" class="ipsImage ipsImage_thumbnailed" data-fileid="83399" data-unique="2tp3re3c6" src="https://academy.hsoub.com/uploads/monthly_2021_11/003.png.8603ea85b05a9b4e962aa314b2bb1832.png"></a>
</p>

<p>
	أضف العنصر <code>&lt;a&gt;</code> حول <code>{{ product.name }}</code> لربط اسم كل منتج بتفاصيله ثم اضبط العنوان ليصبح اسم المنتج باستخدام صياغة التربيط بين الخاصيات Property binding <code>[]</code> كما يلي:
</p>

<ul>
<li>
		الملف src/app/product-list/product-list.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2901_10" style="">
<span class="tag">&lt;h2&gt;</span><span class="pln">Products</span><span class="tag">&lt;/h2&gt;</span><span class="pln">

</span><span class="tag">&lt;div</span><span class="pln"> *</span><span class="atn">ngFor</span><span class="pun">=</span><span class="atv">"let product of products"</span><span class="tag">&gt;</span><span class="pln">

  </span><span class="tag">&lt;h3&gt;</span><span class="pln">
    </span><span class="tag">&lt;a</span><span class="pln"> [</span><span class="atn">title</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"product.name + ' details'"</span><span class="tag">&gt;</span><span class="pln">
      {{ product.name }}
    </span><span class="tag">&lt;/a&gt;</span><span class="pln">
  </span><span class="tag">&lt;/h3&gt;</span><span class="pln">

</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	مرّر مؤشر الفأرة فوق اسم منتج ما في جزء المعاينة لترى قيمة خاصية التربيط بالاسم، والتي تمثّل اسم المنتج بالإضافة إلى كلمة "تفاصيل". يتيح لك التربيط عن طريق الصياغة <code>[]</code> استخدام قيمة خاصية مُعرَّفة أو موجودة في القالب.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="83400" href="https://academy.hsoub.com/uploads/monthly_2021_11/004.png.59e9c0ba37cee2f7c0427f92510e7c3c.png" rel=""><img alt="004.png" class="ipsImage ipsImage_thumbnailed" data-fileid="83400" data-unique="kgqqg548a" src="https://academy.hsoub.com/uploads/monthly_2021_11/004.png.59e9c0ba37cee2f7c0427f92510e7c3c.png"></a>
</p>

<p>
	أضف توصيفات المنتجات عن طريق استخدام الموجِّه <a href="https://angular.io/api/common/NgIf" rel="external nofollow"><code>‎*ngIf</code></a> على عنصر <a href="https://wiki.hsoub.com/HTML/p" rel="external"><code>&lt;p&gt;</code></a>، حيث ينشئ Angular عنصر <code>&lt;p&gt;</code> فقط في حال كان المنتج الحالي يملك وصفًا.
</p>

<ul>
<li>
		الملف src/app/product-list/product-list.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2901_12" style="">
<span class="tag">&lt;h2&gt;</span><span class="pln">Products</span><span class="tag">&lt;/h2&gt;</span><span class="pln">

</span><span class="tag">&lt;div</span><span class="pln"> *</span><span class="atn">ngFor</span><span class="pun">=</span><span class="atv">"let product of products"</span><span class="tag">&gt;</span><span class="pln">

  </span><span class="tag">&lt;h3&gt;</span><span class="pln">
    </span><span class="tag">&lt;a</span><span class="pln"> [</span><span class="atn">title</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"product.name + ' details'"</span><span class="tag">&gt;</span><span class="pln">
      {{ product.name }}
    </span><span class="tag">&lt;/a&gt;</span><span class="pln">
  </span><span class="tag">&lt;/h3&gt;</span><span class="pln">

  </span><span class="tag">&lt;p</span><span class="pln"> *</span><span class="atn">ngIf</span><span class="pun">=</span><span class="atv">"product.description"</span><span class="tag">&gt;</span><span class="pln">
    Description: {{ product.description }}
  </span><span class="tag">&lt;/p&gt;</span><span class="pln">

</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	يعرض التطبيق الآن اسم ووصف كل منتج في القائمة، ولاحظ أنّ المنتج الأخير لا يملك فقرة وصف أي لا يملك قيمةً للخاصية <code>description</code> وبالتالي لم تنشئ Angular عنصر <code>&lt;p&gt;</code> له.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="83401" href="https://academy.hsoub.com/uploads/monthly_2021_11/005.png.7911ad1be89e2b00cff45e862316ca8a.png" rel=""><img alt="005.png" class="ipsImage ipsImage_thumbnailed" data-fileid="83401" data-unique="4yurd7iv8" src="https://academy.hsoub.com/uploads/monthly_2021_11/005.png.7911ad1be89e2b00cff45e862316ca8a.png"></a>
</p>

<p>
	أخيرًا أضف زرًا حتى يتمكّن المستخدِمون من مشاركة المنتج واربط الحدث <code>click</code> مع التابع <code>()share</code> في ملف <code>product-list.component.ts</code>، ويكون التربيط بين الأحداث بوضع اسم الحدث بين قوسين <code>()</code> حول الحدث كما هو الحال في حدث <code>(click)</code> ضمن عنصر <a href="https://wiki.hsoub.com/HTML/button" rel="external"><code>&lt;button&gt;</code></a>.
</p>

<ul>
<li>
		الملف src/app/product-list/product-list.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2901_14" style="">
<span class="tag">&lt;h2&gt;</span><span class="pln">Products</span><span class="tag">&lt;/h2&gt;</span><span class="pln">

</span><span class="tag">&lt;div</span><span class="pln"> *</span><span class="atn">ngFor</span><span class="pun">=</span><span class="atv">"let product of products"</span><span class="tag">&gt;</span><span class="pln">

  </span><span class="tag">&lt;h3&gt;</span><span class="pln">
    </span><span class="tag">&lt;a</span><span class="pln"> [</span><span class="atn">title</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"product.name + ' details'"</span><span class="tag">&gt;</span><span class="pln">
      {{ product.name }}
    </span><span class="tag">&lt;/a&gt;</span><span class="pln">
  </span><span class="tag">&lt;/h3&gt;</span><span class="pln">

  </span><span class="tag">&lt;p</span><span class="pln"> *</span><span class="atn">ngIf</span><span class="pun">=</span><span class="atv">"product.description"</span><span class="tag">&gt;</span><span class="pln">
    Description: {{ product.description }}
  </span><span class="tag">&lt;/p&gt;</span><span class="pln">

  </span><span class="tag">&lt;button</span><span class="pln"> (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"share()"</span><span class="tag">&gt;</span><span class="pln">
    Share
  </span><span class="tag">&lt;/button&gt;</span><span class="pln">

</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	يملك كل منتج الآن زر مشاركة Share.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="83402" href="https://academy.hsoub.com/uploads/monthly_2021_11/006.png.b4bef94f3aa095f15259de1bc228e544.png" rel=""><img alt="006.png" class="ipsImage ipsImage_thumbnailed" data-fileid="83402" data-unique="vtdwi8qmj" src="https://academy.hsoub.com/uploads/monthly_2021_11/006.png.b4bef94f3aa095f15259de1bc228e544.png"></a>
</p>

<p>
	تطلق عملية النقر على زر المشاركة تنبيهًا يخبرنا بأنه "شورِك المنتج!".
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="83403" href="https://academy.hsoub.com/uploads/monthly_2021_11/007.png.ea4c731bfe2979b419ca17d71bd2102f.png" rel=""><img alt="007.png" class="ipsImage ipsImage_thumbnailed" data-fileid="83403" data-unique="4ka3b4khl" src="https://academy.hsoub.com/uploads/monthly_2021_11/007.png.ea4c731bfe2979b419ca17d71bd2102f.png"></a>
</p>

<p>
	بهذا تكون قد استكشفت الآن الميزات الشائعة الاستخدام أثناء العمل في قوالب Angular.
</p>

<h2>
	تمرير البيانات إلى مكون ابن
</h2>

<p>
	تعرض حاليًا قائمة المنتجات اسم وتوصيف كل منتج، كما يعرِّف <code>ProductListComponent</code> أيضًا خاصية <code>products</code> التي تحتوي على بيانات مستورَدة لكل منتج من مصفوفة <code>products</code> في <code>products.ts</code>، وتكون الخطوة التالية هي إنشاء ميزة تنبيه جديدة تستخدِم بيانات المنتج من <code>ProductListComponent</code>.
</p>

<p>
	يتحقّق التنبيه من سعر المنتج، فإذا كان السعر أكبر من 700 دولار، سيعرض زر نبهني Notify Me الذي يتيح للمستخدِمين الاشتراك في الإشعارات عندما يُطرَح المنتج للبيع، كما سيرشدك هذا القسم خلال عملية إنشاء مكوِّن ابن <code>ProductAlertsComponent</code> يمكنه تلقي البيانات من المكوِّن الأب <code>ProductListComponent</code>.
</p>

<p>
	بدايةً، انقر بزر الفأرة الأيمن على مجلد <code>app</code> واستخدم المولد الخاص بـ <code>Angular</code> لإنشاء مكون جديد يُسمّى <code>product-alerts</code>.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="83404" href="https://academy.hsoub.com/uploads/monthly_2021_11/008.png.112b4cfba4f8df20077324275505778b.png" rel=""><img alt="008.png" class="ipsImage ipsImage_thumbnailed" data-fileid="83404" data-unique="3heivjz0b" src="https://academy.hsoub.com/uploads/monthly_2021_11/008.png.112b4cfba4f8df20077324275505778b.png"></a>
</p>

<p>
	ينشئ المولِّد ملفات البدء الأساسية لأجزاء المكوِّن الثلاثة:
</p>

<ul>
<li>
		product-alerts.component.ts
	</li>
	<li>
		product-alerts.component.html
	</li>
	<li>
		product-alerts.component.css
	</li>
</ul>
<p>
	افتح ملف <code>product-alerts.component.ts</code> وستجد فيه مزخرفًا decorator باسم <a href="https://angular.io/api/core/Component" rel="external nofollow"><code>‎@Component()‎</code></a> يشير إلى أن الصنف الحالي هو مكوِّن، كما يوفِّر بيانات وصفية حوله بما في ذلك المحدِّد selector والقوالب templates والتنسيقات styles.
</p>

<ul>
<li>
		الملف src/app/product-alerts/product-alerts.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_2901_16" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Component</span><span class="pun">,</span><span class="pln"> </span><span class="typ">OnInit</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@Component</span><span class="pln">
</span><span class="pun">({</span><span class="pln">
  selector</span><span class="pun">:</span><span class="pln"> </span><span class="str">'app-product-alerts'</span><span class="pun">,</span><span class="pln">
  templateUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./product-alerts.component.html'</span><span class="pun">,</span><span class="pln">
  styleUrls</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'./product-alerts.component.css'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ProductAlertsComponent</span><span class="pln"> </span><span class="kwd">implements</span><span class="pln"> </span><span class="typ">OnInit</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span><span class="pln">

  ngOnInit</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span></pre>

<p>
	تتمثَّل الميزات الرئيسية في <code>‎@Component()‎</code> بما يلي:
</p>

<ul>
<li>
		يعرّف المحدد <code>selector</code> اسم المكون وقيمته هنا <code>app-product-alerts</code>، كما تعارف مبرمجو Angular على بدء اسم المكون بالبادئة <code>-app</code> متبوعة باسم المكوِّن.
	</li>
	<li>
		تشير أسماء ملفات القوالب والتنسيقات إلى HTML وCSS للمكوِّن.
	</li>
	<li>
		يصدِّر تعريف <code>‎@Component()‎</code> الصنف <code>ProductAlertsComponent</code> أيضًا والذي يعالِج وظائف المكوِّن.
	</li>
</ul>
<p>
	ستورد <a href="https://angular.io/api/core/Input" rel="external nofollow"><code>Input</code></a> من <code>‎@angular/core</code> أولًا لإعداد مكوِّن <code>ProductAlertsComponent</code> لاستقبال بيانات المنتج.
</p>

<ul>
<li>
		الملف src/app/product-alerts/product-alerts.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_2901_18" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Component</span><span class="pun">,</span><span class="pln"> </span><span class="typ">OnInit</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Input</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Product</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'../products'</span><span class="pun">;</span></pre>

<p>
	أضف في تعريف الصنف <code>ProductAlertsComponent</code> خاصيةً تسمى <code>product</code> مع مزخرف <code>‎@Input()‎</code> والذي يشير إلى تمرير قيمة الخاصية إلى المكوِّن الرئيسي <code>ProductListComponent</code>.
</p>

<ul>
<li>
		الملف src/app/product-alerts/product-alerts.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_2901_20" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ProductAlertsComponent</span><span class="pln"> </span><span class="kwd">implements</span><span class="pln"> </span><span class="typ">OnInit</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="lit">@Input</span><span class="pun">()</span><span class="pln"> product</span><span class="pun">!:</span><span class="pln"> </span><span class="typ">Product</span><span class="pun">;</span><span class="pln">
  constructor</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span><span class="pln">

  ngOnInit</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span></pre>

<p>
	افتح الملف <code>product-alerts.component.html</code> واستبدل فقرة العنصر النائب بزر نبّهني Notify Me الذي يظهر في حال كان سعر المنتج أكثر من 700 دولار.
</p>

<ul>
<li>
		الملف src/app/product-alerts/product-alerts.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_2901_22" style="">
<span class="tag">&lt;p</span><span class="pln"> *</span><span class="atn">ngIf</span><span class="pun">=</span><span class="atv">"product &amp;&amp; product.price &gt;</span><span class="pln"> 700"&gt;
  </span><span class="tag">&lt;button&gt;</span><span class="pln">Notify Me</span><span class="tag">&lt;/button&gt;</span><span class="pln">
</span><span class="tag">&lt;/p&gt;</span></pre>

<p>
	لاحظ أن المولد يضيف تلقائيًا الصنف <code>ProductAlertsComponent</code> إلى الوحدة <code>AppModule</code> ليكون متاحًا في كافة مكونات التطبيق:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_2901_24" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">ProductAlertsComponent</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'./product-alerts/product-alerts.component'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@NgModule</span><span class="pun">({</span><span class="pln">
  declarations</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="typ">AppComponent</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">TopBarComponent</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">ProductListComponent</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">ProductAlertsComponent</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">],</span></pre>

<p>
	أضف المحدد <code>&lt;app-product-alerts&gt;</code> إلى <code>product-list.component.html</code> لعرض <code>ProductAlertsComponent</code> على أساس ابن للمكوِّن <code>ProductListComponent</code>، ومرِّر المنتج الحالي على أساس دخل إلى المكوِّن باستخدام التربيط عن طريق تربيط الخاصية property binding.
</p>

<ul>
<li>
		الملف src/app/product-list/product-list.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2901_26" style="">
<span class="pun">&lt;</span><span class="pln">button </span><span class="pun">(</span><span class="pln">click</span><span class="pun">)=</span><span class="str">"share()"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="typ">Share</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;</span><span class="pln">app</span><span class="pun">-</span><span class="pln">product</span><span class="pun">-</span><span class="pln">alerts
  </span><span class="pun">[</span><span class="pln">product</span><span class="pun">]=</span><span class="str">"product"</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">app</span><span class="pun">-</span><span class="pln">product</span><span class="pun">-</span><span class="pln">alerts</span><span class="pun">&gt;</span></pre>

<p>
	يأخذ مكوِّن التنبيه الجديد الخاص بالمنتج منتجًا من قائمة المنتج على أساس دخل له، حيث يعرض أو يخفي عن طريقه زر نبهني Notify Me بناءً على قيمة سعر هذا المنتج، فسعر هاتف Phone XL مثلًا، هو أكثر من 700 دولار لذلك عُرِض زر نبهني Notify Me لهذا المنتج.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="83405" href="https://academy.hsoub.com/uploads/monthly_2021_11/009.png.5d5262b86d7ecc9151eb5d6ac3e909b9.png" rel=""><img alt="009.png" class="ipsImage ipsImage_thumbnailed" data-fileid="83405" data-unique="z6zsr5709" src="https://academy.hsoub.com/uploads/monthly_2021_11/009.png.5d5262b86d7ecc9151eb5d6ac3e909b9.png"></a>
</p>

<h2>
	تمرير البيانات إلى المكون الرئيسي
</h2>

<p>
	يحتاج المكون الابن إلى التنبيه وإرسال البيانات إلى المكوِّن الأب لكي يعمل زر نبهني Notify Me، كما يحتاج <code>ProductAlertsComponent</code> إلى إطلاق حدث عندما ينقر المستخدِم على نبهني Notify Me، في حين يحتاج <code>ProductListComponent</code> إلى الاستجابة للحدث.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		توضيح: يتضمن مولد Angular في المكونات الحديثة بانيًا فارغًا <code>constructor()‎</code> وواجهة <a href="https://angular.io/api/core/OnInit" rel="external nofollow"><code>OnInit</code></a> وتابع<code>ngOnInit()‎</code>، وبما أنَّ الخطوات التالية لا تستخدِم ما سبق فقد حُذِفوا من شيفرات الأمثلة للاختصار.
	</p>
</blockquote>

<p>
	أولًا، استورد <a href="https://angular.io/api/core/Output" rel="external nofollow"><code>Output</code></a> و <a href="https://angular.io/api/core/EventEmitter" rel="external nofollow"><code>EventEmitter</code></a> في الملف <code>product-alerts.component.ts</code> من <code>‎@angular/core</code>.
</p>

<ul>
<li>
		الملف src/app/product-alerts/product-alerts.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_2901_28" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Component</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Input</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Output</span><span class="pun">,</span><span class="pln"> </span><span class="typ">EventEmitter</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Product</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'../products'</span><span class="pun">;</span></pre>

<p>
	ثانيًا، عرِّف خاصيةً تسمى <code>notify</code> في صنف المكوِّن مع مزخرف <code>‎@Output()‎</code> ونسخة من <code>EventEmitter()‎</code>، هيِّئ المكوِّن <code>ProductAlertsComponent</code> باستعمال <code>‎@Output()‎</code> لكي تسمح لمكوِّن <code>ProductAlertsComponent</code> بإطلاق حدث عندما تتغير قيمة الخاصية <code>notify</code>.
</p>

<ul>
<li>
		الملف src/app/product-alerts/product-alerts.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_2901_30" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ProductAlertsComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="lit">@Input</span><span class="pun">()</span><span class="pln"> product</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Product</span><span class="pun">|</span><span class="kwd">undefined</span><span class="pun">;</span><span class="pln">
  </span><span class="lit">@Output</span><span class="pun">()</span><span class="pln"> notify </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">EventEmitter</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ثالثًا، عدِّل زر نبهني Notify Me ضمن <code>product-alerts.component.html</code> باستخدام التربيط مع الحدث من أجل استدعاء الدالة <code>notify.emit()‎</code>.
</p>

<ul>
<li>
		الملف src/app/product-alerts/product-alerts.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2901_32" style="">
<span class="tag">&lt;p</span><span class="pln"> *</span><span class="atn">ngIf</span><span class="pun">=</span><span class="atv">"product &amp;&amp; product.price &gt;</span><span class="pln"> 700"&gt;
  </span><span class="tag">&lt;button</span><span class="pln"> (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"notify.emit()"</span><span class="tag">&gt;</span><span class="pln">Notify Me</span><span class="tag">&lt;/button&gt;</span><span class="pln">
</span><span class="tag">&lt;/p&gt;</span></pre>

<p>
	رابعًا، عرِّف سلوكًا يحدث عندما ينقر المستخدِم على الزر، حيث يبدأ المكون الأب <code>ProductListComponent</code> وليس المكون <code>ProductAlertsComponent</code> بالعمل عندما يولِّد الابن الحدث. عرّف تابع <code>onNotify()‎</code> ضمن <code>product-list.component.ts</code> بطريقة مشابهة للتابع<code>share()‎</code>.
</p>

<ul>
<li>
		الملف src/app/product-list/product-list.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_2901_34" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ProductListComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  products </span><span class="pun">=</span><span class="pln"> products</span><span class="pun">;</span><span class="pln">

  share</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    window</span><span class="pun">.</span><span class="pln">alert</span><span class="pun">(</span><span class="str">'The product has been shared!'</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  onNotify</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    window</span><span class="pun">.</span><span class="pln">alert</span><span class="pun">(</span><span class="str">'You will be notified when the product goes on sale'</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	خامسًا، حدِّث <code>ProductListComponent</code> ليستقبل البيانات من <code>ProductAlertsComponent</code>، واربط <code>&lt;app-product-alerts&gt;</code> مع التابع <code>onNotify()‎</code> الموجود في مكون قائمة المنتجات ضمن الملف <code>product-list.component.html</code>، حيث ما يعرضه الزر نبهني Notify Me هو <code>&lt;app-product-alerts&gt;</code>.
</p>

<ul>
<li>
		الملف src/app/product-list/product-list.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2901_36" style="">
<span class="tag">&lt;button</span><span class="pln"> (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"share()"</span><span class="tag">&gt;</span><span class="pln">
  Share
</span><span class="tag">&lt;/button&gt;</span><span class="pln">

</span><span class="tag">&lt;app-product-alerts</span><span class="pln">
  [</span><span class="atn">product</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"product"</span><span class="pln"> 
  (</span><span class="atn">notify</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"onNotify()"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;/app-product-alerts&gt;</span></pre>

<p>
	سادسًا، انقر على زر نبهني Notify Me لإطلاق التنبيه والذي يطبع رسالة "سوف تُعلَم عندما يُطرح المنتج للبيع" على المتصفح.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="83406" href="https://academy.hsoub.com/uploads/monthly_2021_11/010.png.730d3055b1cc3d27c1a4a9be38cfa77f.png" rel=""><img alt="010.png" class="ipsImage ipsImage_thumbnailed" data-fileid="83406" data-unique="cdlaa1qpa" src="https://academy.hsoub.com/uploads/monthly_2021_11/010.png.730d3055b1cc3d27c1a4a9be38cfa77f.png"></a>
</p>

<h2>
	الخطوة التالية
</h2>

<p>
	أنشأتَ في هذا المقال تطبيقًا يمرّ على مجموعة من البيانات، كما أنشأتَ مكوِّنات مميزةً تتواصل مع بعضها، وسنكمل في المقال التالي استشكاف Angular وإنشاء التطبيق الخاص بك عبر مقال <a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%AA%D9%86%D9%82%D9%84-%D9%88%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-angular-r1382/" rel="">إضافة التنقل وإدارة البيانات في تطبيق Angular</a> لإنشاء صفحة تفاصيل المنتج وقائمة المنتجات قبل أن ننتقل إلى خطوة نشر التطبيق على <a href="https://academy.hsoub.com/devops/servers/%D8%AF%D9%84%D9%8A%D9%84-%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%AE%D8%A7%D8%AF%D9%85-%D9%88%D9%8A%D8%A8-%D9%85%D8%AD%D9%84%D9%8A-%D8%AE%D8%B7%D9%88%D8%A9-%D8%A8%D8%AE%D8%B7%D9%88%D8%A9-r422/" rel="">خادم الويب</a> أو أي خادم تريد.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://angular.io/start" rel="external nofollow">Getting started with Angular</a> من موقع <a href="angular.io" rel="">angular.io</a> الرسمي.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1071/" rel="">أساسيات بناء تطبيقات الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/typescript/%D8%A7%D9%84%D8%AE%D8%B7%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D9%88%D9%84%D9%89-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-typescript-r1215/" rel="">الخطوات الأولى في بناء تطبيقات الويب باستعمال TypeScript</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D8%A8%D8%A7%D8%AF%D8%A6-angularjs-r176/" rel="">مبادئ AngularJS</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r662/" rel="">تعلم البرمجة</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1380</guid><pubDate>Fri, 19 Nov 2021 16:00:00 +0000</pubDate></item><item><title>&#x645;&#x627; &#x647;&#x64A; Angular&#x61F;</title><link>https://academy.hsoub.com/programming/javascript/angular/%D9%85%D8%A7-%D9%87%D9%8A-angular%D8%9F-r1379/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_11/61a0b610549d8_-------------.png.3e5039d47ca5474455089f126f739a49.png" /></p>

<p>
	سيساعدك هذا المقال على فهم إطار العمل Angular -والذي ينطق أنجولار- كما سيجاوب على الأسئلة التالية: ما هي منصة Angular؟ وما هي الميزات التي تقدمها؟ وما الذي تأمل أن تبنيه عندما تبدأ بناء تطبيقاتك باستخدام Angular؟
</p>

<p>
	تُعَدّ Angular منصة تطوير مبنية على لغة <a href="https://wiki.hsoub.com/TypeScript/" rel="external">TypeScript</a> وتتضمن ما يلي:
</p>

<ul>
<li>
		إطار عمل قائم على المكوِّنات من أجل بناء تطبيقات ويب قابلة للتوسع.
	</li>
	<li>
		مجموعةً من المكتبات المضمنة التي تقدِّم مجموعةً واسعةً من الميزات بما فيها التوجيه وإدارة النماذج واتصالات الخادم والعميل والكثير من الميزات الأخرى.
	</li>
	<li>
		مجموعةً من أدوات المطوِّرين التي تساعدك في عملية تطوير شيفرة المشروع وبنائها واختبارها وتحديثها.
	</li>
</ul>
<p>
	تقدِّم منصة Angular فوائدًا عديدةً لمستخدميها، حيث ستعمل على توسيع المشاريع التي يعمل عليها مطور واحد إلى مشاريع على مستوى المؤسسة، حيث صُمِّمت Angular لجعل عملية التحديث سهلةً قدر الإمكان، وبالتالي تحقيق إفادة من أحدث التطورات بأقل جهد ممكن، والأفضل من ذلك أنه تتألف المنظومة الخاصة بـ Angular من مجموعة متنوعة تضم أكثر من 1.7 مليون مطوِّر ومؤلف مكتبات ومنشئ محتوى.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		يمكنك الاطلاع على <a href="https://angular.io/generated/live-examples/what-is-angular/stackblitz.html" rel="external nofollow">المثال في بيئة تجريبية</a>/ أو <a href="https://angular.io/generated/zips/what-is-angular/what-is-angular.zip" rel="external nofollow">تنزيله</a> لتحصل على شيفرة تعمل وتحوي جميع مقتطفات الشيفرة الموجودة ضمن هذا المقال.
	</p>
</blockquote>

<h2>
	تطبيقات Angular: الأساسيات
</h2>

<p>
	يشرح هذا القسم الفكرة الأساسية وراء إنشاء Angular، إذ سيساعدك فهم الأفكار الواردة في هذا القسم في تصميم وبناء تطبيقاتك بصورة فعالة أكثر.
</p>

<h3>
	المكونات
</h3>

<p>
	تُعَدّ المكوِّنات أحجار البناء الأساسية التي تكوِّن التطبيق، فيحتوي المكوِّن على صنف TypeScript مع مزخرِف ‎<a href="https://angular.io/api/core/Component" rel="external nofollow"><code>@Component()‎</code></a> وقالب <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> وملف التنسيقات، حيث يعرِّف مزخرِف <code>‎@Component()‎</code> المعلومات التالية في Angular:
</p>

<ul>
<li>
		محدِّد CSS الذي يعرِّف الطريقة التي سيُستخدَم بها المكوِّن ضمن قالب ما، حيث تصبح عناصر HTML التي تستخدمها ضمن القالب الخاص بك والتي تطابق هذا المحدِّد نسخةً من هذا المكوِّن.
	</li>
	<li>
		قالب HTML الذي يوجّه Angular إلى كيفية إخراج هذا المكوِّن.
	</li>
	<li>
		مجموعة اختيارية من تنسيقات <a href="wiki.hsoub.com/CSS" rel="">CSS</a> التي تعرِّف المظهر الخاص بعناصر قالب HTML.
	</li>
</ul>
<p>
	يُعَدّ الكود التالي أبسط صورة لمكوِّن Angular:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_5423_6" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Component</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@Component</span><span class="pun">({</span><span class="pln">
  selector</span><span class="pun">:</span><span class="pln"> </span><span class="str">'hello-world'</span><span class="pun">,</span><span class="pln">
  </span><span class="kwd">template</span><span class="pun">:</span><span class="pln"> </span><span class="str">`
    &lt;h2&gt;Hello World&lt;/h2&gt;
    &lt;p&gt;This is my first component!&lt;/p&gt;
    `</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">HelloWorldComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// تقود الشيفرة الموجودة في هذا الصنف السلوك الخاص بالمكوِّن</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يجب كتابة الشيفرة التالية ضمن قالب HTML الخاص بك من أجل استخدام هذا المكوِّن:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5423_8" style="">
<span class="tag">&lt;hello-world&gt;&lt;/hello-world&gt;</span></pre>

<p>
	سيكون ناتج <a href="https://academy.hsoub.com/programming/html/%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%B4%D8%AC%D8%B1%D8%A9-dom-%D8%A7%D9%84%D8%AE%D9%81%D9%8A%D8%A9-r1353/" rel="">شجرة DOM</a> عندما يقوم Angular بإخراج هذا المكوِّن وتصييره على الصورة التالية:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5423_10" style="">
<span class="tag">&lt;hello-world&gt;</span><span class="pln">
    </span><span class="tag">&lt;h2&gt;</span><span class="pln">Hello World</span><span class="tag">&lt;/h2&gt;</span><span class="pln">
    </span><span class="tag">&lt;p&gt;</span><span class="pln">This is my first component!</span><span class="tag">&lt;/p&gt;</span><span class="pln">
</span><span class="tag">&lt;/hello-world&gt;</span></pre>

<p>
	يوفِّر نموذج مكوِّنات Angular تغليفًا متينًا وبنية تطبيق سهلة الاستخدام، كما تجعل المكوِّنات تطبيقك مريحًا جدًا عند إجراء اختبار <a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%81%D9%8A-angularjs-r196/" rel="">الوحدة</a> وتحسِّن من قابلية القراءة للشيفرة الخاصة بك.
</p>

<h3>
	القوالب
</h3>

<p>
	يملك كل مكوِّن قالب HTML يحدِّد كيفية إخراج هذا المكوِّن وعرضه، كما يمكنك تعريف هذا القالب عن طريق كتابة الشيفرات بصورة خطية أو عن طريق ملف خارجي يُحدَّد مساره، في حين يوسِّع Angular لغة HTML من خلال بنى لغوية إضافية والتي تمكنك من إضافة قيم متغيرة من خلال المكوِّن الخاص بك، حيث تقوم Angular تلقائيًا بتحديث شجرة DOM المصيَّرة الناتجة عندما تتغير حالة المكوِّن الخاص بك، وتتمثل إحدى التطبيقات على هذه الميزة في إدراج نص آلي كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5423_20" style="">
<span class="pun">&lt;</span><span class="pln">p</span><span class="pun">&gt;{{</span><span class="pln"> message </span><span class="pun">}}&lt;/</span><span class="pln">p</span><span class="pun">&gt;</span></pre>

<p>
	تأتي القيمة المرتبطة بالرسالة عن طريق صنف المكوِّن:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_5423_18" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Component</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@Component</span><span class="pln"> </span><span class="pun">({</span><span class="pln">
  selector</span><span class="pun">:</span><span class="pln"> </span><span class="str">'hello-world-interpolation'</span><span class="pun">,</span><span class="pln">
  templateUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./hello-world-interpolation.component.html'</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">HelloWorldInterpolationComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    message </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Hello, World!'</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تكون النتيجة التي يراها المستخدِم عندما يحمِّل التطبيق المكوِّن وقالبه كما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5423_23" style="">
<span class="tag">&lt;p&gt;</span><span class="pln">Hello, World!</span><span class="tag">&lt;/p&gt;</span></pre>

<p>
	يُخبر استخدام الأقواس المعقوصة بأنه يجب على Angular استبدال القيمة الموجودة ضمنها بالقيمة المقابلة لها والموجودة ضمن صنف المكوِّن، كما يدعم Angular عملية التربيط بين الخاصيات property bindings من أجل مساعدتك على ضبط القيم لكل من الخصائص والسمات لعناصر HTML وتمرير هذه القيم إلى قسم العرض الخاص بتطبيقك.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5423_25" style="">
<span class="tag">&lt;p</span><span class="pln"> [</span><span class="atn">id</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"sayHelloId"</span><span class="pln"> [</span><span class="atn">style</span><span class="pln">.</span><span class="atn">color</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"fontColor"</span><span class="tag">&gt;</span><span class="pln">You can set my color in the component!</span><span class="tag">&lt;/p&gt;</span></pre>

<p>
	تشير الأقواس المعقوفة هنا إلى عملية ربط الخاصية أو السمة بقيمة موجودة ضمن صنف المكوِّن، كما يمكنك في Angular أيضًا تعريف مستمعي الحدث والتي مهمتها الاستماع إلى الأحداث الخاصة بالمستخدِم والاستجابة لها مثل حدث الضغط على زر ما وحركات الفأرة والنقرات وحتى اللمسات، حيث يمكنك تعريف مستمع الحدث من خلال تحديد اسم الحدث ضمن أقواس:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5423_27" style="">
<span class="tag">&lt;button</span><span class="pln"> (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"sayMessage()"</span><span class="pln"> [</span><span class="atn">disabled</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"canClick"</span><span class="tag">&gt;</span><span class="pln">Trigger alert message</span><span class="tag">&lt;/button&gt;</span></pre>

<p>
	يستدعي المثال السابق تابعًا والذي عُرِّف ضمن صنف المكوِّن:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_5423_30" style="">
<span class="pln">sayMessage</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    alert</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">message</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُعَدّ المثال التالي مثالًا شاملًا لتوضيح كل من عمليات التبديل والتربيط بين الخاصيات والتربيط مع الحدث ضمن قالب Angular:
</p>

<ul>
<li>
		الملف hello-world-bindings.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5423_33" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Component</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@Component</span><span class="pln"> </span><span class="pun">({</span><span class="pln">
  selector</span><span class="pun">:</span><span class="pln"> </span><span class="str">'hello-world-bindings'</span><span class="pun">,</span><span class="pln">
  templateUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./hello-world-bindings.component.html'</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">HelloWorldBindingsComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fontColor </span><span class="pun">=</span><span class="pln"> </span><span class="str">'blue'</span><span class="pun">;</span><span class="pln">
    sayHelloId </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    canClick </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
    message </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Hello, World'</span><span class="pun">;</span><span class="pln">
    sayMessage</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        alert</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">message</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<ul>
<li>
		الملف hello-world-bindings.component.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5423_35" style="">
<span class="tag">&lt;button</span><span class="pln">
  [</span><span class="atn">disabled</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"canClick"</span><span class="pln">
  (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"sayMessage()"</span><span class="tag">&gt;</span><span class="pln">
  Trigger alert message
</span><span class="tag">&lt;/button&gt;</span><span class="pln">
</span><span class="tag">&lt;p</span><span class="pln">
  [</span><span class="atn">id</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"sayHelloId"</span><span class="pln">
  [</span><span class="atn">style</span><span class="pln">.</span><span class="atn">color</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"fontColor"</span><span class="tag">&gt;</span><span class="pln">
  You can set my color in the component!
</span><span class="tag">&lt;/p&gt;</span><span class="pln">
</span><span class="tag">&lt;p&gt;</span><span class="pln">My color is {{ fontColor }}</span><span class="tag">&lt;/p&gt;</span></pre>

<p>
	أضف وظائف أخرى إلى القوالب الخاصة بك من خلال استخدام <a href="https://angular.io/guide/built-in-directives" rel="external nofollow">الموجِّهات directives</a>، وأكثر الموجِّهات شهرةً في Angular هي الموجه <a href="https://angular.io/api/common/NgIf" rel="external nofollow">‎*ngIf</a> والموجه <code>‎<a href="https://angular.io/api/common/NgForOf" rel="external nofollow">*ngFor</a></code>، حيث تستطيع الموجّهات أداء العديد من المهام المتنوعة مثل تعديل البنية الخاصة بشجرة DOM بصورة آلية وإنشاء الموجِّه الخاص بك من أجل خلق تجربة مستخدِم جيدة، وتُعَدّ الشيفرة التالية مثالًا عن استخدام الموجِّه <code>ngIf*</code>:
</p>

<ul>
<li>
		الملف hello-world-ngif.component.ts:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5423_39" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Component</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@Component</span><span class="pun">({</span><span class="pln">
    selector</span><span class="pun">:</span><span class="pln"> </span><span class="str">'hello-world-ngif'</span><span class="pun">,</span><span class="pln">
    templateUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./hello-world-ngif.component.html'</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">HelloWorldNgIfComponent</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  message </span><span class="pun">=</span><span class="pln"> </span><span class="str">'I\'m read only!'</span><span class="pun">;</span><span class="pln">
  canEdit </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">

  onEditClick</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">canEdit </span><span class="pun">=</span><span class="pln"> </span><span class="pun">!</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">canEdit</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">canEdit</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">message </span><span class="pun">=</span><span class="pln"> </span><span class="str">'You can edit me!'</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">message </span><span class="pun">=</span><span class="pln"> </span><span class="str">'I\'m read only!'</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<ul>
<li>
		الملف hello-world-ngif.component.html
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5423_47" style="">
<span class="tag">&lt;h2&gt;</span><span class="pln">Hello World: ngIf!</span><span class="tag">&lt;/h2&gt;</span><span class="pln">
</span><span class="tag">&lt;button</span><span class="pln"> (</span><span class="atn">click</span><span class="pln">)</span><span class="pun">=</span><span class="atv">"onEditClick()"</span><span class="tag">&gt;</span><span class="pln">Make text editable!</span><span class="tag">&lt;/button&gt;</span><span class="pln">
</span><span class="tag">&lt;div</span><span class="pln"> *</span><span class="atn">ngIf</span><span class="pun">=</span><span class="atv">"canEdit; else noEdit"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;p&gt;</span><span class="pln">You can edit the following paragraph.</span><span class="tag">&lt;/p&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span><span class="pln">
</span><span class="tag">&lt;ng-template</span><span class="pln"> #</span><span class="atn">noEdit</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;p&gt;</span><span class="pln">The following paragraph is read only. Try clicking the button!</span><span class="tag">&lt;/p&gt;</span><span class="pln">
</span><span class="tag">&lt;/ng-template&gt;</span><span class="pln">
</span><span class="tag">&lt;p</span><span class="pln"> [</span><span class="atn">contentEditable</span><span class="pln">]</span><span class="pun">=</span><span class="atv">"canEdit"</span><span class="tag">&gt;</span><span class="pln">{{ message }}</span><span class="tag">&lt;/p&gt;</span></pre>

<p>
	تسمح لك القوالب التصريحية declarative templates في Angular بفصل منطق التطبيق الخاص بك عن طريقة العرض، كما تستند القوالب إلى لغة HTML القياسية لسهولة البناء والصيانة والتحديث.
</p>

<h3>
	حقن التبعية
</h3>

<p>
	يتيح لك حقن التبعية Dependency injection تعريف التبعيات الخاصة بأصناف TypeScript الخاصة بك دون الاهتمام باستنساخها، إذ تقوم Angular بعملية الاستنساخ عوضًا عن ذلك، كما يوفِّر لك نمط التصميم بهذه الطريقة إمكانية كتابة شيفرة مرنة وسهلة الاختبار، فعلى الرغم من أن فهم مصطلح حقن التبعية ليس بهذا الأمر الجلل للبدء باستخدام Angular، إلا أننا نوصي به من أجل ممارسة أفضل، كما تستفيد العديد من الجوانب في Angular من ميزاته إلى حد ما، وإليك المثال التالي لتوضيح عملية حقن التبعية، حيث يعرِّف الملف الأول <code>logger.service.ts</code> صنف <code>Logger</code>، إذ يحتوي هذا الصنف على دالة <code>writeCount</code> والتي تطبع عددًا ضمن الطرفية:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_5423_49" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Injectable</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@Injectable</span><span class="pun">({</span><span class="pln">providedIn</span><span class="pun">:</span><span class="pln"> </span><span class="str">'root'</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Logger</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  writeCount</span><span class="pun">(</span><span class="pln">count</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">warn</span><span class="pun">(</span><span class="pln">count</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تاليًا، يُعرِّف ملف <code>hello- world- di.component.ts</code> مكوِّن Angular، حيث يحتوي هذا المكوِّن على زر يستخدِم دالة <code>writeCount</code> من صنف Logger، كما حُقِنت خدمة <code>Logger</code> في صنف <code>HelloWorldDI</code> من أجل استخدام هذه الدالة من خلال إضافة <code>private logger: Logger</code> إلى الباني.
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_5423_51" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Component</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular/core'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Logger</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'../logger.service'</span><span class="pun">;</span><span class="pln">

</span><span class="lit">@Component</span><span class="pun">({</span><span class="pln">
  selector</span><span class="pun">:</span><span class="pln"> </span><span class="str">'hello-world-di'</span><span class="pun">,</span><span class="pln">
  templateUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./hello-world-di.component.html'</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">HelloWorldDependencyInjectionComponent</span><span class="pln">  </span><span class="pun">{</span><span class="pln">
  count </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

  constructor</span><span class="pun">(</span><span class="kwd">private</span><span class="pln"> logger</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Logger</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  onLogMe</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">logger</span><span class="pun">.</span><span class="pln">writeCount</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">count</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">count</span><span class="pun">++;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	واجهة سطر أوامر Angular
</h3>

<p>
	تُعَدّ Angular CLI من أسرع الطرق المباشرة والمحبّذة لبناء تطبيقات Angular، إذ يقلل استخدام Angular CLI من حدوث المشاكل عند تشغيل المهام، وهنا نورد بعض الأمثلة:
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
<thead><tr>
<th>
				الأمر
			</th>
			<th>
				شرح الوظيفة
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				<a href="https://angular.io/cli/build" rel="external nofollow"><code>ng build</code></a>
			</td>
			<td>
				يترجِم تطبيق Angular إلى دليل الإخراج.
			</td>
		</tr>
<tr>
<td>
				<a href="https://angular.io/cli/serve" rel="external nofollow"><code>ng serve</code></a>
			</td>
			<td>
				يبني ويقدِّم التطبيق الخاص بك ويُعيد بناء التطبيق في حال حدوث تغيرات على الملف.
			</td>
		</tr>
<tr>
<td>
				<a href="https://angular.io/cli/generate" rel="external nofollow"><code>ng generate</code></a>
			</td>
			<td>
				يولِّد ويعدِّل الملفات بناءً على تخطيط مسبق.
			</td>
		</tr>
<tr>
<td>
				<a href="https://angular.io/cli/test" rel="external nofollow"><code>ng test</code></a>
			</td>
			<td>
				تشغيل اختبارات الوحدة unit tests على مشروع معطى.
			</td>
		</tr>
<tr>
<td>
				<a href="https://angular.io/cli/e2e" rel="external nofollow"><code>ng e2e</code></a>
			</td>
			<td>
				يبني ويقدِّم تطبيق Angular ثم يشغِّل اختبارات نهاية- إلى- نهاية.
			</td>
		</tr>
</tbody>
</table>
<p>
	ستجد أنّ Angular CLI أداة مفيدة جدًا من أجل بناء تطبيقاتك.
</p>

<h3>
	المكتبات الداخلية
</h3>

<p>
	قدَّم قسم "تطبيقات Angular: الأساسيات" في الأعلى لمحةً مختصرةً عن اثنين من العناصر الهرمية الرئيسية التي ستستخدمها عند إنشاء تطبيقات Angular، لكن تصبح الفوائد العديدة لـ Angular أكثر وضوحًا عندما ينمو تطبيقك وتود إضافة وظائف إضافية عليه مثل التنقل في الموقع أو إدخالات المستخدِم، لذا استخدِم منصة Angular لتدمج إحدى مكتبات Angular الداخلية العديدة، وتوجد بعض المكتبات المتاحة لك إضافتها كما يلي:
</p>

<table>
<thead><tr>
<th>
				المكتبة
			</th>
			<th>
				الشرح
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				Angular Router
			</td>
			<td>
				عملية تنقّل وتوجيه متقدمة من جانب العميل بالاستناد إلى مكوِّنات Angular، كما تدعم التحميل البطيء والمسارات المتداخلة ومطابقة مسار مخصص وغيرها الكثير.
			</td>
		</tr>
<tr>
<td>
				Angular Forms
			</td>
			<td>
				نظام موحَّد للمشاركة في النموذج والتحقق منه.
			</td>
		</tr>
<tr>
<td>
				Angular HttpClient
			</td>
			<td>
				عميل HTTP قوي يمكنه إجراء اتصالات أكثر تقدمًا بين الخادم والعميل.
			</td>
		</tr>
<tr>
<td>
				Angular Animations
			</td>
			<td>
				نظام غني للتحكم بالحركات في الموقع بالاعتماد على حالة التطبيق.
			</td>
		</tr>
<tr>
<td>
				Angular PWA
			</td>
			<td>
				أدوات لبناء <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%AA%D9%82%D8%AF%D9%85%D9%8A%D8%A9-pwa-r832/" rel="">تطبيقات الويب التقدمية PWA</a> تتضمن عامل الخدمة service worker وقائمة تطبيق الويب Web app manifest.
			</td>
		</tr>
<tr>
<td>
				Angular Schematics
			</td>
			<td>
				شيفرة مساعدة آلية بالإضافة إلى إعادة التوليد وأدوات التحديث التي تبسِّط عملية التطوير على نطاق واسع.
			</td>
		</tr>
</tbody>
</table>
<p>
	تعمل هذه المكتبات على توسيع وظائف التطبيق الخاص بك، كما تسمح لك بالتركيز بصورة أكبر على الميزات التي تجعل تطبيقك فريدًا، لهذا عليك إضافة هذه المكتبات المصممة لتتكامل بسلاسة مع إطار العمل Angular وتحديثه، ويمكنك استخدام هذه المكتبات فقط عندما يمكنها مساعدتك في إضافة وظائف أخرى إلى تطبيقاتك أو في حل مشكلة معيَّنة.
</p>

<h2>
	الخطوات التالية
</h2>

<p>
	يهدف هذا المقال إلى إعطائك لمحةً موجزةً عن Angular والمزايا التي يوفِّرها وما الذي تأمل أن تبنيه عندما تبدأ في إنشاء تطبيقاتك، كما يمكنك الانتقال إلى المقال التالي <a href="https://academy.hsoub.com/programming/javascript/angular/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-angular-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1380/" rel="">كيفية استعمال Angular في بناء تطبيقات الويب</a> لتشاهد طريقة عمل Angular، حيث تستخدِم هذه المقالة بيئة <a href="https://stackblitz.com/" rel="external nofollow">stackblitz.com</a> التجريبية لتتمكن من تجربة أمثلة عملية من Angular دون تثبيت أي متطلبات.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://angular.io/guide/what-is-angular" rel="external nofollow">What is Angular?‎</a> من موقع <a href="angular.io" rel="">angular.io</a> الرسمي.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-angularjs-r171/" rel="">مدخل إلى تعلم AngularJS</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r662/" rel="">تعلم البرمجة</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1379</guid><pubDate>Fri, 05 Nov 2021 16:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x648;&#x62C;&#x64A;&#x647; Routing &#x641;&#x64A; AngularJS</title><link>https://academy.hsoub.com/programming/javascript/angular/%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%8A%D9%87-routing-%D9%81%D9%8A-angularjs-r225/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2016_01/angularjs-routing_(1).png.8dbe4b94600e39626be000145b40e48a.png" /></p>

<p style="text-align: center;"><a href="https://academy.hsoub.com/uploads/monthly_2016_01/angularjs-routing_(1).png.8e9b91ee0a8808660881c5ccfa4c4633.png" class="ipsAttachLink ipsAttachLink_image"><img data-fileid="10890" src="https://academy.hsoub.com/uploads/monthly_2016_01/angularjs-routing_(1).thumb.png.832d999e9f845bff9c75ea9d197a302d.png" class="ipsImage ipsImage_thumbnailed" alt="angularjs-routing_(1).thumb.png.832d999e"></a></p><p>تقتصر بعض تطبيقات Angular على كونها أدوات مساعدة في صفحة ويب تقليدية، إلا أنّ الأغلبية العُظمى لتطبيقاتها هي التطبيقات وحيدة الصفحة (<a rel="external nofollow" href="http://en.wikipedia.org/wiki/Single-page_application">single-page applications</a>)، التي تستبدل تصفح الويب المعتمد على المتصفّح بواجهاتٍ وانتقالاتٍ مشابهة في صفحةٍ واحدة، مقدمة للمستخدم تجربة تفاعليّة رائعة. إلّا أنّ كتابة تطبيقات وحيدة الصفحة بطريقةٍ بسيطة يجعلنا نخسر شيئًا قيّمًا في التطبيقات المعتمدة على المتصفّح وهو الرّابط URL. ففي تطبيقٍ بسيطٍ وحيد الصفحة، سيكون هناك رابط URL واحد فقط، ولن تكون هناك طريقة لمشاركة روابطَ مخصصة لكلّ مورد (resource) على حدة، كتعليق محدّد على تدوينة ما على سبيل المثال. وعلى العكس، فسيقوم تطبيق تقليدي في طرف المخدّم يتبع نمط REST (تبادل الحالة ضمن رابط HTML) بإدارة الواجهات كبنية هرميّة من الروابط المنظّمة سهلة القراءة والتي تظهر حالة التّطبيق الحاليّة بوضوح. تُقدّم هذه الروابط طريقةً للمستخدم ليعود إلى حالةٍ من حالات التطبيق في وقتٍ لاحق، أو ليشارك هذه الحالة مع الآخرين.</p><p>يُعرَّف التوجيه في تطبيق ويب تقليديّ بأنّه صلة الوصل بين عمليّات HTTP مثل (GET وPOST وما إلى ذلك) وأنماط الروابط وبين المتحكّمات. إن لم تكن معتادًا على<span style="line-height: 22.4px;"> التوجيه </span>من طرف المخدّم فسيقدّم لك <a rel="external nofollow" href="http://expressjs.com/guide/routing.html">دليل التوجيه السريع</a> مقدّمةً سهلةً ل<span style="line-height: 22.4px;">لتوجيه</span> في JavaScript. على أي حال، <span style="line-height: 22.4px;">فالتوجيه</span> من طرف المستخدم يقلب الحالة رأسًا على عقب. فبدلًا من أن يتمّ التفاعل مع الأفعال التي ينقلها لنا المتصفّح، فسنحتاج إلى إبقاء المتصفّح متابعًا للتحديثات بينما يتفاعل التطبيق مباشرةً مع مدخلات المستخدم. كمثالٍ على ذلك، كيف يمكننا الإبقاء على شريط التّنقل محدّثًا بروابط تمثّل الحالة بدقّة، وفي نفس الوقت نستجيب لروابط تُدخل فيه أو تُحمّل من العلامات المرجعية؟ لحُسن الحظ، فقد تمّ إيجاد الوحدة <a rel="external nofollow" href="https://docs.angularjs.org/api/ngRoute">ngRoute</a> لتساعدنا في هذا العمل.</p><p>لنتمكّن من استخدام هذه الوحدة، سنحتاج إلى تحميل ملف <span style="font-family:courier new,courier,monospace;">angular-route.js</span> إضافةً إلى ملف مكتبة Angular الرئيسي. كما سنقوم بتحميل بيئة <a rel="external nofollow" href="http://getbootstrap.com/">Bootstrap</a> لاستخدام أنماط CSS الخاصة بها.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;base href="/"&gt;
&lt;link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"&gt;
&lt;script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.2/angular.js"&gt;&lt;/script&gt;
&lt;script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.2/angular-route.js"&gt;&lt;/script&gt;</pre><p>لاحظ تعريف العنصر <a rel="external nofollow" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base">base</a> في السطر الأول في المثال السابق. يُعرِّف هذا العنصر رابط URL القاعديّ لتطبيق Angular الحالي، وهذا العنصر يطلبه دعم Angular لـ<a rel="external nofollow" href="https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history">توابع HTML5 الخاصة بالتاريخ</a> (سنتحدث عنها أدناه). إن كنت تتساءل لمَ تم إسناد الخاصية <span style="font-family:courier new,courier,monospace;">href</span> إلى مسار الجذر، بدلًا من أن تكون مسندةً إلى مسار الصفحة، فهذا لأنّ أمثلة هذه السلسلة تعمل داخل أُطُر iframe خاصة لكل واحد منها.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;body ng-app="app"&gt;
  &lt;!-- كل الأمثلة توضع هنا --&gt;
&lt;/body&gt;</pre><p>بعد ذلك، سيكون علينا جلب <span style="font-family:courier new,courier,monospace;">ngRoute</span> عند تهيئة الوحدة الجذر، وقد قمنا بتغطية هذه الفكرة بعمق في فصل <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%81%D9%8A-angularjs-r196/">الوحدات</a>.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app', ['ngRoute']);</pre><p>وهكذا نكون قد أتممنا تحميل الوحدة <span style="font-family:courier new,courier,monospace;">ngRoute</span> في تطبيقنا.</p><h2>routeProvider$</h2><p>لنتمكن من استخدام<span style="font-family:courier new,courier,monospace;"> ngRoute</span>، سنحتاج إلى ربط مسار URL نسبي واحدٍ أو أكثر مع معالجاتٍ (handlers). يتيح لنا التابع <a rel="external nofollow" href="https://docs.angularjs.org/api/ng/type/angular.Module#config">module.config</a> إعداد وحداتٍ مثل <span style="font-family:courier new,courier,monospace;">ngRoute</span> بعد أن تقوم Angular بتحميلها. كل ما علينا هنا هو معرفة اسم الخدمة (service) أو المزوّد (<a rel="external nofollow" href="https://docs.angularjs.org/guide/providers">provider</a>) الذي سيقوم بإعداد الوحدة. في حالة <span style="font-family:courier new,courier,monospace;">ngRoute</span> يُسمّى المزوِّد بـ<a rel="external nofollow" href="https://docs.angularjs.org/api/ngRoute/provider/%24routeProvider">routeProvider$</a>. سنتمكن عند وضع هذا الاسم في تابع الاستدعاء الخلفي config من عمل سلسلةٍ من عمليّات ربط المسارات باستخدام التابع when. الوسيط الأول للتابع <a rel="external nofollow" href="https://docs.angularjs.org/api/ngRoute/provider/%24routeProvider#when">when</a> هو المسار النسبي الخاص بوجهة<span style="line-height: 22.4px;"> التوجيه</span>، أما الوسيط الثاني فهو كائن الإعدادات الذي يحدد الكيفية التي سيتم بها معالجة محتوى وجهة<span style="line-height: 22.4px;"> التوجيه</span>.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .config(function($routeProvider) {
    $routeProvider
      .when('/', {
        template: "&lt;a href='#/about'&gt;About&lt;/a&gt;"
      })
      .when('/about', {
        template: "&lt;a href='#/'&gt;Home&lt;/a&gt;"
      });
  });</pre><p>يشبه الخيار template الذي يظهر في المثال السابق ذلك الذي استخدمناه في إعداد <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%8A%D9%87%D8%A7%D8%AA-directives-%D9%81%D9%8A-angularjs-r202/">التوجيهات</a> سابقًا. لا يحوي المثال السابق أي محتوىً متغيّر، فقط نص HTML ثابت. يُقدّم كلّ قالبٍ رابطًا إلى المسار النسبي للآخرين، لذا سنتمكن في المثال السابق من التبديل بين الاثنين. قد تتساءل أين سيتم وضع المحتوى المعالَج، حسنًا، تُقدّم الوحدة ngRoute توجيهًا مُميّزًا، هو <a rel="external nofollow" href="https://docs.angularjs.org/api/ngRoute/directive/ngView">ng-view</a>، ويجب أن يكون هذا التوجيه موجودًا في وحدتنا لتعمل. تتكوّن القوالب الرئيسي في هذا الفصل كلّها من عنصرdiv خالٍ يوجد فيه التوجيه ng-view، وكلّ ما سوى ذلك سيقوم المعالج (handler) بمعالجته للمسار الحالي.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;div ng-view&gt;&lt;/div&gt;</pre><p>جرب النقر على الرابط الناتج لتتم معالجة الرابط للمورد الآخر. لم يعمل؟ حسنًا، لقد تجاهلنا أمرًا هامًا، إن نظرت إلى شريط التنقل الخاص بمتصفحك أثناء نقر الرابط فلن تراه قد تغير ليلائم المسار النسبي الصحيح، في الواقع، ليس هناك أي أمرٍ خاطئ في النص البرمجي الخاص بالمثال، وفي التطبيقات الحقيقية ستلاحظ التغيير، فكلّ ما في الأمر أن البيئة التفاعلية التي تمّ استضافة الأمثلة في داخلها ضمن هذه السلسلة لا تسمح لنا برؤية التغيير في الصفحة الرئيسية للفصل.</p><h2>الخدمة location$</h2><p>جميع أمثلة هذه السلسلة موضوعة داخل صناديق sandbox داخل أُطر iframe الخاصة بكل واحد منها. (يُمكنك إلقاء نظرة على الرابط التالي <a rel="external nofollow" href="https://github.com/Codecademy/stuff.js">Codecademy/stuff.js</a> إن كان لديك فضول لمعرفة طريقة القيام بذلك.) وهذا ممتازٌ لعزل البيئة البرمجية، إلا أنّه يمنعك من رؤية رابط الـURL الخاص بالأمثلة في شريط الانتقال. ومن حسن الحظ أنّه توجد طريقةٌ للتحايل على هذا الأمر، مع فائدةٍ إضافيّة في استكشاف خدمةٍ مفيدةٍ في Angular تسمح بمعاينة رابط URL الحالي، وهي الخدمة <a rel="external nofollow" href="http://docs.angularjs.org/api/ng.%24location">location$</a>.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .controller('LocationController', function($scope, $location) {
    $scope.location = $location.absUrl();
  });</pre><p>يتم في المثال السابق التصريح عن <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D9%85%D8%AA%D8%AD%D9%83%D9%85%D8%A7%D8%AA-controllers-%D9%81%D9%8A-angularjs-r186/">متحكم</a> بسيطٍ يستقبل الخدمة <span style="font-family:courier new,courier,monospace;">location$</span> عن طريق <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%AD%D9%82%D9%86-%D8%A7%D9%84%D8%AA%D8%A8%D8%B9%D9%8A%D8%A9-dependency-injection-%D9%81%D9%8A-angularjs-r198/">حقن التبعية</a>، ويستخدمها للدخول إلى رابط URL المُطلق الحالي للتطبيق، حيث تكون القيمة هي دمجًا للرابط الثابت للجذر الخاص بالصفحة المغلّفة للمثال مع رابط الامتداد الذي تُديره الوحدة <span style="font-family:courier new,courier,monospace;">ngRoute</span>.</p><h2>المتحكم</h2><p>يُمكننا أن نقوم بتحميل المتحكّم <span style="font-family:courier new,courier,monospace;">LocationController</span> في المثال السابق بالطريقة المعيارية باستخدام التّوجيه <a rel="external nofollow" href="http://docs.angularjs.org/api/ng.directive:ngController">ng-controller</a>، إلّا أنّه بإمكاننا أيضًا أن نقوم بإعداد متحكّمٍ كخيار (option)، كما سنضيف خاصّيّة المجال location إلى قالبنا.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .config(function($routeProvider) {
    $routeProvider
      .when('/', {
        controller: 'LocationController',
        template: '&lt;div class="well well-sm" ng-bind="location"&gt;&lt;/div&gt;\
                   &lt;a href="#/about"&gt;About&lt;/a&gt;'
      })
      .when('/about', {
        controller: 'LocationController',
        template: '&lt;div class="well well-sm" ng-bind="location"&gt;&lt;/div&gt;\
                   &lt;a href="#/"&gt;Home&lt;/a&gt;'
      });
  });</pre><p>ها نحن ذا، صار لدينا الآن محاكٍ لشريط الانتقال لأمثلتنا، ولكن للأسف لن تتمكّن من تعديلها لرؤية الكيفية التي ستدير بها الوحدة <span style="font-family:courier new,courier,monospace;">ngRoute</span> التغييرات بمعالجتها وإظهار محتوىً جديد، وسيكون عليك اختبار ذلك في تطبيقاتك الخاصة.</p><h2>رمز Hashbang</h2><p>تتوقّع وحدة <span style="font-family:courier new,courier,monospace;">ngRoute</span> في الحالة الافتراضية أن تبدأ مسارات URL النسبيّة برمز hash (#)، ويُمكنك بسهولةٍ تغييره إلى البادئة hashbang متمثّلة بالرمز (!#) بإضافة إعداداتٍ إلى الخدمة<span style="font-family:courier new,courier,monospace;"> locationProvider$</span> كما يبيّن المثال التالي. لاحظ أنّه يجب علينا أيضًا إضافة رمز ! إلى الروابط في قوالبنا.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .config(function($locationProvider) {
    $locationProvider.hashPrefix('!');
  })
  .config(function($routeProvider) {
    $routeProvider
      .when('/', {
        controller: 'LocationController',
        template: '&lt;div class="well well-sm" ng-bind="location"&gt;&lt;/div&gt;\
                   &lt;a href="#!/about"&gt;About&lt;/a&gt;'
      })
      .when('/about', {
        controller: 'LocationController',
        template: '&lt;div class="well well-sm" ng-bind="location"&gt;&lt;/div&gt;\
                   &lt;a href="#!/"&gt;Home&lt;/a&gt;'
      });
  });</pre><p>لا يزال رمز البادئة hashbang مصادقًا عليه من Google في دليل Webmasters الخاص بها، وتحديدًا في المقطع <a rel="external nofollow" href="https://developers.google.com/webmasters/ajax-crawling/docs/getting-started">Making AJAX Applications Crawlable</a>، الذي ينص على أنّ "أقسام الـhash يجب أن تبدأ بعلامة تعجّب"، على أيّ حالٍ، طالما أنّك تقوم بالخطوات اللازمة للتّأكّد من ملاءمة موقعك لقواعد SEO فقد تجد أنّه من الأفضل أن يكون تطبيقك مخدومًا بأسلوبٍ خالٍ من البادئة، وهذا الأسلوب مفعّلٌ في HTML5 الآن.</p><h2>الكائن History من HTML5</h2><p>يُقدّم الكائن <a rel="external nofollow" href="https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history">window.history</a> تحكّمًا بالتّنقّل في المتصفّحات الحديثة، مما يقدّم تجربة مستخدمٍ سلسة.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .config(function($locationProvider) {
    $locationProvider.html5Mode(true);
  });</pre><p>يُمكننا كتابة قيم href الخاصة بنا كمسارٍ بسيط، بدون البادئة # ولا البادئة !#.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .config(function($routeProvider) {
    $routeProvider
      .when('/', {
        controller: 'LocationController',
        template: '&lt;div class="well well-sm" ng-bind="location"&gt;&lt;/div&gt;\
                   &lt;a href="/about"&gt;About&lt;/a&gt;'
      })
      .when('/about', {
        controller: 'LocationController',
        template: '&lt;div class="well well-sm" ng-bind="location"&gt;&lt;/div&gt;\
                   &lt;a href="/"&gt;Home&lt;/a&gt;'
      });
  });</pre><p>استخدام الكائن history من HTML5 وسيلةٌ ممتازةٌ إن كنت غير مضطرٍّ لدعم متصفحاتٍ قديمة في تطبيقك.</p><h2>العنصر templateUrl</h2><p>لنقم الآن بتحليل تطبيقنا البسيط إلى مكوناته الأولية. كما قمنا سابقًا مع <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%8A%D9%87%D8%A7%D8%AA-directives-%D9%81%D9%8A-angularjs-r202/">التوجيهات</a> بجعل شيفراتنا البرمجية أكثر قابلية للإدارة عن طريق استخراج قوالبنا (حتى الصغيرة منها) إلى ملفاتٍ مستقلة، فسيكون علينا للقيام بذلك فقط استبدال العناصر template في إعداداتنا بالعناصر <span style="font-family:courier new,courier,monospace;">templateUrl</span>.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .config(function($routeProvider) {
    $routeProvider
      .when('/', {
        controller: 'LocationController',
        templateUrl: '/views/index.html'
      })
      .when('/about', {
        controller: 'LocationController',
        templateUrl: '/views/about.html'
      });
  });</pre><p>دون تغييرٍ على صفحة About التي سنضعها في مجلد <span style="font-family:courier new,courier,monospace;">views</span>.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;div class="well well-sm" ng-bind="location"&gt;&lt;/div&gt;
&lt;a href="/"&gt;Home&lt;/a&gt;
&lt;h4&gt;About&lt;/h4&gt;</pre><p>سنتكلّم في الفقرات التالية عن فكرة التعامل مع روابط URL غير الصالحة، لذا لنقم بإضافة واحدٍ منها الآن.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;div class="well well-sm" ng-bind="location"&gt;&lt;/div&gt;
&lt;a href="/about"&gt;About&lt;/a&gt; |
&lt;a href="/bad-path"&gt;Bad path&lt;/a&gt;
&lt;h4&gt;Home&lt;/h4&gt;</pre><p>جرّب النقر على Bad path في المثال السابق، ستنتهي التّسلية للأسف، وستضطر لإعادة إنعاش الصفحة لاستعادتها.</p><h2>التابع otherwise</h2><p>ما الذي يمكننا فعله لتجنب النهايات الحزينة كما حدث قبل قليل؟ قد يكون أحد الإجابات هو تجنب تقديم روابط غير صالحة، ولكن في التطبيقات الحقيقيّة لا يمكننا إيقاف المستخدم عن الكتابة في شريط التنقل. يُمكننا أن نُعِدّ المُخدّم ليقوم بإعادة توجيهٍ للروابط غير الصالحة إلى رابط الجذر لتطبيق Angular، ولكن هذا يعني إعادة تحميل الصفحة من جديد، مما يعني إعادة تشغيل التطبيق من البداية. كيف يُمكننا بناء تطبيقٍ من طرف المستخدم ليقوم بإعادة توجيهٍ للروابط غير الصالحة؟</p><p>لو افترضنا بأننا نريد أن نُعلم المستخدم بأن الرابط غير صالح قبل أن نقوم بإعادة التوجيه، فالأمر الأول الذي نحتاجه هو الواجهة التي ستظهر لأي رابط غير صالح. سنسُمّي هذا القالب 404 ليتناسب مع معناه، وطبعًا يمكنك تسميته بأي اسمٍ آخر.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;div class="well well-sm" ng-bind="location"&gt;&lt;/div&gt;
&lt;a href="/"&gt;Home&lt;/a&gt;
&lt;h4 class="text-danger"&gt;404 - Not Found&lt;/h4&gt;</pre><p>كلّ ما نحتاجه الآن هو عبارة <span style="font-family:courier new,courier,monospace;">when</span> أخرى لإضافة المسار 404/. ثم لربط أي رابط غير متوافق مع الروابط الموجودة بالمسار 404/، وسنقوم بذلك بإضافة استدعاءٍ للتابع <a rel="external nofollow" href="https://docs.angularjs.org/api/ngRoute/provider/%24routeProvider#otherwise">otherwise</a> الذي يقوم بإضافة المسار إلى العنصر <span style="font-family:courier new,courier,monospace;">redirectTo</span> فيه. في المثال التالي، سيتم تشغيل الاستدعاء للتابع config إضافةً إلى كل الإعدادات السابقة. (يُمكنك تسجيل عددٍ غير محدودٍ من الاستدعاءات الخلفية (callbacks) للتابع config في المزوِّد (provider))</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .config(function($routeProvider) {
    $routeProvider
      .when('/404', {
        controller: 'LocationController',
        templateUrl: '/views/404.html'

      })
      .otherwise({
        redirectTo: '/404'
      });
  });</pre><p>جّرب النقر الآن على Bad path. صحيحٌ أنّ بيئة الأمثلة لا تسمح لك بإدخال مسارٍ عشوائيّ في شريط التصفح، إلّا أنّه يمكنك تعديل <span style="font-family:courier new,courier,monospace;">"href="/bad-path</span> في القالب السابق إلى أيّ قيمة تريدها لترى كيف سيتم التعامل معها. ربما يجب عليك دومًا إضافة معالجٍ (handler) ليتعامل مع كل الاحتمالات الباقية في إعدادات<span style="line-height: 22.4px;"> التوجيه </span>الخاصة بك.</p><h2>الأحداث (Events)</h2><p>تُقدّم Angular تطبيقًا للرسائل من نمط (<a rel="external nofollow" href="http://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern">publish-subscribe</a>) لأحداث التطبيق، وهي تعتمد على ثلاث توابع: <a rel="external nofollow" href="https://docs.angularjs.org/api/ng/type/%24rootScope.Scope#%24emit">emit$</a> و<a rel="external nofollow" href="https://docs.angularjs.org/api/ng/type/%24rootScope.Scope#%24broadcast">broadcast$</a> و<a rel="external nofollow" href="https://docs.angularjs.org/api/ng/type/%24rootScope.Scope#%24on">on$</a>، حيث يقوم التابع <span style="font-family:courier new,courier,monospace;">emit$</span> و<span style="font-family:courier new,courier,monospace;"> broadcast$</span> بتفعيل نشر أحداثٍ مخصصة، أما التابع on$ فيقوم بتسجيل معالجات (handlers) للأحداث التي تقوم بنشرها الوحدة <span style="font-family:courier new,courier,monospace;">ngRoute</span>. فمثلًا، عندما يتمّ تغيير المسار بنجاح سينتج عن ذلك نشر الأحداث التالية:</p><ul><li><a rel="external nofollow" href="http://docs.angularjs.org/api/ngRoute.%24route#events_%24routechangestart">routeChangeStart$</a></li><li><a rel="external nofollow" href="http://docs.angularjs.org/api/ng.%24location#events_%24locationchangestart">locationChangeStart$</a></li><li><a rel="external nofollow" href="http://docs.angularjs.org/api/ng.%24location#events_%24locationchangesuccess">locationChangeSuccess$</a></li><li><a rel="external nofollow" href="http://docs.angularjs.org/api/ngRoute.%24route#events_%24routechangesuccess">routeChangeSuccess$</a></li></ul><p>هذه الأحداث مرتبةٌ في القائمة بنفس ترتيب ظهورها أثناء دورة حياة عملية تغيير المسار، لنقُم بإثبات ذلك.</p><h3>تسجيل معالج للحدث (event handler)</h3><p>لنستخدم التابع <span style="font-family:courier new,courier,monospace;">on$</span> لتسجيل معالجٍ بسيط لأحداث المسار، حيث سيقوم هذا المعالج بجمع أسماء الأحداث في مصفوفة، ثم سنقوم لاحقًا بطباعة أسماء الأحداث في الصفحة بنفس الترتيب الذي حُفظت به.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .value('eventsLog', [])
  .factory('logEvent', function(eventsLog) {
    return function(event) {
      eventsLog.push(event.name);
    };
  });</pre><p>في هذا المثال، سنقوم بتسجيل الأحداث أثناء انتقالنا من مسار الجذر (والمسؤول عنه هو المتحكم <span style="font-family:courier new,courier,monospace;">HomeController</span>) إلى المسار <span style="font-family:courier new,courier,monospace;">events/ </span>(والمسؤول عنه المتحكم <span style="font-family:courier new,courier,monospace;">EventsController</span>). سنقوم أيضًا بجعل المتحكّم يضيف اسمه إلى القائمة أيضًا لنعرف وقت استدعاء هذا المتحكم.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .controller('HomeController', function($scope, $location, $rootScope, eventsLog, logEvent) {
    $scope.location = $location.absUrl();
    $scope.link = {path: '/events', title: 'Events'};
    eventsLog.push("HomeController: " + $location.path());

    $rootScope.$on('$routeChangeStart', logEvent);
    $rootScope.$on('$locationChangeStart', logEvent);
    $rootScope.$on('$locationChangeSuccess', logEvent);
    $rootScope.$on('$routeChangeSuccess', logEvent);

    $scope.eventsLog = eventsLog;
  });</pre><p>يقوم المتحكم <span style="font-family:courier new,courier,monospace;">EventsController</span> بإضافة اسمه إلى المصفوفة <span style="font-family:courier new,courier,monospace;">eventsLog</span> ثم يقوم بكشف (expose) السجل للمجال بحيث يمكننا رؤيته في الصفحة.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .controller('EventsController', function($scope, eventsLog, $location) {
    $scope.location = $location.absUrl();
    $scope.link = {path: '/', title: 'Home'};

    eventsLog.push("EventsController: " + $location.path());
    $scope.eventsLog = eventsLog;
  });</pre><p>نفس الواجهة ستعمل في كلا المتحكّمين. وسيتم عرض شريط انتقالٍ متغيّر إضافةً إلى محتويات السجل باستخدام التوجيه<span style="font-family:courier new,courier,monospace;"> ng-repeat</span>.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;div class="well well-sm" ng-bind="location"&gt;&lt;/div&gt;
&lt;a ng-href="{{link.path}}"&gt;{{link.title}}&lt;/a&gt;
&lt;ol&gt;
  &lt;li ng-repeat="event in eventsLog track by $index"&gt;
     {{event}}
  &lt;/li&gt;
&lt;/ol&gt;</pre><p>كملاحظةٍ جانبية، يُعدّ المرشح <a rel="external nofollow" href="http://docs.angularjs.org/api/ng.filter:json">json</a> المبني في Angular مفيدًا في التنقيح وإنشاء السجلات، فإن أردنا رؤية ما هو أكثر من اسم الحدث، فيُمكننا استخدامه لعرض كامل المحتويات لكائن الحدث.</p><p>سيكون علينا لإتمام المثال أن نكتب إعداداتٍ مباشرةً للمسارين.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .config(function($routeProvider) {
    $routeProvider
      .when('/', {
        controller: 'HomeController',
        templateUrl: '/views/events/index.html'
      })
      .when('/events', {
        controller: 'EventsController',
        templateUrl: '/views/events/index.html'
      });
  });</pre><p>انقر على الرابط في المثال السابق لرؤية الأحداث المسجلة في الصفحة.</p><h2>الموارد التي تتبع أسلوب REST</h2><p>يُقدّم أسلوب <a rel="external nofollow" href="http://en.wikipedia.org/wiki/Representational_state_transfer#Applied_to_web_services">REST</a> في تطوير تطبيقات الويب مجموعةً من المعايير لتنظيم عمليات CRUD (إنشاء، قراءة، تحديث و حذف) على مجموعات الموارد والموارد المفردة أيضًا. لن تحتاج عند استخدامك لـ Angular أن تتّبع أسلوب REST في<span style="line-height: 22.4px;"> التوجيه</span> إلا إذا قمت بتحميل الوحدة <a rel="external nofollow" href="https://docs.angularjs.org/api/ngResource">ngResource</a> التي لم نقم بتغطيتها في السلسلة. على أيّ حال، سنختم هذا الفصل بالقليل من<span style="line-height: 22.4px;"> التوجيه</span> المتّبع لأسلوب REST لتحصل على خبرةٍ في بعض الأمور العمليّة في<span style="line-height: 22.4px;"> التوجيه</span>.</p><p>سنحتاج إلى مجموعة موارد لنتمكّن من البدء، وسنعرض في الفصل التالي HTTP كيف نقوم بتحميل البيانات من مخدّمٍ في النهاية الخلفية (backend). أما الآن فسنقوم فقط بحقن مصفوفةٍ من كائناتٍ من نوع item في داخل المتحكّم.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .factory('Item', function(filterFilter) {

    var items = [
      {id: 1, name: 'Item 1', color: 'red'},
      {id: 2, name: 'Item 2', color: 'blue'},
      {id: 3, name: 'Item 3', color: 'red'},
      {id: 4, name: 'Item 4', color: 'white'}
    ];

    return {
      query: function(params) {
        return filterFilter(items, params);
      },
      get: function(params) {
        return this.query(params)[0];
      }
    };
  });</pre><p>لكلّ عنصرٍ في المصفوفة قيمة فريدة للخاصية <span style="font-family:courier new,courier,monospace;">id </span>فيه، مما يسمح لنا بالتعامل معه كموردٍ منفرد. تقوم الخدمة التي يُنشئها التابع <span style="font-family:courier new,courier,monospace;">factory</span> بتغليف الوصول إلى المصفوفة items بتابعين مفيدين هما: <span style="font-family:courier new,courier,monospace;">query</span> لمجموعة الموارد، <span style="font-family:courier new,courier,monospace;">وget</span> للموارد المنفردة. يستخدم التابع <span style="font-family:courier new,courier,monospace;">query</span> المرشّح ذا الاسم سيء الحظ، <a rel="external nofollow" href="http://docs.angularjs.org/api/ng.filter:filter">filter</a> (محقونٍ مع filterFilter) لتنفيذ <a rel="external nofollow" href="http://en.wikipedia.org/wiki/Query_by_Example">جلبٍ حسب المثال</a> باستخدام الوسيط <span style="font-family:courier new,courier,monospace;">params</span>، أمّا التابع <span style="font-family:courier new,courier,monospace;">get</span> فيقوم باستدعاء التابع <span style="font-family:courier new,courier,monospace;">query</span> ليقوم بإعادة موردٍ واحد. (راجع فصل <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D9%85%D8%B1%D8%B4%D8%AD%D8%A7%D8%AA-filters-%D9%81%D9%8A-angularjs-r201/">المرشحات</a> إن كنت قد تجاوزته، لمعلوماتٍ إضافيّة عن المرشّحات في Angular.)</p><p>أوّل شيءٍ نحتاجه في تطبيقٍ يتبع أسلوب REST هو دليل (index) أو مسارٌ للقائمة ليتم كشف المجموعة items كاملةً. وكلّ ما على المتحكم القيام به هو كشف كامل المحتويات للمصفوفة items باستدعاء التابع <span style="font-family:courier new,courier,monospace;">query</span> دون وُسطاء.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .controller('ItemsController', function($scope, $location, Item) {
    $scope.location = $location.absUrl();
    $scope.items = Item.query();
  });</pre><p>واجهة هذا المتحكم سهلةٌ أيضًا، وسنستخدم التوجيه<span style="font-family:courier new,courier,monospace;"> ng-repeat</span> لإظهار العناصر.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;div class="well well-sm" ng-bind="location"&gt;&lt;/div&gt;
&lt;a ng-href="/"&gt;Home&lt;/a&gt;
&lt;ol&gt;
  &lt;li ng-repeat="item in items"&gt;
     {{item.name}} - {{item.color}}
  &lt;/li&gt;
&lt;/ol&gt;</pre><p>في هذا المثال الأساسيّ، ستربط إعدادات<span style="line-height: 22.4px;"> التوجيه</span> بين مسار الجذر (/) وبين واجهة المجموعة view.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .config(function($routeProvider) {
    $routeProvider
      .when('/', {
        controller: 'ItemsController',
        templateUrl: '/views/items/index.html'
      });
  });</pre><p>لا يُحقّق المثال السّابق أسلوب REST فعلًا، وذلك لأنّ المسار ليس اسم مجموعة الموارد (<span style="font-family:courier new,courier,monospace;">items/</span>). المشكلة هي أننا نحتاج إلى ننشئ شيئًا ما في مسار الجذر ليكون المكان الافتراضي الذي يتم تحميل تطبيق Angular منه. سنرى الحل في المثال التالي، وهو إعادة التوجيه الفوري من المسار الجذر إلى المسار <span style="font-family:courier new,courier,monospace;">items/.</span></p><h2>العنصر redirectTo</h2><p>لنتمكّن من القيام بإعادة توجيهٍ تلقائيّة من مسارٍ لآخر، سيكون علينا استخدام العنصر <a rel="external nofollow" href="https://docs.angularjs.org/api/ngRoute/provider/%24routeProvider#when">redirectTo</a> بدلًا من متحكّم وقالب.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .config(function($routeProvider) {
    $routeProvider
      .when('/', {
        redirectTo: '/items'
      })
      .when('/items', {
        controller: 'ItemsController',
        templateUrl: '/views/items/index.html'
      });
  });</pre><p>كما يُمكنك أن ترى في شريط الموضع في المثال السابق، فالمجموعة items يتم عرضها في المسار<span style="font-family:courier new,courier,monospace;"> items/</span>. ومهما كان عدد المرات التي تنقر بها على الرابط Home، فلن تتمكن من الانتقال إلى المسار الجذر<span style="font-family:courier new,courier,monospace;"> /</span> وسيتم دومًا إعادة توجيهك إلى المسار <span style="font-family:courier new,courier,monospace;">items/</span>. هذا استخدامٌ بسيط للخيار <span style="font-family:courier new,courier,monospace;">redirectTo</span>. إن كنت تريد تطبيق بعض العمليات على إعادة التوجيه، فيُمكنك توفير تابعٍ بدلًا من مسارٍ نصّيّ.</p><h2>التابع search</h2><p>كما ناقشنا في بداية هذا الفصل، تقدّم روابط URL النصّية تمثيلًا لحالة التطبيق (مثل عملية ترشيحٍ للموارد) يُمكن أن يتم حفظها ومشاركتها بسهولة. كيف يُمكننا معالجة نصّ طلب (query) يطلب فقط الحصول على العناصر التي تكون قيمة العنصر color فيها هو لونًا معيّنًا؟</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .config(function($routeProvider) {
    $routeProvider
      .when('/', {
        controller: 'LocationController',
        template: '&lt;div class="well well-sm" ng-bind="location"&gt;&lt;/div&gt;\
                   &lt;a ng-href="/items?color=red"&gt;Red items&lt;/a&gt;'
      })
      .when('/items', {
        controller: 'ItemsController',
        templateUrl: '/views/items/index.html'
      });
  });</pre><p>في هذا المثال، تحتوي واجهة مسار الجذر الخاص بالتطبيق على رابط التّنقّل الحاوي على نصّ الطلب <span style="font-family:courier new,courier,monospace;">color=red</span>. يُمكننا جلب قيمة هذا الوسيط بسهولةٍ باستخدام التابع <a rel="external nofollow" href="https://docs.angularjs.org/api/ng/service/%24location#search">search</a> للخدمة <span style="font-family:courier new,courier,monospace;">location$</span>.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .controller('ItemsController', function($scope, $location, Item) {
    $scope.location = $location.absUrl();
    $scope.items = Item.query({color: $location.search().color});
  });</pre><p>عند النّقر على الرابط Red items في المثال السابق، سترى كيف استخدم التابع <span style="font-family:courier new,courier,monospace;">query</span> (الذي قُمنا بتعريفه سابقًا في الخدمة items) المُرشّح filter للقيام بعملية جلبٍ حسب المثال.</p><h2>الخدمة routeParams$</h2><p>يعتمد أسلوب REST نموذجيًّا على إلحاق معرّف المورد الفريد في نهاية المسار بدلًا من وضعه في نصّ الطلب. كيف يُمكننا القيام بمعالجةٍ صحيحة للمسار بحيث يحوي المعرّف الفريد؟ أو بعبارةٍ أدقّ، كيف يُمكننا استخراج المعرّف الفريد 3 من المسار <span style="font-family:courier new,courier,monospace;">items/3/</span> على سبيل المثال؟</p><p>لنقم بتحديث واجهة المجموعة لتتضمّن هذا الأسلوب من رابط التّنقّل لكلّ مورد مفرد على حدة.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;div class="well well-sm" ng-bind="location"&gt;&lt;/div&gt;
&lt;p class="lead"&gt;Items&lt;/p&gt;
&lt;ol&gt;
  &lt;li ng-repeat="item in items"&gt;
    &lt;a ng-href="/items/{{item.id}}"&gt;
      {{item.name}}
    &lt;/a&gt;
  &lt;/li&gt;
&lt;/ol&gt;</pre><p>تُقدّم الخدمة <a rel="external nofollow" href="https://docs.angularjs.org/api/ngRoute/service/%24routeParams">routeParams$</a> وصولًا ملائمًا للعناصر في المسار، حيث يقوم بكشفها (expose) كعناصر مُسمّاة. يُمثّل القسم الأخير من المسار المعرّف الفريد لموردٍ وحيد يتبع أسلوب REST. فمثلًا، يجب أن يعيد المسار <span style="font-family:courier new,courier,monospace;">items/3/</span> تمثيلًا للمورد Item بالمعرّف الفريد 3.</p><p>في إعدادات<span style="line-height: 22.4px;"> التوجيه </span>الخاصة بنا، يُمكننا استخدام البادئة الخاصّة : لتعريف متغيّراتٍ ذات أسماءٍ متغيرّة قابلةٍ للاستخراج من المسار. تمّ الاتّفاق على تسمية الوسيط المعرّف للمورد بالاسم id:، لذا سيكون نصّ المسار هو <span style="font-family:courier new,courier,monospace;">items/:id/</span>.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .config(function($routeProvider) {
    $routeProvider
      .when('/', {
        redirectTo: '/items'
      })
      .when('/items', {
        controller: 'ItemsController',
        templateUrl: '/views/items/linked-index.html'
      })
      .when('/items/:id', {
        controller: 'ItemController',
        templateUrl: '/views/items/show.html'
      });
  });</pre><p>تقوم الوحدة بوضع الوسطاء المستخرجة من المسار في الخدمة<span style="font-family:courier new,courier,monospace;"> routeParams$ </span>التي يجب أن نقوم بحقنها في المتحكّم<span style="font-family:courier new,courier,monospace;"> ItemController</span> جنبًا إلى جنبٍ مع الخدمة Item التي أنشأناها.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .controller('ItemController', function($scope, $location, Item, $routeParams) {
    $scope.location = $location.absUrl();
    $scope.item = Item.get({id: $routeParams.id});
  });</pre><p>سنقوم باستدعاء التّابع <span style="font-family:courier new,courier,monospace;">Item.get</span> مستخدمين القيمة التي تمّ إسنادها في<span style="font-family:courier new,courier,monospace;"> routeParams.id$</span>، وهذا التّابع يستخدم المرشّح filter لإيجاد موقع النموذج الصحيح. (يُمكن للتابع <a rel="external nofollow" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find">()Array.prototype.find</a> الموجود في المكتبة المركزيّة أن يُقدّم طريقةً أفضل للقيام بذلك عندما ينتشر هذا التابع على نطاقٍ واسع.)</p><p>طريقة عرض المورد item المفرد سهلةٌ ومباشرة، سنقوم بتضمين رابطٍ للعودة إلى المسار<span style="font-family:courier new,courier,monospace;"> items/</span> بحيث نتمكّن من العودة إلى واجهة عرض القائمة.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;div class="well well-sm" ng-bind="location"&gt;&lt;/div&gt;
&lt;a ng-href="/items"&gt;Items&lt;/a&gt;
&lt;p class="lead"&gt;{{item.name}}&lt;/p&gt;
&lt;p&gt;Color: {{item.color}}&lt;/p&gt;</pre><p>صحيحٌ أنّ الخدمة<span style="font-family:courier new,courier,monospace;"> routeParams$</span> والتابع <span style="font-family:courier new,courier,monospace;">()location.search$</span> كلاهما سهل الاستخدام نوعًا ما، إلّا أنّهما معًا يقدّمان حجر بناءٍ هامًّا في أيّ عمليّة<span style="line-height: 22.4px;"> توجيه</span> متضمّنةٍ لأسلوب REST.</p><h2>خاتمة</h2><p>تعلّمنا في هذا الفصل أساسيات طريقة Angular الخاصة بها في <span style="line-height: 22.4px;">التوجيه</span>. ورغم أنّ <span style="font-family:courier new,courier,monospace;">ngRoute</span> لا تُقدّم بعض الميزات المعقّدة <span style="line-height: 22.4px;">للتوجيه</span>، كدعم الموارد المتداخلة، أو <a rel="external nofollow" href="http://en.wikipedia.org/wiki/Finite-state_machine">آلة الحالة</a> من الدرجة الأولى، أو توليد مسارات URL، إلا أنّها تعطيك مقدّمةً ممتازةً<span style="line-height: 22.4px;"> للتوجيه</span> في Angular. ستجد أنّ العديد من التطبيقات الحقيقية تستخدم <a rel="external nofollow" href="https://github.com/angular-ui/ui-router/wiki">UI-Router</a> من مشروع <a rel="external nofollow" href="http://angular-ui.github.io/">AngularUI</a> بدلًا من ذلك، ولن نتطرّق إليها في هذه السلسلة، بل سيكون الفصل الأخير مقدّمةً لتحميل البيانات من المخدّم في النهاية الخلفية (backend) باستخدام الخدمة <a rel="external nofollow" href="https://docs.angularjs.org/api/ng/service/%24http">http$</a> في Angular.</p><p><span style="line-height: 22.4px;">ترجمة -وبتصرّف- للفصل الثاني عشر (</span><a rel="external nofollow" href="http://www.angularjsbook.com/angular-basics/chapters/routing/">Routing</a><span style="line-height: 22.4px;">) من كتاب: </span><a rel="external nofollow" style="line-height: 22.4px;" href="http://www.angularjsbook.com/">Angular Basics</a><span style="line-height: 22.4px;"> لصاحبه: Chris Smith.</span></p>
]]></description><guid isPermaLink="false">225</guid><pubDate>Wed, 06 Jan 2016 19:48:49 +0000</pubDate></item><item><title>&#x645;&#x62C;&#x627;&#x644;&#x627;&#x62A; &#x627;&#x644;&#x62A;&#x648;&#x62C;&#x64A;&#x647; (Directive Scopes) &#x641;&#x64A; AngularJS</title><link>https://academy.hsoub.com/programming/javascript/angular/%D9%85%D8%AC%D8%A7%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%8A%D9%87-directive-scopes-%D9%81%D9%8A-angularjs-r203/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2015_11/directives-scopes-angularjs.png.dda8bd4d506e9a9dad982a65bf5159f1.png" /></p>

<div id="wmd-preview-section-14">
	<p style="text-align: center;">
		<a class="ipsAttachLink ipsAttachLink_image" href="https://academy.hsoub.com/uploads/monthly_2015_11/directives-scopes-angularjs.png.42614738c91e542f3ee8c72e571e9506.png" data-fileid="7167" rel=""><img alt="directives-scopes-angularjs.thumb.png.d1" class="ipsImage ipsImage_thumbnailed" data-fileid="7167" src="https://academy.hsoub.com/uploads/monthly_2015_11/directives-scopes-angularjs.thumb.png.d17c8a11aff7bc303aa8e1ad3b0c11b0.png"></a>
	</p>

	<p id="مجالات-التوجيه">
		يقترنُ المتحكّم بمجالٍ جديد يتمّ إنشاؤه عند إنشاء المتحكّم، أمّا التّوجيه فلا يتمّ إعطاؤه مجالًا خاصًّا به عند إنشائه في الحالة الافتراضيّة، بل يستخدم المجال المتاح، وذلك اعتمادًا على مكانه في المستند. بالنّسبة لي فأنا أعتبر هذه الحالة الافتراضيّة سيّئة، وذلك لأنّ معظم التّوجيهات تُكتب كمكوّناتٍ سيتمّ إعادة استخدامها مع حالتها المغلَّفة. ولكن هذه الحالة الافتراضيّة تُفيد في جعل تعلُّم استخدام التّوجيهات سهلًا في البداية، وذلك كما رأينا في <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%8A%D9%87%D8%A7%D8%AA-directives-%D9%81%D9%8A-angularjs-r202/" rel="">الفصل السّابق</a>.
	</p>

	<p>
		والآن بعد أن اعتدنا على التّوجيهات المخصّصة، فقد حان الوقت لنتعلّم كيفيّة إدارة حالة التّوجيه (directive state) بطريقةٍ مغلّفة، مما يسمح بإعادة استخدامٍ آمنٍ للتّوجيه المخصّص.
	</p>
</div>

<div id="wmd-preview-section-15">
	<h2 id="عزل-المجال">
		عزل المجال
	</h2>

	<p>
		يُشار إلى عبارة عزل المجال (<a href="https://docs.angularjs.org/guide/directive#isolating-the-scope-of-a-directive" rel="external nofollow">isolate scope</a>) غالبًا كأحد المصطلحات الصّعبة في Angular. ولكن كما هو الحال غالبًا في <a href="https://academy.hsoub.com/programming/general/%D8%B9%D9%84%D9%88%D9%85-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8/" rel="">علوم الحاسوب</a> (وأيضًا في شبه علوم الحاسوب)، فالمفاهيم خلف الأسماء الرّنّانة غالبًا ما تكون بسيطةَ الفهم.
	</p>

	<p>
		عزل المجال يعني فقط إعطاء التّوجيه مجالًا خاصًّا به بحيث لا يكون موروثًا من المجال الحالي.
	</p>

	<p>
		قبل أن نبدأ، نحتاج إلى الاهتمام بالإعدادات المُعتادة. أوّلًا، لنقُم بإضافة Angular و<a href="http://getbootstrap.com/" rel="external nofollow">Bootstrap</a> إلى الصّفحة.
	</p>

	<pre class="html ipsCode prettyprint prettyprinted" data-pbcklang="html" data-pbcktabsize="4" style="">
<span class="tag">&lt;link</span><span class="pln"> </span><span class="atn">rel</span><span class="pun">=</span><span class="atv">"stylesheet"</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"//ajax.googleapis.com/ajax/libs/angularjs/1.4.2/angular.js"</span><span class="tag">&gt;&lt;/script&gt;</span></pre>

	<p>
		لابدّ من تعريف <a href="http://www.angularjsbook.com/angular-basics/chapters/modules" rel="external nofollow">وحدةٍ</a> للتطبيق.
	</p>

	<pre class="javascript ipsCode prettyprint prettyprinted" data-pbcklang="javascript" data-pbcktabsize="4" style="">
<span class="pln">angular</span><span class="pun">.</span><span class="kwd">module</span><span class="pun">(</span><span class="str">'app'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[]);</span></pre>

	<p>
		والآن سيكون علينا تحميل وحدة الجذر الخاصّة بنا عن طريق تمرير اسمها إلى التّوجيه <span style="font-family:courier new,courier,monospace;">ng-app</span>.
	</p>

	<pre class="html ipsCode prettyprint prettyprinted" data-pbcklang="html" data-pbcktabsize="4" style="">
<span class="tag">&lt;body</span><span class="pln"> </span><span class="atn">ng-app</span><span class="pun">=</span><span class="atv">"app"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="com">&lt;!-- الأمثلة توضع هنا --&gt;</span><span class="pln">
</span><span class="tag">&lt;/body&gt;</span></pre>

	<p>
		والآن بعد أن أصبحت Angular ووحدة الجذر مُعدّتين جيّدًا، لنقُم بتعريف متحكّمٍ يقوم بالكشف (expose) عن ثلاث عناصر لتخزين سلاسل نصّيّة، وكلّ عنصرٍ سيُخزّن اسم المكوِّن الذي سيستخدمه في العرض. السلسلة النصية الأولى تُعرّف عن المتحكّم نفسه، والسلسلتان النصيتان الباقيتان لنسختين منفصلتين من توجيهٍ سنقوم بتعريفه لاحقًا.
	</p>

	<pre class="javascript ipsCode prettyprint prettyprinted" data-pbcklang="javascript" data-pbcktabsize="4" style="">
<span class="pln">angular</span><span class="pun">.</span><span class="kwd">module</span><span class="pun">(</span><span class="str">'app'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">controller</span><span class="pun">(</span><span class="str">'MessageController'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">$scope</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    $scope</span><span class="pun">.</span><span class="pln">message  </span><span class="pun">=</span><span class="pln"> </span><span class="str">"the controller"</span><span class="pun">;</span><span class="pln">
    $scope</span><span class="pun">.</span><span class="pln">message1  </span><span class="pun">=</span><span class="pln"> </span><span class="str">"directive 1"</span><span class="pun">;</span><span class="pln">
    $scope</span><span class="pun">.</span><span class="pln">message2  </span><span class="pun">=</span><span class="pln"> </span><span class="str">"directive 2"</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">});</span></pre>

	<p>
		وفيما يلي شيفرة التّوجيه، وفيها نقوم أيضًا بإنشاء عنصرٍ اسمه <span style="font-family:courier new,courier,monospace;">message</span> في نسخة عنصر المجال الذي يتم تمريره إلى تابع الرّبط.
	</p>

	<pre class="javascript ipsCode prettyprint prettyprinted" data-pbcklang="javascript" data-pbcktabsize="4" style="">
<span class="pln">angular</span><span class="pun">.</span><span class="kwd">module</span><span class="pun">(</span><span class="str">'app'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">directive</span><span class="pun">(</span><span class="str">'message'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">       
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">template</span><span class="pun">:</span><span class="pln"> </span><span class="str">"&lt;p&gt;hello, from {{message}}&lt;/p&gt;"</span><span class="pun">,</span><span class="pln">
      link</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">scope</span><span class="pun">,</span><span class="pln"> element</span><span class="pun">,</span><span class="pln"> attrs</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        scope</span><span class="pun">.</span><span class="pln">message </span><span class="pun">=</span><span class="pln"> scope</span><span class="pun">.</span><span class="pln">$eval</span><span class="pun">(</span><span class="pln">attrs</span><span class="pun">.</span><span class="pln">message</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">  
    </span><span class="pun">};</span><span class="pln">
  </span><span class="pun">});</span></pre>

	<p>
		لقد <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%8A%D9%87%D8%A7%D8%AA-directives-%D9%81%D9%8A-angularjs-r202/" rel="">ناقشنا في الفصل السابق</a> استخدام <span style="font-family:courier new,courier,monospace;">scope.$eval</span> للوصول إلى عنصرٍ في المجال تمّ تمريره كقيمة نصّيّة لوسيط التّوجيه، وما يهمّنا تحديدًا في هذا المثال هو ما سيحدث للعنصر <span style="font-family:courier new,courier,monospace;">scope.message</span> الموجود في المتحّكم ونسختي التّوجيه. سنضيف خانات إدخالٍ مرتبطةً بعناصر المجالات الثلاثة التي تمّ إنشاؤها في المتحكّم.
	</p>

	<p>
		قبل أن تنظر إلى استخدام المثال ومخرجاته، ألقِ نظرةً سريعةً مرّةً أخرى على المتحكّم والتّوجيه السابقين. هل ترى أيّ إمكانيّةٍ للتضارب بينهما؟
	</p>

	<pre class="html ipsCode prettyprint prettyprinted" data-pbcklang="html" data-pbcktabsize="4" style="">
<span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">ng-controller</span><span class="pun">=</span><span class="atv">"MessageController"</span><span class="tag">&gt;</span><span class="pln">       
  </span><span class="tag">&lt;h4&gt;</span><span class="pln">hello, from {{message}}</span><span class="tag">&lt;/h4&gt;</span><span class="pln">
  </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"form-control"</span><span class="pln"> </span><span class="atn">ng-model</span><span class="pun">=</span><span class="atv">"message"</span><span class="tag">&gt;</span><span class="pln"> 
  </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"form-control"</span><span class="pln"> </span><span class="atn">ng-model</span><span class="pun">=</span><span class="atv">"message1"</span><span class="tag">&gt;</span><span class="pln"> 
  </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"form-control"</span><span class="pln"> </span><span class="atn">ng-model</span><span class="pun">=</span><span class="atv">"message2"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;br&gt;</span><span class="pln">
  </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"well"</span><span class="pln"> </span><span class="atn">message</span><span class="pun">=</span><span class="atv">"message1"</span><span class="tag">&gt;&lt;/div&gt;</span><span class="pln">     
  </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"well"</span><span class="pln"> </span><span class="atn">message</span><span class="pun">=</span><span class="atv">"message2"</span><span class="tag">&gt;&lt;/div&gt;</span><span class="pln">     
</span><span class="tag">&lt;/div&gt;</span></pre>

	<p>
		خلال الوقت الذي أنهى فيه الاستخدام الثّاني للتّوجيه عمله، تمّ إسناد القيمة “2 directive” للعنصر <span style="font-family:courier new,courier,monospace;">message</span> في مجال المتحكّم. إن قمتَ بكتابة شيءٍ داخل خانة الإدخال العُلويّة، والتي ترتبط بالعنصر <span style="font-family:courier new,courier,monospace;">message</span>، فسترى أنّ التغيير أدّى إلى تحديث مخرجات نسختي التّوجيه معًا. في هذا المثال، تمّت مشاركة مجال المتحكّم ضمن النّسخ الثّلاثة، مما أدّى إلى حدوث هذه المشكلة، فنحن لا نريد أن تقوم أيّ نسخةٍ من نُسخ التّوجيه بتعديل المجال الذي يُشاركه المتحكّم <span style="font-family:courier new,courier,monospace;">MessageController</span> مع نُسخ التّوجيه الأخرى. إذًا، كيف يُمكننا إعطاء كلّ نسخةٍ للتّوجيه مجالها الخاصّ المعزول؟ هل يُمكنك اكتشاف الحلّ بنفسك؟
	</p>

	<p>
		الحلُّ هو التّصريح عن عنصر <span style="font-family:courier new,courier,monospace;">scope</span> في كائن الإعدادات الخاص بالتّوجيه. الخيار الأكثر سهولةً هو إسناد القيمة <span style="font-family:courier new,courier,monospace;">true</span> إلى هذه الخاصّية، مما يعطي نُسخ التّوجيه مجالاتٍ خاصّة بها. هذه المجالات الجديدة ترث من المجال الحاليّ، تمامًا كما لو كانت نُسخ التّوجيه نُسخًا لمتحكّمٍ ما.
	</p>

	<pre class="javascript ipsCode prettyprint prettyprinted" data-pbcklang="javascript" data-pbcktabsize="4" style="">
<span class="pln">angular</span><span class="pun">.</span><span class="kwd">module</span><span class="pun">(</span><span class="str">'app'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">directive</span><span class="pun">(</span><span class="str">'message'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">       
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">template</span><span class="pun">:</span><span class="pln"> </span><span class="str">"&lt;p&gt;hello, from {{message}}&lt;/p&gt;"</span><span class="pun">,</span><span class="pln">
      link</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">scope</span><span class="pun">,</span><span class="pln"> element</span><span class="pun">,</span><span class="pln"> attrs</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        scope</span><span class="pun">.</span><span class="pln">message </span><span class="pun">=</span><span class="pln"> scope</span><span class="pun">.</span><span class="pln">$eval</span><span class="pun">(</span><span class="pln">attrs</span><span class="pun">.</span><span class="pln">message</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
      scope</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">  
    </span><span class="pun">};</span><span class="pln">
  </span><span class="pun">});</span></pre>

	<p>
		لقد استطعنا حلّ مشكلة الحالة الابتدائيّة لكلّ العناصر. ولكن هل انتهينا؟ كلّا. جرّب الكتابة في أيّ خانةٍ من خانات الدّخل.
	</p>

	<p>
		إذا أردنا أن تؤدّي التحديثات للعنصرين <span style="font-family:courier new,courier,monospace;">message1</span> و<span style="font-family:courier new,courier,monospace;">message2</span> التّابعين لمجال المتحكّم إلى تحديثات في التّوجيه، فسيكون علينا القيام بأكثر مما قمنا به حتّى الآن، لأنّ تابع الرّبط لا يتمّ تشغيله إلّا مرّةً واحدة، عندما يتمّ إخراج (render) التّوجيه للمرّة الأولى، وعند استخدام <span style="font-family:courier new,courier,monospace;">eval$</span> لتغيير قيمة عنصرٍ في التّوجيه فلن يكون هذا التّغيير دائمًا ولن يعمل الرّبط ثنائيّ الاتّجاه بين مُخرجات التّوجيه والنّموذج الأصليّ. إن لم تكن قد جرّبت تغيير القيَم في خانات الدّخل بعد، فأنصحك بشدّةٍ بتجربة ذلك لتتأكّد بنفسك من أنّ القيم لا يتمّ تحديثها.
	</p>

	<p>
		إذًا ما الذي يجب علينا إضافته لتابع الرّبط ليعمل الرّبط ثنائيّ الاتّجاه؟ لا نحتاج إلى شيء، علينا حذف تابع الرّبط. فالشيفرة الأمريّة (imperative) المطلوبة في تابع الرّبط مملّةٌ ومتوقّعة. لم لا نقوم باستبدالها بإعداداتٍ تصريحيّة ونقوم بأتمتة العمل البرمجيّ؟ هذا بالضّبط هو ما أراد مخترعوا Angular القيام به.
	</p>
</div>

<div id="wmd-preview-section-16">
	<h2 id="طرق-ربط-المجال-المعزول">
		طرق ربط المجال المعزول
	</h2>

	<p>
		يتمّ التصريح عن طرق ربط المجال المعزول باستخدام الرّموز التّالية:
	</p>

	<ul>
<li>
			الرّمز <strong><span style="font-family:courier new,courier,monospace;">=</span></strong> للرّبط ثنائيّ الاتّجاه (two-way binding)
		</li>
		<li>
			الرّمز <strong><span style="font-family:courier new,courier,monospace;">&amp;</span></strong> للرّبط وحيد الاتّجاه (one-way binding)
		</li>
		<li>
			الرّمز <strong><span style="font-family:courier new,courier,monospace;">@</span></strong> للرّبط النّصّي (text binding) 
		</li>
	</ul>
<p>
		لنلقِ نظرةً على كلّ واحدةٍ من تلك الطّرق على حدة، ولنبدأ بالطريقة المفيدة، الرّبط ثنائيّ الاتّجاه.
	</p>

	<h3 id="الربط-ثنائي-الاتجاه">
		الربط ثنائي الاتجاه (=)
	</h3>

	<p>
		لحلّ مشكلة تحديث مخرجات التّوجيه، فلن يكون علينا إلّا استبدال الطريقة الأمريّة (imperative) في كتابة الشّيفرة، والتي اعتدنا عليها عند كتابة تابع الرّبط، بكائن إعداداتٍ يتمّ إسناده للعنصر <span style="font-family:courier new,courier,monospace;">scope</span>.
	</p>

	<pre class="javascript ipsCode prettyprint prettyprinted" data-pbcklang="javascript" data-pbcktabsize="4" style="text-align: left;">
<span class="pln">angular</span><span class="pun">.</span><span class="kwd">module</span><span class="pun">(</span><span class="str">'app'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">directive</span><span class="pun">(</span><span class="str">'message'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">       
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">template</span><span class="pun">:</span><span class="pln"> </span><span class="str">"&lt;p&gt;hello, from {{message}}&lt;/p&gt;"</span><span class="pun">,</span><span class="pln">
      scope</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        message</span><span class="pun">:</span><span class="pln"> </span><span class="str">'='</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
  </span><span class="pun">});</span></pre>

	<p>
		لاحظ بأنّ قيمة العنصر <span style="font-family:courier new,courier,monospace;">scope</span> أصبحت الآن كائنًا بدلًا من القيمة <span style="font-family:courier new,courier,monospace;">true</span> البوليانيّة الأوّليّة (primitive). ما قمنا به يؤدّي إلى قطع العلاقة الوراثيّة بين المجالات. ولنتمكّن من رؤية ذلك عمليًّا، يُمكنك استبدال <span style="font-family:courier new,courier,monospace;">scope: true</span> في المثال الأسبق، بـ <span style="font-family:courier new,courier,monospace;">{} :scope</span>. لا يعمل تابع الرّبط في ذلك المثال دون الوصول إلى العناصر في المجال الأب.
	</p>

	<p>
		لنتمكّن من أتمتة العمل الذي يقوم به تابع الرّبط ذاك، فيجب علينا إضافة عنصرٍ إلى كائن الإعدادات <span style="font-family:courier new,courier,monospace;">scope</span> بحيث يكون اسمه بنفس اسم عنصر المجال المحلّي الذي نريد ربطه بعنصرٍ ما في المجال الخارجيّ، فبما أنّ توجيهنا يستخدم العنصر <span style="font-family:courier new,courier,monospace;">message</span> في القالب الخاصّ به، فسنضيف عنصرًا اسمه <span style="font-family:courier new,courier,monospace;">message</span> إلى كائن الإعدادات، ونُسند إليه القيمة <strong><span style="font-family:courier new,courier,monospace;">=</span></strong>، مما يدلّ على أنّنا نريد أن يتمّ ربطه باستخدام الرّبط ثنائيّ الاتّجاه مع العنصر في المجال الخارجيّ الذي يتم تمرير اسمه إلى الخاصّيّة message. قد لا يكون الأمر سهلًا، ولكنّه يعمل جيّدًا ويُمكنك تجريب ذلك بكتابة شيءٍ ما في خانات الإدخال لتحديث عناصر المتحكّم في المثال السابق. وبهذا نكون قد أتممنا كتابة المتحكّم <span style="font-family:courier new,courier,monospace;">message</span> أخيرًا.
	</p>
</div>

<div id="wmd-preview-section-17">
	<h3 id="الربط-وحيد-الاتجاه">
		الربط وحيد الاتّجاه (&amp;)
	</h3>

	<p>
		قد تحتاج التّوجيهات أحيانًا إلى طريقةٍ لاستدعاء التّوابع في المجال الشّامل رغم أنّها تكون في مجالٍ معزول. بعبارةٍ أخرى، قد تحتاج إلى معالجة عبارة Angular ضمنالسياق الأصلي. ومن أجل ذلك تمّ إيجاد طريقة الرّبط وحيد الاتّجاه، أو خيار المعالجة. لنبدأ بمتحكّمٍ يقوم بالكشف عن تابع استدعاءٍ خلفيٍّ (callback) اسمه kaboom.
	</p>

	<pre class="javascript ipsCode prettyprint prettyprinted" data-pbcklang="javascript" data-pbcktabsize="4" style="">
<span class="pln">angular</span><span class="pun">.</span><span class="kwd">module</span><span class="pun">(</span><span class="str">'app'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">controller</span><span class="pun">(</span><span class="str">'ClickController'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">$scope</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    $scope</span><span class="pun">.</span><span class="pln">message </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Waiting..."</span><span class="pun">;</span><span class="pln">
    $scope</span><span class="pun">.</span><span class="pln">kaboom </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      $scope</span><span class="pun">.</span><span class="pln">message </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Kaboom!"</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">});</span></pre>

	<p>
		يعرض المثال التالي كيف يُمكن استخدام المتحكّم <span style="font-family:courier new,courier,monospace;">ClickController</span>، حيث يمكن استخدامه مع التّوجيه العام <span style="font-family:courier new,courier,monospace;">bootstrap-button </span>الّذي يُمكن أن يتمّ إعداده ليقوم باستدعاء التّابع <span style="font-family:courier new,courier,monospace;">kaboom</span> عندما يتم نقر الزّر.
	</p>

	<pre class="html ipsCode prettyprint prettyprinted" data-pbcklang="html" data-pbcktabsize="4" style="">
<span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">ng-controller</span><span class="pun">=</span><span class="atv">"ClickController"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;h4&gt;</span><span class="pln">
    {{message}}
  </span><span class="tag">&lt;/h4&gt;</span><span class="pln">
  </span><span class="tag">&lt;bootstrap-button</span><span class="pln"> </span><span class="atn">the-callback</span><span class="pun">=</span><span class="atv">"kaboom()"</span><span class="tag">&gt;&lt;/bootstrap-button&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span></pre>

	<p>
		لنقُم الآن بكتابة شيفرة التّوجيه <span style="font-family:courier new,courier,monospace;">bootstrap-button</span>، في داخل قالبه البسيط، سنستخدم التّوجيه <span style="font-family:courier new,courier,monospace;">ng-click</span> المدمج في Angular لربط حدث النّقر بتابع الاستدعاء الخلفي.
	</p>

	<p>
		<strong>views/button.html</strong>
	</p>

	<pre class="html ipsCode prettyprint prettyprinted" data-pbcklang="html" data-pbcktabsize="4" style="">
<span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"button"</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"btn btn-default"</span><span class="pln"> </span><span class="atn">ng-click</span><span class="pun">=</span><span class="atv">"theCallback()"</span><span class="tag">&gt;</span><span class="pln">
  Submit
</span><span class="tag">&lt;/button&gt;</span></pre>

	<p>
		بما أنّ التّوجيه العام الذي سنكتبه لا يُمكنه معرفة اسم تابع الاستدعاء الخلفيّ الحقيقي الذي سيتم تضمينه، سنستخدم اسمًا عشوائيًّا <span style="font-family:courier new,courier,monospace;">theCallback</span> كاسمٍ للتّابع. حسنًا، والآن كيف سنقوم بربط <span style="font-family:courier new,courier,monospace;">theCallback</span> (في الواقع هي عنصرٌ في المجال) مع kaboom؟
	</p>

	<p>
		ليست هذه بمشكلةٍ، فلدينا الرّبط وحيد الاتّجاه.
	</p>

	<pre class="javascript ipsCode prettyprint prettyprinted" data-pbcklang="javascript" data-pbcktabsize="4" style="">
<span class="pln">angular</span><span class="pun">.</span><span class="kwd">module</span><span class="pun">(</span><span class="str">'app'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">directive</span><span class="pun">(</span><span class="str">'bootstrapButton'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      restrict</span><span class="pun">:</span><span class="pln"> </span><span class="str">'E'</span><span class="pun">,</span><span class="pln">
      scope</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        theCallback</span><span class="pun">:</span><span class="pln"> </span><span class="str">'&amp;'</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
      templateUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/views/button.html'</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">});</span></pre>

	<p>
		يقوم الرّمز (&amp;) بعمليّة الرّبط المطلوبة.
	</p>

	<p>
		والآن ماذا عن خيار الإعداد الأخير، @، المُسمّى بخيار الرّبط النّصّيّ؟ سنحتاج إلى الانتقال مؤقّتًا إلى مثالٍ آخر من المصطلحات الصّعبة في Angular، وذلك كي نتمكّن من تبيان فائدة هذا الخيار بأفضل ما يمكن.
	</p>
</div>

<div id="wmd-preview-section-18">
	<h2 id="الاستبدال-transclusion">
		الاستبدال Transclusion
	</h2>

	<p>
		لابدّ من وجود اسمٍ آخر أفضل من <a href="https://docs.angularjs.org/guide/directive#creating-a-directive-that-wraps-other-elements" rel="external nofollow">Transclusion</a>، فمُستخدموا Ruby on Rails سيلاحظون تشابه آلية عمل الـTransclusion مع <a href="http://guides.rubyonrails.org/layouts_and_rendering.html#understanding-yield" rel="external nofollow">yield</a>، وهذا المصطلح أسهل للفهم.
	</p>

	<p>
		تعني عمليّة الاستبدال transclude لعنصرٍ ما، أن نقوم بالتّحكّم بإعادة إنتاج المكوّن الذي قام بتضمين التّوجيه، وفي حالتنا يكون هذا المكوّن هو القالب الذي يتضمّن التّوجيه. إن لم تكن هذه العبارة واضحةً لك، فيُمكنك التّفكير بأنّ الاستبدال (transclusion) يعطي الأمر التّالي: “قم بقصّ محتويات القالب الموجود داخل شيفرة التّوجيه، ثمّ ألصقها هنا“. ويعتمد مكان اللصق على التّوجيه المدمج <a href="https://docs.angularjs.org/api/ng/directive/ngTransclude" rel="external nofollow">ng-transclude</a> الذي يُمكننا وضعها في مكانٍ ما داخل القالب الموجود في التّوجيه المخصّص.
	</p>

	<p>
		كمثالٍ بسيط، لنقم بكتابة التّوجيه box الذي نريد تضمينه حول محتوىً ما، عن طريق وضعه في عنصرٍ <span style="font-family:courier new,courier,monospace;">div</span> كغلاف.
	</p>

	<pre class="html ipsCode prettyprint prettyprinted" data-pbcklang="html" data-pbcktabsize="4" style="">
<span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">box</span><span class="tag">&gt;</span><span class="pln">
  This </span><span class="tag">&lt;strong&gt;</span><span class="pln">content</span><span class="tag">&lt;/strong&gt;</span><span class="pln"> will be </span><span class="tag">&lt;em&gt;</span><span class="pln">wrapped</span><span class="tag">&lt;/em&gt;</span><span class="pln">.
</span><span class="tag">&lt;/div&gt;</span></pre>

	<p>
		ما سيقوم به التّوجيه box هو تغليف المحتوى الدّاخليّ بعنصر <span style="font-family:courier new,courier,monospace;">p</span> مع بعض التّنسيق من Bootstrap. هناك جزآن يجب الاهتمام بهما عند استخدام الاستبدال (transclusion)، أوّلهما، يجب أن نقوم بإضافة<span style="font-family:courier new,courier,monospace;"> transclude: true</span> إلى إعدادات التّوجيه، وثانيهما، لابُدّ من استخدام التّوجيه <span style="font-family:courier new,courier,monospace;">ng-transclude</span> في مكانٍ ما من القالب الخاصّ بالتّوجيه، وذلك لاختيار المكان الذي نريد أن نقوم فيه بإضافة المحتوى الجديد.
	</p>

	<pre class="javascript ipsCode prettyprint prettyprinted" data-pbcklang="javascript" data-pbcktabsize="4" style="">
<span class="pln">angular</span><span class="pun">.</span><span class="kwd">module</span><span class="pun">(</span><span class="str">'app'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">directive</span><span class="pun">(</span><span class="str">'box'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">template</span><span class="pun">:</span><span class="pln"> </span><span class="str">'&lt;p class="bg-primary text-center" ng-transclude&gt;&lt;/p&gt;'</span><span class="pun">,</span><span class="pln">
      transclude</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
  </span><span class="pun">});</span></pre>

	<p>
		ألم يكن هذا سهلًا؟ بالطّبع.
	</p>

	<p>
		يُمكننا جعل المثال السابق أسهل من ذلك بعدم استخدام التّوجيه من الأساس، وإضافة تنسيق CSS للعنصر المغلِّف مباشرةً. ولذلك سنقوم باستخدام الاستبدال (transclusion) في مثالٍ أكثر واقعيّة لتغليف التّعقيدات الخاصة بتنسيق Bootstrap لـ<a href="http://getbootstrap.com/components/#panels-heading" rel="external nofollow">لوحةٍ مع ترويسة</a>. سيسمح التّوجيه <span style="font-family:courier new,courier,monospace;">panel</span> لمستخدمه بتحديد النّص الخاص بشريط العنوان في رأس اللوحة، وأيضًا بتحديد المحتوى المطلوب إدخاله في جسم اللوحة. بالنّسبة لمحتوى اللوحة، سيتمّ توظيف الاستبدال (transclusion) تمامًا كما في المثال السّابق، أمّا بالنّسبة لنصّ العنوان، فسنستخدم الخيار المتبقّي لربط المجال المعزول الذي تركناه قبل قليل.
	</p>
</div>

<div id="wmd-preview-section-19">
	<h3 id="الربط-النصي">
		الربط النصي (@)
	</h3>

	<p>
		عندما يكون كلّ ما تحتاجه هو نسخ سلسلة نصّيّة إلى مجال التّوجيه الخاصّ بك، عندها سيكون الرّبط البسيط وحيد الاتّجاه (<strong><span style="font-family:courier new,courier,monospace;">@</span></strong>) هو الطّريقة التّصريحيّة للقيام بذلك. تُبيّن الشّيفرة التّالية القالب الخاصّ بالتّوجيه. هدفنا من العنصر <span style="font-family:courier new,courier,monospace;">title</span> هو أن نسمح للنّصّ الذي تحويه بأن يصبح أحد خصائص التّوجيه.
	</p>

	<p>
		<strong>views/panel.html</strong>
	</p>

	<pre class="html ipsCode prettyprint prettyprinted" data-pbcklang="html" data-pbcktabsize="4" style="">
<span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"panel panel-default"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"panel-heading"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;h3</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"panel-title"</span><span class="tag">&gt;</span><span class="pln">
      {{title}}
      </span><span class="tag">&lt;small&gt;</span><span class="pln">
        Static text in the directive template
      </span><span class="tag">&lt;/small&gt;</span><span class="pln">
    </span><span class="tag">&lt;/h3&gt;</span><span class="pln">
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">
  </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"panel-body"</span><span class="pln"> </span><span class="atn">ng-transclude</span><span class="tag">&gt;&lt;/div&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span></pre>

	<p>
		وفي إعدادات التّوجيه الخاصّ بنا، سنقوم بالحدّ من استخدامه للعناصر، وسنصرّح عن طريقة ربط الخاصّيّة <span style="font-family:courier new,courier,monospace;">title</span> مع العنصر <span style="font-family:courier new,courier,monospace;">title</span> في مجاله.
	</p>

	<pre class="javascript ipsCode prettyprint prettyprinted" data-pbcklang="javascript" data-pbcktabsize="4" style="">
<span class="pln">angular</span><span class="pun">.</span><span class="kwd">module</span><span class="pun">(</span><span class="str">'app'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">directive</span><span class="pun">(</span><span class="str">'panel'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      restrict</span><span class="pun">:</span><span class="pln"> </span><span class="str">'E'</span><span class="pun">,</span><span class="pln">
      templateUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/views/panel.html'</span><span class="pun">,</span><span class="pln">
      scope</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        title</span><span class="pun">:</span><span class="pln"> </span><span class="str">'@'</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
      transclude</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
  </span><span class="pun">});</span></pre>

	<p>
		المثال التّالي يوضّح طريقة استخدام التّوجيه <span style="font-family:courier new,courier,monospace;">panel</span> لكتابة النّصّين الثّابتين (static) في العنوان، وذلك في الوقت نفسه الذي يتمّ فيه استخدام الرّبط ثنائيّ الاتّجاه في المحتوى المُستبدل (transcluded).
	</p>

	<pre class="html ipsCode prettyprint prettyprinted" data-pbcklang="html" data-pbcktabsize="4" style="">
<span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">ng-controller</span><span class="pun">=</span><span class="atv">"MessageController"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;panel</span><span class="pln"> </span><span class="atn">title</span><span class="pun">=</span><span class="atv">"Static text in the client template"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;h3&gt;</span><span class="pln">
      </span><span class="tag">&lt;em&gt;</span><span class="pln">Hello</span><span class="tag">&lt;/em&gt;</span><span class="pln">, from </span><span class="tag">&lt;strong&gt;</span><span class="pln">{{message}}</span><span class="tag">&lt;/strong&gt;</span><span class="pln">
    </span><span class="tag">&lt;/h3&gt;</span><span class="pln">
    </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"form-control"</span><span class="pln"> </span><span class="atn">ng-model</span><span class="pun">=</span><span class="atv">"message"</span><span class="tag">&gt;</span><span class="pln"> 
  </span><span class="tag">&lt;/panel&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span></pre>

	<p>
		لا تصلح طريقة الرّبط التي استخدمناها للعنوان باستخدام <strong><span style="font-family:courier new,courier,monospace;">@</span></strong> إلّا مع السّلاسل النّصّيّة التي لن يتمّ التّعديل عليها، فهذه الطّريقة لا يتمّ فيها مراقبة التّغييرات التي تطرأ على العنصر الأصليّ.
	</p>
</div>

<div id="wmd-preview-section-20">
	<h2 id="خاتمة">
		خاتمة
	</h2>

	<p>
		بناءً على خبرتك الحاليّة، فقد يكون هذا الفصل هو الأصعب ضمن هذه السلسلة، لذا أُهنّئُك على إنهائك له. يُفترض بك الآن أن تكون قادرًا على تعريف توجيهاتٍ مخصّصة يُمكن استخدامها دون الخوف من التّأثيرات الجانبيّة النّاتجة عن الحالة المشتركة، وهذه خطوةٌ كبيرة في طريقك نحو إكمال سيطرتك على التّوجيهات في Angular. في هذه النّقطة، سنحوّل تركيزنا عن الفروقات الدّقيقة في استخدام التّوجيهات، وسنتوجّه إلى مفهومٍ أساسيٍّ في مجال تطوير الويب من طرف المُستخدم: التسيير (routing) و الـURLs. 
	</p>

	<p>
		ترجمة -وبتصرّف- للفصل الحادي عشر (<a href="http://www.angularjsbook.com/angular-basics/chapters/directive-scopes/" rel="external nofollow">Directive Scopes</a>) من كتاب: <a href="http://www.angularjsbook.com/" rel="external nofollow">Angular Basics</a> لصاحبه: Chris Smith.
	</p>
</div>

<p><a href="https://academy.hsoub.com/uploads/monthly_2015_10/angularjs-directives.png.3675f33ec7b5956782e4a4ce1f00eba3.png" class="ipsAttachLink ipsAttachLink_image"><img data-fileid="6463" src="https://academy.hsoub.com/uploads/monthly_2015_10/angularjs-directives.thumb.png.2b246a9dbc7cd5ad790cecbcc28c10a5.png" class="ipsImage ipsImage_thumbnailed" alt="angularjs-directives.png"></a></p>]]></description><guid isPermaLink="false">203</guid><pubDate>Thu, 12 Nov 2015 12:24:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x648;&#x62C;&#x64A;&#x647;&#x627;&#x62A; (Directives) &#x641;&#x64A; AngularJS</title><link>https://academy.hsoub.com/programming/javascript/angular/%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%8A%D9%87%D8%A7%D8%AA-directives-%D9%81%D9%8A-angularjs-r202/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2015_10/angularjs-directives.png.676a57b55bf66485b6577a659b8b01cf.png" /></p>

<p>لَعِبت التّوجيهات المبنيّة داخل Angular مثل <a rel="external nofollow" href="http://docs.angularjs.org/api/ng.directive:ngModel">ng-bind</a> و<a rel="external nofollow" href="http://docs.angularjs.org/api/ng.directive:ngModel">ng-model</a> و<a rel="external nofollow" href="http://docs.angularjs.org/api/ng.directive:input">input</a> دور النّجوميّة في هذه السلسلة منذ الفصل الأوّل، <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D9%85%D8%A8%D8%A7%D8%AF%D8%A6-angularjs-r176/">المبادئ</a>، حتّى أنّ فصل <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D9%85%D8%AC%D9%85%D9%88%D8%B9%D8%A7%D8%AA-collections-%D9%81%D9%8A-angularjs-r191/">المجموعات</a> كان يركّز كلّيًّا على استخدام توجيهٍ واحدٍ (قويٍّ إلى حدّ كبير) هو التّوجيه <a rel="external nofollow" href="http://docs.angularjs.org/api/ng.directive:ngRepeat">ng-repeat</a>. قد تظنّ بعد ذلك أن <a rel="external nofollow" href="https://docs.angularjs.org/api/ng/directive">قائمة التّوجيهات المبنيّة</a> في داخل Angular تحوي حلولًا لجميع الحالات، ولكنّ هذا غير صحيحٍ بالطّبع. لقد تصوّر مصمّموا Angular بأنّها ستكون طريقةً للسّماح لغير المبرمجين ببناء صفحاتٍ تفاعليّة، إلّا أنّها تطوّرت لتصبح منصّةً تعطي مطوّري الويب القوّة لإنشاء امتداداتٍ وتخصيص HTML العاديّة، وهذا يعني إنشاء التّوجيهات الخاصّة بك، وهذا تمامًا ما سنقوم به في هذا الفصل.</p><p style="text-align: center;"><a class="ipsAttachLink ipsAttachLink_image" href="https://academy.hsoub.com/uploads/monthly_2015_10/angularjs-directives.png.3675f33ec7b5956782e4a4ce1f00eba3.png"><img data-fileid="6463" class="ipsImage ipsImage_thumbnailed" alt="angularjs-directives.thumb.png.2b246a9db" src="https://academy.hsoub.com/uploads/monthly_2015_10/angularjs-directives.thumb.png.2b246a9dbc7cd5ad790cecbcc28c10a5.png"></a></p><p>يكون إنشاء توجيهٍ مخصّص بشكله الأبسط مشابهًا كثيرًا لإنشاء <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D9%85%D8%B1%D8%B4%D8%AD%D8%A7%D8%AA-filters-%D9%81%D9%8A-angularjs-r201/">مرشّح مخصّص</a>، وهذا هو سبب الحديث عن المرشّحات قبل التّوجيهات في فصلٍ سابق. التّابع الخارجي الذي سنقوم بتمريره إلى التّابع <a rel="external nofollow" href="https://docs.angularjs.org/api/ng/type/angular.Module#directive">directive</a> هو غلافٌ لحقن التّبعيّة ويُدعى بتابع الصّناعة (factory function)، ويُعرف بالوصفة (recipe) ضمن توثيق Angular. يتمّ استدعاء هذا التّابع مرّةً على الأكثر، أو لا يتمّ استدعاؤه أبدًا إن لم يتمّ استخدام التّوجيه، كما تعلّمنا سابقًا في فصل <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D8%AE%D8%AF%D9%85%D8%A7%D8%AA-services-%D9%81%D9%8A-angularjs-r200/">الخدمات</a>. في داخل تابع الصناعة يوجد تابعٌ آخر تقوم Angular فعليًّا باستدعائه وهو المكان الذي نقوم فيهذر عن بالعمل الحقيقيّ للتّوجيه، ويُسمّى بتابع الربط link.</p><h2>التابع directive</h2><p>نستخدم التّابع <a rel="external nofollow" href="https://docs.angularjs.org/api/ng/type/angular.Module#directive">directive</a> لتسجيل توجيهٍ مخصّص، وهذا التّابع ذو الاسم المناسب لوظيفته جزءٌ من الواجهة البرمجيّة <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%81%D9%8A-angularjs-r196/">للوحدات</a>، لذا سنحتاج إلى إنشاء وحدةٍ جذرٍ لتطبيقنا.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app', []);</pre><p>ثمّ نقوم بتحميل الوحدة الجذر عن طريق تمرير اسم التّطبيق إلى التّوجيه <a rel="external nofollow" href="https://docs.angularjs.org/api/ng/directive/ngApp">ng-app</a>.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;body ng-app="app"&gt;
  &lt;!-- الأمثلة توضع هنا --&gt;
&lt;/body&gt;</pre><p>وبذلك أصبحنا جاهزين لإنشاء توجيه مخصّص، ولنبدأ بتوجيهٍ بسيطٍ جدًّا اسمه hello.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .directive('hello', function() {
    return function(scope, element) {
      element.text("hello");
    };
  });</pre><p>للقيام بأيّ شيءٍ مفيدٍ سنحتاج إلى استخدام الوسيط الثّاني لتابع الرّبط، والّذي يكون غلافًا (wrapper) لعنصرٍ من المستند نجلبه من مكتبة <a rel="external nofollow" href="https://code.google.com/p/jqlite/">jqLite</a> الخاصّة بـAngular أو من مكتبة <a rel="external nofollow" href="http://jquery.com/">jQuery</a> حسب الوسيط الذي قمت بتضمينه. في هذا المثال، قمنا باستبدال المحتوى السّابق للعنصر بالعبارة hello.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">The message is &lt;span hello&gt;&lt;/span&gt;.</pre><p><strong>الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">The message is hello.</pre><p>أليس هذا رائعًا؟ لقد أنشأنا لتوّنا خاصّيّة HTML جديدة خاصّةً بنا.</p><h2>الوسيط scope</h2><p>في المثال السابق قمنا يدويًا (hardcoded) بكتابة النصّ التي نريد كتابته وهذا لا يفيدنا كثيرًا، لذا لنرى إن كان بإمكاننا استبدال النّصّ بقيمةٍ متغيّرة. ربّما كنت تتساءل بخصوص الوسيط الأوّل <span style="font-family:courier new,courier,monospace;">scope</span>، لنقم بإضافة متحكّمٍ يقوم بإنشاء العنصر <span style="font-family:courier new,courier,monospace;">message</span> في مجاله.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .controller('MessageController', function($scope) {
    $scope.message = 'hello';
  });</pre><p>والآن لنقم باستخدام هذا العنصر في التّوجيه، وسنُسمّيه <span style="font-family:courier new,courier,monospace;">message</span> أيضًا.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .directive('message', function() {
    return function(scope, element) {
      element.text(scope.message);
    };
  });</pre><p>من الواضح أنّه يتوجّب علينا استخدام هذا التّوجيه في المكان داخل المستند الذي يكون تابعًا للمتحكّم <span style="font-family:courier new,courier,monospace;">MessageController</span>.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;p ng-controller="MessageController"&gt;
  The message is &lt;span message&gt;&lt;/span&gt;.
&lt;/p&gt;</pre><p><strong style="line-height: 24.8889px;">الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">The message is hello.</pre><p>قد تتساءل، لماذا يتمّ في المتحكّم تعريف وسيط المجال مسبوقًا برمز الدولار <span style="font-family:courier new,courier,monospace;">scope$</span> بينما في تابع الرّبط يكون هذا الوسيط بدون علامة الدولار في بدايته <span style="font-family:courier new,courier,monospace;">scope</span>، والسّبب هو أنّ أسماء الوسطاء في تابع الرّبط غير مهمّة (إلّا بالنّسبة لنا فقط) على عكس الوسطاء التي يتم حقنها.</p><h2>الوسطاء</h2><p>كما تعلّمنا سابقًا عند استخدام التّوجيهات المبنيّة داخل Angular، فالتّوجيهات تقبل تمرير الوسطاء، وهذه الوسطاء تُمرّر داخل الوسيط الثّالث لتابع الرّبط، وهو الوسيط <span style="font-family:courier new,courier,monospace;">attrs</span>. مرّةً أخرى، وعلى عكس حقن التّبعيّات، يُمكننا تسمية الوُسطاء بأيّ اسمٍ نريده، ويُفضّل استخدام أسماءٍ مفهومةٍ لتبقى الشّيفرة مفهومة.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .directive('welcomeMessage', function() {
    return function(scope, element, attrs) {
      element.text(attrs.welcomeMessage);
    };
  });</pre><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">The message is &lt;em welcome-message="bonjour"&gt;&lt;/em&gt;.</pre><p><strong style="line-height: 24.8889px;">الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">The message is <em>bonjour</em>.</pre><p>لاحظ أنّه عندما نستخدم أكثر من كلمةٍ واحدةٍ في كتابة اسم التّوجيه بأنّنا نستخدم نمط camel case حيث نكتب أوّل حرفٍ في الكلمة الثّانية بحرفٍ كبير، وهذا هو النّمط المتعارف عليه في JavaScript، ولكنّ النمط المستخدم في HTML هو فصل الكلمات باستخدام إشارة (-) وهو ما يُدعى hyphenated style، وخلاصة الكلام هي أنّك عندما تريد استخدام welcome-message لتضمين التّوجيه الخاص بك في القالب، سيكون عليك استخدام الاسم <span style="font-family:courier new,courier,monospace;">welcomeMessage</span> عندما تقوم بتعريفها في JavaScript.</p><p>قد تكون لاحظت بأنّ بعض التّوجيهات المدمجة في Angular تقوم بمعالجة (evaluate) العبارات، إلّا أنّ هذا السلوك ليس هو السلوك الافتراضي.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">The message is &lt;em welcome-message="'bon' + 'jour'"&gt;&lt;/em&gt;.</pre><p><strong style="line-height: 24.8889px;">الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">The message is '<em>bon</em>' + '<em>jour</em>'.</pre><p>بما أنّنا نعمل داخل قالب Angular، سيكون بإمكاننا دومًا تمرير نتيجة عبارةٍ معالجَة (evaluated).</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">The message is &lt;em welcome-message="{{'bon' + 'jour'}}"&gt;&lt;/em&gt;.</pre><p><strong style="line-height: 24.8889px;">الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">The message is <em>bonjour</em>.</pre><p>يُمكنك أيضًا إدخال سلسلةٍ نصّيّة معرّفة مسبقًا داخل السلسلة النّصّية العاديّة.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;p ng-init="greeting = 'bonjour'"&gt;
  The message is &lt;span welcome-message="I say {{greeting}}, you say hello"&gt;&lt;/span&gt;.
&lt;/p&gt;</pre><p><strong style="line-height: 24.8889px;">الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">The message is I say bonjour, you say hello.</pre><p>لتتمكّن من معالجة العبارة الممرّرة إلى التّوجيه كسلسلةٍ نظاميّة، ستحتاج إلى استخدام التّابع <span style="font-family:courier new,courier,monospace;">eval$</span>.</p><h3>التّابع scope.$eval</h3><p>بالرغم من أنّ Angular لا تعتبر أنّ الوسيط المُمرّر إلى التّوجيه عبارةٌ تحتاج إلى معالجة، إلّا أنّه يمكننا ببساطةٍ أن نعالج هذه العبارة باستخدام التّابع <span style="font-family:courier new,courier,monospace;">eval$</span>، ولأخذ العلم، فهذه الطّريقة لا تؤدّي إلى إعادة تحديث العرض (view) عند تغيُّر النّموذج، وهذا مفيدٌ فقط في حالات الاستخدام الأساسيّة.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .directive('welcomeMessage', function() {
    return function(scope, element, attrs) {
      var result = scope.$eval(attrs.welcomeMessage);
      element.text(result);
    };
  });</pre><p>لنقم بتجربة حالة استخدامٍ بسيطة نقوم فيها بوصل سلسلتين نصّيّتين.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">The message is &lt;em welcome-message="'bon' + 'jour'"&gt;&lt;/em&gt;.</pre><p><strong style="line-height: 24.8889px;">الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">The message is<em> bonjour</em>.</pre><p>يُمكننا أيضًا باستخدام <span style="font-family:courier new,courier,monospace;">scope.$eval</span> أن نقوم بتمرير كائنات، مّما يتيح لنا طريقةً لتلقّي العديد من الوسطاء معًا، وسنلقي نظرةً في فقرةٍ لاحقةٍ من هذا الفصل علىتوجيهات العناصر، والتي تقبل أيضًا العديد من الوسطاء المُسمّاة عن طريقة إتاحة وجود العديد من الخصائص للعنصر، أمّا الآن فسنرى في المثال التالي معالجةً لكائنٍ من نوع options، حيث نقوم بتمرير العديد من الوسطاء إلى التّوجيه.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .directive('welcomeMessage', function() {
    return function(scope, element, attrs) {
      var options = scope.$eval(attrs.welcomeMessage);
      var result = options.emoticon + ' ' + options.message + ' ' + options.emoticon;
      element.text(result);
    };
  });</pre><p>وبهذه الطّريقة استطعنا إدخال وسيطين بفعّاليّة، <span style="font-family:courier new,courier,monospace;">message</span> <span style="font-family:courier new,courier,monospace;">وemoticon</span>، واستطعنا إنجاز الوصل بينهما داخل التّوجيه.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">The message is &lt;em welcome-message="{message: 'bonjour', emoticon: '\u263a'}"&gt;&lt;/em&gt;.</pre><p><strong style="line-height: 24.8889px;">الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">The message is ☺ bonjour ☺.</pre><p>صِرنا الآن نفهم كيفيّة استخدام توجيهاتٍ مخصّصةٍ مع المجالات والمتحكّمات. ولكن ماذا لو أردنا استخدام خدماتٍ أخرى داخل التّوجيه؟</p><h2>حقن التبعية</h2><p>إن أردنا الوصول إلى موارد مُدارة مثل <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D8%AE%D8%AF%D9%85%D8%A7%D8%AA-services-%D9%81%D9%8A-angularjs-r200/">خدمةٍ ما</a>، أو <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D9%85%D8%B1%D8%B4%D8%AD%D8%A7%D8%AA-filters-%D9%81%D9%8A-angularjs-r201/">مُرشّحٍ ما</a> من داخل توجيهنا الخاصّ، فلن يكون علينا سوى التّصريح عن التّبعيّات كوسيطٍ في التّابع الخارجيّ. (راجع فصل <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%AD%D9%82%D9%86-%D8%A7%D9%84%D8%AA%D8%A8%D8%B9%D9%8A%D8%A9-dependency-injection-%D9%81%D9%8A-angularjs-r198/">حقن التّبعيّة</a> وفصل <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D8%AE%D8%AF%D9%85%D8%A7%D8%AA-services-%D9%81%D9%8A-angularjs-r200/">الخدمات</a> إن لم يكن هذا المفهوم مألوفًا لديك.)</p><p>على سبيل المثال، لنفترض بأنّنا نريد توجيهًا يُمكنه تطبيق حسمٍ على السّعر وأيضًا القيام بتنسيقٍ للعملة لقيمة المتغيّر price في المجال. سيحتاج هذا التّوجيه للوصول إلى الخدمة المخصّصة <span style="font-family:courier new,courier,monospace;">calculateDiscount</span> المُعرّفة كما يلي.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .value('discountRate', 0.8)
  .factory('calculateDiscount', function(discountRate) {
    return function(amount) {
      return amount * discountRate;
    };
  });</pre><p>والآن سنستخدم تابع الوصفة (recipe) التّغليفيّ لحقن الخدمة <span style="font-family:courier new,courier,monospace;">calculateDiscount</span> مع المُرشّح <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D9%85%D8%B1%D8%B4%D8%AD%D8%A7%D8%AA-filters-%D9%81%D9%8A-angularjs-r201/">currency</a> أيضًا.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .directive('discount', function(calculateDiscount, currencyFilter) {
    return function(scope, element, attrs) {
      var price = scope.$eval(attrs.discount);
      var discountPrice = calculateDiscount(price);
      element.html(currencyFilter(discountPrice));
    };
  });</pre><p>لاحظ بأنّنا قمنا بتمرير العنصر <span style="font-family:courier new,courier,monospace;">price</span> كقيمةٍ لوسيط التّوجيه في القالب، ويُمكننا الحصول على هذه القيمة للوسيط عن طريق الوسيط <span style="font-family:courier new,courier,monospace;">attrs</span> حيث نصل إلى الخاصّيّة (discount) وهي اسم التّوجيه الخاصّ بنا. في المثال التّالي، سنقوم بإسناد قيمة هذا العنصر في المجال (الّتي نحصل عليها عن طريق <span style="font-family:courier new,courier,monospace;">scope.$eval</span>) إلى المتغيّر المحلّي المُسمّى <span style="font-family:courier new,courier,monospace;">price</span>.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;table ng-init="price = 100"&gt;
  &lt;tr&gt;
    &lt;td&gt;Full price:&lt;/td&gt;
    &lt;td ng-bind="price | currency"&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Sale price:&lt;/td&gt;
    &lt;td discount="price"&gt;&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;</pre><p><strong style="line-height: 24.8889px;">الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">Full price:	$100.00
Sale price:	$80.00</pre><p>قد ترغب في وضع مُرشّحٍ بدلًا من التّوجيه، فالتّحويل الذي قمنا به بسيطٌ للغاية، حيث لم نضِف إلى المستند أيّ سلوكٍ تفاعليّ. لننتقل الآن إلى مثالٍ أكثر تعقيدًا بحيث يحتاج إلى إضافة وسمٍ باستخدام القالب.</p><h2>القوالب</h2><p>كُلّ العمليّات التي قمنا بتنفيذها في الأمثلة السّابقة على الوسيط <span style="font-family:courier new,courier,monospace;">element</span> كانت سهلةً إلى حدٍّ ما، ولكن ماذا لو أردنا إضافة قطعةٍ متغيّرة الحجم من محتوىً جديدٍ إلى المستند؟</p><p>لنقم بمحاولة حلّ هذه المشكلة بالتّدريج. في البداية، لنقُل بأننا نريد فقط تغليف سلسلةٍ نصّيّةٍ نُمرّرها كوسيطٍ للتّوجيه الخاصّ بنا باستخدام عنصر <span style="font-family:courier new,courier,monospace;">strong</span>.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">The message is &lt;span strong-message="a strong hello!"&gt;&lt;/span&gt;</pre><p>يُمكننا دومًا استخدام عمليّة وصل السّلاسل (concatenation).</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .directive('strongMessage', function() {
    return function(scope, element, attrs) {
      element.html('&lt;strong&gt;' + attrs.strongMessage + '&lt;/strong&gt;');
    };
  });</pre><p><strong>الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">The message is <strong>a strong hello!</strong></pre><p>أو يُمكننا بناء المحتوى بطريقةٍ برمجيّة باستخدام jqLite أو jQuery.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .directive('strongMessage', function() {
    return function(scope, element, attrs) {
      element.html('&lt;strong&gt;');
      element.find('strong').text(attrs.strongMessage);
    };
  });</pre><p><strong style="line-height: 24.8889px;">الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">The message is <strong>a strong hello!</strong></pre><p>عمليّة وصل السّلاسل والتّغييرات البرمجيّة على المستند طريقةٌ جيّدةٌ عند استخدامها للأمور الصّغيرة كعنصرٍ واحدٍ مثلًا، ولكن ماذا سنفعل عندما يكون المحتوى يتضمّن العديد من العناصر المتداخلة؟ أو عندما يكون لدينا قائمةٌ متغيّرة الحجم؟ سيجيبك عن هذا السّؤال أيّ خبيرٍ في تطوير النهاية الأماميّة (front-end) بأنّ مكتبة القوالب ستسهّل الأمر كثيرًا، ولحسن الحظّ، فـAngular مكتبة قوالب، وستجعل الأمر أكثر سهولة.</p><p>يُمكن لـAngular أن تقوم بإخراج (render) قوالب متداخلةٍ يقدّمها التّوجيه الخاصّ بنا، وذلك في الوقت نفسه الّذي تقوم فيه بإخراج قالب التّطبيق الخاصّ بنا.</p><p>لإضافة قالبٍ للتّوجيه، سنحتاج إلى تغيير أسلوبنا السّابق، فبدلًا من إعادة (return) تابع الرّبط فقط، سنقوم بإعادة كائنٍ يحوي تابع الربط ومعه قالب. هذا تغيير كبير، لذا فلنكرّر ذلك: سيقوم المُغلّف الوصفيّ (recipe) الآن بإعادة كائنٍ بدلًا من تابع، وسنستخدم العنصر link في هذا الكائن المُعاد للوصول إلى التّابع.</p><p>فيما يلي مثالٌ يستخدم قالبًا لإنشاء عددٍ غير محدّدٍ مسبقًا من عناصر <span style="font-family:courier new,courier,monospace;">li</span>.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .directive('wordList', function() {
    return {
      link: function(scope, element, attrs) {
        scope.words = attrs.wordList.split(" ");
      },
      template: "&lt;li ng-repeat='word in words'&gt;\
                   {{word}}\
                 &lt;/li&gt;"
    };
  });</pre><p>ستستلم Angular مهمّة تضمين القالب في المجال الصحيح، ثمّ إلحاق المخرجات بالعنصر الذي استخدم التّوجيه. بما أنّ مخرجات هذا التّوجيه ستكون عناصر في قائمة، لنقم بتضمين التّوجيه في العنصر<span style="font-family:courier new,courier,monospace;"> ul</span>.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;ul word-list="we love templates"&gt;&lt;/ul&gt;</pre><p><strong style="line-height: 24.8889px;">الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">. we
. love
. templates</pre><p>أظنّ بأن المثال بدأ يوضّح لك قوّة وأناقة التّوجيهات المخصّصة.</p><h3>العنصر templateUrl</h3><p>استخدمنا في المثال البسيط السّابق رمز الشرطة الخلفيّة (\) مرّتين لنتمكّن من كتابة نصّ القالب، وكما ترى، فالقوالب السّطريّة سُرعان ما تصبح مستهجنةً داخل شيفرة JavaScript، ولذلك سنستعين بالقوالب الخارجيّة التي تدعمها Angular، سواء كانت عناصر خاصّة مخفيّة في المستند، أو موارد ويب منفصلةً تمامًا في مكانٍ مستقل.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;li ng-repeat="word in words"&gt;
  {{word}}
&lt;/li&gt;</pre><p>لقد قمنا باستخراج شيفرة القالب إلى مورد ويب موجودٍ في الرابط النّسبيّ <span style="font-family:courier new,courier,monospace;">views/word-list.html</span>، والآن سنقوم بإضافة العنصر <span style="font-family:courier new,courier,monospace;">templateUrl</span> إلى كائن الإعدادات الذي يعيده التّوجيه الخاص بنا، وسنقوم بإسناد مسار القالب إليه، وذلك بدلًا من استخدام العنصر <span style="font-family:courier new,courier,monospace;">template</span> وإسناد شيفرة القالب إليه مباشرةً.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .directive('wordList', function() {
    return {
      link: function(scope, element, attrs) {
        scope.words = attrs.wordList.split(" ");
      },
      templateUrl: '/views/word-list.html'
    };
  });</pre><p>أمّا الاستخدام فيبقى كما هو دون تغيير.</p><p> </p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;ul word-list="external templates rock"&gt;&lt;/ul&gt;</pre><p><strong style="line-height: 24.8889px;">الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">. external
. templates
. rock</pre><p>القوالب الخارجيّة عمومًا أسهلُ بكثيرٍ للقراءة وللتّعديل من القوالب السّطريّة.</p><h3>العنصر replace</h3><p>لا بُدّ من استخدام التّوجيه في المثال السابق ضمن قائمة، ولكن ماذا لو لم يقم المستخدم باستخدام التّوجيه بشكلٍ صحيح؟ أحد الحلول هو استبدال العنصر الذي يتضمّن التّوجيه بدلًا من إلحاق العناصر به. إذًا لنقُم بإضافة عنصر ul لتغليف مخرجات قالبنا، سنحتاج أيضًا إلى عنصرٍ جديدٍ في كائن الإعدادات هو العنصر replace، وسنُسند إليه القيمة true. (القيمة الافتراضيّة له هي بالطّبع false.)</p><p>لنقم أوّلًا بإضافة العنصر<span style="font-family:courier new,courier,monospace;"> ul</span> إلى قالبنا.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;ul&gt;
  &lt;li ng-bind="word" ng-repeat="word in words"&gt;&lt;/li&gt;
&lt;/ul&gt;</pre><p>التّغيير الوحيد الهامّ في التّوجيه هو استخدام العنصر الجديد <span style="font-family:courier new,courier,monospace;">replace</span>.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .directive('wordList', function() {
    return {
      link: function(scope, element, attrs) {
        scope.words = attrs.wordList.split(" ");
      },
      templateUrl: '/views/word-list-ul.html',
      replace: true
    };
  });</pre><p>بما أنّ العنصر الذي يتضمّن التّوجيه سيتمّ استبداله مهما كان، يُمكننا تضمين التّوجيه في عنصرٍ غريبٍ وغير ملائمٍ مثل <span style="font-family:courier new,courier,monospace;">h1</span>.</p><p> </p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;h1 word-list="lists not headlines"&gt;&lt;/h1&gt;</pre><p><strong style="line-height: 24.8889px;">الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">. lists
. not
. headlines</pre><p>يبيّن المثال السّابق قوّة تأثير التّوجيهات على المستند، ولكنّني أُفضّل أن يكون التّصميم أكثر وضوحًا، ودلالته صحيحة، ولذلك فبدلًا من استبدال وسوم HTML، أرى أنّ الصّواب هو التّحقّق من صحّة العنصر الذي قام بتضمين التّوجيه، وإن لم يكن صحيحًا يتمّ إلقاء خطأ (throw an error).</p><h2>العنصر controller</h2><p>في بداية هذا الفصل، رأينا مثالًا يحوي متحكّمًا يقوم بتحضير عنصرٍ في المجال، ثمّ استخدمنا هذا العنصر لاحقًا داخل تابع الربط الخاصّ بالتّوجيه. والآن بعد أن تعرّفنا على طريقة استخدام القوالب، لنقُم بإعادة كتابة هذا المثال، واستبدال تابع الرّبط الذي يحدّد نصّ العنصر في المستند بقالب.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .controller('MessageController', function($scope) {
    $scope.message = 'hello, from the external controller';
  })
  .directive('message', function() {
    return {
      template: "&lt;strong&gt;{{message}}&lt;/strong&gt;"
    };
  });</pre><p>بما أنّ المتحكّم والتّوجيه منفصلان، فكلاهما يحتاج إلى التّضمين بشكلٍ منفصلٍ في المثال التالي. يُمكننا أن نضع المتحكّم في نفس العنصر الذي يحوي التّوجيه أو في عنصرٍ مغلّف، فلا فرق.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">The message is &lt;span message ng-controller="MessageController"&gt;&lt;/span&gt;.</pre><p><strong>الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">The message is <strong>hello, from the external controller.</strong></pre><p>كما ترى في المثال السّابق، لقد قُمنا بإزالة تابع الرّبط، فوجوده أمرٌ اختياريّ، واقتصرنا على العنصر <span style="font-family:courier new,courier,monospace;">template</span>، وذلك لأنّ استخدام تابع الرّبط يكون للقيام بالتّغييرات البرمجيّة على المستند بطريقةٍ أكثر أُلفةً لمطوّري النّهاية الأماميّة. أمّا الآن بعد أن انتقلنا إلى استخدام القوالب، فسنحتاج إليه للقيام بمهمّاتٍ نحتاج فيها إلى الوصول إلى العنصر الحاوي للتّوجيه أو إلى خصائص العنصر (مُتضمّنةً التّوجيه بذاته، كما رأينا سابقًا.)</p><p>إضافةً إلى ذلك، فإنّ تحضير بيانات النّموذج لا يحتاج إلى الوصول إلى العنصر أو إلى خصائصه، وهو من مهمّات المتحكّم، وهذا بالرّغم من إمكانيّة القيام بذلك داخل تابع الرّبط كما بيّنّا في فقرة القوالب. لذا، سيكون من الأفضل أن ننقل مسؤوليّة تحضير النّموذج إلى داخل المتحكّم، إلّا أنّ المشكلة الآن هي أنّ المتحكّم منفصلٌ ويحتاج إلى إعداداتٍ خاصّةٍ به، فهل يُمكننا نقل المتحكّم إلى داخل التّوجيه؟ نعم، يمكننا ذلك.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .directive('message', function() {
    return {
      template: "&lt;strong&gt;{{message}}&lt;/strong&gt;",
      controller: function($scope) {
        $scope.message = 'hello, from the internal controller';
      }
    };
  });</pre><p>أليس هذا رائعًا؟ لم نحصل الآن على مكوِّنٍ مغلّفٍ جيّدًا وحسب، بل تخلّصنا الآن من تضمين المتحكّم واستخدام التّوجيه <span style="font-family:courier new,courier,monospace;">ng-controller</span> في الوسم.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">The message is &lt;span message&gt;&lt;/span&gt;.</pre><p><strong>الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">The message is<strong> hello, from the internal controller.</strong></pre><p>ويمكننا طبعًا القيام بكلّ الأشياء المعتادة مع المتحكّم، كتعريف تابعٍ في المجال وتضمين هذا التّابع من عنصر تحكّمٍ داخل القالب. راجع فصل <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D9%85%D8%AA%D8%AD%D9%83%D9%85%D8%A7%D8%AA-controlers-%D9%81%D9%8A-angularjs-r186/">المتحكّمات</a> للاستزادة.</p><h2>الخيار restrict</h2><p>تُوجد أربع طرقٍ لتضمين توجيهٍ ما، إلّا أنّنا لم نرَ حتّى الآن إلّا طريقة تضمين التّوجيهات كخصائص لعناصر HTML، وذلك لأنّه الأسلوب الأكثر شيوعًا وفائدةً للقيام بذلك، وهي أيضًا القيمة الافتراضيّة للخيار <span style="font-family:courier new,courier,monospace;">restrict</span> الذي يُمكن أن يملك واحدةً أو أكثر من الخيارات التّالية:</p><ul><li>'<strong>A</strong>' يتمّ تضمينه كخاصّيّة (Attribute)</li><li>'<strong>C</strong>' يتمّ تضمينه كفئة (Class)</li><li>'<strong>E</strong>' يتمّ تضمينه كعنصر (Element)</li><li>'<strong>M</strong>' يتمّ تضمينه كتعليق (Comment)</li></ul><p>بالنّسبة للأساليب الثّلاثة الأخرى في التّضمين (الفئة، العنصر والتّعليق) فأسلوب التضمين كعنصرٍ هو الأكثر إثارةً للاهتمام من النّاحية العمليّة، فأسلوبا الفئة والتّعليق موجودان لدعم الحالات الهامشيّة مثل وجود تحقّق صارمٍ لصحّة نصوص HTML.</p><p>أعتبر أنّ عنصر التّوجيه يُشبه مدفعًا كبيرًا، يجب عليك ألا تستخدمه إلّا إن كان الأمر يستحق ذلك. نموذجيًّا، يتمّ استخدام هذه الطّريقة عندما نحتاج إلى وضع عددٍ كبيرٍ من الوسوم داخل مكوّنٍ من مكوّنات HTML ويكون أيضًا عليك تمرير العديد من الوسطاء إليه، فعندما تستخدم عنصر التّوجيه، ستتمكّن من تمرير قيم الوسطاء على شكل خصائص لهذا العنصر.</p><p>في المثال التّالي لن يكون لدينا هذا العدد الكبير من الوسوم إلّا أنّنا سنصمّم جدولًا مع كلّ الزخرفات الخاصة به.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;table class="table table-condensed"&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th ng-bind="column" ng-repeat="column in columns"&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr ng-repeat="model in models"&gt;
      &lt;td ng-bind="model[column]" ng-repeat="column in columns"&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;</pre><p>سيستخدم تابع الرّبط الآن وسيطين في تعريف التّوجيه الخاصّ بنا، وسنصل إلى هذا الوسيطين عن طريق <span style="font-family:courier new,courier,monospace;">attrs.models </span>و <span style="font-family:courier new,courier,monospace;">attrs.columns</span>.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .directive('modelsTable', function() {
    return {
      restrict: 'E',
      templateUrl: '/views/models-table.html',
      link: function(scope, element, attrs) {
        scope.models = scope.$eval(attrs.models);
        scope.columns = attrs.columns.split(",");
      }
    };
  });</pre><p>قد ترغب بإسناد القيمة true إلى العنصر <span style="font-family:courier new,courier,monospace;">replace</span> في عنصر التّوجيه إن كانت البيئة التي تعمل عليها تُسبّب لك بعض المشاكل عند استخدام أسماء عناصر غير معياريّة، على الجانب الآخر، تركُ هذا العنصر المخصص في مستندك قد يساعدك أثناء تصحيح الأخطاء في الشيفرة.</p><p>سنقوم مرّةً أخرى باستخدام متحكّمٍ خارجيّ في هذا المثال.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .controller('ItemsController', function($scope) {
    $scope.items = [
      {name: 'Item 1', color: 'green', price: 5.0},
      {name: 'Item 2', color: 'blue', price: 4.93}
    ];
  });</pre><p>وأخيرًا، سنقوم بإضافة <a rel="external nofollow" href="http://getbootstrap.com/">Bootstrap</a> إلى صفحتنا لجعل مثالنا الأخير أكثر أناقة.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"&gt;
&lt;script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.2/angular.js"&gt;&lt;/script&gt;</pre><p>الخطوة الأخيرة الآن هي تضمين عنصر التّوجيه الخاصّ بنا مع وسيطيه <span style="font-family:courier new,courier,monospace;">models</span> و<span style="font-family:courier new,courier,monospace;">columns</span> حيث سنضيفهما على شكل خصائص للوسم.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;p ng-controller="ItemsController"&gt;
  &lt;models-table models="items" columns="name,color,price"&gt;&lt;/models-table&gt;
&lt;/p&gt;</pre><p>رائع، لقد قمنا بإخفاء الشيفرات المزعجة الخاصّة بجدول HTML بإحكام.</p><h2>خاتمة</h2><p>لقد قدّم لك هذا الفصل عن التّوجيهات معرفةً ستعطيك قوّةً كبيرةً عند استخدامك لـAngular في تطوير تطبيقات طرف المستخدم، فكما رأينا في المثال الأخير، يُمكنك الآن تعريف توجيهٍ مخصّصٍ يستخدم قوّة قوالب Angular ويستخدم التّوجيهات المدمجة معها، مع قدرةٍ كبيرةٍ على التّقليل من الشّيفرات المزعجة ومن ثمّ زيادة الإنتاجيّة.</p><p>لا يزال علينا فهم أمورٍ أخرى بخصوص التّوجيهات، خصوصًا عندما تدخل مشكلة الحالة المشتركة إلى السّاحة. سيعرض الفصل القادم هذه السيناريوهات المتقدّمة، فإن كنت تشعر بأنك حصلت على المعرفة الكافية لخدمة أهدافك، فيُمكنك القفز مباشرةً إلى فصل HTTP. أما إن كنت ترغب في تعلُّم المزيد، فتابع مباشرةً إلى ا<a href="https://academy.hsoub.com/programming/javascript/angularjs/%D9%85%D8%AC%D8%A7%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%8A%D9%87-directive-scopes-%D9%81%D9%8A-angularjs-r203/">لجزء الثّاني من فصل التّوجيهات</a>.</p><p>ترجمة وبتصرّف <a rel="external nofollow" href="http://www.angularjsbook.com/angular-basics/chapters/directives/">للفصل العاشر</a> من كتاب: <a rel="external nofollow" target="_blank" href="http://www.angularjsbook.com/">Angular Basics</a> لصاحبه: Chris Smith.</p>
]]></description><guid isPermaLink="false">202</guid><pubDate>Sun, 01 Nov 2015 10:11:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x645;&#x631;&#x634;&#x62D;&#x627;&#x62A; (filters) &#x641;&#x64A; AngularJS</title><link>https://academy.hsoub.com/programming/javascript/angular/%D8%A7%D9%84%D9%85%D8%B1%D8%B4%D8%AD%D8%A7%D8%AA-filters-%D9%81%D9%8A-angularjs-r201/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2015_10/filters-angularjs.png.436dc4b7f5aa48eb6b2b73286e41592e.png" /></p>

<p>تَستخدم المُرشّحات في Angular نمط خطّ الأنابيب (pipeline) بشكلٍ مشابه للـUnix shell، فقد استعارت رمز الخطّ العمودي المشابه لرمز <a rel="external nofollow" href="http://en.wikipedia.org/wiki/Vertical_bar#Pipe">الأنبوب</a> من Unix لتستخدمه لتمرير عبارات التّهيئة أو خواص المجال للمُرشّح وأيضًا لربط المُرشّحات معًا.</p><p style="text-align: center;"><a class="ipsAttachLink ipsAttachLink_image" href="https://academy.hsoub.com/uploads/monthly_2015_10/filters-angularjs.png.d08e19d1a289f3ae42ef0e4db1b0cf29.png"><img data-fileid="6374" class="ipsImage ipsImage_thumbnailed" alt="filters-angularjs.thumb.png.c5c1d4f293d7" src="https://academy.hsoub.com/uploads/monthly_2015_10/filters-angularjs.thumb.png.c5c1d4f293d71f237d742c065a4e6dee.png"></a></p><p>سنقوم بتحميل إطار عمل <a rel="external nofollow" href="http://getbootstrap.com/">Bootstrap</a> الخاصّ بـCSS لأننا سنقوم بإنشاء بعض الجداول في أمثلة هذا الفصل ممّا سيجعلها أكثر ترتيبًا، وسنجلبه من شبكة توصيل المحتوى (CDN) كما تُبيّن الشّيفرة التّالية.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"&gt;
&lt;script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.2/angular.js"&gt;&lt;/script&gt;</pre><p>ستسخدم الأمثلة أيضًا  <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D9%85%D8%AA%D8%AD%D9%83%D9%85%D8%A7%D8%AA-controlers-%D9%81%D9%8A-angularjs-r186/">المتحكمات</a> و <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D8%AE%D8%AF%D9%85%D8%A7%D8%AA-services-%D9%81%D9%8A-angularjs-r200/">الخدمات</a> لتحضير البيانات للعرض، لذا سنقوم بتعريف <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%81%D9%8A-angularjs-r196/">وحدة</a> جذرٍ لإداراتهم.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app', []);</pre><p>سنقوم بإسناد اسم وحدة الجذر لتطبيقنا إلى التّوجيه <span style="font-family:courier new,courier,monospace;">ng-app</span>.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;body ng-app="app"&gt;
  &lt;!-- الأمثلة توضع هنا --&gt;
&lt;/body&gt;</pre><p>مفهوم المُرشّحات سهلُ التّعلّم، لذا سيكون هذا الفصل مرورًا سريعًا على المُرشّحات المبنيّة داخل Angular، ثُمّ سنستعرض كيف يمكننا إنشاء المُرشّحات الخاصّة بنا.</p><h2>lowercase و uppercase</h2><p>لنبدأ جولتنا مع المُرشّحات المبنيّة في Angular ببعض التّحويلات على السّلاسل النّصية، والمُرشّح <a rel="external nofollow" href="http://docs.angularjs.org/api/ng.filter:lowercase">lowercase</a> بدايةٌ ممتازة. نبدأ بكتابة أيّ عبارة (في مثالنا استخدمنا عبارة "AngularJS")، ثم نضع رمز الأنبوب (|)، وبعده نكتب اسم المُرشّح (lowercase).</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;p&gt;{{"AngularJS" | lowercase}}&lt;/p&gt;</pre><p><strong>الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">angularjs</pre><p>يُمكننا تطبيق المرشّح <a rel="external nofollow" href="http://docs.angularjs.org/api/ng.filter:uppercase">uppercase</a> على النّتيجة عن طريق إضافة رمز أنبوبٍ آخر، ثمّ اسم المرشّح، وهذا ما نُسمّيه بسلسلة المُرشّحات.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;p&gt;{{"AngularJS" | lowercase | uppercase }}&lt;/p&gt;</pre><p><strong style="line-height: 24.8889px;">الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">ANGULARJS</pre><p>بالطّبع ليس هناك فائدةٌ من وضع <span style="font-family:courier new,courier,monospace;">lowercase</span> قبل <span style="font-family:courier new,courier,monospace;">uppercase</span>، وسنرى في نهاية هذا الفصل مثالًا أفضل على سَلسَلة المُرشّحات، ولكن على الأقل رأينا كيف نقوم بذلك الآن.</p><h2>number</h2><p>المُرشّح التّالي سهلٌ أيضًا، المُرشّح <a rel="external nofollow" href="http://docs.angularjs.org/api/ng.filter:number">number</a> يُقدّم المساعدة الأساسيّة لتنسيق الأعداد وتقريبها.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;p&gt;{{1000.12345 | number}}&lt;/p&gt;</pre><p><strong style="line-height: 24.8889px;">الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">1,000.123</pre><p>بالنّسبة لإعداداتي المحلّيّة، en-us، المُخرجات في المثال هي 1,000.123، يقوم المُرشّح بتقريب العدد المُمرّر إليه إلى 3 خاناتٍ بعد الفاصلة، ومن الواضح أنّ هذا لن يناسب جميع الاستخدامات، ولحسن الحظ، يمكننا تمرير وسطاء إلى المرشّحات التي تقبل القيام بذلك، والمُرشّح <span style="font-family:courier new,courier,monospace;">number</span> يقبل ذلك. الوسيط الأول الذي نقوم بتمريره هو القيمة التي نريد تطبيق المُرشّح عليها، أما الوسيط الإضافي الثّاني فيجب أن يتبع اسم المُرشّح ويكون مفصولًا عنه بنقطتين (:).</p><p>الوسيط الإضافيّ الثّاني للمُرشّح <span style="font-family:courier new,courier,monospace;">number</span> هو <span style="font-family:courier new,courier,monospace;">fractionSize</span>، وهو يسمح لنا بالتّحكّم بعدد المنازل التي يتم عرضها بعد الفاصلة، وبهذا يمكننا رؤية القيمة كاملةً.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;p&gt;{{1000.12345 | number:5}}&lt;/p&gt;</pre><p><strong style="line-height: 24.8889px;">الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">1,000.12345</pre><p>ماذا يحدث لو قمنا بتمرير قيمةٍ سالبةٍ إلى الوسيط <span style="font-family:courier new,courier,monospace;">fractionSize</span>؟ استبدل 5 بـ 3- ثمّ جرّب 4-، هل يُمكنك تخمين السّبب؟ لا مشكلة، حتى أنا لا يمكنني ذلك.</p><h2>currency</h2><p>المُرشّح التّالي مفيدٌ بعض الشّيء في التّطبيقات العمليّة: يُقدّم المُرشّح <a rel="external nofollow" href="http://docs.angularjs.org/api/ng.filter:currency">currency</a> الدّعم الأساسيّ لتنسيق العملة وتقريب قيمة العدد.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;p&gt;{{1999.995 | currency}}&lt;/p&gt;</pre><p><strong style="line-height: 24.8889px;">الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">$2,000.00</pre><p>وهو يقبل وسيطين إضافيين، أوّلهما هو رمز العملة، استبدل <span style="font-family:courier new,courier,monospace;">currency</span> بـ<span style="font-family:courier new,courier,monospace;">'€':currency </span>في المثال السّابق لتجريبه.</p><p>الوسيط الثّاني هو <span style="font-family:courier new,courier,monospace;">fractionSize</span>. انتبه عند استخدامك للمُرشّح <span style="font-family:courier new,courier,monospace;">currency</span> عند تقريب المبَالِغ الماليّة، فليس هناك طريقةٌ واحدةٌ فقط هي الصّحيحة عندما ننظر للأمر على أنّه مسألةٌ ماليٌّةٌ وليست تقنيًّة، فهذا المُرشّح يستخدم تقريب النصف للأعلى وقد لا يكون هذا جيّدًا دومًا، كما رأينا في المثال السّابق. لا يوجد حاليًّا طريقةٌ موثّقةٌ لتجاوز طريقة المُرشّح في التّقريب، ما رأيك بأن تحاول التّعديل على المثال السّابق لتجعل المُرشّح يعرض القيمة الصّحيحة.</p><h2>date</h2><p>سيبيّن المُرشّح <a rel="external nofollow" href="http://docs.angularjs.org/api/ng.filter:date">date</a> لك إلى أيّ مدىً تذهب بك إعدادات المُرشّحات.</p><p>لا تسمح Angular بتهيئة كائنٍ من نوع <span style="font-family:courier new,courier,monospace;">Date</span> داخل عبارة، لذا سنستخدم متحكّمًا لتحضير النّموذج الخاصّ بالمثال.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .controller('DateController', function($scope) {
    $scope.now = Date.now();
  });</pre><p>يبدأ المثال التّالي بعنصرٍ في المجال اسمه now، بدلًا من كتابة عبارة.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;p ng-controller="DateController"&gt;
{{now | date}}
&lt;/p&gt;</pre><p><strong style="line-height: 24.8889px;">الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">Oct 28, 2015</pre><p>إن قمت بإزالة<span style="font-family:courier new,courier,monospace;"> date | </span>من المثال السّابق سترى القيمة الأصليّة للعنصر <span style="font-family:courier new,courier,monospace;">now</span>، وهي عدد الميللي ثانية منذ شهر كانون الثّاني عام 1970 حسب التوقيت العالميّ UTC. والآن قم بتجربة بعض التّنسيقات المُعرّفة سابقًا، والتي يدعمها هذا المُرشّح، قم بتجربة<span style="font-family:courier new,courier,monospace;"> 'date:'medium | </span>ثمّ <span style="font-family:courier new,courier,monospace;">'date:'shortTime |</span> وبعدها <span style="font-family:courier new,courier,monospace;">'date:'yyyy-MM-dd HH:mm:ss Z |</span>، وجرّب بعض التّعديلات من عندك بعد ذلك. يُسمح بكتابة السّلاسل المحرفية داخل نمط التّنسيق، مثلًا <span style="font-family:courier new,courier,monospace;">"date:"MMMM 'of the year' yyyy |</span>، وبدلًا من استخدام محارف الهروب مثل % لوضع رموز التّنسيق، سيكون عليك وضع السّلاسل المحرفية بين علامتَي تنصّيّصٍ مفردة (') وأن تكون عبارة التّنسيق الكاملة بين علامتَي تنصّيّصٍ مزدوجة ("). كما أنّ الفراغات في عبارة التّنسيق تدخل في تنسيق العبارة.</p><h2>orderBy</h2><p>يُمكن للمُرشّحات القيام بما هو أكثر من تنسيق المخرجات، حيث يُمكن استخدمها لتحويل المجموعات، كما سنرى في المثال التّالي.</p><p>لنفترض أنّ خدمةً في النهاية الخلفية (backend) أَرجعت سجلًّا مرتًبًا بطريقةٍ غير مرغوبةٍ لأسباب تتعلّق بالعرض. (لتبسيط المثال، ستكون البيانات مكتوبةً يدويًّا داخل الخدمة <span style="font-family:courier new,courier,monospace;">items</span> التي تراها أدناه، وذلك بدلًا من التّعامل مع نهايةٍ خلفيّة بعيدة حقيقيّة.)</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .value('items', [
    {name: 'Item 3', position: 3, color: 'red', price: 2.75},
    {name: 'Item 1', position: 1, color: 'red', price: 0.92},
    {name: 'Item 4', position: 4, color: 'blue', price: 3.09},
    {name: 'Item 2', position: 2, color: 'red', price: 1.95}
  ]);</pre><p>سنقوم بعد ذلك بحقن المصفوفة items في داخل متحكّم.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .controller('ItemsController', function($scope, items) {
    $scope.items = items;
  });</pre><p>بناءً على ترتيب العناصر الحالي في المصفوفة، كيف يمكننا ترتيب العناصر حسب قيمة العنصر <span style="font-family:courier new,courier,monospace;">position</span>؟</p><p>إن كنتَ قد قفزتَ من مقعدك الآن وصرخت قائلًا "<a rel="external nofollow" href="http://underscorejs.org/">Underscore</a>" (أو "<a rel="external nofollow" href="https://lodash.com/">lodash</a>")، فتستحقّ منّي وسام الوفاء لـJavaScript، ولكنّها ليست الإجابة التي نبحث عنها، فـAngular تملك مُرشّحًا للقيام بذلك، إنّه المُرشّح <a rel="external nofollow" href="http://docs.angularjs.org/api/ng.filter:orderBy">orderBy</a>.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;ol ng-controller="ItemsController"&gt;
  &lt;li ng-repeat="item in items | orderBy:'position'"&gt;
     {{item.name}}
  &lt;/li&gt;
&lt;/ol&gt;</pre><p><strong>الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">1. Item 1
2. Item 2
3. Item 3
4. Item 4</pre><p>هذا رائع، ولكن ماذا لو أردنا أن يكون التّرتيب بالجهة المعاكسة؟ في هذا المثال البسيط يمكننا القيام بذلك بطريقتين، الأولى هي إضافة الرمز - قبل اسم العنصر، أي أن نكتب <span style="font-family:courier new,courier,monospace;">'orderBy:'-position</span> بدلًا من<span style="font-family:courier new,courier,monospace;"> 'orderBy:'position</span>. (يمكنك أيضًا إضافة الرمز + لاختيار التّرتيب التّصاعدي، إلا أنّه لا يؤثّر على مثالنا السّابق.) أمّا الطّريقة الثّانية فهي إعطاء قيمةٍ للوسيط البولياني reverse بعد اسم العنصر المطلوب التّرتيبُ حسب قيمته. في مثالنا، قم بكتابة <span style="font-family:courier new,courier,monospace;">orderBy:'position':true</span> بدلًا من <span style="font-family:courier new,courier,monospace;">'orderBy:'position.</span></p><p><strong>فائدةٌ إضافيّة</strong>: عدّل سجلّ البيانات في المتحكّم بحيث تجعل عنصرين في المصفوفة لهما القيمة نفسها بالنّسبة للعنصر <span style="font-family:courier new,courier,monospace;">position</span>، ألا يزال بإمكانك استخدام المُرشّحorderBy لترتيب العناصر بشكلٍ صحيح؟ ماهي الخاصّيّة التي استخدمتها؟</p><h3>انتبه للتحديثات</h3><p>بفضل طريقة Angular في الربط ثنائيّ الاتّجاه، سيكون من السّهل جدًّا أن تجعل النّماذج في قائمة أو جدول العرض قابلةً للتّعديل، كلّ ما تحتاجه هو إضافة خانة إدخالٍ داخل الحلقة مع إسناد اسم عنصر المجموعة إلى التّوجيه<span style="font-family:courier new,courier,monospace;"> ng-model</span>. ولكن انتبه، فبما أنّ Angular جاهزةٌ دومًا وتنتظر إعادة توليد العرض عند أيّ تغيير في الحالة، فقد يتمّ إعادة ترتيب قائمتك قبل أن تنتهي من تعديل العنصر حتّى.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;table class="table table-condensed" ng-controller="ItemsController"&gt;
  &lt;tr ng-repeat="item in items | orderBy:'name'"&gt;
    &lt;td ng-bind="item.name"&gt;&lt;/td&gt;
    &lt;td&gt;&lt;input type="text" ng-model="item.name"&gt;&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
</pre><p>جرّب تعديل اسم أحد العناصر في المثال السّابق بحذف المحتوى أوّلًا ثمّ كتابة الأحرف، وستلاحظ مشكلةً كبيرةً في الاستخدام. فـAngular تقوم بإعادة توليد الجدول كاملًا، ونقل السطر إلى مكانٍ آخر قبل أن تنتهي من كتابته. أفضل الحلول لهذه المشكلة قد يكون استخدام عرضٍ(view) آخر منفصلٍ للتّعديل، كما يمكنك نقل المُرشّح <span style="font-family:courier new,courier,monospace;">orderBy</span> من العرض إلى المتحكّم حيث يتمّ استدعاؤه مرّةً واحدة، وهذا ما سنتكّلم عنه في الفقرة القادمة، استخدام المرشحات في JavaScript.</p><h2>limitTo</h2><p>المُرشّح <a rel="external nofollow" href="http://docs.angularjs.org/api/ng.filter:limitTo">limitTo</a> مفيدٌ عندما ترغب بعرض مجموعةٍ جزئيّةٍ فقط من مجموعةٍ مرتّبة. مثلًا، يمكننا استخدامها بالتّعاون مع المُرشّح <span style="font-family:courier new,courier,monospace;">orderBy</span> لنعرض فقط أغلى عنصرين في المجموعة.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;ol ng-controller="ItemsController"&gt;
  &lt;li ng-repeat="item in items | orderBy:'-price' | limitTo:2"&gt;
     {{item.price | currency}}
  &lt;/li&gt;
&lt;/ol&gt;</pre><p><strong>الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">1. $3.09
2. $2.75</pre><p>لا تُقدّم Angular طريقةً لتمرير وسيط <span style="font-family:courier new,courier,monospace;">offset</span> إلى المُرشّح <span style="font-family:courier new,courier,monospace;">limitTo</span> حاليًّا. ولتقديم حلٍّ كاملٍ للتصحيف (pagination) سيتضمّن ذلك استخدام التّابع <a rel="external nofollow" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice">slice</a> داخل المتحكّم.</p><h2>filter</h2><p>اسم هذا المُرشّح ظريفٌ نوعًا ما، فهو مرشّحٌ اسمه <a rel="external nofollow" href="http://docs.angularjs.org/api/ng.filter:filter">filter</a>، ربّما لم يكن من المناسب تسمية المُرشّحات بهذا الاسم العام (ربّما يكون الاسم مزخرِفات أو مساعِدات أفضل)، إلّا أنّ هذا المرشّح يقوم فعلًا بالتّرشيح. فهو يقوم بإعادة مصفوفةٍ تحوي فقط على العناصر التي تطابق الشرط المُسند. لنرى ما هي العناصر التي ستطابق وضعنا لشرطٍ يقول بأنّ القيمة العدديّة تساوي 3.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;table class="table table-condensed" ng-controller="ItemsController"&gt;
  &lt;thead&gt;
    &lt;tr&gt;&lt;th&gt;name&lt;/th&gt;&lt;th&gt;color&lt;/th&gt;&lt;th&gt;price&lt;/th&gt;&lt;/tr&gt;
  &lt;thead&gt;
  &lt;tbody&gt;
    &lt;tr ng-repeat="item in items | filter:3"&gt;
      &lt;td ng-bind="item.name"&gt;&lt;/td&gt;
      &lt;td ng-bind="item.color"&gt;&lt;/td&gt;
      &lt;td ng-bind="item.price | currency"&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;tbody&gt;
&lt;/table&gt;</pre><p>لقد حصلنا على تطابقات في كلا العنصرين <span style="font-family:courier new,courier,monospace;">name</span> <span style="font-family:courier new,courier,monospace;">وprice</span>. ماذا سيحدث لو استبدلنا الشرط 3 بالسّلسلة النّصية '3' أو بسلسلة نصّيّة خالية؟ أو لو أضفنا فراغًا في البداية ('3 ')؟</p><p>لتجنُّب مطابقة أيّ عنصرٍ، يمكنك تمرير كائنٍ باعتباره شرط التّطابق.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;table class="table table-condensed" ng-controller="ItemsController"&gt;
  &lt;thead&gt;
    &lt;tr&gt;&lt;th&gt;name&lt;/th&gt;&lt;th&gt;color&lt;/th&gt;&lt;th&gt;price&lt;/th&gt;&lt;/tr&gt;
  &lt;thead&gt;
  &lt;tbody&gt;
    &lt;tr ng-repeat="item in items | filter:{price: 2, color: 'red'}"&gt;
      &lt;td ng-bind="item.name"&gt;&lt;/td&gt;
      &lt;td ng-bind="item.color"&gt;&lt;/td&gt;
      &lt;td ng-bind="item.price | currency"&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;tbody&gt;
&lt;/table&gt;</pre><p>في المثال السّابق، لم يتمّ مطابقة 2 مع item 2 لأننا حدّدنا اسم عنصر التّطابق بأنّه <span style="font-family:courier new,courier,monospace;">price</span>.</p><p>أكثر ما يجعل المُرشّح <span style="font-family:courier new,courier,monospace;">filter</span> رائعًا، هو السهولة الفائقة في دمجه مع الربط ثنائيّ الاتّجاه في Angular، لإنتاج صندوق ترشيحٍ قويّ.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">Filter: &lt;input type="text" ng-model="q"&gt;
&lt;table class="table table-condensed" ng-controller="ItemsController"&gt;
  &lt;thead&gt;
    &lt;tr&gt;&lt;th&gt;name&lt;/th&gt;&lt;th&gt;color&lt;/th&gt;&lt;th&gt;price&lt;/th&gt;&lt;/tr&gt;
  &lt;thead&gt;
  &lt;tbody&gt;
    &lt;tr ng-repeat="item in items | filter:q"&gt;
      &lt;td ng-bind="item.name"&gt;&lt;/td&gt;
      &lt;td ng-bind="item.color"&gt;&lt;/td&gt;
      &lt;td ng-bind="item.price | currency"&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;tbody&gt;
&lt;/table&gt;</pre><p>إن كان هناك مثالٌ واحدٌ يبيّن بأفضل طريقةٍ مرونة وقوّة امتدادات HTML التي توفّرها Angular، فقد يكون هذا هو.</p><h2>استخدام المرشحات في JavaScript</h2><p>تحتاج أحيانًا إلى استخدام مُرشّح ما عندما يكون لديك القوّة الكاملة لـJavaScript، بدلًا من أن تكون محدودًا بما تسمح لك العبارات بالقيام به. مثلًا، أمرٌ بسيطٌ كمعرفة عدد العناصر التي أنتجها المُرشّح، لا يبدو ممكنًا. ربما يمكنك أن تقول بأنّه عندما تصبح العبارة كبيرةً وتحتاج إلى منطقٍ برمجيٍّ فمن الأفضل نقل شيفرتها إلى داخل متحكّم.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;p ng-controller="ItemsController"&gt;
  &lt;!-- Invalid code! --&gt;
  {{items | filter:{color:'red'}.length}} red items
&lt;/p&gt;</pre><p><strong>الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">[{"name":"Item 3","position":3,"color":"red","price":2.75},{"name":"Item 1","position":1,"color":"red","price":0.92},{"name":"Item 4","position":4,"color":"blue","price":3.09},{"name":"Item 2","position":2,"color":"red","price":1.95}] red items</pre><p>يُمكنك حقن المُرشّحات أينما أردت، كما أنّه ليس من الغريب استخدام المُرشّح <span style="font-family:courier new,courier,monospace;">filter</span> للتعامل مع مصفوفةٍ داخل متحكّم.</p><p>يكون اسم التّابع الذي يقوم بالترشيح من الشكل <span style="font-family:courier new,courier,monospace;">filtername&gt;Filter&gt;</span>. مثلًا لاستخدام المُرشّح <span style="font-family:courier new,courier,monospace;">date</span> نقوم بحقن <span style="font-family:courier new,courier,monospace;">dateFilter</span>، لاستخدام المُرشّح <span style="font-family:courier new,courier,monospace;">currency</span> نقوم بحقن <span style="font-family:courier new,courier,monospace;">currencyFilter</span>، ولاستخدام المُرشّح <span style="font-family:courier new,courier,monospace;">filter</span> نقوم بحقن <span style="font-family:courier new,courier,monospace;">filterFilter</span>. (أعرف ذلك، أعرف ذلك)</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .controller('FilteredItemsController', function($scope, items, filterFilter) {
    $scope.redItemsCount = filterFilter(items, {color: 'red'}).length;
  });</pre><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;p ng-controller="FilteredItemsController"&gt;
  {{redItemsCount}} red items
&lt;/p&gt;</pre><p><strong>الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">3 red items</pre><p>ما عليك إبقاؤه في ذهنك بشكلٍ أساسيّ هو أنّ استخدام المُرشّح داخل المتحكّم بدلًا من أن يكون في العرض، سيؤدي إلى عدم استدعاء المُرشّح عند حدوث تغييرات في واجهة المستخدم إن كانت تغييراتٍ لا تتضمّن إعادة استدعاء المتحكّم. ولحلّ هذه المشكلة يمكنك ربط استخدام المُرشّح يدويًّا بعنصرٍ في المجال باستخدام التابع <a rel="external nofollow" href="http://www.angularjsbook.com/angular-basics/chapters/scopes/#scope-watch">scope.$watch</a>.</p><h2>مرشح مخصص</h2><p>المُرشّح المخصّص هو أفضل مكانٍ لتضع فيه الشيفرات التي تقوم بتحويل بيانات النّموذج للعرض، وذلك لأنّ المُرشّح نموذجيًّا مكوِّنٌ ثابت الحالة (stateless) ويُمكن استخدامه في أيّ مكانٍ داخل القوالب دون خوفٍ من أيّ تأثيراتٍ جانبيّة، كما أنّ إنشاء مُرشّح مخصّص ليس صعبًا، فبشكلٍ مشابهٍ لبقيّة المكوّنات في Angular، نقوم بتسجيل وصفةٍ للمُرشّح داخل <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%81%D9%8A-angularjs-r196/">الوحدة</a>، باستخدام التّابع <a rel="external nofollow" href="https://docs.angularjs.org/api/ng/type/angular.Module#filter">filter</a>.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .filter('smiley', function() {
    return function(text) {
      return '\u263A ' + text + ' \u263A';
    };
  });</pre><p>في المثال السّابق، المُرشّح الفعليّ هو التابع الدّاخليّ الذي يقوم بوصل السّلسلة النّصية الممرّرة إليه عن طريق الوسيط <span style="font-family:courier new,courier,monospace;">text</span> مع رمز الوجه الضاحك. يتم استدعاءُ هذا التابع في كلّ مرّة يتمّ فيها استدعاء المُرشّح، أمّا التابع الخارجيّ الذي يحيط بالمُرشّح فهو وصفة (recipe) إنشائية، وقد ناقشنا هذه الفكرة بعمقٍ في الفصل السّابق، <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D8%AE%D8%AF%D9%85%D8%A7%D8%AA-services-%D9%81%D9%8A-angularjs-r200/">الخدمات</a>. يتمّ تشغيل الوصفة مرّةً واحدةً، أو لا يتمّ تشغيلها أبدًا إن لم يكن لها استخدامٌ في العرض.</p><p>المُرشّحات المخصّصة تكون متوفّرة تلقائيًّا في أيّ مكانٍ في العرض، ويتمّ استخدامها تمامًا مثل المُرشّحات الأصليّة في المكتبة، حيث نقوم ببساطةٍ بإدخال العبارة فيأنبوب الترشيح داخل العرض، وذلك باستخدام رمز الأنبوب (|)، ويتم تمرير قيم الوسطاء الإضافيّة باستخدام رمز النّقطتين (:).</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;strong ng-bind="'hello' | smiley"&gt;&lt;/strong&gt;</pre><p><strong>الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">☺ hello ☺</pre><p>إليك هذا التّحدّي: هل يمكنك إعادة تشكيل المُرشّح بحيث يصبح بإمكاننا تمرير رمز الوجه الضّاحك كوسيط؟</p><h3>حقن التبعية</h3><p>بالرغم من كون المُرشّحات ثابتة الحالة وبسيطةً نوعًا ما، إلّا أنّها يمكن أن تملك تبعيّاتٍ تحتاج إلى أن تُحقن.</p><p>يسمح تابع الوصفة الإنشائيّة الخارجيّ بالتّصريح عن التّبعيّات تمامًا كما نقوم بذلك في الوحدات. فلنلقِ نظرةً على الكيفيّة التي نقوم بها بحقن التّبعيّات في داخل مُرشّح مخصّص للمحلّيّات (<a rel="external nofollow" href="http://en.wikipedia.org/wiki/Internationalization_and_localization">localization</a>). سنقوم بتسمية المُرشّح بالاسم localize، ولكن قبل ذلك نحتاج إلى شيءٍ ما لنقوم بالحقن. يمكننا إنشاء جدول لمحتوى المتغيّر <span style="font-family:courier new,courier,monospace;">locales</span>، حيث يتمّ تعريفها كخدمةٍ قابلةٍ للحقن باستخدام التّابع <a rel="external nofollow" href="https://docs.angularjs.org/api/auto/service/%24provide#value">value</a>.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .value('locales', {
    'de': {greeting: 'guten Tag'},
    'en-us': {greeting: 'howdy'},
    'fr': {greeting: 'bonjour'}
  });</pre><p>يمكننا حقن هذه البيانات في داخل مُرشّحنا البسيط المخصّص بالتّصريح عن وسيطٍ اسمه <span style="font-family:courier new,courier,monospace;">locales</span>، ولنتمكّن من الحصول على الموضع الحاليّ للمستخدم، يُمكننا استخدام الخدمة <a rel="external nofollow" href="https://docs.angularjs.org/api/ng/service/%24locale">locale$</a> المبنيّة داخل Angular .</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .filter('localize', function(locales, $locale) {
    return function(key) {
      var locale = locales[$locale.id] || locales['en-us'];
      return locale[key];
    };
  });</pre><p>سيعمل المثال السّابق معك فقط إن كانت إعدادات جهازك المحلّيّة من ضمن الأماكن المحدّدة في المثال، إن لم يكن موضعك موجودًا فقم بإضافته بنفسك مع عبارةٍ ترحيبيّة بلغتك.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;p&gt;
  The app says &lt;strong ng-bind="'greeting' | localize"&gt;&lt;/strong&gt;.
&lt;/p&gt;</pre><p><strong>الناتج:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">The app says<strong> howdy</strong>.</pre><p>يُمكنك أيضًا أن تقوم بحقن المُرشّحات في داخل مُرشّح آخر. يمكنك نقل الوسطاء الكثيرة وسلاسل المُرشّحات الطويلة إلى مُرشّح مخصّص، وهذه طريقةٌ رائعةٌ لإزالة بعض الضجّة من شيفرة العرض.</p><h2>خاتمة</h2><p>المُرشّحات المبنيّة في Angular جزءٌ هامٌّ من قدراتها الرائعة، كما أنّ استخدامها سهلٌ وبديهيّ بشكلٍ عامٍّ، كما أنّها مرنة بفضل الوسطاء الإضافيّة وسَلسلة المُرشّحات، وأيضًا فإنّ إنشاء مرشّحٍ مخصّص سيكون سهلًا فورَ إتقانك لنمط الوصفة الإنشائيّة (creation recipe) التي تسمح لك بحقن التّبعيّات. والآن بعد أن اعتدنا استخدام هذه الأنماط، فنحن الآن جاهزون للتّحدّي في الفصل القادم، حيث سنقوم بإعداد التّوجيهات الخاصّة بنا.</p><p>ترجمة وبتصرّف <a rel="external nofollow" href="http://www.angularjsbook.com/angular-basics/chapters/filters/">للفصل التاسع</a> من كتاب: <a rel="external nofollow" target="_blank" href="http://www.angularjsbook.com/">Angular Basics</a> لصاحبه: Chris Smith.</p>
]]></description><guid isPermaLink="false">201</guid><pubDate>Wed, 28 Oct 2015 20:09:09 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62E;&#x62F;&#x645;&#x627;&#x62A; (Services) &#x641;&#x64A; AngularJS</title><link>https://academy.hsoub.com/programming/javascript/angular/%D8%A7%D9%84%D8%AE%D8%AF%D9%85%D8%A7%D8%AA-services-%D9%81%D9%8A-angularjs-r200/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2015_10/angular-services.png.d91623d206ad729046003925ec44d580.png" /></p>

<p>تعاملنا حتّى الآن مع نوعٍ واحد من مكوّنات JavaScript المعدّلة في Angular، وهو <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D9%85%D8%AA%D8%AD%D9%83%D9%85%D8%A7%D8%AA-controlers-%D9%81%D9%8A-angularjs-r186/">المتحكّم</a>. المتحكّمات هي نوعٌ مخصّص من المكوّنات، وكذلك سنجد <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D9%85%D8%B1%D8%B4%D8%AD%D8%A7%D8%AA-filters-%D9%81%D9%8A-angularjs-r201/">المرشحات</a> و<a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%8A%D9%87%D8%A7%D8%AA-directives-%D9%81%D9%8A-angularjs-r202/">التوجيهات</a>، التي سنغطّيها قريبًا. تدعم Angular مكوّنًا غير مخصّص، يُسمّى بالخدمات، تعريف الخدمات في Angular فضفاضٌ نوعًا ما، ولكنّ السّمة المميّزة لها هي عدم اقترانها مباشرةً بالقالب، مما يفترض قرابةً بينها وبين <a rel="external nofollow" href="http://martinfowler.com/eaaCatalog/serviceLayer.html">نمط طبقة الخدمة</a> في الهيكليّة التّقليديّة للمشاريع.</p><p style="text-align: center;"><a class="ipsAttachLink ipsAttachLink_image" href="https://academy.hsoub.com/uploads/monthly_2015_10/angular-services.png.bd1f69fbb66b7ccdb01f063ffeef4aae.png"><img data-fileid="6295" class="ipsImage ipsImage_thumbnailed" alt="angular-services.thumb.png.ec4b3c5e183f2" src="https://academy.hsoub.com/uploads/monthly_2015_10/angular-services.thumb.png.ec4b3c5e183f2dd3ecb84bf3687d0983.png"></a></p><p>تتضمّن وحدة القلب <a rel="external nofollow" href="http://docs.angularjs.org/api/ng"><code>ng</code></a> في Angular عددًا من الخدمات المدمجة، ونذكر الخدمات <a rel="external nofollow" href="http://docs.angularjs.org/api/ng.%24location"><code>location$</code></a> و<a rel="external nofollow" href="http://docs.angularjs.org/api/ng.%24log"><code>log$</code></a> و<a rel="external nofollow" href="http://docs.angularjs.org/api/ng.%24q"><code>q$</code></a> و<a rel="external nofollow" href="http://docs.angularjs.org/api/ng.%24window"><code>window$</code></a> على سبيل المثال، وسنستكشف الخدمة <a rel="external nofollow" href="http://docs.angularjs.org/api/ng.%24http"><code>http$</code></a> في الفصل الأخير من هذه السلسلة، فصل HTTP.</p><p>في تطبيقٍ نموذجيٍّ ستحتاج إلى تعريف خدماتك الخاصّة لأيّ سلوكٍ مشترك بين مكوّنات JavaScript المخصّصة التي تنشئها، مثل المتحكّمات. يُمكن للخدمات أن تكون متابعةً للحالة (stateful) إن احتجت إلى ذلك، فمثلًا، إن أردت مكانًا لتخزين نقاط اللاعب في لعبةٍ ما، يُمكنك إنشاء خدمة <span style="font-family:courier new,courier,monospace;"><code>score</code></span> لتتمكّن من جلب وعرض النّقاط الحاليّة في عدّة أماكن ضمن تطبيقك. جميع الخدمات متفرّدة (singletons) أي أنه لا يوجد غير نسخةٍ واحدةٍ من خدمةٍ معيّنة طوال دورة حياة تطبيق Angular.</p><p>الخدمة في Angular يُمكن أن تكون كائنًا، تابعًا، أو حتّى قيمةً أوّليّة (كالأعداد مثلًا)، أنت من يحدّد ذلك. في الواقع، هذا الفصل لن يشرح إلا القليل من الأمثلة عن الخدمات، وذلك لأنّ الخدمات يمكن أن تحوي أيّ شيفرة من شيفرات JavaScript العاديّة. ما سيُغطّيه هذا الفصل هو كيفيّة تسجيل (register) الخدمات في Angular. نقوم بذلك عن طريق توابع معرّفة بواسطة الخدمة <a rel="external nofollow" href="https://docs.angularjs.org/api/auto/service/%24provide"><code>provide$</code></a>، ومغلّفة بواسطة <a rel="external nofollow" href="https://docs.angularjs.org/api/ng/type/angular.Module"><code>angular.Module</code></a>. لنتمكّن من تسجيل الخدمة نحتاج إلى مرجعٍ (reference) إلى <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%81%D9%8A-angularjs-r196/">وحدةٍ</a> ما. لنقُم بتعريف وحدةٍ جذريّة لتطبيقنا الآن.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app', []);</pre><p>كما تمّ الشّرح في فصل <a rel="external nofollow" href="http://www.angularjsbook.com/angular-basics/chapters/modules/">الوحدات</a>، نقوم بتحميل وحدة الجذر في تطبيقنا عن طريق تمرير اسمها إلى التّوجيه <a rel="external nofollow" href="https://docs.angularjs.org/api/ng/directive/ngApp"><code>ng-app</code></a> ضمن مستند HTML.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;body ng-app="app"&gt;
  &lt;!-- الأمثلة توضع هنا --&gt;
&lt;/body&gt;</pre><p>جيّد، أصبحت وحدتنا جاهزة.</p><p>تُقدّم الخدمة <code>provid<span style="font-family:courier new,courier,monospace;">e$</span></code> خمس توابع مختلفةً للقيام بتسجيل الخدمة، يُمكننا تصنيف هذه التّوابع إلى صنفين، أوّلهما سهلٌ جدًّا والثّاني أصعبُ قليلًا.</p><h2>إنشاء خدمات دون استخدام حقن التبعية</h2><p>قد لا تحتاج إلى حقن أيّ تبعيّاتٍ إلى خدمتك. (قُمنا بتغطية حقن التّبعيّة في <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%AD%D9%82%D9%86-%D8%A7%D9%84%D8%AA%D8%A8%D8%B9%D9%8A%D8%A9-dependency-injection-%D9%81%D9%8A-angularjs-r198/">الفصل الماضي</a>. وكمثالٍ على تبعيّةٍ محقونة، فالخدمة المدمجة <a rel="external nofollow" href="http://docs.angularjs.org/api/ng.%24http"><code>http$</code></a> يُمكن لخدمتك أن تستخدمها لجلب بياناتٍ من النّهاية الخلفيّة (backend) البعيدة.) ولكن حتّى لو لم تكن تريد حقن تبعيّاتٍ في خدمتك، ستحتاج إلى وضعها ضمن وحدةٍ لتجعلها متاحةً للحقن ضمن مكوِّناتٍ أخرى. إنّ جعل الخدمة متاحةً للحقن هو ما يجعلها خدمةً في Angular، وإلّا ستكون مجرّد شيفرة JavaScript عاديّة.</p><p>هناك تابعان خدميّان يسمحان بتسجيل الخدمات من دون تبعيّات: التّابع <a rel="external nofollow" href="https://docs.angularjs.org/api/auto/service/%24provide#value"><code>value</code></a> والتّابع <a rel="external nofollow" href="https://docs.angularjs.org/api/auto/service/%24provide#constant"><code>constant</code></a>. الفرق بينهما في Angular معقّدٌ قليلًا ويخُصّ المحترفين، فـالثّوابت تكون متاحًةً لتطبيق Angular أثناء الـbootstrapping، أما القِيمة فلا. لن نقوم بتغطية <code>constant</code> في هذه السلسلة.</p><h3>التابع value</h3><p>لنفترض أنّنا نريد كتابة شيفرةٍ للعبة حيث يحصل فيها اللاعب على نقاطٍ تبدأ من الصفر. يمكننا باستخدام التّابع <a rel="external nofollow" href="https://docs.angularjs.org/api/auto/service/%24provide#value"><code>value</code></a> من الخدمة <span style="font-family:courier new,courier,monospace;"><code>provide$</code></span> أن نقوم بتسجيل (register) قيمةٍ عدديّةٍ أوّليّة باسم 'score' وستكون متاحةً للمتحكّمات، الخدمات، ومكوّناتٍ أخرى.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .value('score', 0);</pre><p>يُمكننا حقن خدمتنا <span style="font-family:courier new,courier,monospace;"><code>score</code></span> عن طريق وضعها ضمن قائمة الوسطاء للمتحكّم، وقد شرحنا ذلك بالتّفصيل في فصل <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%AD%D9%82%D9%86-%D8%A7%D9%84%D8%AA%D8%A8%D8%B9%D9%8A%D8%A9-dependency-injection-%D9%81%D9%8A-angularjs-r198/">حقن التّبعيّة</a>.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .controller('ScoreController', function($scope, score) {
    $scope.score = score;
    $scope.increment = function() {
      $scope.score++;
    };
  });</pre><p>يستخدم قالب هذا المثال نُسختين من المتحكّم<span style="font-family:courier new,courier,monospace;"> <code>ScoreController</code></span> لإثبات الطّبيعيّة المتفرّدة (singleton) للخدمة <span style="font-family:courier new,courier,monospace;"><code>score</code></span>.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;p ng-controller="ScoreController"&gt;
  Score: {{score}}
&lt;/p&gt;
&lt;p ng-controller="ScoreController"&gt;
  &lt;button ng-click="increment()"&gt;Increment&lt;/button&gt;
&lt;/p&gt;</pre><p>أعتذر، لقد تعمّدت إفشال المثال السّابق، فعند النّقر على زرّ <code>Increment</code> لن يتمّ تغيير قيمة المتغيّر <span style="font-family:courier new,courier,monospace;"><code>score</code></span> المعروضة في النّسخة الأولى من المتحكّم. هذه ليست مشكلة مجالات، ولكنّها بسبب الطّبيعة الثّابتة (immutable) للخدمات التي تمثّل قيمًا أوّليّة. يُمكننا إصلاح المثال السّابق بتغيير الخدمة من قيمةٍ أوّليّة إلى كائنٍ يحوي العنصر <span style="font-family:courier new,courier,monospace;"><code>points</code></span>.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .value('score', {points: 0});</pre><p>سنستخدم نفس جسم المتحكّم تقريبًا، إلّا أنّنا سنغيّر <span style="font-family:courier new,courier,monospace;"><code>++scope.score$</code></span> إلى <span style="font-family:courier new,courier,monospace;"><code>++scope.score.points$</code></span>.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .controller('ScoreController', function($scope, score) {
    $scope.score = score;
    $scope.increment = function() {
      $scope.score.points++;
    };
  });</pre><p>بطريقةٍ مماثلة، سنغيّر في القالب العبارة <span style="font-family:courier new,courier,monospace;"><code>score</code></span> إلى<span style="font-family:courier new,courier,monospace;"> <code>score.points</code></span>.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">&lt;p ng-controller="ScoreController"&gt;
  Score: {{score.points}}
&lt;/p&gt;
&lt;p ng-controller="ScoreController"&gt;
  &lt;button ng-click="increment()"&gt;Increment&lt;/button&gt;
&lt;/p&gt;</pre><p>لقد استخدمنا للتّو خدمةً تشارك بياناتٍ متغيّرة (mutable) ضمن تطبيقنا، هذا رائع. إضافةً إلى القيمة الأوّليّة والكائنات، يمكن لخدمات Angular أن تمثّل توابعًا. لنفترض أنّنا بحاجةٍ إلى خدمةٍ ثابتةٍ (stateless) تُعيد عدًدا عشوائيًّا بين 1 و 10.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .value('randomScore', function() {
     return Math.ceil(Math.random() * 10);
  });</pre><p>إنّ حقن هذه الخدمة أمرٌ سهل، سنقوم ببساطةٍ بإضافة اسمها <span style="font-family:courier new,courier,monospace;"><code>randomScore</code></span> إلى قائمة الوسطاء في متحكّمنا.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .controller('ScoreController', function($scope, score, randomScore) {
    $scope.score = score;
    $scope.increment = function() {
      $scope.score.points += randomScore();
    };
  });</pre><p>لقد تعلّمنا الآن كيفيّة تعريف الخدمات وحقنها ضمن المتحكّم كتبعيّات. ولكن ماذا لو أردنا حقن تبعيّاتٍ إلى خدماتنا؟</p><h2>إنشاء خدمات باستخدام حقن التبعية</h2><p>لعبتنا تبدأ دومًا بنقاطٍ قيمتها صفر، هذا منطقيٌّ، ولكن لنفترض أننا نريد أن تكون القيمة البدائيّة عددًا عشوائيًّا. كيف يمكننا جلب مرجعٍ للخدمة <span style="font-family:courier new,courier,monospace;"><code>randomScore</code></span> عندما ننشئ الكائن <span style="font-family:courier new,courier,monospace;"><code>score</code></span> للمرّة الأولى؟ إنّ التّابع <span style="font-family:courier new,courier,monospace;"><code>value</code></span> الذي كُنّا نستخدمه بسيط جدًّا، فأيّ شيءٍ نقوم بتمريره إليه سيكون هو القيمة أو التّابع أو الكائن الكامل والنّهائي الذي ستقوم Angular بحقنه لاحقًا، وهذا يعني بأنّنا لن نحظى بأيّ فرصةٍ لحقن أيّ تبعيّاتٍ لاحقًا.</p><p>تُقدّم Angular عدّة حلول لهذه المشكلة، وسنبدأ بالحلّ الأوّل، التّابع <span style="font-family:courier new,courier,monospace;"><code>service</code></span> كائنيُّ التّوجّه.</p><h3>التابع service</h3><p>تدعم JavaScript أسلوب البرمجة كائنيّة التّوجّه، ولذلك يمكننا كتابة خدمة في Angular تقبل التّبعيّات عن طريق الحقن بواسطة الباني (<a rel="external nofollow" href="http://www.martinfowler.com/articles/injection.html#ConstructorInjectionWithPicocontainer">constructor injection</a>). كُلّ ما نحتاج إليه هو كتابة التّابع <a rel="external nofollow" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Using_a_constructor_function">الباني</a> لخدمتنا <span style="font-family:courier new,courier,monospace;"><code>score</code></span>، بدلّا من تهيئتها بقيمةٍ بدائيّة عن طريق <a rel="external nofollow" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Using_object_initializers">مهيِّئ الكائن</a> (<code>{}</code>) الذي استخدمناه قبل قليل.</p><p>يجب أن يكون أوّل حرفٍ من اسم الباني في JavaScript حرفًا كبيرًا، ولذلك سنُسمّي الباني بالاسم <span style="font-family:courier new,courier,monospace;"><code>Score</code></span>، وسنقوم بوضع الخدمة <span style="font-family:courier new,courier,monospace;"><code>randomScore</code></span> ضمن قائمة الوسطاء لهذا الباني.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">function Score(randomScore) {
  this.points = randomScore();
}</pre><p>التّابع <a rel="external nofollow" href="https://docs.angularjs.org/api/auto/service/%24provide#service"><code>service</code></a> يحتاج إلى تمرير الباني الخاصّ بالخدمة بدلًا من تمرير الخدمة ذاتها، وعندما تقوم Angular باستدعاء الباني عن طريق العمليّة <span style="font-family:courier new,courier,monospace;"><code>new</code></span> ستقوم آليّة حقن التّابعيّة بإسناد الخدمة <span style="font-family:courier new,courier,monospace;"><code>randomScore</code></span> إلى وسيط الباني.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .service('score', Score);</pre><p>توصف طريقة إنشاء نُسخة الخدمة في Angular بأنّها <em>كسولة</em>، أي أنّ النّسخة ستُنشأ فقط عندما تُشكِّل تبعيّةً لأحد المكوّنات التي يتمّ إخراجها في القالب.</p><h3>التابع factory</h3><p>إن كانت خدمتك شيئًا آخر غير الكائن، أو إن أردت الحصول على مرونةٍ أكبر من طريقة إنشاء بانٍ للكائن، عندها يُمكنك استخدام التّابع <a rel="external nofollow" href="https://docs.angularjs.org/api/auto/service/%24provide#factory"><code>factory</code></a> بدلًا من <span style="font-family:courier new,courier,monospace;"><code>service</code></span>، حيث نقوم بتمرير تابع استدعاءٍ خلفيٍّ (callback) وسيتمّ حقنه لاحقًا بالتّبعيّات التي نكتب أسماءها في قائمة الوسطاء. يُمكنك كتابة ما تشاء داخل هذا التّابع، ولكن يجب عليك في النّهاية أن تعيد قيمةً، تابعًا أو كائنًا يُمثّل الخدمة. يُطلق على هذا النّوع من الإنشاء عن طريق الاستدعاء الخلفيّ في <a rel="external nofollow" href="https://docs.angularjs.org/guide/providers">المرجع الرّسميّ</a> اسمُ <em>الوصفة(recipe)</em>.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .factory('score', function(randomScore) {
     return  {points: randomScore()};
  });</pre><p>المثال السّابق مكافئٌ للمثال الذي يستخدم تركيبة الباني مع التّابع <span style="font-family:courier new,courier,monospace;"><code>service</code></span> الذي رأيناه في الفقرة الماضية.</p><h3>التابع decorator</h3><p>إن كنت تريد تعديل خدمةٍ موجودةٍ سابقًا، يُمكنك استخدام التّابع <a rel="external nofollow" href="https://docs.angularjs.org/api/auto/service/%24provide#decorator"><code>decorator</code></a> الّذي تُوفّره الخدمة <span style="font-family:courier new,courier,monospace;"><code>provide$</code></span>. هذا التّابع ليس مُغلّفًا باستخدام <span style="font-family:courier new,courier,monospace;"><code>angular.Module</code></span> كحال بقيّة التّوابع، لذا يجب أن يتمّ استدعاؤه مباشرةً من الخدمة <span style="font-family:courier new,courier,monospace;"><code>provide$</code></span>. يُمكنك الحصول على مرجعٍ للخدمة <span style="font-family:courier new,courier,monospace;"><code>provide$</code></span> عن طريق تسجيل تابع استدعاءٍ خلفيٍّ (callback) باستخدام التّابع <a rel="external nofollow" href="https://docs.angularjs.org/api/ng/type/angular.Module#config"><code>config</code></a>.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .config(function($provide) {
    $provide.decorator('score', function($delegate) {
      $delegate.points = 1000000;
      return $delegate;
    });
  });</pre><p>يُمكن لتابع <span style="font-family:courier new,courier,monospace;"><code>decorator</code></span> أن يقوم بإعادة الخدمة الأصليّة التي تمّ تمريها ضمن الوسيط ذي علامات الاقتباس ' ' أو أن يقوم بإعادة نُسخةٍ (instance) من خدمةٍ جديدةٍ كُلّيًّا. ولكن عليك بالحذر من التّأثيرات الجانبيّة غير المرغوبة.</p><h3>التغليف (Encapsulation)</h3><p>في الفقرة السّابقة أسأنا استخدام التّابع <span style="font-family:courier new,courier,monospace;"><code>decorator</code></span>، ونتيجةً لذلك تمّ تقييد الوصول إلى العنصر <span style="font-family:courier new,courier,monospace;"><code>points</code></span>. لحسن الحظّ، يُقدّم التّغليف الخاصّ بحقن التّابع <span style="font-family:courier new,courier,monospace;"><code>factory</code></span> مجالًا مُغلقًا (<a rel="external nofollow" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures">closure</a>)، ممّا يسمح لنا بإخفاء بعض المعلومات، أو بعبارةٍ أخرى، التّغليف (<a rel="external nofollow" href="http://en.wikipedia.org/wiki/Encapsulation_%28object-oriented_programming%29">encapsulation</a>).</p><p>سنقوم باستبدال العنصر <span style="font-family:courier new,courier,monospace;"><code>points</code></span> المرئيّ في المجال العام بالمتغيّر المحلّي <span style="font-family:courier new,courier,monospace;"><code>points</code></span> المحدود الرؤية ضمن التّابع الّذي يغلّفه، وهذا سيسمح لنا بحمايته من التّعديل خارج التّابع. والآن سنقوم بجعل كائن الخدمة يكشف عن تابعٍ للوصول إلى قيمة المتغيّر، <span style="font-family:courier new,courier,monospace;"><code>getPoints</code></span>، وعن تابعٍ يقيَّد التعديل فيه فقط، <span style="font-family:courier new,courier,monospace;"><code>increment</code></span>.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .factory('score', function(randomScore) {
    var points = randomScore();
    return {
       increment: function() {
         return ++points;
       },
       getPoints: function() {
         return points;
       }
    };
  });</pre><p>سنحتاج إلى تغيير بسيطٍ في المتحكّم كي نسمح له باستدعاء التّابع <span style="font-family:courier new,courier,monospace;"><code>increment</code></span> من الخدمة.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">angular.module('app')
  .controller('ScoreController', function($scope, score) {
    $scope.score = score;
    $scope.increment = function() {
      $scope.score.increment();
    };
  });</pre><p>وسنغيّر أيضًا القالب، ليكون مرتبطًا بتابع الوصول <span style="font-family:courier new,courier,monospace;"><code>()score.getPoints</code></span> بدلًا من الوصول إلى العنصر <code>points</code> مباشرةً.</p><pre data-pbcklang="javascript" data-pbcktabsize="4" class="javascript ipsCode prettyprint">&lt;p ng-controller="ScoreController"&gt;
  Score: {{score.getPoints()}}
&lt;/p&gt;
&lt;p ng-controller="ScoreController"&gt;
  &lt;button ng-click="increment()"&gt;Increment&lt;/button&gt;
&lt;/p&gt;</pre><p>بما أنّ التابع <span style="font-family:courier new,courier,monospace;"><code>increment</code></span> يقوم أيضًا بإعادة قيمة المتغيّر <span style="font-family:courier new,courier,monospace;"><code>points</code></span> بعد التّعديل، إذًا بإمكاننا الكشف (expose) عنها ضمن العرض (view) في عبارة. قد تُفاجئك النّتيجة، قُم باستبدال الاستدعاء في السّطر الثاني، وضع <span style="font-family:courier new,courier,monospace;"><code>()score.increment</code></span> بدلًا من <span style="font-family:courier new,courier,monospace;"><code>()score.getPoints</code></span>، ثُمّ قُم بنقر الزّر عدّة مرّات. هل يُمكنك معرفة سبب زيادة القيمة بسرعةٍ كبيرة؟ هذا صحيح: تقوم Angular غالبًا باستدعاء العناصر والتّوابع المرتبطة بالقالب عدّة مرّات قبل أن تنتهي دورة الإخراج. هذه معلومةٌ هامّة يجب معرفتها لنفهم التّأثير الجانبيّ لها كالمثال السّابق، وأيضًا لتحسين كفاءة التّطبيق.</p><h2>خاتمة</h2><p>وصلنا إلى ختام الفصول الثّلاثة التي تتحدّث عن دعم Angular للبرمجة باستخدام الوحدات. بدأنا مع فصل <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%81%D9%8A-angularjs-r196/">الوحدات</a>، وتابعنا مع الفصل القصير عن <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%AD%D9%82%D9%86-%D8%A7%D9%84%D8%AA%D8%A8%D8%B9%D9%8A%D8%A9-dependency-injection-%D9%81%D9%8A-angularjs-r198/">حقن التّبعيّة</a>، وختمنا هذه الثّلاثيّة بهذا الفصل عن الخدمات. قد تستغرب إفراد الخدمات بفصلٍ مستقلٍّ عندما ترى أنّها ليست سوى JavaScript المعتادة ليس إلّا، ولكنّنا لم نقم بتغطيةٍ كاملة لما يجب معرفته، فإضافةً إلى التّوابع <code>constant</code> و<code>value</code> و <code>service</code> و<code>factory</code> و <code>decorator</code> لا يزال هناك تابعٌ منخفض المستوى هو التّابع <a rel="external nofollow" href="https://docs.angularjs.org/api/auto/service/%24provide#provider"><code>provider</code></a> الذي يقدّم تعلّقات دورة الحياة (lifecycle hooks) لإعداد خدماتك بطريقةٍ متقدّمة.</p><p>إن كنت تتساءل فيما إذا كنت تحتاج إلى مساعدة Angular في هذا المجال بالفعل، فلتبقِ في ذهنك بأنّك لست <em>مضطرًّا</em> لإدارة كلّ شيفراتك باستخدام Angular، ورغم ذلك فإنّ <a rel="external nofollow" href="http://angularjs.blogspot.com/2014/03/angular-20.html">Angular 2.0</a> ستنتقل إلى <a rel="external nofollow" href="http://jsmodules.io/">نظام وحدات ES6</a> كما أشار العرض التّقديمي <a rel="external nofollow" href="https://docs.google.com/presentation/d/1XQP0_NTzCUcFweauLlkZpbbhNVYbYy156oD--KLmXsk/preview?usp=drive_web&amp;sle=true&amp;slide=id.g3f27dfe38_010">"RIP angular.module"</a> الّذي قُدّم في <a rel="external nofollow" href="https://www.youtube.com/watch?v=gNmWybAyBHI">خطاب</a> فريق التّطوير في <a rel="external nofollow" href="http://ngeurope.org/">ng-europe 2014</a>. وإلى أن يتمّ ذلك فإنّك تحتاج بالفعل إلى أن تضع شيفراتك داخل وحدات Angular عندما تحتاج إلى استخدام مكوّناتٍ مدمجة في Angular (أو مكوّناتٍ طوّرها طرفٌ ثالث) يتمُّ الوصول إليها عن طريق حقن التّبعيّة. لقد قمتُ بعرض <a rel="external nofollow" href="http://www.angularjsbook.com/angular-basics/chapters/dependency-injection">بديلٍ لحقن التّبعيّة</a> في نهاية الفصل الماضي. فقط أبقِ في ذهنك أنّ دعم اختبار الوحدة (<a rel="external nofollow" href="https://docs.angularjs.org/guide/unit-testing">unit testing support</a>) في Angular قد عزّز من أهمّيّة استخدام حقن التّبعيّة، لذا عليك أن تهتمّ باستخدامك له أيضًا.</p><p>هناك أيضًا بعض المكوِّنات المخصّصة التي <em>يجب</em> عليك تسجيلها باستخدام نظام الوحدات في Angular، لتجعلها متوفّرة ضمن القالب. أحد هذه المكوّنات هو <a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D9%85%D8%B1%D8%B4%D8%AD%D8%A7%D8%AA-filters-%D9%81%D9%8A-angularjs-r201/">المُرشّحات</a>، وسنُغطّيها في الفصل القادم.</p><p>ترجمة وبتصرّف <a rel="external nofollow" href="http://www.angularjsbook.com/angular-basics/chapters/services/">للفصل الثامن</a> من كتاب: <a rel="external nofollow" target="_blank" href="http://www.angularjsbook.com/">Angular Basics</a> لصاحبه: Chris Smith.</p>
]]></description><guid isPermaLink="false">200</guid><pubDate>Tue, 27 Oct 2015 21:49:00 +0000</pubDate></item></channel></rss>
