Git Product home page Git Product logo

ngactiveresource's Introduction

ActiveResource for Angular.js

ActiveResource provides a Base class to make modelling with Angular easier. It provides associations, caching, API integration, validations, and Active Record pattern persistence methods.

Installation:

Download

With Bower

If you have bower install just run

bower install ngActiveResource --save-dev

Manually

To manually download head over to the latest release and hit the big 'source code' button.

Install

Once downloaded you'll find everything you need in the /dist directory, include either file and add it as a dependency in your module.

angular.module('app', ['ActiveResource']);

Note: Don't forget to include lodash.js before active-resource.js.

Installation:

In your bower.json:

"ngActiveResource": "latest"

Simple:

Say you want a form to add comments to a post:

<form ng-submit="comment.$save()">
    <input ng-model="comment.text">
    <input type="submit">
</form>

<div ng-repeat="comment in post.comments">
    {{comment.text}}
</div>

In your controller, all you have to setup is something like this:

Post.find(postId).then(function(response) {
    $scope.post      = response;
    $scope.comment   = $scope.post.comments.new();

    Comment.after('$save', function() {
        $scope.comment = $scope.post.comments.new();
    });
};

You don't even have to tell ng-repeat to load in the new comment. The new comment will already be added to the post by association. Simply tell the comment object on the scope to clear, so that the user can enter another comment.

Writing a Model:

Create an Angular factory or provider that relies on ActiveResource:

angular.module('app', ['ActiveResource'])
    .factory('Post', ['ActiveResource', function(ActiveResource) {

        function Post(data) {
            this.number('id');
            this.string('title');
            this.string('subtitle');

            this.computedProperty('fullTitle', function() {
                return this.title + this.subtitle;
            }, ['title', 'subtitle']);

            this.hasMany('comments');
            this.belongsTo('author');
        };

        Post.inherits(ActiveResource.Base);
        Post.api.set('http://api.faculty.com');
        Post.dependentDestroy('comments');

        return Post;
  });

The model is terse, but gains a lot of functionality from ActiveResource.Base.

It declares a has-many relationship on Comment, allowing it to say things like:

var post    = Post.new({ title: "My First Post" });
var comment = post.comments.new({ text: "Great post!" });

The new comment will be an instance of the class Comment, which will be defined in its own model.

It also declares a belongs-to relationship on Author. This allows it to say things like:

var author     = Author.new();
comment.author = author;
comment.$save().then(function(response) { comment = response; });

This will also cause author.comments to include this instance of Comment.

Post also declares a dependent-destroy relationship on comments, meaning:

Post.$delete().then(function(response) { post = comment = response; });

expect(post).not.toBeDefined();
expect(comment).not.toBeDefined();
expect(Post.find({ title: "My First Post" }).not.toBeDefined();
expect(Comment.find({ text: "Great post!" }).not.toBeDefined();

This means the post and its comments have been deleted both locally and from the database.

The astute reader will notice methods prefaced with $ are interacting with an API. The API calls are established in the model definition under Post.api.set().

Computed Properties:

Following the syntax of Ember.js' computed properties, you can create properties that auto-magically update with or without Angular's two-way binding:

function TShirt() {
    this.number('price');
    
    this.computedProperty('salePrice', function() {
        return this.price - (this.price * 0.2);
    }, 'price');
    
    this.computedProperty('superSalePrice', function() {
        return this.price - this.salePrice;
    }, ['price', 'salePrice']);
}

Establish Associations:

A has many association can be setup by naming the field. If the field name is also the name of the provider that contains the foreign model, this is all you have to say. If the name of the provider is different, you can set it explicitly via the provider option:

this.hasMany('comments', { provider: 'CommentModel' });

Foreign keys will also be intuited. For instance:

this.belongsTo('post');

Expect the model to define a post_id attribute mapping to the primary key of the post to which it belongs. If the foreign key is different, you can set it explicitly:

this.belongsTo('post', { foreign_key: 'my_post_id' });

Any number of options can be set on the association:

this.belongsTo('post', { provider: 'PostModel', foreign_key: 'my_post_id' });

Methods:

ActiveResource adds two types of methods to your models and instances:

  1. API-updating methods. These are prefaced with $, such as $create, $save, $update, and $delete, and are the 'unsafe' methods in a RESTful API (POST, PUT, and DELETE). These methods will call the API using the URLs you've set as ModelName.api.createURL, ModelName.api.updateURL, and ModelName.api.deleteURL. The api.set method sets default API URLs for you, but you can override these defaults by setting them explicitly.

  2. Local-instance creating and finding methods. These include new, find, where, all, and update. new creates a new instance of a model on the client, and update updates that instance without issuing a PUT request. find will attempt to find local instances in the model's client-side cache before issuing a GET request, and where and all will always issue a GET request to ensure it has all instances of a model that match given terms. These are the 'safe' methods in a RESTful API (GET).

Query Interface:

Getting Set Up:

Best case scenario: You have an API that adheres to ActiveResource's RESTful convention. Here's that convention:

HTTP Verb CRUD Path Action Used To
GET Retrieve /users index Display a list of all users, or all users filtered by querystring
GET Retrieve /users/:id show Display a specific user, found by params or querystring
POST Create /users create Create a user
PUT Update /users/:id update Update a specific user
DELETE Destroy /users/:id destroy Delete a specific user

If you do have an API that follows these conventions, hooking it up to ActiveResource is as easy as:

Post.api.set('http://api.faculty.com');

Optionally, you can specify a format for your requests:

Post.api.set('http://api.faculty.com').format('json');

Many APIs are set up to respond with specified data types if they are specified in the URLs. A request like:

Post.find({id: 1});

Will send the request:

http://api.faculty.com/posts/1.json

If you need to override specific URLs:

Post.api.indexURL  = 'http://api.faculty.com/list-all-the-users';
Post.api.showURL   = 'http://api.faculty.com/show-me-user';
Post.api.deleteURL = 'http://api.faculty.com/show-me-user/:param.json';

Parameterized URLS versus Query Strings:

To signal to ActiveResource that you want it to replace parameters in your URL, simply type them following a colon:

Post.api.showURL = /posts/:id
Post.api.showURL = /posts/:_id  // MongoDB

The parameters themselves will be replaced:

Post.find({ id: 1 });
>> http://faculty.api.com/posts/1

If no parameters are not provided or your request utilizes parameters that are not specified in the search URL, then a querystring will be generated:

Post.findURL = 'http://api.faculty.com/posts/:id';
Post.find({author_id: 1});

// 'http://faculty.api.com/posts/?author_id=1'

The indexURL is intended as a search URL. It is not expected to be parameterized (though you can parameterize it). By default, that means it will search using a querystring:

Post.api.set('http://faculty.api.com').format('json');
Post.where({author_id: 1});

// 'http://faculty.api.com/posts.json/?author_id=1'

Find:

Post.find({ title: 'My Great Post' });

find is used to retrieve a single instance of a model. It is a method akin to the show action in a RESTful API. Therefore, it first attempts to use the showURL, and will fall back on the indexURL if a showURL is not defined.

find returns only the first instance it finds. If the instance is already stored into the browser's cache, it will not make a backend request. To force a backend request, you can add the forceGET request option:

Post.find({ title: 'My Great Post' }, { forceGET: true });

By default, find will also eagerly load a single level of associations. If a Post has many comments, and we find a post, then its comments will be loaded as well, but the comments will not load their authors, or other comment-based associations. To load associations' associations, pass the option:

{ overEager: true }

Warning: Over-eager loading is potentially very resource-intensive, and will often pull down sizeable portions of the database.

To lazily load associations (not load even the first level of associations, aka comments in the example above), pass the option:

{ lazy: true }

Let's say you're working with a sort of crummy API. It doesn't have an endpoint to find a single instance of a particular model, or it won't parse a variety of options (like title for our post). Maybe it only parses by id, and you must find the post by title. In that case, hit your index API (the endpoint that returns all instances of a given resource), and pass the option:

{ noInstanceEndpoint: true }

This option will do the parsing on the client-side for you to overcome the gnarly API.

Where:

Similar to the find method, but it will pull all instances matching the given parameters. Where will always query the backend, assuming that it does not have the necessary instances.

where is akin to the index action in a RESTful API, and therefore first attempts to use the indexURL, and will fall back on the showURL if an indexURL is not defined.

Post.where({ author_id: author.id })

All:

Returns all instances. Takes no parameters:

Post.all()

all is just a shorthand for a where request with no search parameters specified. It therefore will use the whereURL, if defined.

Promise-based:

All queries are promise-based:

Post.where({ author_id: author.id }).then(function(response) {
    post = response;
});

Custom Primary Keys

By default, models will assume a primary key field labeled id, but you can set a custom one like so:

function Post(attributes) {}
Post.primaryKey = '_id';

Destroy Dependent Associations

If you want a model to delete certain associated resources when they themselves are deleted, use dependentDestroy:

Post.dependentDestroy('comments');

Now when you destroy a post, any associated comments will also be destroyed.

Serialize/toJSON

The serialize and toJSON methods (aliases of one another) change associations to foreign keys and remove circular references.

Post.serialize()

These methods also take several options:

Post.serialize({ prettyPrint: true })

Prints a formatted JSON string.

Post.serialize({ includeEmptyKeys: true })

Changes instances of null or undefined to empty strings, in the event your backend requires all properties to be sent with values. If you include presence validations on these fields, they will still fail as empty strings, and will not be sent using the built-in methods.

var dummyData = { hi: 'there' };
post.toJSON({ instance: dummyData });

Can tap into ActiveResource's serialization method to serialize arbitrary Javascript objects. If the instance option is not passed, the model instance itself will be serialized.

serialize and toJSON are non-mutating methods. They will not change the instance itself. To save the serialized data as a variable, assign it:

var json = post.serialize();

Write Validations:

Models can describe validations required before data will be persisted successfully:

function User(data) {
    this.name  = data.name;
    this.email = data.email;
    
    this.validates({
        name: { presence: true },
        email: { format: { validates: true, message: 'Must provide a valid email.' } }
    });
});

Validations also work with the Simple Form directive to perform easy form styling.

Helper Methods:

user.$valid
>> false 

user.$invalid
>> true

user.$errors
>> { name: ['Must be defined'] }

user.validate('name')
>> true

user.validateIfErrored('name')
>> true

Usage in Forms:

Helper methods make form validation simple:

<input ng-model="user.name" ng-blur="user.validate('name')">

Displaying errors is equally simple. Errors will only be added for a given field once it's been validated. Validate them one-by-one with directives like ng-blur or ng-change, or validate them all at once by passing no arguments to the validate method:

<div ng-show="user.$errors.name" class="alert alert-warning">{{user.$errors.name}}</div>

The interaction we prefer at Faculty Creative usually looks like this:

<input
    ng-model="user.name"
    ng-blur="user.validate('name')
    ng-change="user.validateIfErrored('name')>

This causes validations to run on blur, and, if errors exist on the field, to run on change, so that as soon as a user corrects an error, the error will disappear immediately.

When a model instance is saved, all validations are automatically run, so errors will appear if the form contains any errors.

You can also clear errors on a given field, or on the whole instance:

<button ng-submit="user.clearErrors()"></button>

<button ng-submit="user.clearErrors('name')"></button>

Presence:

Validates that a user has entered a value:

name: { presence: true }

Required If:

Validates that a user has entered a value if a certain requirement is met:

username: {requiredIf: { requiredIf: emailIsBlank,  message: 'You must enter a username' } }

function emailIsBlank(value, field, instance) {
    return !instance.email || instance.email.length === 0;
}

Absence:

Validates that a field does not have a value:

name: { absence: true }

Length:

Validates using ranges, min, max, or exact length:

username: { length: { in: _.range(1, 20); } },
email:    { length: { min: 5, max: 20 } },
zip:      { length: { is: 5 } }

Format:

Validates several built-in formats, and validates custom formats using the regex key:

zip:   { format: { zip: true   } },
email: { format: { email: true } },
uuid:  { format: { regex: /\d{3}\w{5}/ } } 

Numericality:

Validates that a value can be cast to a number. Can be set to ignore values like commas and hyphens:

zip:    { numericality: { ignore: /[\-]/g } }

Acceptance:

Validates truthiness, as in checkbox acceptance:

termsOfService: { acceptance: true }

Inclusion:

Validates inclusion in a set of terms:

size: { inclusion: { in: ['small', 'medium', 'large'] } }

Exclusion:

Validates exclusion from a set of terms:

size: { exclusion: { from: ['XS', 'XL'] } } 

Confirmation:

Validates that two fields match:

password:             { confirmation: true },
passwordConfirmation: { presence: true }

Validates Association:

If an association must be valid in order for an instance to be validate, use the association validation:

author: { association: 'author' },
comments: { association: 'comments' }

The MIT License (MIT)

Copyright (c) 2013-2014 Brett Shollenberger

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

ngactiveresource's People

Contributors

brettshollenberger avatar jahkeup avatar jcrombez avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ngactiveresource's Issues

One sided Association

Is it possible to create an association from one model to another but leave out the reverse?

For example I have an Analysis and a Frame.
(1) Each Analysis hasOne (or belongsTo a) Frame.
(2) Each Frame hasMany Analyses.

I don't want to create the second association where each Frame hasMany Analyses. Can I accomplish this?

Tidy Up Mission

Hey @brettshollenberger,

I have made some contributions & pull requests regarding the addition of new features and think this may have been a mistake, at least for the time being, because I've been very busy at work and haven't had the chance to follow these up yet. That's not the only reason I think they were a mistake, I also think time would be better spent standardising and polishing off existing features while improving the documentation as well. Anyway, if you don't mind I'm going to put the sideload and custom serializer issues to the side temporarily (anybody else is welcome to work on them). Instead I was thinking a few PRs regarding simplifying code and adding documentation/comments where
neccessary.

What do you think? Feel free to tell me to gtfo, I just feel solidifying existing features is better than adding new ones that could break things. If you're game then maybe we can create a issue listing all the things relating to a tidy up.

Sideload Resources [feature]

Making a http request per resource isn't always desired. Any chance sideloading is on your roadmap?

Take the following data structure for example:

{
  "title": "example post",
  "body": "...",
  "comments": [/* embedded comments */]
}

I can envision two possible apis for this to work.

  1. Automatically match any indexes that match relationship names defined in models.
  2. Have a method on the api module where you define indexes to match.
Model.api.embedable({
  'comments': 'comments'
});

Is this worth investigating further?

Coffeescript Problems

Hey guys,

First off, this library is totally awesome and also badly needed in the angular community. I'm incredibly happy to see it!

Secondly, I have the following coffeescript model:

angular.module('models').
factory 'Account', [\
"ActiveResource"
(ActiveResource) ->
  Account = (data) ->
    @number 'id'
    @string 'phone_number'

    @hasMany 'orders'

  Account.inherits ActiveResource.Base
  Account.api.set('/accounts').format 'json'
  Account
]

Part of the code that makes associations work in your library fails because Account.name returns "" instead of "Account". I can use the class keyword to get the right behavior but that won't survive minification. (I think)

Can you guys support a .className property that gets set explicitly?

For reference here is an example using the name property in your code:

function nameOfBelongsToModel(model) {
  if (!model)
    return;
  if (!model.klass && !model.name)
    return;
  if (!model.klass)
    return model.name.camelize();
  return model.klass.name.camelize();
}

General usage issues

I would love to use this library but I'm have a lot of issues working with it. One of the biggest problems is the inconsistency between the README and the example code.

For starters, the only dependency mentioned is lodash, yet it also seems to require active-support and async to run, and also downloads meld (though it doesn't appear to require this).

The README also shows a completely different syntax for declaring models from the example code. I'm finding I can get one way to work at runtime but fails at testing and the other works for testing but fails at runtime.

There are a lot of desirable features in this library but right now it's a bit messy to use and that's holding me back from actively working with it. If you have some time please give the docs and examples some love, thanks!

Methods should more closely match RESTFUL endpoint schema

Currently the find methods limitation to a single object and lack of show method hinder proper API integration.

The following API schema is expected:

GET        /resource          Index page, lists all resources, returns array
POST       /resource          Create a new resource
GET        /resource/{id}     Get a single resource by id, returns object
PUT        /resource/{id}     Update a single resource by id
DELETE     /resource/{id}     Delete a single resource by id
GET        /resource/find     Finds many resources that match query params, returns array

However ngActiveResource deviates from this pattern which creates the following limitations:

  • Doesn't provide a show method. It forces the API to be designed to find by id from query params, rather then getting id from the url itself.
  • find(), which you would expect to return an array in some cases, always returns a single object. This limits queries such as posts/?published=true.

This would require refactoring the methods find() and where() which would need to be discussed.

Working directly with cache

What about working and querying on cache explicitly? Sometimes there are needs to work in "offline" mode and then sync data after some period of time.

Update:

  • all query methods should also be available for cache
  • cached.$save should be also available with some flag like $transient=true

add more methods to resources

The following comes to mind, assuming a model called Sensor

Sensor.all(); // alternate way to write Sensor.where({})
Sensor.show(1); // alternate way to write Sensor.find(1) 

What other methods would be good to add?

URL parameter for .hasMany relationships

Is there a way to force the .hasMany() relationship to use URL parameters and not query strings?

My Rest API wants the parameters in the form:

 /API/Incident/Activity/:ProblemID
 which resolves to...
 /API/Incident/Activity/2

I can correctly call the model directly by passing in the correct named parameter, but the automatic association of parent-to-child is always called with the query string like:

 /API/Incident/Activity?problem_id=2

Quick question about headers

Is there currently a way to set custom headers for the API requests?
This is a nice project. I am really hoping I can use it.

.find() response should be more clear if nothing is found

Currently .find() will return a blank model if nothing is found. It would be more ideal if this method returned false, null, an empty array, or something similar.

Currently I've worked around this by using:

// find sensor and redirect if not found
Sensor.find({
    id: sensorId
}).then(function(response) {
    if(!response.id) $redirect('settings_sensors_path');
    $scope.sensor = response;
});

But obviously checking for .id is not ideal.

Root element in json response

Our rails API returns a root element in the response:
{ user: { id: 1, username: "user1" } }

Is there a way to tell ngActiveResource to parse the data correctly?

Using a Serializer & Adapter to interact with custom APIs

I'm currently looking to implement ngActiveResource, but am unable to make it communicate with our API because ours greatly deviates from the conventions assumed in the docs. Which would basically force me to fork the repo and change the code to comply with the structure we're using.

Some kind of Adapter & Serializer base class would be an ideal situation. This would allow developers to write a minimal amount of code to make their APIs work with ngActiveResource. These would essentially be the same concept used in Ember Data, see Adapter & Serializer from the Ember docs.

Custom serializer [Feature]

Hey,

I've been digging through base.js and I can't seem to find anything related to registering a custom serializer.

I'm asking because my model's data is within the data key of the following response example:

// multiple resources
{
  "data": [
    {
      "id": 1,
      ...
    },
    {
      "id": 2,
      ...
    }
  ]
}

// single resource
{
  "data": {
    "id": 1,
    ...
  }
}

Does this feature already exist? If so how can one override the default behaviour? If not I'd be more than happy to help if you think it's possible.

lodash dependency

with util methods like

  • angular.bind
  • angular.copy
  • angular.equals
  • angular.extend
  • angular.forEach
  • angular.fromJson
  • angular.isArray
  • angular.isDate
  • angular.isDefined
  • angular.isElement
  • angular.isFunction
  • angular.isNumber
  • angular.isObject
  • angular.isString
  • angular.isUndefined
  • angular.lowercase
  • angular.toJson
  • angular.uppercase

is there really a need for lodash dep?


don't get me wrong - I ❤️ me some lodash, but i just find it a bit weird to include yet another lib

GET methods params should use $http.get(url, {params: paramObj}) to be more easily interceptable

ngActive GET requests should use the following syntax:

$http.get(url, {params: paramObj})

In the above example we pass in the paramObj as params to the request config. Angular then handles stringifying them and appending the the url. This eliminates the need to do this within ngAactiveResource. More importantly, it gives us access to the params within our interceptors within the request(config) provider as config.params. This allows us to manipulate the params before sending them off - for example we could change the request to POST, delete the params, and add them instead to data.body.

Crazy amounts of errors

I followed the installation instructions and received nothing but errors. Looks like the name to include ngActiveResource is now 'ActiveResource', even though the read me says ngActiveResource. After changing it I am now receiving the error 'has no method 'hyphenate''.

Validation rules gets add multiple times

I found that "addValidations" get call multiple times within the code and for each time a validation function get added which make it to run same function multiple times.

Query String Trailing Slash

If you append a trailing slash to the url like you do in this function:

function appendSlashForQueryString(url) {
  if (url.slice(-1) == '/') return url;
  return url + '/';
};

It seems to prevent developers from using custom query string parameters like so.

Dashboard.api.showURL = 'http://localhost:8000/api/dashboards/:id/?embed=:embed';

Dashboard.find({
  id: $routeParams.id,
  embed: 'widgets,users'
}).then(function (dashboard) {
  // ...
});

Which results in the following http://localhost:8000/api/dashboards/1/?embed=widgets,users/

I can't speak for others but I sometimes need to pass query string parameters to my server, maybe to paginate results or to define what resources to embed. If the trailing slash isn't necessary I can send a pull request your way.

If item is not cached, and get request is made which returns many items, find doesn't consider params and instead returns first item.

Consider the following case:

  1. User accesses 'Sensors.find(1)'
  2. No sensors are cached so ngActiveResource makes a GET request to findURL which is getSensorList.do?id=1.
  3. The API returns (because it's not smart) an array of sensors (all the sensors).
  4. The first item is returned to the user. This is NOT the item with id === 1 because the API didn't recognize that the query params and incorrectly returned the complete list.

In the above case, YES the API should be a bit smarter. However ngActiveResource could be smarter itself and intervene between step 3 and 4:

3a. find() attempts to match from the new cache that was created by the GET request.

Basically, by doing one more check after a GET request which generates a new cache we could provider a more consistent interface.

djson is not defined

I keep getting the following error:

ReferenceError: djson is not defined

Any idea why? Is there a dependency that I haven't included somewhere?

TypeError when setting .hasMany in my models

The "Problem" model (see code below) works when it is stand-alone, but as soon as i attempt to establish a "this.hasMany" relationship with the "Activity" model I get the following error.

  TypeError: Cannot set property 'problem' of undefined
  at Function.<anonymous> (http://localhost:8000/lib/js/ngActiveResource/ng-active-resource.js:1669:50)
    at http://localhost:8000/lib/js/lodash/lodash.js:912:25
    at Function.forEach (http://localhost:8000/lib/js/lodash/lodash.js:3307:15)
    at Function._this.new (http://localhost:8000/lib/js/ngActiveResource/ng-active-resource.js:1667:13)
    at Function.Associations.get (http://localhost:8000/lib/js/ngActiveResource/ng-active-resource.js:698:20)
    at transformSearchTermsToForeignKeys (http://localhost:8000/lib/js/ngActiveResource/ng-active-resource.js:1194:41)
    at generateGET (http://localhost:8000/lib/js/ngActiveResource/ng-active-resource.js:1242:32)
    at Function._this.where (http://localhost:8000/lib/js/ngActiveResource/ng-active-resource.js:1709:18)
    at Scope.$scope.load (http://localhost:8000/js/controllers.js:113:17)
    at http://localhost:8000/lib/js/angular/angular.js:10347:21 

Please can you guide me as what the problem might be (API?, primaryKey?)

This is my model definition.

    angular.module('opsCentralApp.models', ['ActiveResource'])        

        .factory('Problem', ['ActiveResource', function (ActiveResource) {

          function Problem(data) {
            this.number('ProblemID');
            this.number('PortalID');
            this.string('Title');
            this.string('Status');
            this.string('Priority');
            this.string('CreatedDate');
            this.string('ProblemStart');
            this.string('ProblemResolved');
            this.string('Category');
            this.string('Service');
            this.string('SubService');
            this.hasMany('activity');
          }

          Problem.inherits(ActiveResource.Base);
          Problem.primaryKey = "ProblemID";
          Problem.api.set('http://myurl/API/Incident');
          Problem.api.indexURL  = 'http://myurl/API/Incident/Problem';

          return Problem;
        }])

        .factory('Activity', ['ActiveResource', function (ActiveResource) {

              function Activity(data) {
                this.ActivityID   = data.ActivityID;
                this.ProblemID    = data.ProblemID;
                this.UserName     = data.UserName;
                this.UpdateTime   = data.UpdateTime;
                this.Description  = data.Description;
                this.belongsTo('problem');

              }

              Activity.inherits(ActiveResource.Base);
              Activity.primaryKey     = "ProblemID";
              Activity.api.set('http://myurl/API/Incident');
              Activity.api.indexURL   = 'http://myurl/API/Incident/Activity/[:ProblemID]';

              return Activity;

        }]);

Custom methods

Hi,
Is there a facility to add a custom server connection method to an ngActiveResource Entity?
for instance a more robust /search endpoint that could accept JSON POST payload of parameters (instead of the built-in where() call)?
Also is there a way to push into the ngActiveResource object cache entities that were loaded with a different $http call?

thanks

Port removed

How come you're removing : from the url here? It only seems to remove my port number.

url = url.replace(/\:\w+/, '');

I didn't want to make a pull request because I don't know why it's there in the first place.

Return Model Metadata with Request [feature]

What's a good place to store metadata from each request if I'm paginating data?

EDIT: I realize I actually probably don't need to store it in the model I'd just like to figure out a mechanism for returning it so I can know out how many requests need to be made. I'll try to explore a custom $http interceptor.

EDIT2: I created an $http interceptor and can modify the data to work properly with ngActiveResource but I then lose the metadata. I need to modify the promise returned by active-resource's "where" function to return an object with data and metadata.

EDIT3: Figured it out. Sorry for all the notifications!

View Diff Here: https://github.com/AlJohri/ngActiveResource/commit/1e68e3cb0e4875e1c1627727f40ea431e97d7a4b
base.js in _this.where function

// Generate a GET request for all instances matching the given params, deserialize each
// into the appropriate class, and return the found collection
return GET(_this, url, terms, options).then(function(json) {
  data = json.data;
  meta = json.meta;

  var results = [];
  for (var i in data) {
    var instance = _this.new(data[i]);
    results.push(instance);
    serializer.deserialize(data[i], instance, options);
  }

  // Watch all collections that get assigned out as variables
  _this.watchedCollections.push(results);

  _this.emit('where:complete', {instance: results, data: data, meta: meta});
  return {data: results, meta: meta};
});

CC: @brettshollenberger

Function.name property used - fails in IE

Love the project, not sure if anyone is actively working on issues. I am seeing that this library does not work with IE at all due to the use of the ECMAScript6 function.name which is not supported at all by IE. I see 17 instances of klass.name in the source code.

Any plans on updating this so that it will work with IE?

Cheers

Uncaught TypeError: Cannot read property 'downcase' of undefined

I'm trying to recreate the Post/Comment sample in a codepen but I'm getting the error "Uncaught TypeError: Cannot read property 'downcase' of undefined". And I can't find the cause. I want to know if i'm doing something wrong?

The error happens on line 278 in active-support.js because the string is empty (""). I think it has something to do with the relations because when I comment out this.hasMany('comments') the error doesn't occur.

active-support.js is a heavy dependency

Active-support.js is ~236kb (> active-resource.js ~115kb) and is not commonly used in apps.
Is it possible to have a leaner replacement for active-support? (inflectionjs)
I'm using lodash and async in other parts of my app so having it as a dependency for active-resource is not much of an issue.
Overall, this is an amazing plugin. Love the functionality it provides to an angular app.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.