Giter Site home page Giter Site logo

angular2-jsonapi's People

Contributors

abrararshad avatar aimcom avatar bonejon avatar dependabot[bot] avatar emmanueldaher avatar frans-beech-it avatar fvoska avatar ghidoz avatar hennerm avatar hpawe01 avatar jacobbullock avatar kopz9999 avatar magneticflux- avatar mariiapunda avatar martinnaughton avatar mernen avatar michdostal avatar mrjmd avatar reva2 avatar ricardoneves avatar rootooz avatar safo6m avatar zeevkatz avatar zeiv 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

angular2-jsonapi's Issues

make buildUrl in datastore.service protected instead of private

Hello
First of all - good job with this lib.

I have an change request/question.

In our software we want the jsonapi resource Type in singular and the url-paths in plural.
Unfortunately in your lib is the method "buildUrl" in "json-api-datastore.service.ts" private and i am not allow to overwrite it.

My objective was to insert a new variable "path" in JsonApiModelConfig and use this instead in buildUrl.

@JsonApiDatastoreConfig missing from npm package

I've recently pulled your package from npm as a dependency of another project but it seems that the JsonApiDatastoreConfig decorator js and type definition files are missing.

It works if I npm link the cloned repo so could it be that the files didn't make it in the last npm publish ?

Thanks for the project by the way ๐Ÿ‘

Update relationship attribute having a @BelongsTo() decorator

I have two models with OneToMany relationship:

@JsonApiModelConfig({
    type: 'posts'
})
export class Post extends JsonApiModel {

    @Attribute()
    title: string;

    @HasMany()
    comments: Comment[];
}

@JsonApiModelConfig({
    type: 'comments'
})
export class Comment extends JsonApiModel {

    @Attribute()
    title: string;

    @BelongsTo()
    post: Post;
}

I'm getting one Comment and I update the Post it's belonging to:

this.datastore.findRecord(Comment, '1').subscribe(
      (comment: Comment) => {
        comment.post = anotherExistingPost;
        comment.save().subscribe();
      }
);

When I save, the Post my Comment is belonging to is not updated.

Actually my request that is being sent to the server doesn't contain anything to update:

{
  "data": {
    "type": "comments",
    "id": "1",
    "attributes": {
      "id": "1"
    }
  }
}

For what I can see in the Library code, the save method is only taking in consideration the properties with @Attribute() decoration.
Unfortunately, my post property of Comment has a @BelongsTo() decoration.

JsonApiModel.prototype.save = function (params, headers) {
    var attributesMetadata = Reflect.getMetadata('Attribute', this);
    return this._datastore.saveRecord(attributesMetadata, this, params, headers);
};

Then what should be done so that I can update the Post my Comment is belonging to ?

Use inherited models in two-way databinding

Hi,

I created a Datastore service, and updated a model extending JsonApiModel, as described in the doc.
Now, I don't know how to make the model works in a basic two-way databinding form.

In my component :

export class CreateNoteComponent {

    note: Note = new Note();

    constructor() { }

    onSubmit() { 
         console.log(this.note);
    }

}

My Note model :

@JsonApiModelConfig({
    type: 'notes'
})
export class Note extends JsonApiModel {

    @Attribute()
    title: string;

    @Attribute()
    content: string;

    /** Removed this constructor, as it enters in conflict with the properties declared with @Attributes */
    /** constructor ( 
         public title: string,
         public content: string
    ) { } */
}

My view :

<input type="text" name="title" [(ngModel)]="note.title" />
<input type="text" name="content" [(ngModel)]="note.content" />
<button type="submit" (click)="onSubmit()">Submit</button>
{{ JSON.stringify(claimFile) }}

Before I change my model and make it inherit from JsonApiModel, everything worked fine. Each change I typed in the inputs was reflected in the model and displayed in the debug string interpolation.

Now Typescript gives me this error :

create-note.component.ts
(11,28): error TS2346: Supplied parameters do not match any signature of call target.

It comes from the note: Note = new Note() line.
Seems normal as the JsonApiModel classes has its own constructor signature...

So my questions are :

  • is it still possible to instanciate my model like before in a component ?
  • If not, how can I achieve the two-way databinding ?
  • in the doc, for a creation request, it is said :
this.datastore.createRecord(Post, {
    title: 'My post',
    content: 'My content'
});

I'd like to pass my model as the second argument and not have to explicitely describe each values (especially in a Service). How can I make it ?

Thx a lot for your help

extend JsonApiModel when the class has extended another class

Hi there,
As you now multi inheritance is not allowed in Javascript . So if we have a class which is extending an abstract class , we can not inherit from JsonApiModel anymore .

A sample is here :
export class Market extends ReferenceDate{
id: string;
name: string;
}

if I change the abstract class like this:
export abstract class ReferenceData extends JsonApiModel {
}

then I can not create any new instance of market in this way:
var market= new Market();

I will have syntax error because it needs datastore: JsonApiDatastore, data: any

Single instance (question)

Hi, I need to know how this library works with same data. For example: When I call

this.datastore.findRecord(Post, '1').subscribe(
    (post: Post) => console.log(post)
);

twice. Will post be the same instance?

@angular/http version dependency

What work is involved with removing the strict dependency on the @angular/http version? We're inconveniently locked into v2.0.0 at the moment.

Could the dependency be a more permissive peerDependency instead and allow the project to define the version?

Error while trying to load tutorial app with angular2-jsonapi

when adding the component to the @ngModules you get an error when opening the page

Error: patchProperty/desc.set/wrapFn@http://localhost:3000/node_modules/zone.js/dist/zone.js:794:27
    Zone</ZoneDelegate</ZoneDelegate.prototype.invokeTask@http://localhost:3000/node_modules/zone.js/dist/zone.js:365:24
    Zone</Zone</Zone.prototype.runTask@http://localhost:3000/node_modules/zone.js/dist/zone.js:265:29
    ZoneTask/this.invoke@http://localhost:3000/node_modules/zone.js/dist/zone.js:433:29

    Error loading http://localhost:3000/angular2-jsonapi as "angular2-jsonapi" from http://localhost:3000/app/app.module.js

after running npm start

Subscribe versus promise

Where can I find information regarding how to handle subscribes.

Previously when using the http object, I can convert the observable into a promise and then pass that back to controller to manage the behavior of the app - for cases with success or failure.

How do I accomplish the same with the subscribe() function?

Many thanks in advance...

Resource identifier id doesn't have to be a number

Angular2-jsonapi only supports numbers for resource identifier (id). Ex:

findRecord(type: { new (data: any): JsonApiModel; }, id: number, params?: any, headers?: Headers): Observable<JsonApiModel>;

It's not compatible with Uuid for example.
The JSON API specification doesn't specify that identifiers have to be number.

Therefore, can we allow the identifier to be a string as well ?

Does not export JsonApiDatastoreConfig

Hi, I'm use [email protected]

Ionic Framework Version: 2.0.0-rc.0
Ionic CLI Version: 2.1.0
Ionic App Lib Version: 2.1.0-beta.1
OS: Mac OS X El Capitan
Node Version: v6.7.0

and have error

[15:13:13]  bundle dev failed:  Module /Users/ivan/github/Mush/node_modules/angular2-jsonapi/dist/index.js does not export JsonApiDatastoreConfig (imported by /Users/ivan/github/Mush/src/services/datastore.ts)

[15:13:13]  Error: Module /Users/ivan/github/Mush/node_modules/angular2-jsonapi/dist/index.js does not export JsonApiDatastoreConfig (imported by /Users/ivan/github/Mush/src/services/datastore.ts)
    at Module.trace (/Users/ivan/github/Mush/node_modules/rollup/dist/rollup.js:7706:29)
    at ModuleScope.findDeclaration (/Users/ivan/github/Mush/node_modules/rollup/dist/rollup.js:7329:22)
    at Scope.findDeclaration (/Users/ivan/github/Mush/node_modules/rollup/dist/rollup.js:5349:39)
    at CallExpression.bind (/Users/ivan/github/Mush/node_modules/rollup/dist/rollup.js:5834:28)
    at /Users/ivan/github/Mush/node_modules/rollup/dist/rollup.js:5149:50
    at ArrayExpression.eachChild (/Users/ivan/github/Mush/node_modules/rollup/dist/rollup.js:5163:19)
    at ArrayExpression.bind (/Users/ivan/github/Mush/node_modules/rollup/dist/rollup.js:5149:7)
    at /Users/ivan/github/Mush/node_modules/rollup/dist/rollup.js:5149:50
    at CallExpression.eachChild (/Users/ivan/github/Mush/node_modules/rollup/dist/rollup.js:5163:19)
    at CallExpression.bind (/Users/ivan/github/Mush/node_modules/rollup/dist/rollup.js:5149:7)

nested relationships part 2

I've made some adjustments to json-api.model.js to make it more robust..

"use strict";
var _ = require('underscore');
require('reflect-metadata');
var JsonApiModel = (function () {
    function JsonApiModel(data) {
        if (data) {
            this.id = data.id;
            _.extend(this, data.attributes);
        }
    }
    JsonApiModel.prototype.syncRelationships = function (data, included, level, datastore) {
        if (data) {
            this.parseHasMany(data, included, level, datastore);
            this.parseBelongsTo(data, included, level, datastore);
        }
    };
    JsonApiModel.prototype.parseHasMany = function (data, included, level, datastore) {
        var hasMany = Reflect.getMetadata('HasMany', this);
        if (hasMany) {
            for (var _i = 0, hasMany_1 = hasMany; _i < hasMany_1.length; _i++) {
                var metadata = hasMany_1[_i];
                if (data.relationships[metadata.relationship] && data.relationships[metadata.relationship].data && data.relationships[metadata.relationship].data.length > 0) {
                    var typeName = data.relationships[metadata.relationship].data[0].type;
                    var objectType = Reflect.getMetadata('JsonApiDatastoreConfig', datastore.constructor).models[typeName];
                    this[metadata.propertyName] = this.getHasManyRelationship(objectType, data, included, metadata.relationship, typeName, level, datastore);
                }
            }
        }
    };
    JsonApiModel.prototype.parseBelongsTo = function (data, included, level, datastore) {
        var belongsTo = Reflect.getMetadata('BelongsTo', this);
        if (belongsTo) {
            for (var _i = 0, belongsTo_1 = belongsTo; _i < belongsTo_1.length; _i++) {
                var metadata = belongsTo_1[_i];
                if (data.relationships[metadata.relationship] && data.relationships[metadata.relationship].data) {
                    var typeName = data.relationships[metadata.relationship].data.type;
                    var objectType = Reflect.getMetadata('JsonApiDatastoreConfig', datastore.constructor).models[typeName];
                    this[metadata.propertyName] = this.getBelongsToRelationship(objectType, data, included, metadata.relationship, typeName, level, datastore);
                }
            }
        }
    };
    JsonApiModel.prototype.getHasManyRelationship = function (objectType, data, included, relationship, typeName, level, datastore) {
        var relationshipList = [];
        data.relationships[relationship].data.forEach(function (item) {
            var relationshipData = _.findWhere(included, { id: item.id, type: typeName });
          if( relationshipData ) {
            var newObject = new objectType(relationshipData);
            if (relationshipData.relationships && level <= 1) {
              newObject.syncRelationships(relationshipData, included, ++level, datastore);
            }
            relationshipList.push(newObject);
          }
        });
        return relationshipList;
    };
    JsonApiModel.prototype.getBelongsToRelationship = function (objectType, data, included, relationship, typeName, level, datastore) {
        var id = data.relationships[relationship].data.id;
        var relationshipData = _.findWhere(included, { id: id, type: typeName });
        if(relationshipData) {
          var newObject = new objectType(relationshipData);
          if (relationshipData.relationships && level <= 1) {
            newObject.syncRelationships(relationshipData, included, ++level, datastore);
          }
          return newObject;
        }
    };
    return JsonApiModel;
}());
exports.JsonApiModel = JsonApiModel;
//# sourceMappingURL=json-api.model.js.map

Unexpected value 'JsonApiModule' imported by the module 'AppModule'

After upgrading to v3.2, I've got this error message :

client?8fb9:75 ./client/app/app.module.ts (5,31): error TS2307: Cannot find module 'angular2-jsonapi'.
compiler.umd.js?9df7:13996 Uncaught Error: Unexpected value 'JsonApiModule' imported by the module 'AppModule'

I tried to change the position of the import at the top of my app.module.ts, and in the imports[] of my ngModule. No success.

Also tried the fix proposed in #13 and related issues :
module.exports = { resolve: { modules: [ path.join(__dirname, "node_modules") ] } };

Doesn't work either.
Note that the "Unexpected value" displays only in Chrome console, not in my build process.

I use Webpack 1.13.2, but not angular-cli, my project boilerplate is created from scratch.

Thx for your help.

Error: Unexpected value '...' imported by the module 'AppModule'

Running under Angular 2 release and when adding JsonApiModule to the imports in app.module.ts I get the following error in the browser:

zone.js:129 Uncaught Error: Unexpected value 'NgaModule' imported by the module 'AppModule'

It seems that after I add JsonApiModule to the app imports it invalidates other imports, not sure if this is an issue with this module or the other but I don't have the issue unless I add JsonApiModule to the app imports

parseHasMany issue with JsonApiDatastoreConfig metadata

When I use findRecord with an HasMany relationship, I get the following error:

stack trace

The request is properly sent and the json response is correct.
The issue happens during the parsing of the results at this step: parseHasMany.

For some reasons, objectType is undefined after this line:

var objectType = Reflect.getMetadata('JsonApiDatastoreConfig', datastore.constructor).models[typeName];

This is what I have in my Datastore:

import { Injectable } from '@angular/core';
import { JsonApiDatastoreConfig, JsonApiDatastore } from 'angular2-jsonapi';
import { Country, Brand, Model } from "../";

@Injectable()
@JsonApiDatastoreConfig({
  baseUrl: 'http://www.example.com/api/',
  models: {
    brands: Brand,
    models: Model,
    countries: Country
  }
})
export class Datastore extends JsonApiDatastore { }

And this is what I get when I print Reflect.getMetadata('JsonApiDatastoreConfig', datastore.constructor) in the console:

stack trace

I precise that:

  • it works if I hard code my model into objectType variable
  • I'm using "angular2-jsonapi": "^1.2.1" and "reflect-metadata": "0.1.3"

Any idea ?

Error while trying to save Object

when calling the save() method on the Model. I get an JSON.parse EXCEPTION error.

after some debugging I found that the attributesMetadata variable filled in the JsonApiModel.prototype.save function returns an 'undefined' value.

this is the payload send to the api:

"data": {
    "type": "organisations",
    "id": "1",
    "attributes": {}
  }

Cannot save HasMany relationship

How do I save a HasMany relationship?

It's clear from the readme that I can save a HasOne relationship just by setting the parameter with the object like

{post: Post}

but that doesn't seem to work for HasMany relationships when I use something like

{comments: [Comment, Comment, Comment]}

Is this not implemented yet?

If not maybe I can help out (although I've never contributed to a github project before, so I may need some help).

Unable to load relationships

Might be me doing something wrong here.

"relationships": { "control_record": { "data": { "type": "control_records", "id": "1" }, "links": { "self": "\/api\/control_records\/view\/1" } } },

I can see it getting picked up in parseHasMany, however relationship.data.length is undefined due to it being an array. Sadly as the test suite is broken I'm unable to see if that's a thing in the provided examples.

If I manually make relationship.data into [{type: "control_records", id: "1"}] then it finds the relationship just fine however.

rollbackAttributes() only rolls back one change

Hi,

I have a use case where I need to rollback all attributes of a model to their last saved state. In the code of the JsonApiModel I found the method rollbackAttributes() which I excpected to do the job. But when doing two changes on a model attribute it only rolls back one change and not two changes.

Example:

let test = this.datastoreService.createRecord(Role, {});
test.name = "test";
test.save();
console.log(test.name); // prints "test"
test.name = "test2";
test.name = "test22";
test.rollbackAttributes();
console.log(test.name); //prints "test2" but I expected it to print "test"

Is this a bug or expected behaviour? If it's a bug I would like to propose a fix in the saveAnnotations() method of the Attribute decorator and make a pull request.

Roadmap to v4.0.0

Hi @ghidoz,

Do you have a detailed roadmap for this project?

I really like your idea about using decorators. It's very much like the Hibernate or Doctrine ORM. But currently this library missing several important features and couldn't be used in real products.

At work we started a long-term project that have JSON API backend and currently looking for library that allow us consume this backend in Angular 2 apps. I studied several existing libraries and found that your library most in line with our vision of how client-side should interact with JSON API backend.

I would like to help you improve this library and make it useful for real products.

Includes / Relationship - causing server error

I'm trying to consume some JSON with angular2-jsonapi. The JSON has a relationship between Building and Room. I get a uber ambigious error coming back:

json-api-datastore.service.ts:158 Server errorJsonApiDatastore.handleError @ json-api-datastore.service.ts:158(anonymous function) @ json-api-datastore.service.ts:35CatchSubscriber.error @ catch.ts:58MapSubscriber._next @ map.ts:81Subscriber.next @ Subscriber.ts:95onLoad @ http.umd.js:1230ZoneDelegate.invokeTask @ zone.js:236onInvokeTask @ core.umd.js:6149ZoneDelegate.invokeTask @ zone.js:235Zone.runTask @ zone.js:136ZoneTask.invoke @ zone.js:304

Can you tell me what I'm doing wrong? Thanks in advance!

My call goes something like this

this.datastore.findRecord(Building, building_id, {
            include: 'rooms'
        }, this.gserv.getHeadersAPI()).subscribe(
                        (building: Building) =>{ 

                        },
                        (e) =>{

                        }
                    );

My JSONAPIMODELS are defined as following..

import { JsonApiModelConfig, JsonApiModel, Attribute, HasMany, BelongsTo } from  'angular2-jsonapi';

@JsonApiModelConfig({
    type: 'buildings'
})

export class Building extends JsonApiModel {
    @Attribute()
    id : string;

    @Attribute()
    name : string;

    @Attribute()
    address_1 : string;

    @Attribute()
    address_2 : string;

    @Attribute()
    address_3 : string;

    @Attribute()
    town_or_city : string;        

    @Attribute()
    county : string;        

    @Attribute()
    postcode : string;  

    @Attribute()
    country : string;    

    @Attribute()
    description : string;   

    @HasMany()
    rooms : Room[];                 
}

@JsonApiModelConfig({
    type: 'rooms'
})

export class Room extends JsonApiModel {
    @Attribute()
    id : string;

    @Attribute()
    type_of_room: string;

    @Attribute()
    status: string;

    @Attribute()
    notes: string;

    @BelongsTo()
    building: Building;

}

The JSON being return from the API service is as follows...

{
  "data": {
    "id": "14",
    "type": "buildings",
    "attributes": {
      "name": "Rodolfo Beahan",
      "address_1": "8545 Leffler Union",
      "address_2": "Suite 742",
      "address_3": "",
      "town_or_city": "Wardland",
      "county": "Wisconsin",
      "postcode": "40461",
      "country": "Solomon Islands",
      "description": "MyText"
    },
    "relationships": {
      "rooms": {
        "data": [
          {
            "id": "107",
            "type": "rooms"
          },
          {
            "id": "108",
            "type": "rooms"
          }
        ]
      }
    }
  },
  "included": [
    {
      "id": "107",
      "type": "rooms",
      "attributes": {
        "name": "",
        "type_of_room": "Single",
        "status": "OPEN",
        "notes": ""
      },
      "relationships": {
        "building": {
          "data": {
            "id": "14",
            "type": "buildings"
          }
        }
      }
    },
    {
      "id": "108",
      "type": "rooms",
      "attributes": {
        "name": "",
        "type_of_room": "Double",
        "status": "OPEN",
        "notes": ""
      },
      "relationships": {
        "building": {
          "data": {
            "id": "14",
            "type": "buildings"
          }
        }
      }
    }
  ]
}

Refactoring: Use plain old javascript objects as data models

In current implementation data models highly coupled with data storage. I think it will be better if we will use POJO augmented with decorators for data models. I.e. all methods that change data in underlying storage (currently, save()) should be moved from JsonApiModel to JsonApiDataStore.

Typical interaction with data storage can look like:

// create record
let author = new AuthorModel();
storage.saveRecord(author);

// updating record
storage.findRecord(BookModel, '1').subscribe(
  (book) => {
    book.title = "New title";
    storage.saveRecord(book);
  }
);

// deleting record
storage.findRecord(BookModel, '1').subscribe(
  (book) => storage.deleteRecord(book);
);

This approach have following advantages:

  • data models can be plain old javascript objects without dependency on JsonApiDataStorage. It can be easily created and tested.
  • in the future we can easily support multiple operations in the single request (json-api/json-api#795). Something like:
storage.beginTransaction();

/* posts: BlogPostModel[] */
posts.forEach((post) => {
  post.published = true;
  storage.saveRecord(post);  // request will be delayed because of transaction
});

storage.commit(); // update all posts in underlying storage using single request

Console error when making request with relationship using @BelongsTo

Aloha,

I'm receiving the following console error when attempting to make a request for data using a model with a @BelongsTo relationship.

parseBelongsTo - Model type for relationship customers not found.

component.ts

this.datastore.query( Receivable, {
  include: 'customers' // include customers with receivables
}).subscribe( ( receivables: Receivable[] ) => {
  console.log(receivables)
});

datastore.ts

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import {
  JsonApiDatastoreConfig,
  JsonApiDatastore,
  JsonApiModelConfig,
  JsonApiModel,
  Attribute,
  HasMany,
  BelongsTo
} from 'angular2-jsonapi';

import { environment } from '../../environments/environment';

/**
 * JSON API Service Configuration Decorator
 */
@Injectable()
@JsonApiDatastoreConfig({
  baseUrl: environment.hostname + '/api/v1/',
  models: {
    customers: Customer,
    receivables: Receivable
  }
})

export class DatastoreService extends JsonApiDatastore {
  constructor(http: Http) {
    super(http);
  }
}

/**
 * Customers model
 * @desc JSON API Model Configuration
 */
@JsonApiModelConfig({
  type: 'customers'
})
export class Customer extends JsonApiModel {

  @Attribute()
  name: string;

  @Attribute()
  city: string;

  @Attribute()
  phone: string;

  @Attribute()
  state: string;

  @Attribute()
  street: string;

  @Attribute()
  zip: string;

  @HasMany()
  receivables: Receivable[];
}

/**
 * Receivables model
 * @desc JSON API Model Configuration
 */
@JsonApiModelConfig({
  type: 'receivables'
})
export class Receivable extends JsonApiModel {
  @Attribute()
  invoice_date: Date;

  @Attribute()
  invoice_due_date: Date;

  @Attribute()
  invoice_amount_due: number;

  @Attribute()
  owner_id: string;

  @Attribute()
  supplier_id: string;

  @Attribute()
  customer_id: string;

  @BelongsTo()
  customer: Customer;
}

JSON from server

{
      "data": [
          {
              "attributes": {
                  "created_at": "2017-01-24T02:05:47.259Z",
                  "customer_id": 2470,
                  "invoice_amount_due": "93.356",
                  "invoice_date": "2017-01-19",
                  "invoice_due_date": "2017-04-07",
                  "owner_id": 7476,
                  "supplier_id": 7477,
                  "updated_at": "2017-01-24T02:05:47.259Z"
              },
              "id": "3839",
              "relationships": {
                  "customer": {
                      "data": {
                          "id": "2470",
                          "type": "customers"
                      }
                  }
              },
              "type": "receivables"
          }
      ],
      "included": [
          {
              "attributes": {
                  "city": "South Coltenhaven",
                  "name": "Koch and Sons",
                  "phone": "1-410-235-4477 x230",
                  "state": "AK",
                  "street": "3563 Louisa Walk",
                  "zip": "90083-5352"
              },
              "id": "2470",
              "type": "customers"
          }
      ]
}

Any help here would be appreciated.

Mahalo!

Remove dependency on typings

Typescript 2.0 now supports getting type definitions directly from npm.

The project I am working on no longer has typings as a dependency, installing this library causes an error.

Please see the pull request below for changes.
#18

Getting errors after installing angular2-jsonapi

Hi,

first thx for this library - sounds very promising. Unfortunately I can't get it to run as I get lots of errors after installing. As I'm fairly new to all this npm and JS stuff (only did python and jquery until a few months ago shame ) I don't know what to do with this error - maybe it's an easy fix. Googling didn't help me, so I hope you guys maybe have an idea.

The problem:
I bootstrapped an ng project with angular-cli's ng new ( https://cli.angular.io/ ) this is working perfectly and ng serve runs without problems and gives me the hello world page in the browser. Now I try to install angular2-jsonapi with npm install angular2-jsonapi --save and already get some "UNMET PEER DEPENDENCY" error thing I don't know what to do with.

asmaps@naos ~/git/letsmeet-angular2-ui (master*) $ npm install angular2-jsonapi --save
[email protected] /home/asmaps/git/letsmeet-angular2-ui
โ”œโ”€โ”€ UNMET PEER DEPENDENCY @angular/[email protected]
โ”œโ”€โ”€ UNMET PEER DEPENDENCY @angular/[email protected]
โ”œโ”€โ”€ UNMET PEER DEPENDENCY @angular/[email protected]
โ”œโ”€โ”€ UNMET PEER DEPENDENCY @angular/[email protected]
โ””โ”€โ”ฌ [email protected] 
  โ”œโ”€โ”€ @angular/[email protected] 
  โ”œโ”€โ”€ @angular/[email protected] 
  โ”œโ”€โ”€ @angular/[email protected] 
  โ”œโ”€โ”€ @angular/[email protected] 
  โ”œโ”€โ”€ @angular/[email protected] 
  โ”œโ”€โ”€ @types/[email protected] 
  โ”œโ”€โ”€ @types/[email protected] 
  โ”œโ”€โ”€ @types/[email protected] 
  โ”œโ”€โ”€ [email protected] 
  โ””โ”€โ”€ [email protected] 

npm WARN optional Skipping failed optional dependency /chokidar/fsevents:
npm WARN notsup Not compatible with your operating system or architecture: [email protected]
npm WARN @angular/[email protected] requires a peer of @angular/[email protected] but none was installed.
npm WARN @angular/[email protected] requires a peer of @angular/[email protected] but none was installed.
npm WARN @angular/[email protected] requires a peer of @angular/[email protected] but none was installed.
npm WARN @angular/[email protected] requires a peer of @angular/[email protected] but none was installed.
npm WARN @angular/[email protected] requires a peer of @angular/[email protected] but none was installed.

My resulting package.json:

{
  "name": "letsmeet-angular2-ui",
  "version": "0.0.0",
  "license": "MIT",
  "angular-cli": {},
  "scripts": {
    "start": "ng serve",
    "lint": "tslint \"src/**/*.ts\"",
    "test": "ng test",
    "pree2e": "webdriver-manager update",
    "e2e": "protractor"
  },
  "private": true,
  "dependencies": {
    "@angular/common": "~2.0.0",
    "@angular/compiler": "~2.0.0",
    "@angular/core": "~2.0.0",
    "@angular/forms": "~2.0.0",
    "@angular/http": "~2.0.0",
    "@angular/platform-browser": "~2.0.0",
    "@angular/platform-browser-dynamic": "~2.0.0",
    "@angular/router": "~3.0.0",
    "angular2-jsonapi": "^3.2.0",
    "core-js": "^2.4.1",
    "rxjs": "5.0.0-beta.12",
    "ts-helpers": "^1.1.1",
    "zone.js": "^0.6.23"
  },
  "devDependencies": {
    "@types/jasmine": "^2.2.30",
    "@types/node": "^6.0.42",
    "angular-cli": "1.0.0-beta.17",
    "codelyzer": "~0.0.26",
    "jasmine-core": "2.4.1",
    "jasmine-spec-reporter": "2.5.0",
    "karma": "1.2.0",
    "karma-chrome-launcher": "^2.0.0",
    "karma-cli": "^1.0.1",
    "karma-jasmine": "^1.0.2",
    "karma-remap-istanbul": "^0.2.1",
    "protractor": "4.0.9",
    "ts-node": "1.2.1",
    "tslint": "3.13.0",
    "typescript": "2.0.2"
  }
}

when I now try to run ng serve I get lots of errors, also when I try to add import { JsonApiModule } from 'angular2-jsonapi'; to my AppModule I only get an import error (not found).

asmaps@naos ~/git/letsmeet-angular2-ui (master*) $ ng serve                           
Could not start watchman; falling back to NodeWatcher for file system events.
Visit http://ember-cli.com/user-guide/#watchman for more info.
** NG Live Development Server is running on http://localhost:4200. **
4892ms building modules                                                                  
13ms sealing
0ms optimizing 
0ms basic module optimization 
69ms module optimization
1ms advanced module optimization 
7ms basic chunk optimization        
0ms chunk optimization 
1ms advanced chunk optimization 
0ms module and chunk tree optimization 
84ms module reviving
1ms module order optimization 
3ms module id optimization 
3ms chunk reviving 
0ms chunk order optimization 
9ms chunk id optimization 
49ms hashing
0ms module assets processing 
99ms chunk assets processing
2ms additional chunk assets processing 
0ms recording 
0ms additional asset processing 
1304ms chunk asset optimization
1442ms asset optimization
33ms emitting
Hash: 6901610443f768eb66c3
Version: webpack 2.1.0-beta.25
Time: 8031ms
            Asset       Size  Chunks             Chunk Names
   main.bundle.js    2.78 MB    0, 2  [emitted]  main
 styles.bundle.js    10.2 kB    1, 2  [emitted]  styles
        inline.js    5.53 kB       2  [emitted]  inline
         main.map    2.86 MB    0, 2  [emitted]  main
       styles.map    14.1 kB    1, 2  [emitted]  styles
       inline.map    5.59 kB       2  [emitted]  inline
       index.html  481 bytes          [emitted]  
assets/.npmignore    0 bytes          [emitted]  

ERROR in [default] /home/asmaps/git/letsmeet-angular2-ui/node_modules/@types/es6-shim/index.d.ts:6:13 
Duplicate identifier 'PropertyKey'.

ERROR in [default] /home/asmaps/git/letsmeet-angular2-ui/node_modules/@types/es6-shim/index.d.ts:10:4 
All declarations of 'value' must have identical modifiers.

ERROR in [default] /home/asmaps/git/letsmeet-angular2-ui/node_modules/@types/es6-shim/index.d.ts:248:4 
All declarations of 'EPSILON' must have identical modifiers.

ERROR in [default] /home/asmaps/git/letsmeet-angular2-ui/node_modules/@types/es6-shim/index.d.ts:283:4 
All declarations of 'MAX_SAFE_INTEGER' must have identical modifiers.

ERROR in [default] /home/asmaps/git/letsmeet-angular2-ui/node_modules/@types/es6-shim/index.d.ts:290:4 
All declarations of 'MIN_SAFE_INTEGER' must have identical modifiers.

ERROR in [default] /home/asmaps/git/letsmeet-angular2-ui/node_modules/@types/es6-shim/index.d.ts:346:4 
All declarations of 'flags' must have identical modifiers.

ERROR in [default] /home/asmaps/git/letsmeet-angular2-ui/node_modules/@types/es6-shim/index.d.ts:498:4 
All declarations of 'prototype' must have identical modifiers.

ERROR in [default] /home/asmaps/git/letsmeet-angular2-ui/node_modules/@types/es6-shim/index.d.ts:561:4 
All declarations of 'size' must have identical modifiers.

ERROR in [default] /home/asmaps/git/letsmeet-angular2-ui/node_modules/@types/es6-shim/index.d.ts:570:4 
All declarations of 'prototype' must have identical modifiers.

ERROR in [default] /home/asmaps/git/letsmeet-angular2-ui/node_modules/@types/es6-shim/index.d.ts:581:4 
All declarations of 'size' must have identical modifiers.

ERROR in [default] /home/asmaps/git/letsmeet-angular2-ui/node_modules/@types/es6-shim/index.d.ts:591:4 
All declarations of 'prototype' must have identical modifiers.

ERROR in [default] /home/asmaps/git/letsmeet-angular2-ui/node_modules/@types/es6-shim/index.d.ts:606:4 
All declarations of 'prototype' must have identical modifiers.

ERROR in [default] /home/asmaps/git/letsmeet-angular2-ui/node_modules/@types/es6-shim/index.d.ts:620:4 
All declarations of 'prototype' must have identical modifiers.

ERROR in [default] /home/asmaps/git/letsmeet-angular2-ui/node_modules/typescript/lib/lib.es2015.core.d.ts:17:13 
Duplicate identifier 'PropertyKey'.
Child html-webpack-plugin for "index.html":
         Asset     Size  Chunks       Chunk Names
    index.html  2.81 kB       0       
webpack: bundle is now VALID.

Can you point me somewhere how I can fix this?

Thx in advance

PS: Do you have an IRC channel or similar?

angular-cli clean install npm install angular2-jsonapi --save fails because of dependency conflicts

npm install angular2-jsonapi --save
[email protected] /Users/stockn/source/Tech4team/aerometrix
โ”œโ”€โ”€ UNMET PEER DEPENDENCY @angular/[email protected]
โ”œโ”€โ”€ UNMET PEER DEPENDENCY @angular/[email protected]
โ”œโ”€โ”€ [email protected]
โ””โ”€โ”€ UNMET PEER DEPENDENCY [email protected]

npm WARN [email protected] requires a peer of @angular/[email protected] but none was installed.
npm WARN [email protected] requires a peer of @angular/[email protected] but none was installed.
npm WARN [email protected] requires a peer of [email protected] but none was installed.

My cores, directly from angular-cli generator are "@angular/common": "^2.3.1",
"@angular/compiler": "^2.3.1",
"@angular/core": "^2.3.1",

I spent over an hour trying to play with dependencies to get it to work, any help or guidance is appreciated.

Replace underscore with lodash

Angular 2 uses lodash internally and here we use underscore. This has the possibility for errors as both libraries try to define window._

I have trialled this change locally and it seems to work without issue.

nested relationship

Hi,

I am using your library for loading JSON Spec data and I can't load nested relationship .
here is my class diagram:
class Person{
id:number;
name:string;

@BelongsTo()
car: Car;
}

class Car{
id:number;

@BelongsTo()
market: Market;

}
class Market{
id:nmber
}

when I want to load Persons data I use include:"car.market" and it loads all persons with relation to car but it doesn't load nested relation of car and market

Could you please let me know if there is any solution ?

Add some tests

If more people are getting involved it would be good to add some tests to ensure we don't regress any functionality.

Not working with angular-cli

Using the latest angular-cli, after following your instructions I get the following errors:

zone.js:461 Unhandled Promise rejection: Error: XHR error (404 Not Found) loading http://localhost:4200/reflect-metadata

It appears that systemjs is trying to load these via an xhr and obviously failing. It would seem that your projects dependencies would also need to be included in the vendor folder and thus, part of angular-cli-build.js and system-config.ts. I'm fairly new to TS so maybe I'm missing a crucial step here?

Adding headers in DataStore

Hello,

I'm trying to add headers to provide a token access to my API. The guide gives this line :

this.datastore.headers = new Headers({'Authorization': 'Bearer ' + accessToken});

and 2 other examples, but they seem to be on the consumer components side. I'd like to avoid specifying headers it in each of my HTTP calls.
First I tried to add it in a "headers" key within the @JsonApiDatastoreConfig decorator, but didn't work.

Then I tried to put it in my Datastore service, in the constructor :

export class Datastore extends JsonApiDatastore {

    constructor(http: Http) { 
        super(http);
        this.headers = new Headers({ 'Authorization': 'Bearer ' + localStorage.getItem('access_token')});
    }

}

Now something strange, each time I validate my form and check the headers that are sent to the server, it turns out that the "accept" and "Content-type" keys are repeated.
Example, if I submit my form 3 times, I get on the third call :

accept:application/vnd.api+json,application/vnd.api+json,application/vnd.api+json
authorization:Bearer NDgyZGQ1YTIzMTZiNjg4MzY0ZDVmOTk1NzkyZjZjZGNmZjIyZDZmMDU2YzY0NThmYzc5OGM1ZmE2ZTcxZWE3ZA
content-type:application/vnd.api+json,application/vnd.api+json,application/vnd.api+json

Does it come from the way I add my header ?
Just for information, when I submit a form, server doesn't return a valid response yet (test purpose), so the app is assumed to be crashed.

Thx for your help

Dynamic configuration of urlBase

During the development and test cycle of an application it's quite normal to have different environments for development and testing purposes. These environments could have different domain names, and or ports associated with the configuration.

Having the baseUrl parameter as part of the DataStore attributes makes this quite difficult to achieve. I've got a workaround for now, but it's not ideal. I have a think into a better solution, any suggestions would be appreciated.

Support for more general json:api documents (links, data, meta)

Hi,

I read the discussion about the roadmap in #19 so I'm going to create a new issue to have some space to discuss a topic that needs some more refactoring. I'm currently using this package in a small opensource client. The client makes use of HATEOAS and thus needs support for the top level link attribute in json:api.

Currently this package only handles the data attribute. But there is a RFC for ember-data that proposes how to implement support for more general json:api documents. If I got the idea of the RFC adapted to this package right, then you should be able to make a request to the API and get something like a DocumentModel (instead of a JsonApiModel) as response. The DocumentModel gives you access to all the top level attributes of a json:api document using the following getter methods:

  • data(): JsonApiModel (existing JsonApiModel)
  • links(): LinksModel (new model)
  • meta(): MetaModel (new model)

A basic query then would look like this:

        this.datastoreService.query(User, {
            page: { size: 10, number: 1},
            include: 'roles'
        }).subscribe(
            (document: DocumentModel) => {
                // access to the JsonApiModel
                this.users = document.data();
                // access to the top level links attribute
                this.links = document.links();
                // access to the json:api document in general
                this.usersDocument = document
            }
        );

The response part is worth discussing. I think in the RFC they won't return a DocumentModel like I propose. Instead they return a JsonApiModel that has an getter method document() to access the top level attributes. Thus the programming API does not change. But this approach has a big disadvantage when adopting the RFC to this package. Because if you want to access the top level attributes of an empty collection this package will give you just an empty array as response. This is why I would propose to return a general DocumentModel instead of JsonApiModel or Array<JsonApiModel>.

All other parts of the package should not change. This means if you do peek() then you should get an JsonApiModel on which you can call document() to get the coresponding document including all the top level attributes like links() and meta().

For testing this approach I did a very basic implementation in my jsonapi-document branch and also adopted this changes into the client:

I'm going to further test this implementation next week but I just wanted to hook into the discussion about the roadmap with this proposal early so that you can consider this feature in further plans.

Infinite nested relationships

in the README it says that this library resolves infinite levels of relationships, but I think I it's just 2 levels, beacuase in JsonApiModel syncRelationships() is only called if the level is <= 1. Would there be any problem if this isn't limited?

Converting circular structure to JSON

Hi there,

I'm facing a new problem with the object that Datastore returns me after making a findRecord().
I just added a service in my application to store some shared states and variables between my components. This service is inspired by the Angular2-Webpack starter. As you can see, it contains getter and setter definitions to make sure you never set directly a property and you always get a clone of the state object.

    get state() {
        return this._state = this._clone(this._state);
    }
    set state(value) {
        throw new Error('You should not mutate the `.state` directly. Use set(prop, value) instead');
    }
    get(prop?: any) {
        const state = this.state;
        return state.hasOwnProperty(prop) ? state[prop] : state;
    }
    set(prop: string, value: any) {
        debugger
        // Mutate our state internally
        return this._state[prop] = value;
    }
    private _clone(object: InternalStateType) {
        return JSON.parse(JSON.stringify( object ));
    }

Here's for example one usecase in a component :

constructor(private route: ActivatedRoute, 
            public appState: AppState, 
            private datastore: Datastore){ }

ngOnInit(){
        // Save current folder ID to the AppState service
        let folderId = this.route.snapshot.params['id'];
        this.appState.set('folderId', folderId);
        console.log(this.appState.get('folderId')); // Works fine.

        // Get current folder if needed
        if (!this.folder && !this.appState.get('folder')){
            this.datastore.findRecord(Folder, folderId)
                          .subscribe(
                              (folder: Folder) => {
                                  this.appState.set('folder', folder);
                                  console.log(this.appState.get('folder')) // Breaks here...
                              }
                          )
        }
    }

I have this output :
Uncaught TypeError: Converting circular structure to JSON(โ€ฆ)
anywhere I'll call this.appState.get('folder')
I guess it's because all the properties added to my folder object, like _datastore node or in the constructor, the object can't be passed into the return JSON.parse(JSON.stringify( object )); function.

What worry me is as for now I only use this basic state manager, but I'd like to implement something more substancial like ng2-redux, and I will likely encounter the same problem...

Any solution or workaround ?
Thx for your help :)

Error: Cannot read property 'type' of null

Hi there ,

I used JsonApi with RC2 and it worked fine . I hace upgraded my project to RC5 and after that I have a problem with using JsonApiDatastoreConfig objects.
I have a class like this:
@Injectable()
@JsonApiDatastoreConfig({
baseUrl: 'http://localhost:9010/datasets/',
models: {
dataset: MarketTradeDataset,
datasetSpecifications: DatasetSpecification,
marketTradeDatasetItems: MarketTradeDatasetItem,
markets: Market
}
})
export class DataSetDatastore extends JsonApiDatastore {
}

when I want to inject this class to one my services I have the below error:
main.ts:78 TypeError: Cannot read property 'type' of null
at eval (http://localhost:3000/node_modules/@angular/compiler/bundles/compiler.umd.js:13673:45)
at Array.forEach (native)
at getTransitiveModules (http://localhost:3000/node_modules/@angular/compiler/bundles/compiler.umd.js:13672:17)
at CompileMetadataResolver._getTransitiveNgModuleMetadata (http://localhost:3000/node_modules/@angular/compiler/bundles/compiler.umd.js:13387:37)
at CompileMetadataResolver.getNgModuleMetadata (http://localhost:3000/node_modules/@angular/compiler/bundles/compiler.umd.js:13259:47)
at eval (http://localhost:3000/node_modules/@angular/compiler/bundles/compiler.umd.js:13228:58)
at Array.forEach (native)
at CompileMetadataResolver.getNgModuleMetadata (http://localhost:3000/node_modules/@angular/compiler/bundles/compiler.umd.js:13215:48)
at eval (http://localhost:3000/node_modules/@angular/compiler/bundles/compiler.umd.js:13228:58)
at Array.forEach (native)

I tried to add JsonApiDatastore, JsonApiDatastoreConfig to my module.ts but I still have the same error.
Is anyone use this library with RC5?

Allow Http service to be injected in JsonApiDatastore

I am using this angular2-jsonapi in the context of an Nativescript application. We use Nativescript-angular's NS_HTTP_PROVIDERS instead of HTTP_PROVIDERS. They basically extend the Http from Angular.

My issue is that the JsonApiDatastore completly ignores the provided Http from NS because it always creates an injector from the HTTP_PROVIDERS so none of my calls to our json-api work.

I think that, ideally, the http service should be injected directly in the JsonApiDatastore because it is the reponsiability of the consumer of this library to provide it.

"createOrPeek" produces missing data on second "findRecord" with relationships and sparse fieldsets

Hi

I use findRecord method with include a relationship and sparse fieldsets:

        return this.datastore.findRecord(Task, id, {
            include: 'taskType',
            fields: {
                taskType: 'name',
            }
        });

Only "name" will selected on "taskType"

Now the second call of the method (and all others methods like query etc.)

        return this.datastore.findRecord(Task, id, {
            include: 'taskType',
            fields: {
                taskType: 'name,desciption,foo,far',
            }
        });

This will not update the fields "desciption,foo,far" in the model "taskType"

I can also see any functions to handle it. For example "clearStore", or removeFromStore

What can i do?

Support Embedded Relationships

I want to start a discussion about supporting creation of embedded relationships. Currently the JSON API specification doesn't allow to create more than one entity in one request, but there are multiple issues to support it (json-api/json-api#1089, json-api/json-api#795).

I already implemented it, but don't know if I should create a pull-request, because it's not in the official spec yet: https://github.com/HennerM/angular2-jsonapi/blob/compound-resource-creation/src/services/json-api-datastore.service.ts#L107

What are your opinions on this topic?

Deleting records

Even if there is a PR for the delete record feature (#20), actually, I would prefer having the deleteRecord on the model and not on the Datastore. Moreover, I would like to soft delete the record and then destroy it only when saving. Something like this:

this.datastore.findRecord(Post, '1').subscribe(
    (post: Post) => {
        post.deleteRecord();
        post.isDeleted; // true
        post.save(); // => DELETE /posts/1
    }
);

While for immediately deleting the method, I would like to use:

this.datastore.findRecord(Post, '2').subscribe(
    (post: Post) => {
        post.destroyRecord(); // => DELETE /posts/2
    }
);

The only tricky thing is handling the rollbackAttributes() method on a deleted record, before saving it.

Like the rest of the interface, this is inspired by EmberData.

Do you like this approach or do you prefer having the deleteRecord on the Datastore?

Custom HTTP headers

I am calling a JsonApi service that requires OAuth2 authentication. It would be cool, if we could set custom HTTP headers somehow...

Btw.. nicely done!

Possibility to customize structure of response

Hi guys, can you help me please ? I not sure if it issue or it problem of JSON API

Example of JSON API response

{
  "success": true,
  "response": {
    "data": [
      {
        "type": "products",
        "id": "7",
        "attributes": {
          "id": "7",
          "urlCode": "regular-e-8713568281133",
          "title": "Sparta regular e e-bike"    
        },
        "links": [],
        "relationships": []
      }
    ]
  },
  "error": "",
  "ver": 1
}

As you can see, field data not in the root of response, and i have this error https://www.screencast.com/t/HFRquYLD.
I see that in private method JsonApiDatastore::extractQueryData you trying to get it from root.
Are there any options to provide correct work with custom response?

Refactoring: Serializers

Hi guys,

Currently, code that parse responses and generate payloads for requests spread out between two components JsonApiDatastore and JsonApiModel. This cause unnecessary complexity of each component and high coupling between them. Code is a bit messy and it hard to test it. Also it's not flexible because end user couldn't change any parts of data handling process.

I think that we should refactor this code to series of separate components - serializers. We should have three types of serializers:

  • ResponseSerializer - it should be able to convert response data to DocumentModel (see #26). Under hood it should use models serializers (see below) to convert separate resources.
  • RequestSerializer - it should be able to convert specified JsonApiModel to request payload. Again, under hood it should use models serializers to convert separate resource.
  • ModelSerializer - it should support two things: unserialize part of response data to JsonApiModel instance and serialize JsonApiModel instance into part of request payload.

All serializers should be configurable. I.e. we need to support following scenarios:

  • user should be able to replace default response, request and model serializers
  • user should be able to use cumstom serializer for specific JsonApiModel

Please let me know what you think about it.

Patch method in service

Hi,

do you plan to add a "patch" or a generic "save" method to the jsonapi-data-store.service?

Best whishes

baseURL of parse server

I have my parse server running in app-engine.
after initializing parse i could't fetch the objects, no idea how to move further.

Is their any alternative jsonapi to sync parse sdk classes?

Thanks in advance

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.