Saturday 17 November 2012

Implementing Multi select component using Palette in Tapestry5

Yes it's tough if you try to implement Palette  or multi select component in Tapestry5. I learned it the hard way, so thought of sharing it.
The example provided by Tapestry is using enum object. Practically we may need to create Select lists for any type of Object.  Here find some sample code of one implementation for a users list



First the AssociateUsers.tml



Page Bean

@AuthorizePage
public class AssociateUsers extends SessionContextPageImpl {

    @Inject
    private ComponentResources resources;
    private int selectedButton;
    @Persist
    private List users;
    @Persist
    private List initialAssociatedusers;
    @Inject
    private Messages messages;
    @Inject
    private TypeCoercer typeCoercer;
    @Inject
    private SelectModelFactory selectModelFactory;
    @Property
    @Persist
    private int staffUserId;
    @Property
    @Persist
    private SelectModel userSelectModel;
    @Inject
    private FriendManager frndMgr;
    @Inject
    private UserManager userMgr;
    @InjectPage
    private UserDashboard userDashboard;
    @Property
    private FullUserProfile userProfile;
    public static final int SELECTED_BUTTON_CANCEL = -1;
    public static final int SELECTED_BUTTON_SUBMIT = 1;

    @Override
    public void onActivate() throws Exception {
        super.onActivate();
        super.canLoadPage(this.getClass());
    }

    public void onActivate(int staffUserId) throws Exception {
        this.staffUserId = staffUserId;
        userProfile = userMgr.getUserProfileOfInvestor(staffUserId);
        setupRender();
    }

    public List getUsers() {
        return users;
    }

    public void setUsers(List selected) {
        users = selected;
    }

    public SelectModel getUserModel() throws Exception {
        return userSelectModel;
    }

    public void onSelectedFromCancel() {
        selectedButton = SELECTED_BUTTON_CANCEL;

    }

    void setupRender() throws Exception {
        users = new ArrayList();
        initialAssociatedusers = frndMgr.getPeopleListMutualFollowersForUser(staffUserId, getCurrentFirmId());
        // invoke service to find all users in the firm
        List usersSelectlist = frndMgr.getPeopleListByFirmId(getCurrentFirmId());
        //add alredy associated users to list
        for (FriendProfile profile : usersSelectlist) {
            if (initialAssociatedusers.contains(profile)) {
                users.add(profile);
            }
        }
        //remove present Assign user from list
            for (FriendProfile profile : usersSelectlist) {
                if (profile.getUserId() == staffUserId) {
                    usersSelectlist.remove(profile);
                    break;
                }
            }
        // create a SelectModel from my list of users
        userSelectModel = selectModelFactory.create(usersSelectlist, "FullName");
    }

    @SuppressWarnings("unchecked")
    public UserValueEncoder getUserEncoder() {
        return new UserValueEncoder(typeCoercer, FriendProfile.class, frndMgr);
    }

    public Object onSubmitFromAssociateUsers() throws Exception {
        switch (selectedButton) {
            case SELECTED_BUTTON_CANCEL:
                return userDashboard;
            default:
               //your code based on the selected users
...........................................
                return userDashboard;

        }


    }
}


New Encoder class for your object. In my case it's a user object


public class UserValueEncoder implements ValueEncoder, ValueEncoderFactory{

    @Inject
    private FriendManager friendMngr;

    @Override
    public String toClient(FriendProfile user) {
        return String.valueOf(user.getUserId());
    }

    public UserValueEncoder(TypeCoercer typeCoerce, Class obj, FriendManager friendMngr) {
        this.friendMngr = friendMngr;
    }

    @Override
    public FriendProfile toValue(String id) {
        try {
            return friendMngr.getFriendProfileByUserId(Integer.parseInt(id));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public ValueEncoder create(Class type) {
        return this;
    }

}

App Module changes


 public static void contributeValueEncoderSource(MappedConfiguration                                                    ValueEncoderFactory> configuration) {
        configuration.addInstance(User.class, UserValueEncoder.class);
    }


If you have any questions...please post it as comments.

Ajith.