angular-restmod icon indicating copy to clipboard operation
angular-restmod copied to clipboard

Add CachedModel plugin

Open iobaixas opened this issue 11 years ago • 22 comments

The cached model plugin should be able to resolve by id or url objects that have already been fetched.

iobaixas avatar Jan 10 '14 20:01 iobaixas

Some sort of caching would be great. Is this the plugin you are referring to porting? https://github.com/scosman/Backbone.CachedModel/blob/master/BackboneLocalCache.js

amcdnl avatar Aug 05 '14 16:08 amcdnl

Not sure if the Backbone api will match $restmod's, but is probable a good starting point for this, specially for stuff related to integration with local storage.

Basically this is how I think the plugin should work:

  • Override the ScopeApi.$find method to look in cache before attempting to fetch from server.
  • Hook to the after-fetch event to store serialized items in cache every time server returns them.

Some doubts:

  • How to handle cached collections?
  • Do we need to generate local temporary keys like backbone does?

iobaixas avatar Aug 08 '14 16:08 iobaixas

@iobaixas off topic but how do you define custom url's? For example how ng-resource allows you to override a specific method with a custom url. Typically all my URLs fall into restful spec but sometimes i have custom fn's or slightly different urls than others.

amcdnl avatar Aug 08 '14 23:08 amcdnl

You can give a single resource instance a different url by building it using the $single method:

var myModelInst =  MyModel.$single('/api/wharever');

If you need to change the url for one of the RESTful actions, then it depends on the action.

  • for GET you should implement the $fetchUrlFor instance method or override the $url() instance method.
  • for UPDATE you should implement the $updateUrlFor instance method or override the $url() instance method.
  • for DELETE you should implement the $destroyUrlFor instance method or override the $url() instance method.
  • for CREATE you should implement the $createUrlFor instance method.

If you are talking about custom actions, then you would need to call $send and build the request yourself, like this:

$restmod.model('/api/restful', {
  notVeryRestful: function() {
    return this.$send({
      url: this.$url() + '/unrestfulAction',
      method: 'POST'
    }).$then(function() {
      // maybe do something with request response...
    ));
  }
});

Hope that helps!

iobaixas avatar Aug 09 '14 03:08 iobaixas

Back on the topic of caching, ngResource has caching built in:

cache – {boolean|Cache} – If true, a default $http cache will be used to cache the GET request, otherwise if a cache instance built with $cacheFactory, this cache will be used for caching.

wonder if its easy just to hook into that?

amcdnl avatar Aug 11 '14 13:08 amcdnl

@iobaixas What about if my request is 'restful' but doesn't adhere quite to your spec. For example:

      findOne: function(formId, id){
            return $http.get('api/group/' + formId + '/' + id);
       }

is there anyway to format the url dynamically like that?

amcdnl avatar Aug 11 '14 17:08 amcdnl

@iobaixas Here is my first cut of it ... Would LOVE feedback ... https://gist.github.com/amcdnl/f14cbfc3738668457ebb

amcdnl avatar Aug 11 '14 20:08 amcdnl

@iobaixas on the notVeryRestful: function() { how would you send it data? I was looking at $encode but it infers the data from the instance. Just JSON.stringify() ?

amcdnl avatar Aug 12 '14 11:08 amcdnl

@iobaixas sorry for all the spam but how could you add a method to the 'class'? You could do something like:

    var model = $restmod.model('api/account');

    model.setPassword = function(val) {
        return this.$send({
            url: this.$url() + '/setpassword',
            method: 'POST',
            data: JSON.stringify(val)
        });
    };

but thats not very 'pretty'. In the past, I've used a pattern where you could pass the 'static' properties on the 'class' as another object. For instance:

    var model = $restmod.model('api/account', { ..Static.. }, { ..Prototype.. });

Thoughts?

amcdnl avatar Aug 12 '14 11:08 amcdnl

You don't need to JSON.stringify $http will handle that for you.

To add a method to a class use '@' at the model definition object:

$restmod.model('url', {
  '@myClassMethod': function() { /* do something */ }
});

Actually that will add the method both to the class and to the model collections.

iobaixas avatar Aug 12 '14 14:08 iobaixas

@amcdnl, about the custom url, you could always override the $url method. Maybe the findOne example if better handled by a hasMany relation?

iobaixas avatar Aug 12 '14 15:08 iobaixas

@iobaixas I updated the gist w/ some updates.

re hasMany: The hasMany would be interesting. Its mainly just a qualifier for my models. Maybe I should reconsider that api.

re stringify: Thats perfect!

amcdnl avatar Aug 12 '14 16:08 amcdnl

@iobaixas 2 more questions ... sorry ...

  1. If I have a model that only returns 1 thing, how do I use restmod to return that? For example: api/settings only returns a object ( has update/delete though). It accepts no params, etc. $find / $search don't seem to be the fit for this. It seems like $single might be the right fit but unsure how to use it.

  2. If I use the @myClassMethod the $send function is unavailable. Example:

        '@loginBanner': function(){
            return this.$send({
                url: this.$url() + '/login-banner',
                method: 'GET'
            });
        }

amcdnl avatar Aug 12 '14 19:08 amcdnl

  1. You should use $single: var oneRecord = Settings.$single('api/settings').$fetch() (fetch is optional).
  2. Thats weird, could you copy here the entire code you are using when defining the model?

iobaixas avatar Aug 12 '14 20:08 iobaixas

The single is a bit odd ... I ended up with something like this ....

module.factory('SettingsModel', function($restmod){
    var model = $restmod.model('api/settings', {        
        "@get": function(){
            return this.$single(this.$url()).$fetch();
        },
        "@loginBanner": function(){
            return this.$send({
                url: this.$url() + '/login-banner',
                method: 'GET'
            });
        }
    });
    return model;
});

and Im using it in a route resolve like:

   $stateProvider.state('settings', {
        url: '/settings',
        templateUrl: 'app/admin/settings/settings.tpl.html',
        controller: 'SettingsCtrl',
        resolve: {
            settings: function(SettingsModel) {
                return SettingsModel.get();
            }
        }
    });

amcdnl avatar Aug 12 '14 22:08 amcdnl

I ended up making it quite a bit simpler ... reason being I needed pointers ( not just cache ) of the items. Any thoughts? https://gist.github.com/amcdnl/f14cbfc3738668457ebb

amcdnl avatar Aug 13 '14 21:08 amcdnl

You should have a way to invalidate the cache as well, or parts of the cache.

kenjiqq avatar Aug 14 '14 17:08 kenjiqq

@iobaixas Above you mention a $send example, that works for 'singles' but not 'collections'. How would I implement something like this for collections

        "@$searchLight": function(){
            return this.$send({
                url: this.$url() + '/light',
                method: 'GET'
            }).$promise;
        }

P.S. - I updated my gist to reduce more code, take a gander when you get a sec.https://gist.github.com/amcdnl/f14cbfc3738668457ebb . The only problem is it returns promises ( yours doesn't ) by default and doesn't delete them.

amcdnl avatar Aug 20 '14 16:08 amcdnl

Interesting plugin I ran across today: https://github.com/ClouDesire/angular-browser-cache-manager

amcdnl avatar Aug 23 '14 16:08 amcdnl

This is interesting: https://github.com/VasilioRuzanni/angular-modelizer and they have cache built in

amcdnl avatar Sep 18 '14 14:09 amcdnl

I am also very interested in a caching mechanism. Are there any current plans to integrate this into restmod?

For now, I have built a simple CacheModel mixin, which is based on the gist by @amcdnl and the corresponding pull request: cachemod. Though, it stills needs to handle updates and deletes. Would this generally be the right approach?

Who would be interested to work on this with me?

cornerman avatar May 26 '15 13:05 cornerman

Maybe I'm missing something, and not purposefully trying to step on toes, but this should be trivial. Provided there is some sort of UoW implemented.

kleijnweb avatar Dec 05 '15 12:12 kleijnweb