الدردار. مريح ومحرج. تكوين

نواصل الحديث عن Elm 0.18 .


الدردار. مريح ومحرج
الدردار. مريح ومحرج. Json.Encoder و Json.Decoder
الدردار. مريح ومحرج. Http ، المهمة


في هذه المقالة ، نعتبر قضايا بنية تطبيق Elm والخيارات الممكنة لتطبيق نهج تطوير المكون.


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


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


تكوين محرج


الكود المصدري لتطبيق ساذج . كجزء من هذا التطبيق ، سنقوم بتخزين كل شيء في نموذج واحد.


جميع البيانات اللازمة للترخيص واستقصاء المستخدم على نفس المستوى في النموذج. نفس الوضع مع الرسائل (مسج).


type alias Model = { user: User , ui: Maybe Ui -- Popup is not open is value equals Nothing , login: String , password: String , question: String , message: String } type Msg = OpenPopup | LoginTyped String | PasswordTyped String | Login | QuestionTyped String | SendQuestion 

يتم وصف نوع الواجهة كنوع اتحاد Ui ، والذي يتم استخدامه مع نوع ربما.


 type Ui = LoginUi -- Popup shown with authentication form | QuestionUi -- Popup shown with textarea to leave user question 

وبالتالي ، ui = لا شيء يصف عدم وجود نافذة منبثقة ، وفتح النافذة المنبثقة Just بواجهة محددة.

في وظيفة التحديث ، يتم مطابقة الزوج والرسالة وبيانات المستخدم. يتم تنفيذ إجراءات مختلفة اعتمادًا على هذا الزوج.


 update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case (msg, model.user) of 

على سبيل المثال ، عندما تنقر على زر "Open popup" ، يتم إنشاء رسالة OpenPopup. يتم التعامل مع رسالة OpenPopup في وظيفة التحديث بطرق مختلفة. بالنسبة لمستخدم مجهول ، يتم إنشاء نموذج تفويض ، وبالنسبة إلى المستخدم المصرح له ، نموذج يمكنك من خلاله ترك سؤال.


 update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case (msg, model.user) of -- Anonymous user message handling section (OpenPopup, Anonymous) -> ( { model | ui = Just LoginUi, message = "" }, Cmd.none) -- Authenticated user message handling section (OpenPopup, User userName) -> ( { model | ui = Just QuestionUi, message = "" }, Cmd.none) 

من الواضح أن هذا النهج قد يواجه مشاكل في نمو وظائف التطبيق:


  1. لا يوجد تجميع للبيانات في النموذج والرسائل. كل شيء يكمن في طائرة واحدة. وبالتالي ، لا توجد حدود للمكونات ؛ من المرجح أن يؤثر تغيير منطق جزء واحد على الباقي ؛
  2. يمكن إعادة استخدام التعليمات البرمجية على مبدأ النسخ واللصق مع جميع العواقب المترتبة على ذلك.

تكوين ملائم


كود المصدر هو تنفيذ مناسب . في إطار هذا التطبيق ، سنحاول تقسيم المشروع إلى مكونات مستقلة. التبعيات المسموح بها بين المكونات.


هيكل المشروع:


  1. في مجلد النوع يتم إعلان الأنواع المخصصة ؛
  2. يتم تعريف المكونات المخصصة في مجلد المكونات ؛
  3. نقطة إدخال مشروع ملف Main.elm ؛
  4. تُستخدم ملفات login.json و Questions.json كبيانات اختبار لاستجابة الخادم للتفويض وحفظ المعلومات حول السؤال ، على التوالي.

مكونات مخصصة


يجب أن يحتوي كل مكون ، بناءً على بنية اللغة ، على:


  1. نموذج
  2. الرسائل (Msg)
  3. نتيجة التنفيذ (العودة) ؛
  4. وظيفة التهيئة (الحرف الأول) ؛
  5. وظيفة الطفرة (التحديث) ؛
  6. وظيفة العرض.

قد يحتوي كل مكون على اشتراك إذا لزم الأمر.


الصورة
التين. 1. مخطط نشاط المكون


التهيئة


يجب تشغيل كل مكون ، أي يجب استلام:


  1. نموذج
  2. الأمر أو قائمة الأوامر التي يجب أن تهيئ حالة المكون
  3. نتيجة التنفيذ. قد تكون نتيجة التنفيذ في وقت التهيئة ضرورية للتحقق من صحة تفويض المستخدم ، كما هو الحال في الأمثلة على هذه المقالة.

تعتمد قائمة الحجج الخاصة بدالة التهيئة (init) على منطق المكون ويمكن أن تكون عشوائية. قد يكون هناك العديد من وظائف التهيئة. لنفترض ، بالنسبة لمكون التفويض ، أنه يمكن توفير خيارين للتهيئة: مع رمز الجلسة وبيانات المستخدم.


التعليمات البرمجية التي تستخدم المكون ، بعد التهيئة ، يجب أن تمرر الأوامر إلى وقت التشغيل باستخدام دالة Cmd.map .


طفرة


