标记 Allibone

Lead Mobile Developer Rey Automation, Microsoft MVP

所以我们知道我们可以获得每个扬声器的图像,名称,标语和ID。所以,让我们创建一个记录来存储该信息:type Speaker = {Id:string; Name:string; Photo:string; Tagline:string} 创建记录并不严格必要。但它确实使数据稍后更轻松地使用数据。另一个优势是我们可以在.NET标准库中胶囊键入提供程序代码,然后使用非F#.NET代码共享。不,您可以直接从C#播放类型提供商数据类型,而C#的某些功能受到F#的启发。从我听到的那样,我不会屏住呼吸,希望能够在C#中看到类型的提供商很快 - 使用记录到位,所有剩余的都是从HTML中提取数据。 F#数据与HTML CSS选择器一起出现的好处。 HTML CSS选择器允许在ID,类和标记类型之后过滤。因此,如果我们想获取扬声器名称,我们可以过滤组件,然后提取值如下:// .. other parsing methodslet private getName (htmlNode:HtmlNode) = htmlNode.CssSelect("h3.sz-speaker__name > a") |> Seq.map (fun h -> h.DirectInnerText()) |> Seq.headlet getSpeakers (html:string) = HtmlDocument.Parse(html) .CssSelect("li.sz-speaker") |> Seq.map (fun s -> {Id = (getId s); Name = (getName s); Photo = (getPhoto s); Tagline = (getTagline s)})同样,可以访问其余数据以填充我们记录中的其他字段。同样用于轨道,我们将首先创建一个记录,我们将每个曲目的名称,时间,房间和扬声器ID存储在一起。我们将能够将曲目与ID联系到扬声器:type Track = {Room:string; Time:string; Title:string; SpeakerId:string option}let getTracks (html:string) = HtmlDocument.Parse(html) .CssSelect("div.sz-session__card") |> Seq.map(fun s -> {Room = (getRoom s); Time = (getTime s); Title = (getTitle s); SpeakerId = (getSpeakerId s) })现在我们拥有所有数据到位,是时候在应用程序上崩溃了。神话般的Xamarin专家应用程序Before we start writing our UI code, there is still that shortcut we took above with loading the information out of a file. While this works great when using a script in the mobile world, this means we have to pack that HTML doc into the app. There are two approaches: either put it into the Assets folder on Android and in the Resources folder (you can also use XCAssets…) on iOS or make an Embedded Resource in the .Net Standard library. While the first option would be what Apple and Google intended you to use when adding docs, you want to ship with your app. You will have to jump through some hoops to access the document. So let’s again save some time and just pack the file as an Embedded Resource in our .Net Standard project. Embedded Resources are packed into your apps binary. This results in an awkward fashion of accessing the data. While described in the official docs here,这是它在Xamarin专家日会议应用程序中实现的方式(我们需要更短的姓名):let loadFile filename = let assembly = IntrospectionExtensions.GetTypeInfo(typedefof).Assembly; let stream = assembly.GetManifestResourceStream(filename); use streamReader = new StreamReader(stream) streamReader.ReadToEnd()随着这种方式。让我们创建一个与标题,时间和房间的所有会谈的列表。选择轨道时,我们将显示演示者信息的演示信息。所以我们可以像这样放在一起的列表:let showTrackCell track = View.ViewCell( view = View.StackLayout(children = [ View.Label (text = track.Title, fontSize = FontSize 22.) View.Label (text = track.Time + " in " + track.Room, fontSize = FontSize 14., fontAttributes = FontAttributes.Italic) ]))let view (model: Model) dispatch = View.ContentPage( content = match model.SelectedTrack with | Some track -> showTrackInfo track model dispatch | None -> View.ListView( rowHeight = 80, hasUnevenRows = true, margin = Thickness(8.,0.,0.,0.), items = (model.Tracks |> List.map showTrackCell), selectionMode = ListViewSelectionMode.Single, itemSelected = (fun args -> dispatch (TrackSelected args)) ) )和œ尾视图是这样做的:let showTrackInfo track (model:Model) dispatch = let speaker = match track.SpeakerId with | Some speakerId -> model.Speakers |> Seq.tryPick(fun s -> if s.Id = speakerId then Some s else None) | None -> None let addSpeakerInfo (speaker:Speaker) = View.StackLayout(margin = Thickness(0.,32.,0.,0.), children = [ View.Label (text = "Speaker", fontSize = FontSize 22. ) View.Image (source = (Image.Path speaker.Photo)) View.Label (text = "Presenter: " + speaker.Name) View.Label (text = "Tagline: " + speaker.Tagline) ]) let speakerViewElements = match (speaker |> Option.map addSpeakerInfo) with | Some speakerInfo -> speakerInfo | None -> View.Label(text = "Brought to you by the Organizers"); View.Grid (margin = Thickness(8.,8.,8.,16.), rowdefs = [Star; Auto], children = [ View.StackLayout(children = [ View.Label (text = track.Title, fontSize = FontSize 22.) View.Label (text = "In: " + track.Room, fontSize = FontSize 14.) View.Label (text = "At: " + track.Time, fontSize = FontSize 14., margin = Thickness(0.,-4.,0.,0.)) speakerViewElements ]) (View.Button (text = "Back", command = (fun () -> dispatch (TrackSelected None)))).Row(1) ])您可能已经注意到缺少谈话描述。该网站有一个JavaScript函数检索该附加信息。我认为可以将JavaScript调用复制到后端,然后通过答案解析JSON / HTML答案。但是插入leame-stallate - 为什么为-i-am-not-lazy-jother。该应用程序仍然感觉有点皱纹,我认为我可能不得不在另一个博客帖子中跟进并制作它pretty ğŸ˜Conclusion在这个小实验中,我们开始看看是否有可能编写一个移动应用程序,该应用程序显示与网站上已存在的相同信息。虽然道路上有一些颠簸 - 我正在看着你的JavaScript。为在Android和iOS上运行的Xamarin专家日来编写一个应用程序。虽然我真的应该在我的下一篇文章上开始,并使应用程序漂亮ğÿ〜‡您可以查看整个应用程序GitHub.这篇文章是THEF#Advent日历的一部分。务必检查另一个posts.HTH">

使用F#数据在预算中创建一个神话般的Xamarin应用程序

TitleImage.

一段时间后,我使用F#类型提供程序创建转换表。如果可以编写从网站获取其数据的应用程序,那帖子给了我这个想法。也许您还收到了对应用程序的请求。没有任何昂贵的并且实际上应该显示的所有数据都在网上存在,即在此处就在此网站上。所以问题是:可以在不必编写一行后端代码的情况下创建这样的应用程序吗?

在这个博客文章中,我会尝试为我最喜欢的Xamarin会议创建一个应用程序 - Xamarin专家节。所以,让我们看看我们是否可以创建我们的神话般的Xamarin专家日应用程序。

获取数据

类型提供商是F#的令人愉快的功能。在编译类型提供程序期间生成在数据源中表示的数据模型。我写了一个Blogpost.在解析HTML的主题之前。这次我们将使用F#数据附带的另一个工具集尼古特包裹。

由于我们希望获取有关Xamarin专家日会议的信息,我们可以直接尝试解析网站。所以我们可以使用以下行来执行以下操作:

HtmlDocument.Load "//expertday.forxamarin.com/"

不幸的是,我们生活在javascript的现代衰剧中。我不想在这里进行切线,但只是说明了Xamarin专家日网站似乎正在加载初始HTML后的谈话和曲目的信息。幸运的情况下,在浏览器中加载页面时,我们会收到一个包含我们正在寻找的所有信息的HTML版本。因此,我们可以从文件中加载数据,而是可以从文件加载数据。预算项目有其局限性 - ğÿ™ƒ

HtmlDocument.Parse("ExpertXamarin.html")

