हम एल्म 0.18 के बारे में बात करना जारी रखते हैं।
एल्म। आरामदायक और अजीब
एल्म। आरामदायक और अजीब। Json.Encoder और Json.Decoder
एल्म। आरामदायक और अजीब। Http, टास्क
इस लेख में, हम एल्म एप्लिकेशन आर्किटेक्चर के मुद्दों और घटक विकास दृष्टिकोण को लागू करने के संभावित विकल्पों पर विचार करेंगे।
कार्य के रूप में, ड्रॉप-डाउन विंडो के कार्यान्वयन पर विचार करें जो एक पंजीकृत उपयोगकर्ता को एक प्रश्न जोड़ने की अनुमति देता है। एक अनाम उपयोगकर्ता के मामले में, वह पहले लॉग इन या पंजीकरण करने का सुझाव देता है।
हम यह भी मानते हैं कि बाद में अन्य प्रकार की उपयोगकर्ता सामग्री के स्वागत को लागू करना आवश्यक हो सकता है, लेकिन अधिकृत और अनाम उपयोगकर्ताओं के साथ काम करने का तर्क समान रहेगा।
अजीब रचना
एक भोली कार्यान्वयन का स्रोत कोड । इस कार्यान्वयन के हिस्से के रूप में, हम सब कुछ एक मॉडल में संग्रहीत करेंगे।
प्राधिकरण और उपयोगकर्ता मतदान के लिए आवश्यक सभी डेटा मॉडल में समान स्तर पर हैं। संदेशों के साथ भी यही स्थिति (Msg)।
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
इंटरफ़ेस प्रकार को यूनियन प्रकार यूआई के रूप में वर्णित किया गया है, जिसका उपयोग शायद प्रकार के साथ किया जाता है।
type Ui = LoginUi -- Popup shown with authentication form | QuestionUi -- Popup shown with textarea to leave user question
इस प्रकार, ui = कुछ भी पॉप-अप विंडो की अनुपस्थिति का वर्णन नहीं करता है, और जस्ट पॉपअप एक विशिष्ट इंटरफ़ेस के साथ खुला है।
अपडेट फ़ंक्शन में, जोड़ी का मिलान किया जाता है, संदेश और उपयोगकर्ता डेटा। इस जोड़ी के आधार पर विभिन्न क्रियाएं की जाती हैं।
update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case (msg, model.user) of
उदाहरण के लिए, जब आप "ओपन पॉपअप" बटन पर क्लिक करते हैं, तो एक ओपनपॉप संदेश उत्पन्न होता है। अद्यतन फ़ंक्शन में OpenPopup संदेश को विभिन्न तरीकों से नियंत्रित किया जाता है। एक अनाम उपयोगकर्ता के लिए, एक प्राधिकरण फ़ॉर्म जेनरेट किया जाता है, और अधिकृत उपयोगकर्ता के लिए, ऐसा फ़ॉर्म जिसमें आप एक प्रश्न छोड़ सकते हैं।
update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case (msg, model.user) of
जाहिर है, इस दृष्टिकोण से अनुप्रयोग कार्यों की वृद्धि में समस्या हो सकती है:
- मॉडल और संदेशों में डेटा का कोई समूह नहीं है। एक विमान में सब कुछ निहित है। इस प्रकार, कोई घटक सीमाएं नहीं हैं; एक भाग के तर्क में बदलाव से बाकी की संभावना सबसे अधिक प्रभावित होगी;
- सभी आगामी परिणामों के साथ कॉपी-पेस्ट के सिद्धांत पर कोड का पुन: उपयोग संभव है।
सुविधाजनक रचना
स्रोत कोड एक सुविधाजनक कार्यान्वयन है । इस कार्यान्वयन के ढांचे में, हम परियोजना को स्वतंत्र घटकों में विभाजित करने का प्रयास करेंगे। घटकों के बीच अनुमत निर्भरताएँ।
परियोजना संरचना:
- प्रकार फ़ोल्डर में कस्टम प्रकार घोषित किए जाते हैं;
- घटक घटक में कस्टम घटक घोषित किए जाते हैं;
- Main.elm फ़ाइल परियोजना प्रविष्टि बिंदु;
- लॉगिन। Json और questions.json फ़ाइलों को सर्वर के जवाब के लिए परीक्षण डेटा के रूप में उपयोग किया जाता है, क्रमशः प्राधिकरण और सवाल के बारे में जानकारी को बचाने के लिए।
कस्टम घटक
प्रत्येक घटक, भाषा वास्तुकला पर आधारित होना चाहिए:
- मॉडल (मॉडल);
- संदेश (संदेश)
- निष्पादन का परिणाम (रिटर्न);
- आरंभीकरण समारोह (init);
- म्यूटेशन फ़ंक्शन (अपडेट);
- समारोह देखें।
यदि आवश्यक हो तो प्रत्येक घटक में सदस्यता हो सकती है।

अंजीर। 1. घटक गतिविधि आरेख
प्रारंभ
प्रत्येक घटक को ट्रिगर किया जाना चाहिए, अर्थात प्राप्त किया जाना चाहिए:
- मॉडल;
- आदेश या कमांड की सूची जो घटक की स्थिति को शुरू करना चाहिए
- निष्पादन परिणाम। प्रारंभ के समय निष्पादन का परिणाम उपयोगकर्ता प्राधिकरण को मान्य करने के लिए आवश्यक हो सकता है, जैसा कि इस लेख के उदाहरणों में है।
आरंभीकरण फ़ंक्शन (init) के तर्कों की सूची घटक के तर्क पर निर्भर करती है और मनमानी हो सकती है। कई आरंभीकरण कार्य हो सकते हैं। मान लीजिए, एक प्राधिकरण घटक के लिए, दो आरंभीकरण विकल्प दिए जा सकते हैं: एक सत्र टोकन के साथ और उपयोगकर्ता डेटा के साथ।
कोड, जो घटक का उपयोग करता है, आरंभीकरण के बाद, कमांड कमांड को Cmd.map फ़ंक्शन का उपयोग करके पास करना चाहिए।
परिवर्तन
प्रत्येक घटक संदेश के लिए अद्यतन घटक फ़ंक्शन को कॉल किया जाना चाहिए। निष्पादन के परिणामस्वरूप, फ़ंक्शन एक ट्रिपल देता है:
- नया मॉडल या नया राज्य (मॉडल);
- एल्म रनटाइम (Cmd Msg) के लिए कमांड या कमांड सूची। आदेश HTTP अनुरोधों को निष्पादित करने के लिए कमांड हो सकते हैं, बंदरगाहों के साथ बातचीत, और बहुत कुछ;
- निष्पादन का परिणाम (शायद वापसी)। शायद टाइप में दो स्टेट्स नथिंग एंड जस्ट ए है। हमारे मामले में, कुछ भी नहीं - कोई परिणाम नहीं है, बस एक - एक परिणाम है। उदाहरण के लिए, प्राधिकरण के लिए, परिणाम बस हो सकता है (प्रामाणिक उपयोगकर्ताडेटा) - उपयोगकर्ता उपयोगकर्ता डेटा के साथ लॉग इन है।
उत्परिवर्तन के बाद, घटक का उपयोग करने वाले कोड को घटक मॉडल को अपडेट करना चाहिए और Cmd.map फ़ंक्शन का उपयोग करके एल्म रनटाइम को कमांड पास करना चाहिए।
एल्म एप्लिकेशन आर्किटेक्चर के अनुसार, अपडेट फ़ंक्शन के लिए आवश्यक तर्क:
- संदेश (संदेश)
- मॉडल (मॉडल)।
यदि आवश्यक हो, तो तर्कों की सूची को पूरक किया जा सकता है।
विचार
सामान्य अनुप्रयोग दृश्य में घटक दृश्य सम्मिलित करने के लिए आवश्यक होने पर दृश्य फ़ंक्शन को कहा जाता है।
व्यू फ़ंक्शन के लिए आवश्यक तर्क घटक मॉडल होना चाहिए। यदि आवश्यक हो, तो तर्कों की सूची को पूरक किया जा सकता है।
दृश्य फ़ंक्शन को निष्पादित करने का परिणाम Html.map फ़ंक्शन को पास किया जाना चाहिए।
अनुप्रयोग एकीकरण
उदाहरण दो घटकों का वर्णन करता है: प्रामाणिक और प्रश्न । ऊपर वर्णित सिद्धांतों के घटक। विचार करें कि उन्हें आवेदन में कैसे एकीकृत किया जा सकता है।
सबसे पहले, आइए निर्धारित करें कि हमारा आवेदन कैसे काम करना चाहिए। स्क्रीन पर एक बटन होता है, जब आप उस पर क्लिक करते हैं:
- एक अनधिकृत उपयोगकर्ता के लिए, एक प्राधिकरण फ़ॉर्म प्रदर्शित किया जाता है, प्राधिकरण के बाद - एक प्रश्न पोस्टिंग फॉर्म;
- अधिकृत उपयोगकर्ता के लिए, एक प्रश्न प्लेसमेंट फॉर्म प्रदर्शित किया जाता है।
आपके द्वारा आवश्यक एप्लिकेशन का वर्णन करने के लिए:
- मॉडल (मॉडल);
- संदेश (संदेश)
- आवेदन शुरू बिंदु (मुख्य);
- आरंभीकरण समारोह (init);
- उत्परिवर्तन समारोह;
- प्रस्तुति समारोह;
- सदस्यता समारोह।
आदर्श
type alias Model = { user: User , ui: Maybe Ui } type Ui = AuthUi Component.Auth.Model | QuestionUi Component.Question.Model
मॉडल में उपयोगकर्ता (उपयोगकर्ता) और वर्तमान इंटरफ़ेस के प्रकार (ui) के बारे में जानकारी होती है। इंटरफ़ेस या तो डिफ़ॉल्ट स्थिति में हो सकता है (कुछ नहीं) या बस एक घटक में से एक।
घटकों का वर्णन करने के लिए, हम यूआई प्रकार का उपयोग करते हैं, जो प्रत्येक घटक मॉडल को एक विशिष्ट प्रकार के प्रकार के सेट से संबद्ध (टैग) करते हैं। उदाहरण के लिए, एक प्रामाणिक टैग एक आवेदन मॉडल के साथ एक प्राधिकरण मॉडल (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 }
एप्लिकेशन प्रविष्टि बिंदु को आमतौर पर एल्म एप्लिकेशन के लिए वर्णित किया जाता है।
प्रारंभिक कार्य
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 = Just (AuthUi difModel)) में निर्दिष्ट किया जाता है, तो हम घटक संदेश और घटक मॉडल को Auth.updit फ़ंक्शन में पास करते हैं। नतीजतन, हमें एक नया घटक मॉडल, कमांड और परिणाम मिलता है।
यदि उपयोगकर्ता अधिकृत है, तो हम प्रश्न घटक को प्रारंभ करते हैं, अन्यथा हम एप्लिकेशन मॉडल में इंटरफ़ेस डेटा को अपडेट करते हैं।
(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
कोई सदस्यता नहीं।
आगे
यह उदाहरण, हालांकि थोड़ा अधिक सुविधाजनक है, लेकिन काफी भोला है। क्या गायब है:
- डाउनलोड प्रक्रिया के दौरान आवेदन के साथ बातचीत को अवरुद्ध करना;
- डेटा सत्यापन। एक अलग वार्तालाप की आवश्यकता है;
- वास्तव में बंद करने की क्षमता के साथ पॉप-अप विंडो।