Route to another page from a sub-page - elm

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 )

Related

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

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 }

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)

Grails - How to send a personInstance ID from gsp to domain class?

I'm a complete newbie in grails and I need you guys' help.
I have my sql query in the domain class. I put [1] to see the result but ultimately I'd like to send an argument in that place to display the result according to the person's id number.
def dataSource
def someMethod() {
def sql = new Sql(dataSource)
def resultRows = sql.rows('select * from result where id = ?', [1])
}
And this is what I have in my gsp.
<g:each in="${personInstance.someMethod()}" status="i" var="results">
<tr class="${(i % 2) == 0 ? 'even' : 'odd'}">
<td>${results.column_1}</td>
<td>${results.column_2}</td>
<td>${results.column_3}</td>
</tr>
</g:each>
How do I send a parameter from view to domain class?
Please help.
Thank you in advance.
If you want to see how Grails 'would like' your controller and view code to look, try letting Grails generate the code for you. Even if you don't keep that code in your final project, it is still useful as an instructional tool.
arc$ grails create-app Tester1
arc$ cd Tester1
arc$ grails
grails> create-domain-class Person
-- add some attributes to your Person domain class, save the file
grails> generate-all tester1.Person
Now go look at the PersonController.groovy, and the various views. Basically, it's marshal your data in the controller, pass it to the views, views operate on what they're given.
Very basic example of passing arbitrary data to the gsp:
// show method for an Adventure
def show(Adventure adventure) {
// a String to pass to the gsp
def attribute = 'Bilbo'
// an Array to pass to the gsp
def attributeList = ['Dwalin','Balin','etc']
// create a map of values that are 'automagically' passed
// to the show.gsp
[adventure: adventure, hobbit: attribute, dwarves: attributeList]
}
The adventure, hobbit, and dwarves variables are all available in the gsp. The template code likes to use verbose naming, like adventureInstance, but as long as your gsp code uses the keynames in the map you define, you're good to go.

Editing saved Multiple Checkbox selections in Django

Whats the best way of performing the save because at the moment. When it comes to editing, I'm not getting the saved responses to populate the form. Other fields such as drop downs are fine. Is there somehthing I should do in the view to make this work? Here is my view:
def populateaboutme(request):
extractlinkedindata(request)
if request.method == "POST":
form = AboutMeForm(request.POST)
if form.is_valid():
today = datetime.date.today()
currentYYMMDD = today.strftime('%Y-%m-%d')
model_instance = form.save(commit=False)
model_instance.save()
request.session["AboutMe_id"] = model_instance.pk
StoreImage(settings.STATIC_ROOT, str(request.session["fotoloc"]), '.jpg', str(request.session["AboutMe_id"]))
return redirect('/dashboard/')
else:
myid = request.session["AboutMe_id"]
if not myid:
form = AboutMeForm()
else:
aboutme = AboutMe.objects.get(pk=int(myid))
form = AboutMeForm(instance=aboutme)
return render(request, "aboutme.html", {'form': form})
Here are the models:
class AboutMe(models.Model):
MyRelationshipIntent = models.CharField(max_length=50)
and the forms:
class AboutMeForm(ModelForm):
class Meta:
model = AboutMe
exclude = ()
MyRelationshipIntent = forms.MultipleChoiceField(choices=RELATIONSHIPINTENT_CHOICES,widget=forms.CheckboxSelectMultiple())
RELATIONSHIPINTENT_CHOICES = (
('JL', 'Just Looking'),
('FL', 'Looking for friendship'),
('FN', 'Looking for fun'),
('FL', 'Looking for a relationship'),
)
You want to use the initial option on the form:
form = AboutMeForm(initial={'name': aboutme.name})
The instance= you are using is what you need to use when saving to tell django this isn't a new object:
if request.method == 'POST':
form = AboutMeForm(request.POST, instance=aboutme)
Now using instance can give the initial values as well, but only when using a modelform, and you still need it for the saving part.
Edit
It took me a while to notice it because I was focusing on the form, but the problem you are having stems, essentially, from the fact that you are using a CharField where you should be using a ManyToManyField. I mean - how would four checked boxes be translated into one CharField and vice-versa? Django can't just guess it. It makes no sense.
You can use a CharField if you somehow add a method to translate it to the checkboxes. But it's also a wrong approach so don't. Instead, I'll give you two solutions, and you'll choose the one you see fit.
The most natural thing to do would be to use a ManyToMany field here, and then tell the django form to use the checkbox field for it (the default would be a multiselect, and if you want you can use a client side plugin to make that look nice as well). Your models would look something like this:
class Intent(models.Model):
relationship = models.CharField(max_length=50)
class AboutMe(models.Model):
intents = models.ManyToManyField(Intent)
Then you just create four Intent instances for each of the values in your RELATIONSHIPINTENT_CHOICES:
rels = ('Just Looking',
'Looking for friendship',
'Looking for fun',
'Looking for a relationship')
for i in rels:
new = Intent(relationship=i)
new.save()
This is especially good if you think that you might want to add more options later on (and you can create a model on the admin site to ease that proccess instead of the script I wrote up there). If you don't like that solution and you're sure your options would remain the same, another good solution that might suit you is creating a boolean field for each option. Like this:
class AboutMe(models.Model)
jl = models.BooleanField(verbose_name='Just Looking')
fl = models.BooleanField(verbose_name='Looking for friendship')
fn = models.BooleanField(verbose_name='Looking for fun')
fl = models.BooleanField(verbose_name='Looking for a relationship')
Then you don't even need the widget, because checkbox is the default for boolean fields. After doing this, using form(instance=aboutme) and form(initial={'jl': aboutme.jl}) would both work. I know those might look a little scary and more complex than your simple CharField, but this is the right way to go.
p.s.
Other python tips to keep in mind:
Don't name your class "AboutMe". That should be the view, not the model. It makes more sense (to me at least) to make it an extension of the built-in User, name it User or give it a similar fitting name (Profile or Account or the sort)
Field names should not look like class names (check out PEP8 for more conventions). So it should be my_relationship_intent. However, that's also a long and wearying name. relationship_intent or simply intents is a lot better.