当我们查看网站的HTML(在浏览器中)时,我们可以看到扬声器在以下HTML结构下列出:

  • 标记 Allibone

    Lead Mobile Developer Rey Automation, Microsoft MVP

  • 所以我们知道我们可以获得每个扬声器的图像,名称,标语和ID。所以,让我们创建一个记录来存储该信息:

    type Speaker = {Id:string; Name:string; Photo:string; Tagline:string}
    

    创建记录并不严格必要。但它确实使数据稍后更轻松地使用数据。另一个优势是我们可以在.NET标准库中胶囊键入提供程序代码,然后使用非F#.NET代码共享。不,您可以直接从C#播放类型提供商数据类型,而C#的某些功能受到F#的启发。从我听到的那样,我不会屏住呼吸,希望能够在C#中看到类型的提供商很快 -

    使用记录到位,所有剩余的都是从HTML中提取数据。 F#数据与HTML CSS选择器一起出现的好处。 HTML CSS选择器允许在ID,类和标记类型之后过滤。因此,如果我们想获取扬声器名称,我们可以过滤组件,然后提取值如下:

    // .. other parsing methods
    let private getName (htmlNode:HtmlNode) =
        htmlNode.CssSelect("h3.sz-speaker__name > a") |> Seq.map (fun h -> h.DirectInnerText()) |> Seq.head
    
    let getSpeakers (html:string) =
        HtmlDocument.Parse(html)
            .CssSelect("li.sz-speaker")
            |> Seq.map (fun s -> {Id = (getId s); Name = (getName s); Photo = (getPhoto s); Tagline = (getTagline s)})
    

    同样,可以访问其余数据以填充我们记录中的其他字段。同样用于轨道,我们将首先创建一个记录,我们将每个曲目的名称,时间,房间和扬声器ID存储在一起。我们将能够将曲目与ID联系到扬声器:

    type Track = {Room:string; Time:string; Title:string; SpeakerId:string option}
    
    let getTracks (html:string) =
        HtmlDocument.Parse(html)
            .CssSelect("div.sz-session__card")
            |> Seq.map(fun s -> {Room = (getRoom s); Time = (getTime s); Title = (getTitle s); SpeakerId = (getSpeakerId s) })
    

    现在我们拥有所有数据到位,是时候在应用程序上崩溃了。

    神话般的Xamarin专家应用程序

    Before we start writing our UI code, there is still that shortcut we took above with loading the information out of a file. While this works great when using a script in the mobile world, this means we have to pack that HTML doc into the app. There are two approaches: either put it into the Assets folder on Android and in the Resources folder (you can also use XCAssets…) on iOS or make an Embedded Resource in the .Net Standard library. While the first option would be what Apple and Google intended you to use when adding docs, you want to ship with your app. You will have to jump through some hoops to access the document. So let’s again save some time and just pack the file as an Embedded Resource in our .Net Standard project. Embedded Resources are packed into your apps binary. This results in an awkward fashion of accessing the data. While described in the official docs 这里,这是它在Xamarin专家日会议应用程序中实现的方式(我们需要更短的姓名):

    let loadFile filename =
        let assembly = IntrospectionExtensions.GetTypeInfo(typedefof).Assembly;
        let stream = assembly.GetManifestResourceStream(filename);
        use streamReader = new StreamReader(stream)
        streamReader.ReadToEnd()
    

    随着这种方式。让我们创建一个与标题,时间和房间的所有会谈的列表。选择轨道时,我们将显示演示者信息的演示信息。

    所以我们可以像这样放在一起的列表:

    let showTrackCell track =
        View.ViewCell( view =
            View.StackLayout(children = [
                View.Label (text = track.Title, 
                            fontSize = FontSize 22.)
                View.Label (text = track.Time + " in " + track.Room, 
                            fontSize = FontSize 14.,
                            fontAttributes = FontAttributes.Italic)
                ]))
    
    let view (model: Model) dispatch =
    
        View.ContentPage(
            content = match model.SelectedTrack with 
                        | Some track -> showTrackInfo track model dispatch
                        | None -> View.ListView(
                                        rowHeight = 80,
                                        hasUnevenRows = true,
                                        margin = Thickness(8.,0.,0.,0.),
                                        items = (model.Tracks |> List.map showTrackCell),
                                        selectionMode = ListViewSelectionMode.Single,
                                        itemSelected = (fun args -> dispatch (TrackSelected args))
                                        )
            )
    

    和œ尾视图是这样做的:

    let showTrackInfo track (model:Model) dispatch =
        let speaker = match track.SpeakerId with
                      | Some speakerId -> model.Speakers |> Seq.tryPick(fun s -> if s.Id = speakerId then Some s else None)
                      | None -> None
    
        let addSpeakerInfo (speaker:Speaker) =
            View.StackLayout(margin = Thickness(0.,32.,0.,0.), children = [
                    View.Label (text = "Speaker", fontSize = FontSize 22. )
                    View.Image (source = (Image.Path speaker.Photo))
                    View.Label (text = "Presenter: " + speaker.Name)
                    View.Label (text = "Tagline: " + speaker.Tagline)
                ])
            
        let speakerViewElements = match (speaker |> Option.map addSpeakerInfo) with
                                  | Some speakerInfo -> speakerInfo
                                  | None -> View.Label(text = "Brought to you by the Organizers");
    
        View.Grid (margin = Thickness(8.,8.,8.,16.),
                    rowdefs = [Star; Auto],
                    children = [
                        View.StackLayout(children = [
                            View.Label (text = track.Title, fontSize = FontSize 22.)
                            View.Label (text = "In: " + track.Room, fontSize = FontSize 14.)
                            View.Label (text = "At: " + track.Time, fontSize = FontSize 14., margin = Thickness(0.,-4.,0.,0.))
                            speakerViewElements
                            ])
                        (View.Button (text = "Back", command = (fun () -> dispatch (TrackSelected None)))).Row(1)
                    ])
    

    您可能已经注意到缺少谈话描述。该网站有一个JavaScript函数检索该附加信息。我认为可以将JavaScript调用复制到后端,然后通过答案解析

    JSON / HTML答案。但是插入leame-stallate - 为什么为-i-am-not-lazy-jother。

    该应用程序仍然感觉有点皱纹,我认为我可能不得不在另一个博客帖子中跟进并制作它漂亮ğÿ〜

    结论

    在这个小实验中,我们开始看看是否有可能编写一个移动应用程序,该应用程序显示与网站上已存在的相同信息。虽然道路上有一些颠簸 - 我正在看着你的JavaScript。为在Android和iOS上运行的Xamarin专家日来编写一个应用程序。

    虽然我真的应该在我的下一篇文章上开始,并使应用程序漂亮ğÿ〜‡

    您可以查看整个应用程序GitHub..

    这篇文章是THEF#Advent日历的一部分。务必检查另一个帖子.

    Hth.

    更新: