jasonkneen / RESTe

A simple JavaScript REST / API helper for Titanium

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

dynamic dataCollection with RESTe

adriendillens opened this issue · comments

Hi everyone and @jasonkneen !

I have a simple question :
How to have dynamic dataCollection with RESTe ?

Before RESTe implementation, i tried to make my components re-usable (it works very well). For example, i have TabGroup > Tab > View like followings :

friendList/TabGroup.xml :

<Alloy>
  <TabGroup id="tabGroup" title="Mes Amis">
    <Require src="friendsList/tab" ref="now" subTitle="now"/>
    <Require src="friendsList/tab" ref="evening" subTitle="afternoon"/>
    <Require src="friendsList/tab" ref="night" subTitle="night"/>
  </TabGroup>
</Alloy>

friendList/Tab.xml :

<Alloy>
  <Tab id="tab">
    <Window id="friendsList" class="container" fullscreen="false" navBarHidden="false" exitOnClose="true">
      <Drawer id="drawer" module="com.tripvi.drawerlayout" />
    </Window>
  </Tab>
</Alloy>

friendList/View.xml :

<Alloy>
  <Collection src="user" instance="true" id="collection" />
  <ListView id="listView" class="defaultListView" searchView="searchView" searchText="" caseInsensitiveSearch="true" searchAsChild="false" defaultItemTemplate="friendsList/template" onItemclick="itemClickFunction">
    <SearchView id="searchView" iconifiedByDefault="false" hintText="Recherchez un nom ou prénom" softKeyboardOnFocus="Titanium.UI.Android.SOFT_KEYBOARD_HIDE_ON_FOCUS" ns="Ti.UI.Android" platform="android" />
    <Templates>
      <Require src="friendsList/template" id="template"/>
    </Templates>
    <ListSection name="elements" dataCollection="$.collection" dataTransform="transformUsers">
      <ListItem template="friendsList/template" photo:image="{photo}" fullName:text="{fullName}" nickName:text="{nickName}" data="{data}" searchableText="{fullName}"/>
    </ListSection>
  </ListView>
</Alloy>

And in View.js controller, i dynamically populate collection based on ref's param provided by Tab's parent :

// Retrieve request parameters (provided by Tab's ref)
var ref = arguments[0] || {};

// Switch on ref value for populate Collection dynamically
switch (ref) {
    case "now":
        $.collection.reset(Alloy.Globals.user.get("friendsListNow"));
        break;
    case "evening":
        $.collection.reset(Alloy.Globals.user.get("friendsListEvening"));
        break;
    case "night":
        $.collection.reset(Alloy.Globals.user.get("friendsListNight"));
        break;
    default:
        $.collection.reset(Alloy.Globals.user.get("friendsList"));
        break;
}

=> This way, I have only 1 View, and 1 Controller (instead of 3 Views and 3 Controllers).

But RESTe don't allow us to use Collections / Models XML tags.
RESTe only allow us to use dataCollection, which can not be dynamic (tell me if i'm wrong ?).

So, if i can't find a solution, i probably will not implement RESTe in my project.
For instance, the fact of not being able to use Alloy collection tags, and models represent too many disadvantages for me (even if the REST implementation is very useful and convenient).

Thank you for help, will be appreciated :-)

Collection and Model tags are Alloy implementations and not required with RESTe.

Remove them and create dynamic collections on the fly with the reste.createCollection method. I use it all the time.

Also, your required views should ideally not include the Tab Tag. Doing that makes them only reusable in TabGroup. Put the Tab in the TabGroup and require just the window and your views are truly reusable as windows.

(really this UI should be implemented with a single list, tabbed bar for filtering rather than required tabs -- then it would be even less code)

@jasonkneen Thanks for your answer.

Ok, so i have remove all Alloy Collection and Models Tags.
I will create dynamic collections on the fly with reste.createCollection.

If i understand, you recommend me to have only one collection that i will reset (filter) every time a tab become active (triggers by TabGroup's focus event) ?

And what do you mean by "tabbed bar for filtering rather than required tabs" ?
-> We talk about the same UI component : a Tab, right ?
-> Or maybe use hacky horizontal ScrollView component instead ?

Can you provide me a piece of code for demonstrate the way to accomplish this ?

Note : I develop for Android (ftm), but i will develop for iOS too later. A generic solution would be better.

Thank you so much ! ;-)

