In Elm 0.19.1, is it possible to make an http-get request within a decoder for the initial model? - elm

I have a component created in Elm where users can create a list of different criteria. For this component, users should be able to create criteria to search for contacts. These contacts will be shown on a different (non-Elm) page. If the users return to the criteria builder, the previous filled in criteria should be shown again.
To do this, I use the JSON that was used to create the query in Elm. This should be decoded to create the objects that will show the input that the user has made before.
One of the objects I use is a list. This list contains of tuples with id and name. For the query builder, I only send the id of the objects in the JSON to the back-end. This means that, if a user returns to the criteria builder, the decoder can only decode the list of id's. For my list selection, I also want to fetch the names of the objects with the id's.
Now this is where I have some problems. To make an http-request, I have to catch the result with an Cmd.Msg. In the update function, I must then update my Model. The problem is, I don't have a model yet, because I'm still decoding my initial model. Also, I guess using a Decoder (for the result of the http-request) within a Decoder (for my initial model) is not the best of ideas.
Is there a way to solve this problem where I am making an http-request within a Decoder for my initial Model?

There are a handful of options here, depending on how complicated your situation is. At the end of the day, init produces a (Model, Cmd Msg) value, and your HTTP request(s) can be part of that Cmd.
Option 1: Generate HTTP requests based on the decoded model
init : Decode.Value -> (Model, Cmd Msg)
init json =
let
initialRequests model =
List.map initialRequest model.items
in
case Decode.decodeValue flagsDecoder json of
Ok model ->
( model, initialRequests model |> Cmd.batch )
Err _ ->
( initialModel, Cmd.none )
Option 2: Generate HTTP requests while decoding
init : Decode.Value -> ( Model, Cmd Msg )
init json =
let
flagsDecoder =
Decode.map (\items -> ({ items = List.map Tuple.first items }, List.map Tuple.second items))
(Decode.field "items" (Decode.list itemDecoder))
itemDecoder =
Decode.field "name" Decode.string
|> Decode.map (\name -> (name, initialRequest name))
in
case Decode.decodeValue flagsDecoder json of
Ok ( model, cmds ) ->
( model, cmds |> Cmd.batch )
Err _ ->
( initialModel, Cmd.none )
Your init function will need to be able to return a Model value that is "incomple", waiting on these responses to come back. Sometimes that's as simple as modeling values that you're waiting on as Maybe values, sometimes you might want to wrap a large portion of your model in a value that indicates if it's valid or not.
type Model
= Initializing String (Maybe Thing1) (Maybe Thing2)
| Ready { baseUrl : String, thing1 : Thing1, thing2 : Thing2 }

Related

Combine lists of 2 different objects into list of single object using Java 8 Stream API

I have two lists as below:
List ids = Arrays.asList(1,2,3);
List reps = Arrays.asList("abc","pqr","xyz");
Now I want to create list of Prediction objects with values mapped from above two lists in sequence like below:
List results = [ Prediction(1,"abc") , Prediction(2,"pqr"), Prediction(3,"xyz") ]
class Prediction {
int id;
String rep;
}
How this can be done using Java8 Stream API.
The operation you described is called zipping.
If you are sure the lists are evenly of length, you could do something like:
IntStream.range(0, ids.size())
.mapToObj(i -> new Prediction(ids.get(i), reps.get(i)))
.toList()
Assuming the Prediction class has a constructor like that...

Route to another page from a sub-page