يجب استدعاء وظيفة مكون التحديث لكل رسالة مكون. نتيجة التنفيذ ، ترجع الدالة الثلاثي:


  1. نموذج جديد أو حالة جديدة (نموذج) ؛
  2. قائمة الأوامر أو الأوامر لـ Elm runtime (Cmd Msg). يمكن أن تكون الأوامر أوامر لتنفيذ طلبات HTTP ، والتفاعل مع المنافذ ، والمزيد ؛
  3. نتيجة الإعدام (ربما العودة). ربما اكتب حالتين لا شيء وفقط. في حالتنا ، لا شيء - لا توجد نتيجة ، فقط - هناك نتيجة. على سبيل المثال ، للحصول على تفويض ، قد تكون النتيجة Just (Authenticated UserData) - قام المستخدم بتسجيل الدخول باستخدام بيانات UserData.

بعد الطفرة ، يجب أن تقوم التعليمات البرمجية التي تستخدم المكون بتحديث نموذج المكون وتمرير الأوامر إلى Elm runtime باستخدام وظيفة Cmd.map .


الحجج المطلوبة لوظيفة التحديث ، وفقًا لبنية تطبيق Elm:


  1. رسالة (رسالة)
  2. نموذج (نموذج).

إذا لزم الأمر ، يمكن استكمال قائمة الحجج.


التقديم


يتم استدعاء وظيفة العرض عندما يكون من الضروري إدراج عرض المكون في عرض التطبيق العام.


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


يجب تمرير نتيجة تنفيذ وظيفة العرض إلى دالة Html.map .


تكامل التطبيق


يصف المثال مكونين: Auth و Question . مكونات المبادئ الموضحة أعلاه. فكر في كيفية دمجها في التطبيق .


أولاً ، دعنا نحدد كيفية عمل تطبيقنا. يوجد زر على الشاشة عند الضغط عليه:


  1. لمستخدم غير مصرح به ، يتم عرض نموذج تفويض ، بعد التفويض - نموذج نشر سؤال ؛
  2. بالنسبة للمستخدم المعتمد ، يتم عرض نموذج موضع السؤال.

لوصف التطبيق تحتاج:


  1. نموذج
  2. الرسائل (Msg)
  3. نقطة بدء التطبيق (الرئيسي) ؛
  4. وظيفة التهيئة (الحرف الأول) ؛
  5. وظيفة الطفرة
  6. وظيفة العرض ؛
  7. وظيفة الاشتراك.

نموذج


 type alias Model = { user: User , ui: Maybe Ui } type Ui = AuthUi Component.Auth.Model | QuestionUi Component.Question.Model 

يحتوي النموذج على معلومات حول المستخدم (المستخدم) ونوع الواجهة الحالية (واجهة المستخدم). يمكن أن تكون الواجهة إما في الحالة الافتراضية (لا شيء) أو أحد المكونات فقط.


لوصف المكونات ، نستخدم نوع واجهة المستخدم ، الذي يربط (علامات) كل نموذج مكون بمتغير محدد من مجموعة من الأنواع. على سبيل المثال ، تربط علامة AuthUi نموذج التفويض (Component.Auth.Model) بنموذج تطبيق.


الرسائل


 type Msg = OpenPopup | AuthMsg Component.Auth.Msg | QuestionMsg Component.Question.Msg 

في الرسائل ، يجب عليك وضع علامة على جميع الرسائل المكونة وتضمينها في رسائل التطبيق. تربط علامتا AuthMsg و QuestionMsg بين رسائل مكون التفويض وسؤال المستخدم ، على التوالي.


مطلوب رسالة OpenPopup لمعالجة طلب لفتح واجهة.


الوظيفة الرئيسية


 main : Program Never Model Msg main = Html.program { init = init , update = update , subscriptions = subscriptions , view = view } 

يتم وصف نقطة إدخال التطبيق عادةً لتطبيق Elm.


دالة التهيئة


 init : ( Model, Cmd Msg ) init = ( initModel, Cmd.none ) initModel : Model initModel = { user = Anonymous , ui = Nothing } 

تنشئ وظيفة التهيئة نموذج بدء ولا تتطلب تنفيذ الأوامر.


دالة الطفرة


كود مصدر الوظيفة
 update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case (msg, model.ui) of (OpenPopup, Nothing) -> case Component.Auth.init model.user of (authModel, commands, Just (Component.Auth.Authenticated userData)) -> let (questionModel, questionCommands, _) = Component.Question.init userData in ( { model | ui = Just <| QuestionUi questionModel, user = User userData }, Cmd.batch [Cmd.map AuthMsg commands, Cmd.map QuestionMsg questionCommands] ) (authModel, commands, _) -> ( { model | ui = Just <| AuthUi authModel }, Cmd.map AuthMsg commands ) (AuthMsg authMsg, Just (AuthUi authModel)) -> case Component.Auth.update authMsg authModel of (_, commands, Just (Component.Auth.Authenticated userData)) -> let (questionModel, questionCommands, _) = Component.Question.init userData in ( { model | ui = Just <| QuestionUi questionModel, user = User userData }, Cmd.batch [Cmd.map AuthMsg commands, Cmd.map QuestionMsg questionCommands] ) (newAuthModel, commands, _) -> ( { model | ui = Just <| AuthUi newAuthModel }, Cmd.map AuthMsg commands ) (QuestionMsg questionMsg, Just (QuestionUi questionModel)) -> case Component.Question.update questionMsg questionModel of (_, commands, Just (Component.Question.Saved record)) -> ( { model | ui = Nothing }, Cmd.map QuestionMsg commands ) (newQuestionModel, commands, _) -> ( { model | ui = Just <| QuestionUi newQuestionModel }, Cmd.map QuestionMsg commands ) _ -> ( model, Cmd.none ) 

