2016-03-03 19 views
7

Ho un servizio di base che assomiglia a questo:AngularJS servizio eredità emette

.service('BaseImageService', ['$q', 'ApiHandler', 'UploadService', function ($q, api, uploadService) { 

    // Get our api path 
    var apiPath = 'logos'; 

    // Creates our logo 
    var _createLogo = function (model) { 

     // Handle our uploads 
     return _handleUploads(model).then(function() { 

      // Create our logo 
      return api.post(apiPath, model); 
     }); 
    }; 

    // Edit our logo 
    var _editLogo = function (model) { 

     // Handle our uploads 
     return _handleUploads(model).then(function() { 

      // Create our logo 
      return api.put(apiPath, model); 
     }); 
    }; 

    // Handles our files 
    var _handleUploads = function (model) { 

     // Create a promises array 
     var promises = []; 

     // Get our file 
     var file = model.file, 
      old = model.old; 

     // If we have a file 
     if (file) { 

      // Try to upload the file 
      promises.push(uploadService.upload(model.file).then(function (response) { 

       // Update our model 
       model.path = response.path; 
       model.thumbnail = response.thumbnail; 
      })); 

      // If we have an old model 
      if (old) { 

       // Delete both our files 
       promises.push(uploadService.delete(old.path)); 
       promises.push(uploadService.delete(old.thumbnail)); 
      } 
     } 

     // After all promises have completed 
     return $q.all(promises); 
    }; 

    // Create our service 
    var service = { 

     // Update our api path 
     updateApiPath: function (path) { 

      // Set the api path 
      apiPath = path; 
     }, 

     // Gets a list of logos 
     list: function (t) { 

      if (t) { 
       console.log(apiPath); 
      } 

      // Get our logo 
      return api.get(apiPath); 
     }, 

     // Get a single logo 
     get: function (id) { 

      // Get our logo 
      return api.get(apiPath, { id: id }); 
     }, 

     // Create our logo 
     save: function (model) { 

      // If we are editing 
      if (model.id) { 

       // Edit our logo 
       return _editLogo(model); 

       // If we are creating 
      } else { 

       // Create our logo 
       return _createLogo(model); 
      } 
     }, 

     // Deletes our logo 
     delete: function (id) { 

      // Delete our logo 
      return api.delete(apiPath, { id: id }); 
     }, 

     // Prepare for editing 
     prepareForEditing: function (model) { 

      // Create our old object 
      model.old = { 
       path: model.path, 
       thumbnail: model.thumbnail 
      }; 
     } 
    }; 

    // Return our service 
    return service; 
}]) 

e poi ho un paio di servizi che "ereditano" questo servizio, come questo:

.service('ImageService', ['BaseImageService', function (baseService) { 

    // Get our api path 
    var apiPath = 'images'; 

    // Update the apiPath 
    baseService.updateApiPath(apiPath); 

    // Return our service 
    return baseService; 
}]) 

.service('LogoService', ['BaseImageService', function (baseService) { 

    // Get our api path 
    var apiPath = 'logos'; 

    // Update the apiPath 
    baseService.updateApiPath(apiPath); 

    // Return our service 
    return baseService; 
}]) 

.service('PlayerTextService', ['BaseImageService', function (baseService) { 

    // Get our api path 
    var apiPath = 'playerText'; 

    // Update the apiPath 
    baseService.updateApiPath(apiPath); 

    // Return our service 
    return baseService; 
}]) 

ho pensato che questo stava funzionando bene. Ma ho questa pagina che chiama in sequenza tutti e 3 i servizi (ImageService, LogoService e PlayerTextService). Alla prima vista della pagina va tutto bene, se mi allontano e poi ritorna il servizio di immagini rimuove in realtà le cose dal servizio di testo del giocatore. Ora so che questo è dovuto al fatto che i servizi sono singletons ma non sono sicuro di come risolvere il problema.

Qualcuno può darmi una mano?


Ho aggiunto un codepen con una soluzione tentata:

http://codepen.io/r3plica/pen/ONVBJO


Tentativo 2

http://codepen.io/r3plica/pen/jqPeMQ?editors=1010

+0

Si sta creando fabbriche non 'service' e ogni servizio non dovrebbero restituire il baseService. Non è chiaro cosa stai provando a fare – charlietfl

+0

Converti base in effettivo 'servizio' che è un costruttore che usa' this' per i membri ... quindi restituisci 'nuovo baseService' da altre fabbriche che hai – charlietfl

+0

Puoi modificare la codepen come esempio ? – r3plica

risposta

2

Dopo un sacco di guai; Ho finalmente trovato una soluzione adapting this bit of code

Il mio servizio di base si presenta così:

.factory('BaseImageService', ['$q', 'ApiHandler', 'UploadService', 'vectorExtensions', function ($q, api, uploadService, vectorExtensions) { 

    // Creates our logo 
    var _createLogo = function (model) { 

     // Handle our uploads 
     return _handleUploads(model).then(function() { 

      // Create our logo 
      return api.post(BaseImageService.apiPath, model); 
     }); 
    }; 

    // Edit our logo 
    var _editLogo = function (model) { 

     // Handle our uploads 
     return _handleUploads(model).then(function() { 

      // Create our logo 
      return api.put(BaseImageService.apiPath, model); 
     }); 
    }; 

    // Handles our files 
    var _handleUploads = function (model) { 

     // Create a promises array 
     var promises = []; 

     // Get our file 
     var file = model.file, 
      old = model.old; 

     // If we have a file 
     if (file) { 

      // Try to upload the file 
      promises.push(uploadService.upload(model.file).then(function (response) { 

       // Update our model 
       model.path = response.path; 
       model.thumbnail = response.thumbnail; 
       model.fileName = response.fileName; 
      })); 

      // If we have an old model 
      if (old) { 

       // Delete both our files 
       promises.push(uploadService.delete(old.path)); 
       promises.push(uploadService.delete(old.thumbnail)); 
      } 
     } 

     // After all promises have completed 
     return $q.all(promises); 
    }; 

    // Addes a property to the image array to state if they are vector images or not 
    var _addVectorProperties = function (images) { 

     // Loop through our images 
     for (var i = 0; i < images.length; i++) { 

      // Get our current image 
      var image = _addVectorProperty(images[i]); 
     } 

     // Return our images 
     return images; 
    }; 

    // Adds a property to the image to state if it is vector or not 
    var _addVectorProperty = function (image) { 

     // Vector flag 
     var vector = false; 

     // Get our file extension 
     var parts = image.path.split('.'); 

     // If we have any parts 
     if (parts.length) { 

      // Get our last part 
      var ext = parts[parts.length - 1], 
       index = vectorExtensions.indexOf(ext); 

      // If our extension exists in our vector array 
      if (index > -1) { 

       // Change our vector property 
       vector = true; 
      } 
     } 

     // Update our image with the new property 
     image.vector = vector; 

     // Return our image 
     return image; 
    }; 

    // Create our service 
    var BaseImageService = function (path) { 

     // Set our apiPath 
     this.apiPath = path; 

     // Update our api path 
     this.updateApiPath = function (path) { 

      // Set the api path 
      apiPath = path; 
     }; 

     // Gets a list of logos 
     this.list = function() { 

      // Get our logo 
      return api.get(this.apiPath).then(_addVectorProperties); 
     }; 

     // Get a single logo 
     this.get = function (id) { 

      // Get our logo 
      return api.get(this.apiPath, { id: id }).then(_addVectorProperty); 
     }; 

     // Create our logo 
     this.save = function (model) { 

      // If we are editing 
      if (model.id) { 

       // Edit our logo 
       return _editLogo(model); 

       // If we are creating 
      } else { 

       // Create our logo 
       return _createLogo(model); 
      } 
     }; 

     // Deletes our logo 
     this.delete = function (id) { 

      // Delete our logo 
      return api.delete(this.apiPath, { id: id }); 
     }; 

     // Set our active image 
     this.setActive = function (images, image) { 

      // Loop through our images 
      for (var i = 0; i < images.length; i++) { 

       // Get our current image 
       var current = images[i]; 

       // Set whether we are active or not 
       current.active = image.id === current.id ? true : false; 
      } 
     }; 

     // Prepare for editing 
     this.prepareForEditing = function (model) { 

      // Create our old object 
      model.old = { 
       path: model.path, 
       thumbnail: model.thumbnail 
      }; 
     }; 
    }; 

    // Return our service 
    return BaseImageService; 
}]) 

e il bambino servizi simile a questa:

.service('ImageService', ['BaseImageService', function (baseService) { 

    // Create our base service 
    var child = new baseService('images'); 

    // Return our new service 
    return child; 
}]) 

.service('LogoService', ['BaseImageService', function (baseService) { 

    // Create our base service 
    var child = new baseService('logos'); 

    // Return our new service 
    return child; 
}]) 

.service('PlayerTextService', ['BaseImageService', function (baseService) { 

    // Create our base service 
    var child = new baseService('playerText'); 

    // Return our new service 
    return child; 
}]) 

che funziona bene.

+0

Ricordati che con questa versione in qualche modo ignori il meccanismo di dipendenza dipendenza rendendolo molto più difficile testare i controller perché non è più possibile iniettare qualche "servizio figlio" modificato durante l'esecuzione del test ... (solo se si prende in giro l'intero servizio principale) –

2

La soluzione si è tentato non funzionaperché BaseService è un singleton. Quindi si inserisce esattamente la stessa istanza in tutte e tre le funzioni di registrazione del servizio e tutte configurano lo stesso oggetto. Quindi fondamentalmente l'ultimo vince.

Sembra che si desideri avere servizi separati con diverse configurazioni. Questo è ciò per cui i fornitori sono usati. Consentono un processo in due fasi di creazione di un'istanza di servizio. Si prega di vedere questo grande risposta StackOverflow sul tema:

AngularJS: Service vs provider vs factory

Per riferimento, Restangular è una libreria che ha bisogno di ottenere esattamente lo stesso come si desidera. Si potrebbe utilizzare questo come un modello e vedere come Restangular gestisce questo requisito:

https://github.com/mgonto/restangular#how-to-create-a-restangular-service-with-a-different-configuration-from-the-global-one

Si prega di essere consapevole del fatto che questi concetti si basano su AngularJS 1 ed è necessario gestire in modo diverso quando si desidera utilizzare AngularJS 2 tardi sopra.

0

Sì, acclude perché è singleton. Devi eseguire l'ereditarietà se vuoi farlo.

Ecco un codice che io uso e aggiungere al angolare:

angular.baseResourceServiceMaker = function(service){ 
    return ['$injector', '$resource', 'TypeService', '$http', '_', 'BackEndpoint', 
     function($injector, $resource,TypeService, $http, _, BackEndpoint){ 

    //skipping not interesting code 
    // sample fields to inherits 
    this.sample = "test"; 
    this.sampleFN = function(){[...]} 
    // THE line that does the magic 
    $injector.invoke(service, this); 
} 

Ora tempo di utilizzo

.service('MyService',angular.baseResourceServiceMaker(['$http', function($http){ 
    // overriding fields 
    this.sample="inherits"; 
this.sampleFN = function(){[...]} 
}])); 

Quindi, in pratica quello che abbiamo qui? Una funzione baseResourceServiceMaker che rappresenta un servizio generico particolare. Il $ injector che chiama il servizio che vogliamo istanziare e impostare l'ambito al servizio generico, in modo che questo sulla classe figlio si legherà allo stesso riferimento rispetto alla classe generica. Il servizio generico verrà istanziato tutte le volte che lo chiami, nessun confitto.

Personalmente utilizzo questo codice per il modulo di risorse di angolare per definire alcuni metodi di base con un serializzatore/deserializzatore personalizzato rispetto alle date di handle e altre cose. Nel tuo caso, baseResourceServiceMaker sarà il tuo baseImageService che termina con $ injector.invoke (service, this).

EDIT: trovato un legame con qualcosa di probabilmente più pulito: AngularJS service inheritance

+0

Ho provato tutti quelli in quel collegamento che hai fornito, ma nessuno di loro ha funzionato. Darò il tuo tentativo e ti farò sapere cosa succede. – r3plica

+0

se faccio angular.baseService Ricevo un errore che afferma che angular.baseService non è una funzione – r3plica

+0

puoi creare una codepen o qualcosa del genere? Non riesco a far funzionare tutto questo allo – r3plica

0

Diventa molto semplice se si utilizza (o si passa a) ES6 o TypeScript.

export class Base { 
    // . . . 
} 

Poi:

import {app} from '../app'; 
import {Base} from './base'; 

export class Child extends Base { 
    // . . . 
} 

app.service('child', Child); 
0

Dal tuo Tentativo 1:

  1. tuo BaseService in cerca di apiPath a livello globale,
  2. mentre angolare dipendenza preparare la tua ultima dipendenza è ImageService,
  3. prima preparerà il dep endency allora eseguirà list() metodo,
  4. tutti i modi la vostra apiPath farà riferimento il valore dichiarato livello globale, cioè apiPath = 'images'; dalla 2

Soluzione: utilizzare this operatore di fronte a apiPath sotto BaseService e list().

lavoro Plunker