[{"data":1,"prerenderedAt":1440},["ShallowReactive",2],{"article-\u002Farticles\u002F6703caeaf1aaf592bac7544e":3},{"id":4,"title":5,"articleId":6,"body":7,"cover":1425,"coverThumb":1425,"created":1426,"date":1427,"description":1428,"extension":1429,"meta":1430,"navigation":125,"path":1431,"seo":1432,"stem":1433,"tags":1434,"updated":1438,"__hash__":1439},"articles\u002Farticles\u002F6703caeaf1aaf592bac7544e.md","دليلك لفهم Angular Signals بأسلوب مُبسَّط","6703caeaf1aaf592bac7544e",{"type":8,"value":9,"toc":1412},"minimark",[10,14,17,20,28,31,34,37,151,172,175,178,305,320,326,329,332,338,341,347,350,353,356,359,365,368,396,406,409,412,418,440,443,484,489,509,518,553,556,562,615,618,621,628,631,634,637,640,643,646,715,718,1041,1047,1057,1060,1066,1069,1072,1075,1078,1081,1091,1094,1100,1114,1118,1131,1183,1186,1190,1200,1312,1318,1322,1335,1382,1388,1393,1396,1399,1402,1405,1408],[11,12,13],"p",{},"الحمد لله الذي علَّم بالقلم، علَّم الإنسان ما لم يعلم، والصلاة والسلام على خير معلِّم الناس الخير سيدنا محمد - صلَّى الله عليه وسلم … أما بعد ،،",[11,15,16],{},"في الآونة الأخيرة، شهدت Angular تحديثات هامة مع تقديم ميزة الإشارات (Signals)، التي من المتوقع أن تحدث تغيرًا جذريًا في كيفية استخدامنا وتعاملنا مع تطبيقات Angular، إذ على مدار الأشهر الماضية، أصبح الحديث عن الإشارات شائعًا في أوساط مجتمع Angular، ويبدو أن الجميع إما معجبون بها بشدة أو معارضون لها بالكلية.",[11,18,19],{},"إن كنت من أولئك الذين لم يلحقوا بركب هذه الضجة بعد، ولا تعرف ما هي Signals ؟ وما الذي يمكنها فعله وما الذي تفتقر إليه؟ فلا داعي للقلق من ضياع الفرصة، فبعد اطلاعك على هذا المقال - بإذن الله - ستكون لديك رؤية شاملة حول الإشارات، وكيفية عملها، والأهم من ذلك معرفة أنسب الحالات العملية لاستخدامها.",[21,22,24],"h2",{"id":23},"لماذا-دعت-الحاجة-إلى-تطبيق-signals-في-angular",[25,26,27],"strong",{},"لماذا دعت الحاجة إلى تطبيق Signals في Angular ؟",[11,29,30],{},"شهدت Angular تحديثًا كبيرًا مع تقديم مفهوم Signals كحل جذري للتحديات المتعلقة بالتفاعل مع البيانات وإدارة عملية الكشف عن التغييرات (Change Detection).",[11,32,33],{},"لتوضيح الحاجة لهذا التحديث، يجب أولاً فهم البرمجة التفاعلية (Reactive Programming)، وهو مفهوم يشير إلى قدرة النظام على التفاعل تلقائيًا مع التغييرات التي تحدث في البيانات، بحيث يتم تحديث واجهة المستخدم ديناميكيًا بناءً على تلك التغييرات دون الحاجة إلى كتابة تعليمات برمجية صريحة في كل مرة يحدث فيها تغيير.",[11,35,36],{},"في البرمجة التقليدية، إذا أردت تحديث واجهة المستخدم بناءً على تغير قيمة متغير ما، يجب أن تتولى إدارة هذا التحديث يدويًا. على سبيل المثال:",[38,39,44],"pre",{"className":40,"code":41,"language":42,"meta":43,"style":43},"language-typescript shiki shiki-themes github-dark","let x = 5;\nlet y = 3;\nlet z = x + y;\nconsole.log(z); \u002F\u002F 8\n\nx = 10;\nconsole.log(z); \u002F\u002F 8\n","typescript","",[45,46,47,70,85,103,120,127,140],"code",{"__ignoreMap":43},[48,49,52,56,60,63,67],"span",{"class":50,"line":51},"line",1,[48,53,55],{"class":54},"snl16","let",[48,57,59],{"class":58},"s95oV"," x ",[48,61,62],{"class":54},"=",[48,64,66],{"class":65},"sDLfK"," 5",[48,68,69],{"class":58},";\n",[48,71,73,75,78,80,83],{"class":50,"line":72},2,[48,74,55],{"class":54},[48,76,77],{"class":58}," y ",[48,79,62],{"class":54},[48,81,82],{"class":65}," 3",[48,84,69],{"class":58},[48,86,88,90,93,95,97,100],{"class":50,"line":87},3,[48,89,55],{"class":54},[48,91,92],{"class":58}," z ",[48,94,62],{"class":54},[48,96,59],{"class":58},[48,98,99],{"class":54},"+",[48,101,102],{"class":58}," y;\n",[48,104,106,109,113,116],{"class":50,"line":105},4,[48,107,108],{"class":58},"console.",[48,110,112],{"class":111},"svObZ","log",[48,114,115],{"class":58},"(z); ",[48,117,119],{"class":118},"sAwPA","\u002F\u002F 8\n",[48,121,123],{"class":50,"line":122},5,[48,124,126],{"emptyLinePlaceholder":125},true,"\n",[48,128,130,133,135,138],{"class":50,"line":129},6,[48,131,132],{"class":58},"x ",[48,134,62],{"class":54},[48,136,137],{"class":65}," 10",[48,139,69],{"class":58},[48,141,143,145,147,149],{"class":50,"line":142},7,[48,144,108],{"class":58},[48,146,112],{"class":111},[48,148,115],{"class":58},[48,150,119],{"class":118},[11,152,153,154,157,158,161,162,164,165,167,168,171],{},"في هذا المثال، حتى بعد تغيير قيمة ",[45,155,156],{},"x"," إلى 10، تظل قيمة ",[45,159,160],{},"z"," كما هي (8) لأن المتغير ",[45,163,160],{}," لا يتفاعل مع التغيير الذي حدث في قيمة ",[45,166,156],{}," أو ",[45,169,170],{},"y",".",[11,173,174],{},"في التطبيقات الكبيرة، مثل أنظمة التجارة الإلكترونية، نرغب في أن تتفاعل الصفحات مثل عربة الشراء مع التغييرات تلقائيًا؛ فعندما يقوم المستخدم بتعديل كمية المنتجات، يجب أن يتم تحديث إجمالي السعر بشكل تلقائي.",[11,176,177],{},"هنا تأتي الحاجة إلى أدوات ومكتبات مثل Angular التي تسهل البرمجة التفاعلية. ولكي نجعل الكود أكثر تفاعلية، يمكن إعادة صياغته باستخدام Signals كما يلي:",[38,179,183],{"className":180,"code":181,"language":182,"meta":43,"style":43},"language-javascript shiki shiki-themes github-dark","const x = signal(5);\nconst y = signal(3);\nconst z = computed(() => x() + y());\nconsole.log(z()); \u002F\u002F 8\n\nx.set(10);\nconsole.log(z()); \u002F\u002F 13\n","javascript",[45,184,185,208,226,256,271,275,290],{"__ignoreMap":43},[48,186,187,190,193,196,199,202,205],{"class":50,"line":51},[48,188,189],{"class":54},"const",[48,191,192],{"class":65}," x",[48,194,195],{"class":54}," =",[48,197,198],{"class":111}," signal",[48,200,201],{"class":58},"(",[48,203,204],{"class":65},"5",[48,206,207],{"class":58},");\n",[48,209,210,212,215,217,219,221,224],{"class":50,"line":72},[48,211,189],{"class":54},[48,213,214],{"class":65}," y",[48,216,195],{"class":54},[48,218,198],{"class":111},[48,220,201],{"class":58},[48,222,223],{"class":65},"3",[48,225,207],{"class":58},[48,227,228,230,233,235,238,241,244,246,249,251,253],{"class":50,"line":87},[48,229,189],{"class":54},[48,231,232],{"class":65}," z",[48,234,195],{"class":54},[48,236,237],{"class":111}," computed",[48,239,240],{"class":58},"(() ",[48,242,243],{"class":54},"=>",[48,245,192],{"class":111},[48,247,248],{"class":58},"() ",[48,250,99],{"class":54},[48,252,214],{"class":111},[48,254,255],{"class":58},"());\n",[48,257,258,260,262,264,266,269],{"class":50,"line":105},[48,259,108],{"class":58},[48,261,112],{"class":111},[48,263,201],{"class":58},[48,265,160],{"class":111},[48,267,268],{"class":58},"()); ",[48,270,119],{"class":118},[48,272,273],{"class":50,"line":122},[48,274,126],{"emptyLinePlaceholder":125},[48,276,277,280,283,285,288],{"class":50,"line":129},[48,278,279],{"class":58},"x.",[48,281,282],{"class":111},"set",[48,284,201],{"class":58},[48,286,287],{"class":65},"10",[48,289,207],{"class":58},[48,291,292,294,296,298,300,302],{"class":50,"line":142},[48,293,108],{"class":58},[48,295,112],{"class":111},[48,297,201],{"class":58},[48,299,160],{"class":111},[48,301,268],{"class":58},[48,303,304],{"class":118},"\u002F\u002F 13\n",[11,306,307,308,310,311,313,314,316,317,319],{},"في المثال السابق، عندما قمنا بتغيير قيمة ",[45,309,156],{}," إلى 10، تم تحديث قيمة ",[45,312,160],{}," تلقائيًا إلى 13. والسبب في ذلك هو أننا استخدمنا الإشارات المحسوبة (Computed Signals) التي تتفاعل مع التغييرات في الإشارات الأخرى (",[45,315,156],{}," و ",[45,318,170],{},"). وبالتالي، فإن التغييرات تحدث بطريقة أكثر ديناميكية وسلاسة دون الحاجة إلى التدخل اليدوي في إدارة التغييرات.",[21,321,323],{"id":322},"مشكلة-الكشف-عن-التغييرات-في-angular-قبل-تطبيق-signals",[25,324,325],{},"مشكلة الكشف عن التغييرات في Angular قبل تطبيق Signals",[11,327,328],{},"من أبرز التحديات التي كانت تواجهها Angular هي آلية الكشف عن التغييرات (Change Detection)، حيث يتم تنفيذ دورة الكشف بشكل متكرر حتى في الحالات التي لا يحدث فيها أي تغيير فعلي في البيانات، هذا التكرار المفرط يؤدي إلى إبطاء الأداء ويضيف عبئًا غير ضروري على كفاءة التطبيق.",[11,330,331],{},"في النسخ السابقة من Angular، تعتمد عملية الكشف عن التغييرات على مكتبة تُسمى zone.js، وعلى الرغم من أن هذه المكتبة تسهل إدارة تغييرات البيانات، إلا أنها تضيف تعقيدًا وحجمًا إضافيًا على حزمة التطبيق، ولا توفر دقة كافية لتحديد الجزء المحدد الذي تغيّر في التطبيق.",[11,333,334],{},[335,336],"img",{"alt":43,"src":337},"\u002Fimages\u002F6703d0a3f1aaf592bac7549c.png",[11,339,340],{},"بدلاً من ذلك، يتم إعادة عرض شجرة المكونات (Component Tree) بالكامل في كل مرة يحدث فيها تغيير، مما يزيد من استهلاك الموارد ويبطئ التطبيق.",[21,342,344],{"id":343},"كيف-تعالج-signals-هذه-المشكلة",[25,345,346],{},"كيف تعالج Signals هذه المشكلة ؟",[11,348,349],{},"مع Signals، يتم تبسيط عملية الكشف عن التغييرات وتحسين الأداء بشكل كبير، بدلاً من إعادة عرض كل المكونات عند حدوث تغيير، تتبع Signals التغييرات بدقة أكبر، الإشارات المحسوبة (Computed Signals) تتيح للمطورين إنشاء علاقات بين البيانات بطريقة تفاعلية، حيث يتم حساب القيم تلقائيًا عندما تتغير أي من الإشارات المعتمدة عليها.",[11,351,352],{},"إضافةً إلى ذلك، تساعد Signals على تقليل حجم الحزم لأنها تقلل من الاعتماد على مكتبة zone.js، مما يحسن الكفاءة العامة للتطبيق، فعندما يتم تغيير قيمة معينة داخل القالب (template)، يقوم Angular بتحديث العرض المرتبط بتلك الإشارة فقط، دون الحاجة إلى إعادة عرض الشجرة الكاملة للمكونات.",[11,354,355],{},"ومن هنا ظهرت الحاجة إلى Signals في Angular كحل لتحسين التفاعل بين البيانات وواجهة المستخدم و للتغلب على التحديات المرتبطة بآلية الكشف عن التغييرات.",[11,357,358],{},"من خلال تقنيات مثل الإشارات المحسوبة، أصبح بإمكان المطورين إنشاء تطبيقات تفاعلية وفعّالة، حيث يتم تحديث البيانات تلقائيًا دون التأثير على الأداء أو زيادة حجم التطبيق.",[21,360,362],{"id":361},"كيفية-استخدام-signals-في-angular",[25,363,364],{},"كيفية استخدام Signals في Angular",[11,366,367],{},"لفهم Signals بشكل عملي، دعونا نلقي نظرة على مثال مبسَّط.",[38,369,371],{"className":40,"code":370,"language":42,"meta":43,"style":43},"count = signal\u003Cnumber>(1);\n",[45,372,373],{"__ignoreMap":43},[48,374,375,378,380,382,385,388,391,394],{"class":50,"line":51},[48,376,377],{"class":58},"count ",[48,379,62],{"class":54},[48,381,198],{"class":111},[48,383,384],{"class":58},"\u003C",[48,386,387],{"class":65},"number",[48,389,390],{"class":58},">(",[48,392,393],{"class":65},"1",[48,395,207],{"class":58},[11,397,398,399,402,403,171],{},"في الكود السابق قمنا بإنشاء وتعيين قيمة افتراضية للإشارة ",[45,400,401],{},"count"," باستخدام الدالة ",[45,404,405],{},"signal",[11,407,408],{},"بشكل اختياري يمكنك تحديد نوع معين للبيانات داخل الإشارة، يمكن أن تكون الإشارة من نوع سلسلة نصية (string)، رقم (number)، مصفوفة (array)، كائن (object)، أو أي نوع بيانات آخر. في العديد من الحالات، يتم استنتاج نوع البيانات تلقائيًا، وبالتالي ليس من الضرورى تحديد النوع بشكل عام.",[11,410,411],{},"لاحظ أيضا وجود قيمة افتراضية للدالة بقيمة الرقم 1، وذلك لأن الإشارات يجب أن تحدد لها من البداية قيمة افتراضية تبدأ بها.",[11,413,414,415,417],{},"والآن لقراءة قيمة هذه الإشارة في ملفات Typescript الخاصة بـ Angular يكون ذلك بالشكل التالي \"لاحظ وجود الأقواس في نهاية كلمة ",[45,416,401],{},"\"",[38,419,421],{"className":40,"code":420,"language":42,"meta":43,"style":43},"console.log(this.count());\n",[45,422,423],{"__ignoreMap":43},[48,424,425,427,429,431,434,436,438],{"class":50,"line":51},[48,426,108],{"class":58},[48,428,112],{"class":111},[48,430,201],{"class":58},[48,432,433],{"class":65},"this",[48,435,171],{"class":58},[48,437,401],{"class":111},[48,439,255],{"class":58},[11,441,442],{},"أما لو أردنا عرض قيمة الإشارة في القالب (Template) فيكون بالشكل التالي",[38,444,448],{"className":445,"code":446,"language":447,"meta":43,"style":43},"language-html shiki shiki-themes github-dark","\u003Cdiv>\n  \u003Cp>القيمة الحالية: {{ count() }}\u003C\u002Fp>\n\u003C\u002Fdiv>\n","html",[45,449,450,461,475],{"__ignoreMap":43},[48,451,452,454,458],{"class":50,"line":51},[48,453,384],{"class":58},[48,455,457],{"class":456},"s4JwU","div",[48,459,460],{"class":58},">\n",[48,462,463,466,468,471,473],{"class":50,"line":72},[48,464,465],{"class":58},"  \u003C",[48,467,11],{"class":456},[48,469,470],{"class":58},">القيمة الحالية: {{ count() }}\u003C\u002F",[48,472,11],{"class":456},[48,474,460],{"class":58},[48,476,477,480,482],{"class":50,"line":87},[48,478,479],{"class":58},"\u003C\u002F",[48,481,457],{"class":456},[48,483,460],{"class":58},[11,485,486,487,171],{},"وعندما تحتاج إلى تحديث القيمة بغض النظر عن قيمتها السابقة، استخدم ",[45,488,282],{},[38,490,492],{"className":40,"code":491,"language":42,"meta":43,"style":43},"this.count.set(10);\n",[45,493,494],{"__ignoreMap":43},[48,495,496,498,501,503,505,507],{"class":50,"line":51},[48,497,433],{"class":65},[48,499,500],{"class":58},".count.",[48,502,282],{"class":111},[48,504,201],{"class":58},[48,506,287],{"class":65},[48,508,207],{"class":58},[11,510,511,512,515,516,171],{},"أما إذا كانت القيمة الجديدة تعتمد على القيمة السابقة، فاستخدم ",[45,513,514],{},"update"," بدلاً من ",[45,517,282],{},[38,519,521],{"className":40,"code":520,"language":42,"meta":43,"style":43},"this.count.update((a) => a + 1);\n",[45,522,523],{"__ignoreMap":43},[48,524,525,527,529,531,534,538,541,543,546,548,551],{"class":50,"line":51},[48,526,433],{"class":65},[48,528,500],{"class":58},[48,530,514],{"class":111},[48,532,533],{"class":58},"((",[48,535,537],{"class":536},"s9osk","a",[48,539,540],{"class":58},") ",[48,542,243],{"class":54},[48,544,545],{"class":58}," a ",[48,547,99],{"class":54},[48,549,550],{"class":65}," 1",[48,552,207],{"class":58},[11,554,555],{},"يمكن إنشاء Signals واشتقاقها من إشارات أخرى. وعند تحديث أي إشارة، يتم تلقائيًا تحديث جميع الإشارات المعتمدة عليها.",[11,557,558,559,171],{},"على سبيل المثال، إذا أردنا الحصول على عدد مشتق، بحيث يمثل قيمة العداد مضروبة في 2، يمكننا إنشاء إشارة مشتقة باستخدام الدالة ",[45,560,561],{},"computed",[38,563,565],{"className":40,"code":564,"language":42,"meta":43,"style":43},"count = signal\u003Cnumber>(1);\ndouble = computed(() => this.count() * 2);\n",[45,566,567,585],{"__ignoreMap":43},[48,568,569,571,573,575,577,579,581,583],{"class":50,"line":51},[48,570,377],{"class":58},[48,572,62],{"class":54},[48,574,198],{"class":111},[48,576,384],{"class":58},[48,578,387],{"class":65},[48,580,390],{"class":58},[48,582,393],{"class":65},[48,584,207],{"class":58},[48,586,587,590,592,594,596,598,601,603,605,607,610,613],{"class":50,"line":72},[48,588,589],{"class":58},"double ",[48,591,62],{"class":54},[48,593,237],{"class":111},[48,595,240],{"class":58},[48,597,243],{"class":54},[48,599,600],{"class":65}," this",[48,602,171],{"class":58},[48,604,401],{"class":111},[48,606,248],{"class":58},[48,608,609],{"class":54},"*",[48,611,612],{"class":65}," 2",[48,614,207],{"class":58},[11,616,617],{},"الإشارات المحسوبة (Computed Signals) تستمد قيمتها بناءً على إشارة واحدة أو أكثر. في كل مرة تتغير فيها إحدى تلك الإشارات، تتغير الإشارة المحسوبة تلقائيًا.",[11,619,620],{},"من المهم أن نلاحظ أن القيم المحسوبة يتم تخزينها مؤقتًا (Cached)، مما يعني أنه عند قراءة الإشارة المحسوبة في المستقبل، سيتم استرجاع القيم المخزنة بدلاً من إعادة حسابها، إلا إذا حدث تغيير في الإشارات التي تعتمد عليها.",[11,622,623,624,167,626,171],{},"تعتمد الإشارات المحسوبة على دالة اشتقاق لحساب القيمة، ولا يمكن تغيير قيمتها باستخدام دوال ",[45,625,282],{},[45,627,514],{},[11,629,630],{},"ملاحظة أخرى هامة، أي شيء مشتق من إشارة يعتبر إشارة بحد ذاته.",[11,632,633],{},"وهذا ينقلنا إلى دالة أخرى اسمها effect تستخدم في بعض الحالات التي لا يناسب فيها استخدام الدالة computed.",[11,635,636],{},"دالة effect تُستخدم لتنفيذ تأثيرات جانبية (أوامر برمجية) عندما تتغير واحدة أو أكثر من الإشارات داخلها. يتم تشغيل كل دالة effect على الأقل مرة واحدة، وتصبح جميع الإشارات التي تم استدعاؤها داخلها متتبعة. إذا تغيرت أي من تلك الإشارات، يتم تشغيل effect مرة أخرى تلقائيًا.",[11,638,639],{},"من المهم استخدام effect بحذر، وعدم استخدامها لعمل التغييرات في الحالة (state)، حيث قد يؤدي ذلك إلى إدخال مراجع دائرية (circular references) أو تكرار في دورات الكشف عن التغييرات.",[11,641,642],{},"يتم إلغاء التأثيرات (effects) تلقائيًا عند تدمير (destroy) السياق الذي تم إنشاؤها فيه سواء (Component \u002F service)، كما يمكن إزالتها يدويًا باستخدام دالة destroy.",[11,644,645],{},"من المهم دائمًا التأكد من إلغاء التأثيرات، كما تفعل مع الاشتراكات (subscriptions) في التطبيق، لتجنب حدوث مشكلات في الذاكرة (memory leaks).",[38,647,649],{"className":40,"code":648,"language":42,"meta":43,"style":43},"count = signal\u003Cnumber>(1);\n\neffect(()=> {\n    console.log(`The count signal changed value to ${count()}`)\n })\n",[45,650,651,669,673,686,710],{"__ignoreMap":43},[48,652,653,655,657,659,661,663,665,667],{"class":50,"line":51},[48,654,377],{"class":58},[48,656,62],{"class":54},[48,658,198],{"class":111},[48,660,384],{"class":58},[48,662,387],{"class":65},[48,664,390],{"class":58},[48,666,393],{"class":65},[48,668,207],{"class":58},[48,670,671],{"class":50,"line":72},[48,672,126],{"emptyLinePlaceholder":125},[48,674,675,678,681,683],{"class":50,"line":87},[48,676,677],{"class":111},"effect",[48,679,680],{"class":58},"(()",[48,682,243],{"class":54},[48,684,685],{"class":58}," {\n",[48,687,688,691,693,695,699,701,704,707],{"class":50,"line":105},[48,689,690],{"class":58},"    console.",[48,692,112],{"class":111},[48,694,201],{"class":58},[48,696,698],{"class":697},"sU2Wk","`The count signal changed value to ${",[48,700,401],{"class":111},[48,702,703],{"class":697},"()",[48,705,706],{"class":697},"}`",[48,708,709],{"class":58},")\n",[48,711,712],{"class":50,"line":122},[48,713,714],{"class":58}," })\n",[11,716,717],{},"بناء على ما سبق؛ لرؤية الصورة بشكل كامل لنفترض أن لدينا تطبيقًا صغيرًا يعرض عدّادا يزيد قيمته عند الضغط على زر، ويمكن أيضا إعادة ضبط قيمته، كما يوجد عدّاد آخر يعرض بشكل دائم ضعف القيمة الموجودة في العداد الأول.",[38,719,721],{"className":40,"code":720,"language":42,"meta":43,"style":43},"@Component({\n  selector: 'app',\n  template: `\n    \u003Ch3>Counter value {{counter()}}\u003C\u002Fh3>\n    \u003Ch3>2x counter: {{double()}}\u003C\u002Fh3>\n    \u003Cbutton (click)=\"increment()\">Increment\u003C\u002Fbutton>\n    \u003Cbutton (click)=\"reset()\">Reset\u003C\u002Fbutton>\n  `\n})\nexport class AppComponent {\n    \u002F\u002F Signal with initial value 0\n    counter = signal(0);\n\n    \u002F\u002F Computed double of counter\n    double = computed(() => this.counter() * 2);\n\n    \u002F\u002F Log changes in counter and double\n    constructor() {\n        effect(() => {\n            console.log(`current values: ${this.counter()} ${this.double()}`);\n        });\n    }\n\n    \u002F\u002F Increment counter by 1\n    increment() {\n        this.counter.update(value => value + 1);\n    }\n    \n    \u002F\u002F Reset counter to 0\n    reset() {\n        this.counter.set(0);\n    }\n}\n",[45,722,723,734,745,753,758,763,768,773,779,785,799,805,822,827,833,862,867,873,882,894,931,937,943,948,954,962,990,995,1001,1007,1015,1030,1035],{"__ignoreMap":43},[48,724,725,728,731],{"class":50,"line":51},[48,726,727],{"class":58},"@",[48,729,730],{"class":111},"Component",[48,732,733],{"class":58},"({\n",[48,735,736,739,742],{"class":50,"line":72},[48,737,738],{"class":58},"  selector: ",[48,740,741],{"class":697},"'app'",[48,743,744],{"class":58},",\n",[48,746,747,750],{"class":50,"line":87},[48,748,749],{"class":58},"  template: ",[48,751,752],{"class":697},"`\n",[48,754,755],{"class":50,"line":105},[48,756,757],{"class":697},"    \u003Ch3>Counter value {{counter()}}\u003C\u002Fh3>\n",[48,759,760],{"class":50,"line":122},[48,761,762],{"class":697},"    \u003Ch3>2x counter: {{double()}}\u003C\u002Fh3>\n",[48,764,765],{"class":50,"line":129},[48,766,767],{"class":697},"    \u003Cbutton (click)=\"increment()\">Increment\u003C\u002Fbutton>\n",[48,769,770],{"class":50,"line":142},[48,771,772],{"class":697},"    \u003Cbutton (click)=\"reset()\">Reset\u003C\u002Fbutton>\n",[48,774,776],{"class":50,"line":775},8,[48,777,778],{"class":697},"  `\n",[48,780,782],{"class":50,"line":781},9,[48,783,784],{"class":58},"})\n",[48,786,788,791,794,797],{"class":50,"line":787},10,[48,789,790],{"class":54},"export",[48,792,793],{"class":54}," class",[48,795,796],{"class":111}," AppComponent",[48,798,685],{"class":58},[48,800,802],{"class":50,"line":801},11,[48,803,804],{"class":118},"    \u002F\u002F Signal with initial value 0\n",[48,806,808,811,813,815,817,820],{"class":50,"line":807},12,[48,809,810],{"class":536},"    counter",[48,812,195],{"class":54},[48,814,198],{"class":111},[48,816,201],{"class":58},[48,818,819],{"class":65},"0",[48,821,207],{"class":58},[48,823,825],{"class":50,"line":824},13,[48,826,126],{"emptyLinePlaceholder":125},[48,828,830],{"class":50,"line":829},14,[48,831,832],{"class":118},"    \u002F\u002F Computed double of counter\n",[48,834,836,839,841,843,845,847,849,851,854,856,858,860],{"class":50,"line":835},15,[48,837,838],{"class":536},"    double",[48,840,195],{"class":54},[48,842,237],{"class":111},[48,844,240],{"class":58},[48,846,243],{"class":54},[48,848,600],{"class":65},[48,850,171],{"class":58},[48,852,853],{"class":111},"counter",[48,855,248],{"class":58},[48,857,609],{"class":54},[48,859,612],{"class":65},[48,861,207],{"class":58},[48,863,865],{"class":50,"line":864},16,[48,866,126],{"emptyLinePlaceholder":125},[48,868,870],{"class":50,"line":869},17,[48,871,872],{"class":118},"    \u002F\u002F Log changes in counter and double\n",[48,874,876,879],{"class":50,"line":875},18,[48,877,878],{"class":54},"    constructor",[48,880,881],{"class":58},"() {\n",[48,883,885,888,890,892],{"class":50,"line":884},19,[48,886,887],{"class":111},"        effect",[48,889,240],{"class":58},[48,891,243],{"class":54},[48,893,685],{"class":58},[48,895,897,900,902,904,907,909,911,913,915,918,920,922,925,927,929],{"class":50,"line":896},20,[48,898,899],{"class":58},"            console.",[48,901,112],{"class":111},[48,903,201],{"class":58},[48,905,906],{"class":697},"`current values: ${",[48,908,433],{"class":65},[48,910,171],{"class":697},[48,912,853],{"class":111},[48,914,703],{"class":697},[48,916,917],{"class":697},"} ${",[48,919,433],{"class":65},[48,921,171],{"class":697},[48,923,924],{"class":111},"double",[48,926,703],{"class":697},[48,928,706],{"class":697},[48,930,207],{"class":58},[48,932,934],{"class":50,"line":933},21,[48,935,936],{"class":58},"        });\n",[48,938,940],{"class":50,"line":939},22,[48,941,942],{"class":58},"    }\n",[48,944,946],{"class":50,"line":945},23,[48,947,126],{"emptyLinePlaceholder":125},[48,949,951],{"class":50,"line":950},24,[48,952,953],{"class":118},"    \u002F\u002F Increment counter by 1\n",[48,955,957,960],{"class":50,"line":956},25,[48,958,959],{"class":111},"    increment",[48,961,881],{"class":58},[48,963,965,968,971,973,975,978,981,984,986,988],{"class":50,"line":964},26,[48,966,967],{"class":65},"        this",[48,969,970],{"class":58},".counter.",[48,972,514],{"class":111},[48,974,201],{"class":58},[48,976,977],{"class":536},"value",[48,979,980],{"class":54}," =>",[48,982,983],{"class":58}," value ",[48,985,99],{"class":54},[48,987,550],{"class":65},[48,989,207],{"class":58},[48,991,993],{"class":50,"line":992},27,[48,994,942],{"class":58},[48,996,998],{"class":50,"line":997},28,[48,999,1000],{"class":58},"    \n",[48,1002,1004],{"class":50,"line":1003},29,[48,1005,1006],{"class":118},"    \u002F\u002F Reset counter to 0\n",[48,1008,1010,1013],{"class":50,"line":1009},30,[48,1011,1012],{"class":111},"    reset",[48,1014,881],{"class":58},[48,1016,1018,1020,1022,1024,1026,1028],{"class":50,"line":1017},31,[48,1019,967],{"class":65},[48,1021,970],{"class":58},[48,1023,282],{"class":111},[48,1025,201],{"class":58},[48,1027,819],{"class":65},[48,1029,207],{"class":58},[48,1031,1033],{"class":50,"line":1032},32,[48,1034,942],{"class":58},[48,1036,1038],{"class":50,"line":1037},33,[48,1039,1040],{"class":58},"}\n",[21,1042,1044],{"id":1043},"متى-يفضل-استخدام-كلا-من-computed-و-effect",[25,1045,1046],{},"متى يفضل استخدام كلا من Computed و effect ؟",[11,1048,1049,1050,167,1053,1056],{},"بشكل عام يفضل استخدام computed لجعل الكود أكثر وضوحًا وتفاعليًا بطريقة آمنة. لكن تذكر نقطتين: أولاً، لا تقم بتعديل أي شيء داخله، مثل تغيير الـ DOM أو المتغيرات، لأنه يجب أن يكون دالة نقية بدون تأثيرات جانبية. ثانيًا، تجنب العمليات غير المتزامنة مثل ",[45,1051,1052],{},"Promises",[45,1054,1055],{},"setTimeout","، لأن إشارات Angular تعمل بشكل تزامني فقط.",[11,1058,1059],{},"أما دالة effect نادرًا ما تحتاج استخدامها إذا كان الكود منظمًا بشكل جيد. لكن إذا استخدمتها، احرص على أن أي كود داخلها سيتم تنفيذه في سياق تفاعلي، مما يعني أن أي إشارات تقرأها داخلها ستصبح جزءًا من هذا السياق، وقد يؤدي ذلك إلى مشكلات مثل الحلقات اللانهائية (infinite loops) أو سلوك غير متوقع.",[21,1061,1063],{"id":1062},"هل-تعتبر-signals-بديلا-عن-rxjs-observables",[25,1064,1065],{},"هل تعتبر Signals بديلا عن RxJS observables ؟",[11,1067,1068],{},"في هذه المرحلة من المقال، قد تتساءل عزيزي القارئ: \"حتي الآن، تبدو الإشارات مشابهة تمامًا لـ RxJS Observables، حيث تقوم بنفس الوظيفة. فهل نحن هنا بصدد إعادة اختراع العجلة؟\"",[11,1070,1071],{},"للإجابة على هذا التساؤل، علينا التوقف لحظة لنوضح بشكل مختصر - لمن لا يعرف - ما هي الـ Observables، حتى نتمكن من فهم السياق بشكل أوضح.",[11,1073,1074],{},"إن Observable هو مفهوم من مكتبة (RxJS - Reactive Extensions for JavaScript) يُستخدم بغرض إدارة التدفق غير المتزامن للبيانات. ببساطة، يمكن اعتباره كتيار (stream) من البيانات الذي يمكنك الاشتراك فيه، بحيث يتلقى المشترك إشعارات عند توفر بيانات جديدة. هذا التيار يمكن أن يمثل أي شيء مثل استجابة من خادم، أحداث من المستخدم (كالنقرات)، أو حتى مجموعة من القيم التي تصل على فترات زمنية مختلفة.",[11,1076,1077],{},"الميزة الكبيرة في Observables هي قدرتها على التعامل مع العمليات غير المتزامنة بشكل سلس ومنظم. عندما تشترك في Observable، يمكنك التحكم في طريقة التعامل مع البيانات (مثل تحويلها أو تصفيتها) وتحديد ما يحدث عند إتمام التدفق أو في حالة حدوث خطأ. هذه المرونة تجعل Observables أداة قوية لإدارة الأحداث والبيانات بشكل ديناميكي وتفاعلي في التطبيقات الحديثة.",[11,1079,1080],{},"بناء على ما سبق، إذا كنت بحاجة إلى مراقبة قيم تتغير بمرور الوقت، دون معرفة مسبقة متى ستتغير (أي بشكل غير متزامن)، فإن Observables هي الأداة الأنسب لك. أما إذا لم يكن الزمن عاملًا مهمًا في المعادلة، فستكون الإشارات Signals كافية وأسهل للاستخدام. واقعيًا، من المرجح أننا سنستخدم Signals في أغلب الحالات مستقبلاً، وسنحتاج إلى Observables فقط في ظروف خاصة تتطلب إمكانياتها المتقدمة.",[11,1082,1083,1084,316,1087,1090],{},"بعد فهم هذه الفروقات، من الواضح أن فكرة استبدال Observables بـ Signals هى فكرة غير دقيقة. المسألة تتعلق فقط باستخدام الأداة المناسبة للموقف المناسب. والأهم من ذلك، أن فريق RxJS قد استوعب الصورة الكاملة، وقام بتنفيذ دالتين تتيحان التوافق بين Signals و Observables: وهما ",[45,1085,1086],{},"toObservable",[45,1088,1089],{},"toSignal","، مما يتيح إدارة تدفقات البيانات المعقدة أو غير المتزامنة دون التخلي عن استخدام Signals.",[11,1092,1093],{},"أريد أن أشير هنا إلى أن هذا التوافق بين الأدوات القديمة والجديدة يعد من المبادئ الأساسية في Angular ومكتباتها، حيث تضمن دائمًا التوافق الرجعي، مما يسمح بتعايش الوظائف القديمة والجديدة دون المخاطرة بتعطيل أي شيء، وهذا ليس بالأمر البسيط.",[21,1095,1097],{"id":1096},"أحدث-إضافات-angular-17-فيما-يتعلق-بـ-signals",[25,1098,1099],{},"أحدث إضافات Angular 17 فيما يتعلق بـ Signals",[11,1101,1102,1103,316,1106,1109,1110,1113],{},"مع إصدار Angular 17، أصبحت الإشارات (Signals) أكثر قوة مع إضافة عدة APIs جديدة مثل ",[45,1104,1105],{},"input",[45,1107,1108],{},"output","  و ",[45,1111,1112],{},"model",". هذه التحديثات تهدف إلى تسهيل إدارة البيانات بين المكونات، وإضافة تفاعلية أفضل، وتقليل التعقيدات الناتجة عن استخدام Decorators التقليدية. دعونا نستعرض كل API بالتفصيل ونرى كيفية استخدامها مقارنة بالطريقة التقليدية.",[21,1115,1117],{"id":1116},"_1-المدخلات-input","1- المدخلات Input",[11,1119,1120,1121,1123,1124,1127,1128,1130],{},"تمثل ",[45,1122,1105],{}," بديلاً عن ",[45,1125,1126],{},"@Input"," حيث أصبح بإمكانك تعريف المدخلات كإشارات بدلاً من الاعتماد على Decorator، ما يميز ",[45,1129,1105],{}," هو أنها تجعل القيم المدخلة تفاعلية مباشرة، مما يعني أن أي تغيير على المدخلات سيتم تحديثه تلقائيًا في جميع أنحاء التطبيق بدون الحاجة لإعادة ربط القيم أو استخدام Hooks إضافية.",[38,1132,1134],{"className":40,"code":1133,"language":42,"meta":43,"style":43},"\u002F\u002F Before Signals\n@Input() data: string; \n\n\u002F\u002F After Signals\ndata = input\u003Cstring>();\n",[45,1135,1136,1141,1156,1160,1165],{"__ignoreMap":43},[48,1137,1138],{"class":50,"line":51},[48,1139,1140],{"class":118},"\u002F\u002F Before Signals\n",[48,1142,1143,1145,1148,1150,1153],{"class":50,"line":72},[48,1144,727],{"class":58},[48,1146,1147],{"class":111},"Input",[48,1149,248],{"class":58},[48,1151,1152],{"class":111},"data",[48,1154,1155],{"class":58},": string; \n",[48,1157,1158],{"class":50,"line":87},[48,1159,126],{"emptyLinePlaceholder":125},[48,1161,1162],{"class":50,"line":105},[48,1163,1164],{"class":118},"\u002F\u002F After Signals\n",[48,1166,1167,1170,1172,1175,1177,1180],{"class":50,"line":122},[48,1168,1169],{"class":58},"data ",[48,1171,62],{"class":54},[48,1173,1174],{"class":111}," input",[48,1176,384],{"class":58},[48,1178,1179],{"class":65},"string",[48,1181,1182],{"class":58},">();\n",[11,1184,1185],{},"هذا التغيير يجعل البيانات المدخلة تعمل كإشارة تفاعلية، مما يسهل عملية تحديث القيم دون الحاجة إلى أي تدخل إضافي.",[21,1187,1189],{"id":1188},"_2-المخرجات-output","2- المخرجات Output",[11,1191,1192,1193,1195,1196,1199],{},"أصبحت ",[45,1194,1108],{}," تحل محل ",[45,1197,1198],{},"@Output"," وتستخدم إشارات للتعامل مع الأحداث الصادرة، هذه الإشارة تقوم بإرسال قيم تفاعلية وتسمح بتدفق البيانات بشكل أكثر وضوحًا وسهولة، حيث يمكن للإشارة إخطار المستمعين بالأحداث التي تحدث في المكون (Component) دون الحاجة إلى EventEmitter.",[38,1201,1203],{"className":40,"code":1202,"language":42,"meta":43,"style":43},"\u002F\u002F Before Signals\n@Output() valueChanged = new EventEmitter\u003Cstring>();\n\nonChange(value: string) {\n  this.valueChanged.emit(value);\n}\n\n\u002F\u002F After Signals\nvalueChanged = output\u003Cstring>();\n\nonChange(value: string) {\n  this.valueChanged(value);\n}\n",[45,1204,1205,1209,1233,1237,1245,1259,1263,1267,1271,1287,1291,1297,1308],{"__ignoreMap":43},[48,1206,1207],{"class":50,"line":51},[48,1208,1140],{"class":118},[48,1210,1211,1213,1216,1219,1221,1224,1227,1229,1231],{"class":50,"line":72},[48,1212,727],{"class":58},[48,1214,1215],{"class":111},"Output",[48,1217,1218],{"class":58},"() valueChanged ",[48,1220,62],{"class":54},[48,1222,1223],{"class":54}," new",[48,1225,1226],{"class":111}," EventEmitter",[48,1228,384],{"class":58},[48,1230,1179],{"class":65},[48,1232,1182],{"class":58},[48,1234,1235],{"class":50,"line":87},[48,1236,126],{"emptyLinePlaceholder":125},[48,1238,1239,1242],{"class":50,"line":105},[48,1240,1241],{"class":111},"onChange",[48,1243,1244],{"class":58},"(value: string) {\n",[48,1246,1247,1250,1253,1256],{"class":50,"line":122},[48,1248,1249],{"class":65},"  this",[48,1251,1252],{"class":58},".valueChanged.",[48,1254,1255],{"class":111},"emit",[48,1257,1258],{"class":58},"(value);\n",[48,1260,1261],{"class":50,"line":129},[48,1262,1040],{"class":58},[48,1264,1265],{"class":50,"line":142},[48,1266,126],{"emptyLinePlaceholder":125},[48,1268,1269],{"class":50,"line":775},[48,1270,1164],{"class":118},[48,1272,1273,1276,1278,1281,1283,1285],{"class":50,"line":781},[48,1274,1275],{"class":58},"valueChanged ",[48,1277,62],{"class":54},[48,1279,1280],{"class":111}," output",[48,1282,384],{"class":58},[48,1284,1179],{"class":65},[48,1286,1182],{"class":58},[48,1288,1289],{"class":50,"line":787},[48,1290,126],{"emptyLinePlaceholder":125},[48,1292,1293,1295],{"class":50,"line":801},[48,1294,1241],{"class":111},[48,1296,1244],{"class":58},[48,1298,1299,1301,1303,1306],{"class":50,"line":807},[48,1300,1249],{"class":65},[48,1302,171],{"class":58},[48,1304,1305],{"class":111},"valueChanged",[48,1307,1258],{"class":58},[48,1309,1310],{"class":50,"line":824},[48,1311,1040],{"class":58},[11,1313,1314,1315,1317],{},"الطريقة الجديدة تجعل إدارة الأحداث أكثر سهولة وتفاعلية من خلال الإشارات، حيث تتعامل ",[45,1316,1108],{}," مع القيم والأحداث بشكل مباشر دون الحاجة إلى استخدام EventEmitter.",[21,1319,1321],{"id":1320},"_3-النموذج-model","3- النموذج Model",[11,1323,1324,1325,1327,1328,1331,1332,1334],{},"تعتبر ",[45,1326,1112],{}," كـ API جديدة تسهل عملية الربط الثنائي (two-way data binding) التي كانت تتم سابقًا باستخدام ",[45,1329,1330],{},"[(ngModel)]",". مع استخدام ",[45,1333,1112],{},", أصبح الربط الثنائي بين المكونات أكثر وضوحًا وتفاعلية باستخدام إشارات، مما يلغي الحاجة إلى الربط اليدوي بين المدخلات والمخرجات.",[38,1336,1338],{"className":40,"code":1337,"language":42,"meta":43,"style":43},"\u002F\u002F Before Signals\n\u003Cinput [(ngModel)]=\"userData\">\n\n\u002F\u002F After Signals\nuserData = model\u003Cstring>();\n",[45,1339,1340,1344,1358,1362,1366],{"__ignoreMap":43},[48,1341,1342],{"class":50,"line":51},[48,1343,1140],{"class":118},[48,1345,1346,1348,1351,1353,1356],{"class":50,"line":72},[48,1347,384],{"class":54},[48,1349,1350],{"class":58},"input [(ngModel)]",[48,1352,62],{"class":54},[48,1354,1355],{"class":697},"\"userData\"",[48,1357,460],{"class":54},[48,1359,1360],{"class":50,"line":87},[48,1361,126],{"emptyLinePlaceholder":125},[48,1363,1364],{"class":50,"line":105},[48,1365,1164],{"class":118},[48,1367,1368,1371,1373,1376,1378,1380],{"class":50,"line":122},[48,1369,1370],{"class":58},"userData ",[48,1372,62],{"class":54},[48,1374,1375],{"class":111}," model",[48,1377,384],{"class":58},[48,1379,1179],{"class":65},[48,1381,1182],{"class":58},[11,1383,1384,1385,1387],{},"الفرق هنا هو أن ",[45,1386,1112],{}," تسهل عملية الربط الثنائي الاتجاه باستخدام إشارات، مما يعني أن أي تغييرات على المدخلات يتم تحديثها في الحال بدون الحاجة إلى أي تعقيدات إضافية.",[21,1389,1391],{"id":1390},"الخاتمة",[25,1392,1390],{},[11,1394,1395],{},"في الختام، تشكل Signals إضافة ثورية في Angular تسهم في تعزيز التفاعل مع البيانات وتحسين استجابة التطبيقات بشكل فعال. هذه التقنية لا تقتصر فقط على تحسين الأداء، بل تبسط أيضًا عملية إدارة البيانات. ",[11,1397,1398],{},"لقد انتهى عصر المتغيرات البسيطة للبيانات، حيث أصبحت الإشارات بمثابة رسل ذكية تقوم بتنبيه Angular عند حدوث أي تغيير في المعلومات، مما يوفر تجربة برمجة أكثر سلاسة ومرونة.",[11,1400,1401],{},"إذا كنت ترغب في تحسين أداء تطبيقك وتقليل التعقيد، فإن تجربة Signals تستحق وقتك. ابدأ بتجربة هذا المفهوم في جزء صغير من مشروعك، وستجد أنها قد تسهّل الكثير من الجوانب وتقلل من الأخطاء.",[11,1403,1404],{},"ولا تنسى أن تشارك معى تجربتك وانطباعاتك حول هذه التطويرات الجديدة في بيئة العمل Angular.",[11,1406,1407],{},"وفقنى الله وإياك إلى ما يحبه ويرضاه ،،",[1409,1410,1411],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s4JwU, html code.shiki .s4JwU{--shiki-default:#85E89D}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}",{"title":43,"searchDepth":72,"depth":87,"links":1413},[1414,1415,1416,1417,1418,1419,1420,1421,1422,1423,1424],{"id":23,"depth":72,"text":27},{"id":322,"depth":72,"text":325},{"id":343,"depth":72,"text":346},{"id":361,"depth":72,"text":364},{"id":1043,"depth":72,"text":1046},{"id":1062,"depth":72,"text":1065},{"id":1096,"depth":72,"text":1099},{"id":1116,"depth":72,"text":1117},{"id":1188,"depth":72,"text":1189},{"id":1320,"depth":72,"text":1321},{"id":1390,"depth":72,"text":1390},"\u002Fimages\u002F6703cc8df1aaf592bac75491.png","2024-10-07T11:50:02.904Z","2024-10-07T12:41:59.917Z","يوضح المقال كيف تساهم Signals في إحداث نقلة نوعية في Angular من خلال تحسين أداء التطبيقات وجعل إدارة التغييرات أكثر سهولة وفعالية.","md",{},"\u002Farticles\u002F6703caeaf1aaf592bac7544e",{"title":5,"description":1428},"articles\u002F6703caeaf1aaf592bac7544e",[1435],{"id":1436,"label":1437},"65ae26201d50740dab36fdb3","اطار Angular","2025-03-01T10:51:37.259Z","8YAicULi-PAahF82wZb3uhdr37jZgun1TXgS377q7e8",1788209571270]