لأن تم توصيل النموذج والرسائل بالتطبيق ، سنقوم بمعالجة بضع رسائل (Msg) ونوع الواجهة (model.ui: Ui).


 update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case (msg, model.ui) of 

منطق العمل


إذا تم تلقي رسالة OpenPopup وتم تحديد الواجهة الافتراضية في النموذج (model.ui = لا شيء) ، فإننا نقوم بتهيئة مكون المصادقة. إذا أبلغ مكون Auth أن المستخدم مخول ، فإننا نقوم بتهيئة مكون السؤال وحفظه في نموذج التطبيق. خلاف ذلك ، نقوم بحفظ نموذج المكون في نموذج التطبيق.


 (OpenPopup, Nothing) -> case Component.Auth.init model.user of (authModel, commands, Just (Component.Auth.Authenticated userData)) -> let (questionModel, questionCommands, _) = Component.Question.init userData in ( { model | ui = Just <| QuestionUi questionModel, user = User userData }, Cmd.batch [Cmd.map AuthMsg commands, Cmd.map QuestionMsg questionCommands] ) (authModel, commands, _) -> ( { model | ui = Just <| AuthUi authModel }, Cmd.map AuthMsg commands ) 

إذا تم استلام رسالة تحتوي على علامة AuthMsg وتم تحديد واجهة المصادقة في النموذج (model.ui = فقط (AuthUi authModel)) ، فإننا نمرر رسالة المكون ونموذج المكون إلى وظيفة Auth.update. ونتيجة لذلك ، نحصل على نموذج مكون جديد ، وأوامر ، ونتيجة.


إذا كان المستخدم مخولاً ، فإننا نقوم بتهيئة مكون السؤال ، وإلا فإننا نقوم بتحديث بيانات الواجهة في نموذج التطبيق.


 (AuthMsg authMsg, Just (AuthUi authModel)) -> case Component.Auth.update authMsg authModel of (_, commands, Just (Component.Auth.Authenticated userData)) -> let (questionModel, questionCommands, _) = Component.Question.init userData in ( { model | ui = Just <| QuestionUi questionModel, user = User userData }, Cmd.batch [Cmd.map AuthMsg commands, Cmd.map QuestionMsg questionCommands] ) (newAuthModel, commands, _) -> ( { model | ui = Just <| AuthUi newAuthModel }, Cmd.map AuthMsg commands ) 

على نحو مماثل لمكون المصادقة ، تتم معالجة رسائل مكون السؤال. إذا تم نشر السؤال بنجاح ، تتغير الواجهة إلى الافتراضي (model.ui = لا شيء).


 (QuestionMsg questionMsg, Just (QuestionUi questionModel)) -> case Component.Question.update questionMsg questionModel of (_, commands, Just (Component.Question.Saved record)) -> ( { model | ui = Nothing }, Cmd.map QuestionMsg commands ) (newQuestionModel, commands, _) -> ( { model | ui = Just <| QuestionUi newQuestionModel }, Cmd.map QuestionMsg commands ) 

يتم تجاهل جميع الحالات الأخرى.


  _ -> ( model, Cmd.none ) 

وظيفة العرض


 view : Model -> Html Msg view model = case model.ui of Nothing -> div [] [ div [] [ button [ Events.onClick OpenPopup ] [ text "Open popup" ] ] ] Just (AuthUi authModel) -> Component.Auth.view authModel |> Html.map AuthMsg Just (QuestionUi questionModel) -> Component.Question.view questionModel |> Html.map QuestionMsg 

بناءً على نوع الواجهة (model.ui) ، تقوم وظيفة العرض التقديمي إما بإنشاء واجهة افتراضية أو استدعاء وظيفة عرض المكون وتعيين نوع رسالة المكون إلى نوع رسالة التطبيق (Html.map).


وظيفة الاشتراك


 subscriptions : Model -> Sub Msg subscriptions model = Sub.none 

لا يوجد اشتراك.


التالي


هذا المثال ، على الرغم من أنه أكثر ملاءمة ، ولكن ساذج للغاية. ما هو مفقود:


  1. منع التفاعل مع التطبيق أثناء عملية التنزيل ؛
  2. التحقق من صحة البيانات. يتطلب محادثة منفصلة.
  3. نافذة منبثقة حقًا مع إمكانية الإغلاق.

Source: https://habr.com/ru/post/ar424341/


All Articles