Dynamic Search?


#1

So, basically I am working on an app for a basketball league. In this part of the app, I am allowing the user to be able to search. I am currently putting in 2 parameters for the search, a button to select which State, and another for which role (Player, coach, etc) they are searching for. I will probably add searching by name later.

I am running into 2 issues. First, when the search does the GET call to my API, it pulls in the JSON correctly, but for some reason it is only adding the first entry of the json into my search array in the app. I have done this before, but for some odd reason its not working here.

Also, even with it adding in that first entry into the search array in the app, its not showing up on the UI. Am I missing something to make it update as it searches? I hope these questions make sense.

Here is Javascript code for the search call, and the UI code (sorry if the code doesnt copy in correctly.):

Javascript:

roleSelected.onValueChanged(module, search);
stateSelected.onValueChanged(module, search);

function search() {
var url = encodeURI(Backend.rootURL + stateSelected.value + “/” + roleSelected.value + “.json”);

console.log(url)

fetch(url).then(function(response) {
    console.log(JSON.stringify(response));
    return response.json();
}).then(function(json) {

    var searchJSON = json;
    var keys = Object.keys(searchJSON);
    keys.forEach(function(x) {
        searchResults.add(new Result(searchJSON[x]));
        console.log(JSON.stringify(searchResults.value));
    });

    function Result(data) {
        this.name = data.name;
        this.email = data.email;
    }

});

// console.log(JSON.stringify(searchResults.value));

};

UX:

<ScrollView>

<StackPanel>

<Text Alignment=“HorizontalCenter” FontSize=“30” Margin=“0,20,0,20” >Search</Text>

<CBL.SearchDropdown ux:Name=“stateDropDown” Items="{states}" Value="{stateSelected}" Width=“40” Margin=“10” />

<CBL.SearchDropdown ux:Name=“roleDropDown” Items="{roles}" Value="{roleSelected}" Width=“120” Margin=“10” />

<Each Items="{searchResults}" >

<Separator Margin=“0,10,0,0” />

<Panel HitTestMode=“LocalBoundsAndChildren” Clicked="{listClick}">

<CBL.Text Value="{name}" Margin=“20” />

<WhilePressed>

<Scale Factor=".95" Duration=".08" Easing=“QuadraticOut” />

</WhilePressed>

</Panel>

</Each>

<Separator />

</StackPanel>

</ScrollView>


#2

Hi Caleb,

I have dynamic search working (albeit for a single search attribute). But essentially what I did was separate the fetch from the actual search.

Here’s the fetch:

		var Observable = require("FuseJS/Observable");
		var posts = Observable();
		var errorMessage = Observable();
		var searchString = Observable("");

		fetch("https://api.myjson.com/bins/1hgv0w")
			.then(function(result) {
				if (result.status !== 200) {
					debug_log("Something went wrong, status code: " + result.status);
					errorMessage.value = "Oh noes! :(";
					return;
				}

				//throw "Some error";

				return result.json();
			}).then(function(data) {
				debug_log("Success!");

				// If you want all posts comments out if statement and just use line below
				//posts.replaceAll(data);
			
				for (var i = 0; i < 11; i++) {
					var item = data[i];
					console.log("item.postedby = " + item.postedby);
					switch (item.postedby) {
							case "@timmy":
							case "@marie":
							case "@yeswilliamsburg":
							posts.add(item);

					
					}
					/*
					if (item.postedby == "@timmy" || item.postedby == "@marie") {
							posts.add(item);
					}*/
					
				}
				
			}).catch(function(error) {
				debug_log("Fetch error " + error);
				errorMessage.value = "Oh noes! :(";
			});

#3

So essentially I am building an array (in this case any posts with a set of dummy user names - this way I can hook it to a real auth later).

Then the second thing I do is once I have the posts I want I have a separate function that is called when someone starts typing into the search field (which is an observable). So in effect I am passing “posts” array into a function which then gets called as someone started typing and I use “contains” so that as you type it continues to filter down…

            function stringContainsString(main,filter){
                return main.toLowerCase().indexOf(filter.toLowerCase()) != -1;
            }
            
            var filteredPlaces = posts.where(function(e){
                return Observable(function() {
                    return  stringContainsString(e.neighborhood, searchString.value);
                    
                });
            });

#4

Function stringContainsString is so I convert everything to lowercase. Then what happens is instead of using “Posts” in my loop in the UX I used filteredPlaces. Like below.

	<placelist.Search />
		<StackPanel Margin="0,110,0,0" Width="97%" ClipToBounds="true" Alignment="TopCenter">
			<!-- THIS SECTION IS FOR SEARCH //-->

						<Each Items="{filteredPlaces}" ux:Name="Places">