I am using Hop at the moment and I am wondering how you can route to another page outside the main / app module. If you follow the Hop documentation, there are two specific types of messages, well - they are called that in the docs, NavigateTo and SetQuery. How do you raise those messages from sub-modules?
I have tried the following:
view =
button [ onClick (Main.Types.NavigateTo "test") ] []
However, this would mess up the typing.
The problem wasn't with Hop, but my understanding of how parent-child communication works within Elm. To say lightly, you need to be careful for what and when you use this type of communication, but in my case, I read a few good blog posts by Brian Thicks and Alex Lew talking about this form of communication. Especially Alex' post is typical for my use case.
What I did is the following: I added a separate update statement for the type of message I want to route. This is not the best implementation and it can be done more elegantly like Alex describes with the translator pattern.
update msg model =
case msg of
NavigateTo path ->
let
command =
Hop.outputFromPath hopConfig path
|> Navigation.newUrl
in
( model, command )
SetQuery query ->
let
command =
model.address
|> Hop.setQuery query
|> Hop.output hopConfig
|> Navigation.newUrl
in
( model, command )
ExampleMsg InterestingMsg exampleInteger ->
update (NavigateTo "<path here>") model --Update model in let-statement
ExampleMsg subMsg ->
let
( updatedExampleModel, pageCmd ) =
Page.Example.State.update subMsg model.exampleModel
in
( { model | exampleModel = updatedExampleModel }, Cmd.map ExampleMsg pageCmd )

Elm: making successive network requests

I am trying to learn elm from the past week and want build a simple Hacker News client by calling the official Hacker News API.
I'm calling https: //hacker-news.firebaseio.com/v0/topstories.json to get the top stories which would return an array of story Ids. Once I have the Ids I need to make subsequent calls to https ://hacker-news.firebaseio.com/v0/item/[/* Id goes here */].json fetch the details of each story item.
I have a Main.elm file which would fetch list of top stories.
type Msg = Request
| RequestSuccess (List Int)
| RequestFail Http.Error
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
Request ->
(model, getTopNews)
RequestSuccess list->
(Model list, Cmd.none)
RequestFail error->
(Model [], Cmd.none)
Next part is where I am confused, fetching details for each of the item returned. I also have a NewsItem component to display the details of each news item.
How can I solve this, by creating union types inside NewsItem component(child component) to fetch details? If thats how I should do it..
How can I can call the fetch details api from the NewsItem component as soon as the first api call inside Main.elm is complete?
Or am I missing something obvious here? That's not the correct approach at all?
You can see what I have tried so far here.
Here's my recommendation. It assumes that you'll be loading each NewsItem independently and that they can all fail independently as well. If this isn't the case then we can definitely come up with something that works better for you.
1) Represent your NewsItem not as just a record but a record wrapped in a type to represent the possibility that loading the details could fail. See http://blog.jenkster.com/2016/06/how-elm-slays-a-ui-antipattern.html for more info.
module NewsItem
-- imports and such
type Model = Loading | Success NewsItem | Failure Http.Error
2) Write an init function in your NewsItem module that accepts an Int and returns a (NewsItem.Model, Cmd NewsItem.Msg) pair
init : Int -> (Model, Cmd Msg)
init newsItemId =
( Loading
, getNewsItem newsItemId |> Task.perform GetItemFailure GetItemSuccess
)
3) In your Main module, once you've fetched your list of IDs, map them onto a List (NewsItem.Model, Cmd NewsItem.Msg) using your init function and use the techniques of the Elm architecture to store them as children in your parent model. I recommend storing them as a Dict Int NewsItem.Model which maps ID onto child model.
RequestSuccess list ->
let
children =
list |> List.map (\id -> (id, NewsItem.init id))
childModels =
children
|> List.map (\(id, (model, cmd)) -> (id, model))
|> Dict.fromList
childCmds =
children
|> List.map (\(id, (model, cmd)) -> Cmd.map (NewsItemMsg id) cmd)
|> Cmd.batch
in
(Model childModels, childCmds)

Elm/html: ordered list of different kind of elements

I was looking at some Elm examples and I found "Example 3: A Dynamic List of Counters". I wanted to alter it to include checkboxes, so a single list with counters and/or checkboxes. Each having their own actions and model (such as a name, and a checked bool). On top of that I would like to order the list, so that checked checkboxes all appear at the top, unchecked at the bottom and the counters in the middle.
I couldn't find any examples on how to do and structure this though. Any help?
Note: I'm very new to Elm (and functional programming in general).
Thanks!
One way to achieve this is to use a union type for the item (untested, I don't have a Elm environment right now to check this):
type Item = Checkbox String Bool | Counter
type alias Model =
{ items : List ( ID, Item )
, nextID : ID
}
Depending on the type of item, you'd then render it as
viewItem item =
case item of
Counter value -> renderCounter value
CheckBox name state -> renderCheckbox name state
All your view has to do is call viewItem for each Item, sort the items and put everything in a container (e.g. a div):
view address model =
let
items = List.map (viewItem address) model.items
|> List.sort (your custom sorting here)
in
div [] ([remove, insert] ++ items)

CRM Dynamics SDK how to access related data

I'm using early-bound entities, generated by CrmSvcUtil, and I'm testing the SDK by retrieving an account entity:-
var account = myContext.AccountSet.FirstOrDefault(o => o.Id == Guid.Parse("..."));
(BTW is there an easier way to retrieve an entity by ID?!)
Looking at the account object that is returned, I can see various properties of type OptionSetValue (e.g. "PreferredContactMethodCode"). How do I get the actual item from this OptionSetValue object?
Similarly, there are numerous properties of type EntityReference, which contains an Id and LogicalName (the entity name I presume). How can I populate such a property - is it one of the Get... methods? And do these have to be called separately, or is it possible to "pre-fetch" certain relationships as part of the initial query that retrieves the account entity?
Similarly with the various IEnumerable<> properties (which presumably correspond to 1-M entity relationships), e.g. a property called "opportunity_customer_accounts" of type IEnumerable. How do I populate one of these properties? And (again) does this have to be done as a separate query/method call, or can it be "pre-fetched"?
Retrieve
I'm not really sure how much simpler the retrieve operation could get but for a single record the user of the context is probably overkill. So you could retrieve a specific record where you know directly with the IOrganizationService:
account = service.Retrieve("account", Guid.Parse("..."), new ColumnSet(true));
Option Set Labels
For the OptionSet labels you can look at my answer here: How to get the option set from a field in an entity in CRM 2011 using crm sdk and C#.
If you need the label for multiple OptionSet's on an entity you may want to just retrieve the Entity's metadata once (http://srinivasrajulapudi.blogspot.com/2012/01/retrieve-entity-metadata-in-crm-2011.html):
string entityName ="contact";
// Get the metadata for the currently list's entity
// This metadata is used to create a "Property Descriptor Collection"
RetrieveEntityRequest mdRequest = new RetrieveEntityRequest ( )
{ EntityFilters = EntityFilters.All,
LogicalName = entityName,
RetrieveAsIfPublished = false
};
// Execute the request
RetrieveEntityResponse entityResponse = ( RetrieveEntityResponse ) this.ServiceProxy.Execute ( mdRequest );
EntityMetadata entityData = entityResponse.EntityMetadata;
//To Get Option Set Data
var preferdList= ( entityData.Attributes.Where ( p => p.LogicalName == "preferredcontactmethodcode" ) ).ToList ( ).FirstOrDefault ( ); ;
if ( preferdList != null ) {
PicklistAttributeMetadata optionList= preferdList as PicklistAttributeMetadata;
OptionSetMetadata opValues= optionList.OptionSet;
foreach ( var op in opValues.Options ) {
preferedMethod.Add ( new OptionSet { Value = op.Value.Value, Text = op.Label.LocalizedLabels[0].Label.ToString() } );
}
}
EntityReference()
To set an EntityReference typed field:
account.primarycontact = new EntityReference("contact", Guide.Parse("..."));
If they have a value and you requested the column in your ColumnSet() they should be populated, so I'm not really sure I understand your question. If you mean, you want the full record then you need to do a service.Retrieve(...) for the record.
Related Entities (i.e., opportunity_customer_accounts)
This is where using an OrganizationServiceContext makes life easier (https://msdn.microsoft.com/en-us/library/gg695791.aspx):
context.LoadProperty(contact, "transactioncurrency_contact");
// read related entity dynamically
var currency = contact.GetRelatedEntity("transactioncurrency_contact");
Console.WriteLine(currency.GetAttributeValue("currencyname"));
// read related entity statically
var currencyStatic = contact.transactioncurrency_contact;
Console.WriteLine(currencyStatic.CurrencyName);
If you are not using an OrganizationServiceContext you can try using a QueryExpression using LinkedEntities, although I've never done this to populate an early-bound entity so I don't know if it works (perhaps someone will add a comment with the answer.)