Yep -- will dig out some code you can use where I've re-used the same table/list view before with dynamic collections -- also on the tabbed bar -- just flown into Atlanta this weekend bur around today and will look at it.

Ok, i really appreciate your help @jasonkneen.
This could surely be used by other developers.

So, re the tabbed bar -- you could do this a couple of ways:

  1. You trap the onClick on the TabbedBar, determine the one selected and do a new fetch on a single friends list collection, passing the type through -- your RESTe config methods would then return the appropriate list -- this means it refreshes each time.

  2. You get the whole friends list in one collection, trap the onClick to work out what they clicked, and use the dataFilter attribute of the repeater to specify a function to handle the filtering. The filtering function would check the model to see if it matches, and if so return true (show it) or false (ignore it).

With option 2 you're fetching the whole list, and not refreshing unless forced -- the filtering is happening on the client.

With option 1 you're only fetching the list you want, and it's being refreshed from the server.

ok.
the option 2 is more appropriate, because, like you say, data is refreshed only on client side.
So there are fewer database calls.

But :

  • I must change my data returned by the server (add new properties that will be checked by dataFilter function) OR change my data on client side.
  • dataFilter function is only triggers once, at iteration ?
  • I don't know if i can get a reference on the selected tab (since TabGroup receive focus event)
    i can get the activeTab.

I will try it.
Thanks a lot, I'll keep you informed ;-)

@jasonkneen I'm implementing option 2.

I have implemented dataFilter function -> It works fine !
For the moment, i can't change my data returned by the server.
So, i try to transform the models in my collection by using transform function -> Here is my problem !

I try to understand your code, and apparently, transform function only works on models ?
It doesn't for collections ?

The only way i have make it work is by calling model's transform function locally (like in the readme) :

function transform(model) {
    var m = model.transform(model);
    return m;
}

But it doesn't help me.

There's no way to automatically map the collection with corresponding transform function ? , like this :

    models: [{
      name: "event",
      id: "guid",
      read: "invitations",
      content: "records",
      collections: [{
          name: "invitations",
          content: "records",
          read: "findInvitationsByGuid",
          transform: function(m) {
            m = m.toJSON();
            m.type = "invitation";
            return m;
          },
      }, {
          name: "creations",
          content: "records",
          read: "findCreationsByGuid",
          transform: function(m) {
            m.type = m.toJSON();
            m.test = "creation";
            return m;
          },
      }],
    }],

I tried many things, but I did not get there.
Thanks ..... again ;-)

You don't do transform on collections -- you transform the models in a collection -- you can do this either a) in the RESTe config by specifying a transform in the model config or b) in the controller by having a transform function and specifying this in the XML.

So you need to move the transform function OUR of the collection config in the RESTe config and into the model config so insert a transform property under content and above collections.

@jasonkneen thanks for fast answer !

a) Yes, i have already tried the transform function to the model (in config).
-> It doesn't automatically maps the collection. (but i know i can use it later model.transform...)
b) I have also set the dataTransform function in XML, and use it in the controller.
-> It's too late in the controller for transform the model, i need to do it when fetching the collection.

Is it possible to transform model at collection fetch ? If possible, how ?

add dataTransform="transformFunction" to whatever repeater to you are using e.g tableview etc. -- then the function called transformFunction should be in the controller and takes a model as param.

I'll look at what can be done to do auto-transform

Ok, yes i already use dataTransform on every listView.
I also use alloy binding in my templates (bindId, photo:image, label:text, etc...)

But it's too late to transform models in the controller because dataFilter function is called just before dataTransform !

in dataFilter function, i would like to do :

function filterEvents(collection) {
  if (ref === "invitations") {
    return collection.where({type: "invitation"});
  }
  if (ref === "creations") {
    return collection.where({type: "creation"});
  }
}

Sadly, if i do this in my dataTransform function :

function transformEvents(event) {
  var m = event.transform(event); 
  // in the config transform function of the model i do : m.type = "invitation" OR m.type="creation"
  return m;
}

But It's too late.

The auto-transform would be an awesome feature since you allow different collections on same model.
If auto-transform can be done on every collection, independently, this would open new possibilities.

I do not know enough Backbone and Alloy for help you on that, but It would have been with great pleasure ;-)

Thank you man, you do the job :-)

In any case, if you need help or info, I will be there. No problem !