You’ll notice I made Search a component so I can simply inject it into any view:

	 <DockPanel Margin="0,55,0,0" Width="100%" Alignment="TopCenter" Color="#E9F0F0">
				 <TextInput Value="{searchString}" PlaceholderText="Search Neighborhood" Font="OpenSansLt" Alignment="Center" PlaceholderColor="#ccc" Height="50" Width="97%" Padding="10" Margin="0,0,0,0" Background="#FFF">
					        <Rectangle Layer="Background" CornerRadius="0">
					            <Stroke Width="0" Brush="#BBB" />
					        </Rectangle>
					        <DropShadow Angle="45" Size="1" Distance="1" Spread="0.1" Color="#ccc" />
				</TextInput>
	</DockPanel>

Here’s a final demo so you can see what it looks like:

http://recordit.co/fkBgk9uZ0n


#5

Thanks a ton! That helps a lot, because I was going to add on a third parameter where they could filter my search with text, like you do here. And I realized why my previous search wasn’t working… I didn’t put my searchResults variable in my exports to be called on the UX page… I’m an idiot haha.


#6

No worries man, yeah there’s a lot to navigate. I’ve only been using Fuse since beg. December. Plus it’s those little things that can just stop it from working.

Hey btw, did you get a dropdown pick list to work? I haven’t figured that out yet.

But happy to help any way I can.


#7

Yeah for sure, heres the component I made

<!-- A simple custom ComboBox. -->

<Panel ux:Class=“CBL.DropdownMenu” HitTestMode=“LocalBoundsAndChildren”>

<object ux:Property=“Items” />

<string ux:Property=“Value” />

<Panel ux:Name=“theOuter” Padding=“10,10,0,10” Height=“40”>

<Rectangle Layer=“Background” CornerRadius=“4”>

<Stroke Width=“1” Color=“Gray” />

</Rectangle>

<Text ux:Name=“theSelected” />

<Tapped>

<Set thePopup.Value=“true” />

</Tapped>

</Panel>

<!-- Note, the ZOrdering of the popup only works here since our page lays out from top-to-bottom.

If you would need to overlap items before the ComboBox (thus above it), you’d need to place

this in an AlternateRoot with a LayoutMaster to this control. -->

<Panel ux:Name=“theList” Visibility=“Hidden” Layer=“Overlay” Padding=“0”>

<StackPanel Alignment=“Top” BoxSizing=“NoImplicitMax”>

<Rectangle ux:Class=“Separator” Height=“1” Fill=“Black” />

<Rectangle Layer=“Background” Color="#D8D8D8" CornerRadius=“4”>

<Stroke Width=“1” Color=“Black”/>

</Rectangle>

<Selection Value="{Property Value}" MinCount=“1” MaxCount=“1” />

<Each Items="{ReadProperty Items}">

<Panel ux:Name=“thePanel” Height=“40” Padding=“10,10,0,10”>

<Selectable Value="{value}" />

<Text Value="{label}" />

<WhileSelected>

<Change thePanel.Color=“Gray” />

<Change Target=“theSelected.Value” Value="{label}" />

</WhileSelected>

<Tapped>

<ToggleSelection />

<Set thePopup.Value=“false” />

</Tapped>

</Panel>

<Separator />

</Each>

</StackPanel>

</Panel>

<WhileTrue ux:Name=“thePopup”>

<Change theList.Visibility=“Visible” />

<Change theOuter.Visibility=“Hidden” />

</WhileTrue>

</Panel>

Here is my using it in UX

<Text Alignment="Left" Opacity=".5">League: *</Text>

<CBL.DropdownMenu ux:Name=“leagueDropdown” Items="{leagues}" Value="{leagueSelected}" Width=“92%” />

And Javascript:

var leagues = { Men: { label: "Men", options: "OtherLeagues" }, Women: { label: "Women", options: "OtherLeagues" }, Youth: { label: "Youth", options: "OtherLeagues" }, };

var leagueSelected = Observable(“Men”);

function mapOptions(opts) {
return Object.keys(opts).map(function(key) {
return { value: key, label: opts[key].label };
});
};

module.exports = {
leagues: mapOptions(leagues),
leagueSelected: leagueSelected,
}

This is the github library that I got it from, then I edited it for my specific usage: https://github.com/fuse-open/fuse-samples/tree/master/Samples/Controls/Selection.

Have you used Base64 to upload an image to a server? That’s what I am stuck on now.


#8

Dope thanks so much.


#9

I haven’t used that to upload an image to a server. The next big thing I have to crack is a searchable map.