Is there a decent drop down menu for Nativescript 7?

Viewed 309

I'm looking for an open source NS7 replacement for the nativescript-drop-down menu. Does anyone have suggestions. I'm porting my app to NS7 and having trouble finding a replacement for what has gone to a paid version of the plugin.

1 Answers

My workaround was to open a modal that has the dropdown items and then receiving the chosen item in the closeCallback function, like so:

dropdown-modal.xml

<Page ios:class="bg-light" xmlns="http://schemas.nativescript.org/tns.xsd" shownModally="onShownModally">
    <StackLayout class="modal-view">
        <Label android:class="p-l-10 font-weight-normal font-size-larger" ios:class="h2 font-weight-bold p-l-15 p-t-10" text="{{ title }}"></Label>
        <Label class="hr"></Label>
        <ScrollView class="page" height="40%">
            <ListView class="list-group" itemTap="{{ onItemTap }}" items="{{ data }}">
                <ListView.itemTemplate>
                    <Label android:class="h4 text-center font-weight-bold p-b-10" ios:class="h3 text-center font-weight-bold p-y-10" width="100%" text="{{ value }}" 
                            textWrap="true"></Label>
                </ListView.itemTemplate>
            </ListView>
        </ScrollView>
    </StackLayout>
</Page>

Notice the difference between Android and iOS classes. This makes the dropdown more system-like.

dropdown-modal.js

import { DropDownViewModel } from "./dropdown-view-model";

export const onShownModally = function(args) {
    const dropDownViewModel = new DropDownViewModel(args.context.title, args.context.list, args.closeCallback);
    const page = args.object;
    page.bindingContext = dropDownViewModel;
};

dropdown-view-model.js

import { Observable, ObservableArray } from "@nativescript/core";

export class DropDownViewModel extends Observable {
    constructor(title, items, closeCallback) {
        super();
        this.title = title;
        this.data = new ObservableArray(items);
        this.selectedItem = '';
        this.closeCallback = closeCallback;
    }

    onItemTap(args) {
        this.selectedItem = args.view.bindingContext;
        this.closeCallback(this.selectedItem);
    }
}

And this is how it is called in another page.

sample-page.xml

<!--Roles-->
<StackLayout class="m-y-10 m-x-2" row="2" col="1">
    <Label class="far h3 p-l-15 text-black m-b-1" text="&#xf507;  Role" textWrap="true" />
    <TextField editable="false" tap="{{ toggleDropdown }}" dataListId="roles" dataName="role" dataTitle="Role" class="h4 fal text-black input-border-rounded-lg" text="{{ role.value }}" />
</StackLayout>

Notice how dataListId, dataName, and dataTitle were passed.

sample-page.js

import { SamplePageViewModel } from "./sample-page-view-model";

const samplePageViewModel = new SamplePageViewModel();

export const onNavigatingTo = async function(args) {
    await samplePageViewModel.populateRolesList();
};

export const onNavigatedTo = async function(args) {
    const page = args.object;
    page.bindingContext = samplePageViewModel;
};

sample-page-view-model.js

import { Frame, Observable } from "@nativescript/core";
import { LookupService } from "~/services/lookupService";
import { AppSettingsUtility } from "~/utilities/appSettingsUtility";

export class SamplePageViewModel extends Observable {
    constructor() {
        super();
        this.lookupService      = new LookupService();
        this.appSettingsUtility = new AppSettingsUtility();
        this.routes             = this.appSettingsUtility.getRoutes();
        this.roles              = [];
        this.role               = { code: 0, value: '' };
    }

    async populateRolesList() {
        this.set('roles', await this.lookupService.getRoles()); // assume roles list
    }

    toggleDropdown(args) {
        const page = args.object.page;
        const that = this;
        const options = {
            context: { title: args.object.dataTitle, list: that.get(args.object.dataListId) },
            closeCallback: (selectedItem) => {
                if(selectedItem)
                    that.set(args.object.dataName, selectedItem);
            }
        };
        page.showModal(this.routes.dropdown, options);
    }


}
Related