How to test an application when each call changes state of the system?

Testing is simple when you have methods that you can call 100 times and they yield the same results. But how do you test something like an Api, where you have something like this:
int CreateUser(username,password); //returns the id of the user, -1 if error
int SubmitOrder(username,password,productName,quantity);//returns the id of the order -1 if error
int CancelOrder(username,password,orderId); //returns -1 if error
How would you test that this Api works? How do you create test data? I can not write unit tests fot it, I can not use the same test data, since I can not create a user two times(UserName is unique). When I submit an order I always get different orderIds as responses.
You need to find some way to "reset" system to well-known initial state, i.e. state with no users nor orders.
Also you need to find some way to observe state in the system. This way could be actually destructive, i.e. it could modify or even damage state of the system, but though. In your case, method CreateUser could be used as such observer to check whether user already exists, because it is known to return -1 in such situation.
Here is how one of your test cases could look like:
reset (); // Each test case should start with reset
assertNotEquals (-1, CreateUser ("foo", "bar")); // This should work fine
assertEquals (-1, CreateUser ("foo", "zoo")); // Make sure user "foo" does exist
assertNotEquals (-1, SubmitOrder ("foo", "bar", "apple", 1)); // Make sure user can pass authentication
assertEquals (-1, SubmitOrder ("foo", "zoo", "apple", 1)); // Make sure password is actually checked
The above test case checks that CreateUser actually creates user with given name and passwords, and does not allow creating two users with the same name.
Here is another test case:
reset ();
CreateUser ("foo", "bar");
orderID = SubmitOrder ("foo", "bar", "apple", 1); // Submit order
assertNotEquals (-1, CancelOrder (orderID)); // Make sure order was really created
assertEquals (-1, CancelOrder (orderID)); // Make sure order was cancelled
And so on. Of cause it would be better to find more straightforward ways to observe system state, e.g. by querying database directly.
I assume you are talking about some OOP language. I think you can solve your problem by Unit Testing and Dependency Injection.
Core concept:
first you do test of your DataBase class
when testing API you inject a FakeDataBase class into it (that shares same Interface)
So in testing environment API writes to some other fake database or simply prints queries to a file and you just check if content of this file is what you expected.
Great video:
http://www.youtube.com/watch?feature=player_embedded&v=wEhu57pih5w#!
http://en.wikipedia.org/wiki/Dependency_injection
http://en.wikipedia.org/wiki/Unit_testing