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

TitleImage.

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

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

获取数据

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

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

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

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

HtmlDocument.Parse("ExpertXamarin.html")

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

<li data-speakerid="df2bc5ca-5a6b-48a9-87ac-71c817d7b240" class="sz-speaker sz-speaker--compact ">
<div class="sz-speaker__photo">
  <a href="#" onclick="return ...');">
    <img src="...894db1.jpg">
  </a>
</div>
<h3 class="sz-speaker__name">
  <a href="#" onclick="return ...');">Mark Allibone</a>
</h3>
<h4 class="sz-speaker__tagline">Lead Mobile Developer Rey Automation, Microsoft MVP</h4>
</li>

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

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

创建记录并不严格必要。但它确实使数据稍后更轻松地使用数据。另一个优势是我们可以在.NET标准库中胶囊键入提供bet188地址代码,然后使用非F#.NET代码共享。否,您无法直接从C#访问类型提供bet188地址数据类型,而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) })

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

神话般的Xamarin专家应用bet188地址

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专家日会议应用bet188地址中实现的方式(我们需要更短的名称......):

let loadFile filename =
    let assembly = IntrospectionExtensions.GetTypeInfo(typedefof<Model>).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。

该应用bet188地址仍然感觉有点皱纹,我认为我可能不得不在另一个博客帖子中跟进并制作它 漂亮的 -

结论

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

虽然我真的应该开始我的下一篇文章并使应用bet188地址漂亮 -

您可以查看整个应用bet188地址 GitHub..

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

Hth.

Updated: