Angular/ Javascript module similar to gson in Java? - javascript

I was working on the Android and found Gson as a handy utility to convert JSON objects into Java objects. Since I am a big fan of Object Oriented architecture, I am trying to optimize angular code using OO architecture. Every object is mapped to a factory. I am wondering if there is any plugin in javascript or angular that can convert JSON to Angular Objects. e.g. If I have a card factory in Angular
app.factory('Card', [function() {
function Card(cardData) {
if (cardData) {
this.setData(cardData);
}else{
this.new();
}
};
Card.prototype = {
new: function(){
var cardData = {
title: 'Add your recommendations',
}
this.setData(cardData);
}
};
return Card;
}]);
and I am getting JSON data like this
{card: {title: 'demo_title'}}
it should map it automatically like GSON does. I can create a new module to do that, just wondering if someone already did that.

You don't need to create something to work like GSON because JSON has native support on javascript via JSON.parse("{\"card\": {\"title\": 'demo_title'}}" which will produce a javascript object like {card: {title: 'demo_title'}}. In addition, the angularjs' $http service, parses your JSON internally, so you don't even have to think about it.
However, what I think you are looking for, is an approach for using data from the server and instantiate a class from the parsed JSON. A regular approach for that, is using a class which in its constructor you provide the raw object and merge it into its properties. It's convenient to use angular.extend() but you can do it manually if you prefer.
It'd be something like this:
function Card(data) {
angular.extend(this, data);
}
var myCard = new Card({ title: 'demo_title' });
Furthermore, if you want to keep a more reliable model, you can declare a class to work as the object to hold the data, and add methods to retrieve and post data to the server.
angular.module('app')
.factory('Card', ['$http', function CardFactory($http) {
var Card = function (data) {
angular.extend(this, data);
}
Card.get = function (id) {
return $http.get('https://my.api/cards/' + id).then(function(response) {
return new Card(response.data.card);
});
};
Card.all = function () {
return $http.get('https://my.api/cards').then(function (res) {
return res.data.cards.map(function (x) {
return new Card(x);
});
});
};
Card.prototype.create = function () {
var card = this;
return $http.post('https://my.api/cards', card).then(function(response) {
card.id = response.data.card.id;
return card;
});
}
return Card;
}]);
So that you can use it like this:
angular.module('app')
.controller('CardCtrl', ['Card', function CardsCtrl(Card) {
// get a card
this.card = Card.get(1);
// create a new card
this.newCard = function newCard() {
var card = new Card({ title: 'new_card' });
card.create();
};
});
Ref.: Recommended way of getting data from the server

Related

Return integer to AngularJs from WebAPI

I'm new to angular so I apologise is this is a dumb question. I have a WebAPI method that creates data. I would like to return an integer from this method that indicates the new resource's id. If I return the whole object it works in my angular controller, yet when I only return the int I get an object with a prototype and I can't seem to get the id. Fiddler shows my integer value in the TextView, so the answer is there, its just a matter of reading it with angular (javascript).
Any suggestions will be appreciated.
My service looks like this:
(function () {
'use strict';
var app = angular.module('app');
app.factory('itemSvc', function ($resource) {
return $resource("api/item",
{},
{
createNew:
{
method: 'POST'
}
});
})
})();
and I call it like this:
itemSvc.createNew($scope.newItem).$promise.then(
function (item) {
var y = item;
$scope.items.push(item);
},
function (error) {})
You can create an instance of the resource, save it and obtain its id:
var newItem = new itemSvc($scope.newItem);
newItem.$save();
$scope.items.push(newItem.id);

How to create an empty $resource when using the factory pattern

I am using angular $resource in a factory pattern, where it is injected, so I only have to create the templates once and in one place. This works.
I can not seem to find any documentation on how to create a new resource object. This creates conditional branching when it is time to save, as I do not have an object to call $save() on.
For example: imagine I have this resource:
myService.factory('myWidget', [ '$resource',
function($resource){
return $resource('/my/widget/:id', {
[...]
So my controller can easily get access to myWidget thusly:
function( $scope, ... myWidget ) {
$scope.widget = myWidget.get({id: 'myId'});
$scope.save = function() {
$scope.widget.$save(); // plus progress dialog error handling etc
};
}
Which is very clean and awesome and as it should be. However, if I want to create a new one, I need conditional code both on create and save.
function( $scope, ... myWidget, mode ) {
if (mode === 'create') {
$scope.widget = {
id: 'myNewId',
property: <lots and lots of properties>
};
}
else {
$scope.widget = myWidget.get({id: 'myId'});
}
$scope.save = function() {
$scope.widget.$save(); // Not a $resource; $save() does not exist
};
}
Obviously, I can put conditional code in save(), or I can pass the widget as a parameter, as myWidget.save($scope.widget), but that seems lame. Is there no easy way to simply create a new $resource from the factory? IE:
if (mode === 'create') {
$scope.widget = myWidget.new({
id: 'myNewId',
property: <lots and lots of properties>
});
}
This would be functionally equivalent to :
if (mode === 'create') {
$scope.widget = $resource('/my/widget/:id');
But obviously without duplicating the resource code in the factory.
Surely there is some easy syntax for doing this. Yes?
I am using AngularJS 1.3. I really hope this is a stupid question as it seems like something that is an obvious use case. Even Backbone has a way to create a new REST-backed object with default values. :) Thanks much.
(Maybe the question should be "how do I create an object using the angularjs factory, as if I were the injector?")
You need to be creating a new widget:
if (mode === 'create') {
$scope.widget = new myWidget();
$scope.widget = {
id: 'myNewId',
property: <lots and lots of properties>
};
}

Object oriented approach with AngularJS

It seems that Angular does not provide a built-in solution to define class instances with properties and methods and that it's up the developer to build this.
What is the best practice to do this in your opinion?
How to you link this with the backend?
Some of the tips I have gathered use factory services and named functions.
Sources :
Tuto 1
Tuto 2
Thanks for your insights
I think that the closest structure to an Object it's probably a factory, for several reasons:
Basic Syntax:
.factory('myFactory', function (anInjectable) {
// This can be seen as a private function, since cannot
// be accessed from outside of the factory
var privateFunction = function (data) {
// do something
return data
}
// Here you can have some logic that will be run when
// you instantiate the factory
var somethingUseful = anInjectable.get()
var newThing = privateFunction(somethingUseful)
// Here starts your public APIs (public methods)
return {
iAmTrue: function () {
return true
},
iAmFalse: function () {
return false
},
iAmConfused: function () {
return null
}
}
})
And then you can use it like a standard Object:
var obj = new myFactory()
// This will of course print 'true'
console.log( obj.iAmTrue() )
Hope this helps, I perfectly know that the first impact with angular modules can be pretty intense...
You would use an angular service.
All angular services are singletons and can be injected into any controller.
Ideally you would keep only binding/actions on html in your controller and the rest of the logic would be in your service.
Hope this helps.
I got idea by evaluating this library : https://github.com/FacultyCreative/ngActiveResource
However this library assumes strict rest so I it wasn't work for me. What did work for is this:
I created base Model
var app = angular.module('app', []);
app .factory('Model', function(){
var _cache = {}; // holding existing instances
function Model() {
var _primaryKey = 'ID',
_this = this;
_this.new = function(data) {
// Here is factory for creating instances or
// extending existing ones with data provided
}
}
return Model;
});
Than I took simple function extensions "inherits"
Function.prototype.inherits = function (base) {
var _constructor;
_constructor = this;
return _constructor = base.apply(_constructor);
};
and now I cam creating my models like this
app.factory('Blog', [
'Model',
'$http',
function(Model, $http) {
function Blog() {
// my custom properties and computations goes here
Object.defineProperty(this, 'MyComputed' , {
get: function() { return this.Prop1 + this.Prop2 }
});
}
// Set blog to inherits model
Blog.inherits(Model);
// My crud operations
Blog.get = function(id) {
return $http.get('/some/url', {params: {id:id}}).then(function(response) {
return Blog.new(response.data);
});
}
return Blog;
}
]);
Finally, using it in controller
app.controller('MyCtrl', [
'$scope', 'Blog',
function($scope, Blog) {
Blog.get(...).then(function(blog) {
$scope.blog = blog;
});
}
])
Now, there is much more in our Model and extensions but this would be a main principle. I am not claiming this is best approach but I am working pretty big app and it really works great for me.
NOTE: Please note that I typed this code here and could be some errors but main principle is here.
As my question does not really reflect the issue I was facing, I'll just post my approach for the sake of it :
As Domokun put it, rule of thumb is to decouple front and back. But as I am only building a prototype and managing both ends, I would like to keep things in only one place and let the rest of the application use the central information as a service.
What I want to do here is to build a form through ng-repeat containing the model fields and most importantly how to display information in the form (e.g. 'Last name' instead of 'lastname')
So as I started working around with mongoose models here's what I have managed to do :
Firstly, it is possible to pass the mongoose schema of a model from node side to angular side with an app.get request with the following response :
res.send(mongoose.model('resources').schema.paths);
this spitts out an object containing all fields of the 'resources' collection. On top of that I included some additional information in the model like this :
var resourceSchema = new Schema({
_id: { type: Number },
firstname: { type: String, display:'First name' },
lastname: { type: String, display:'Last name' }
});
mongoose.model('resources', resourceSchema);
So basically I can retrieve this symmetrically on angular side and I have all I need to map the fields and display them nicely. It seems I can also describe the validation but I'm not there yet.
Any constructive feedback on this approach (whether it is valid or totally heretic) is appreciated.

Add methods to a collection returned from an angular resource query

I have a resource that returns an array from a query, like so:
.factory('Books', function($resource){
var Books = $resource('/authors/:authorId/books');
return Books;
})
Is it possible to add prototype methods to the array returned from this query? (Note, not to array.prototype).
For example, I'd like to add methods such as hasBookWithTitle(title) to the collection.
The suggestion from ricick is a good one, but if you want to actually have a method on the array that returns, you will have a harder time doing that. Basically what you need to do is create a bit of a wrapper around $resource and its instances. The problem you run into is this line of code from angular-resource.js:
var value = this instanceof Resource ? this : (action.isArray ? [] : new Resource(data));
This is where the return value from $resource is set up. What happens is "value" is populated and returned while the ajax request is being executed. When the ajax request is completed, the value is returned into "value" above, but by reference (using the angular.copy() method). Each element of the array (for a method like query()) will be an instance of the resource you are operating on.
So a way you could extend this functionality would be something like this (non-tested code, so will probably not work without some adjustments):
var myModule = angular.module('myModule', ['ngResource']);
myModule.factory('Book', function($resource) {
var service = $resource('/authors/:authorId/books'),
origQuery = service.prototype.$query;
service.prototype.$query = function (a1, a2, a3) {
var returnData = origQuery.call(this, a1, a2, a3);
returnData.myCustomMethod = function () {
// Create your custom method here...
return returnData;
}
}
return service;
});
Again, you will have to mess with it a bit, but that's the basic idea.
This is probably a good case for creating a custom service extending resource, and adding utility methods to it, rather than adding methods to the returned values from the default resource service.
var myModule = angular.module('myModule', []);
myModule.factory('Book', function() {
var service = $resource('/authors/:authorId/books');
service.hasBookWithTitle = function(books, title){
//blah blah return true false etc.
}
return service;
});
then
books = Book.list(function(){
//check in the on complete method
var hasBook = Book.hasBookWithTitle(books, 'someTitle');
})
Looking at the code in angular-resource.js (at least for the 1.0.x series) it doesn't appear that you can add in a callback for any sort of default behavior (and this seems like the correct design to me).
If you're just using the value in a single controller, you can pass in a callback whenever you invoke query on the resource:
var books = Book.query(function(data) {
data.hasBookWithTitle = function (title) { ... };
]);
Alternatively, you can create a service which decorates the Books resource, forwards all of the calls to get/query/save/etc., and decorates the array with your method. Example plunk here: http://plnkr.co/edit/NJkPcsuraxesyhxlJ8lg
app.factory("Books",
function ($resource) {
var self = this;
var resource = $resource("sample.json");
return {
get: function(id) { return resource.get(id); },
// implement whatever else you need, save, delete etc.
query: function() {
return resource.query(
function(data) { // success callback
data.hasBookWithTitle = function(title) {
for (var i = 0; i < data.length; i++) {
if (title === data[i].title) {
return true;
}
}
return false;
};
},
function(data, response) { /* optional error callback */}
);
}
};
}
);
Thirdly, and I think this is better but it depends on your requirements, you can just take the functional approach and put the hasBookWithTitle function on your controller, or if the logic needs to be shared, in a utilities service.

JavaScript OOP Models Formula

Working on creating a dirt simply MVC framework for one of my own projects. Rather than using one that is public, I decided to create one since my needs are very unusual.
I've got my structure down for the Controllers and Views, however, I'm having some issues creating my model structure.
This is what I have for my model structure:
model.models = function(args){
init: function(){
this.on_init();
},
on_init: args.on_init || noop,
data: args.data || {},
};
So then, I would call this as a basic formula for all of the models I want to create. For example, I want to create employees, notifications and some other models using this as a basic blueprint, then make some basic adjustments.
I call:
model.employees = new model.models({
on_init: function(){
//something specific
},
data: {
//defaults
}
});
And we're all good up to this point, but here is where I'm having troubles. Now, when I want to create my end result, the model, I cannot create a new object from an object.. it must be a function.
The only thing I can think of is creating a return function for the second method, but that renders some issues in itself. I have done some research looking at other MVC code, but I was unable to wrap my head around it.
Any help would be very much appreciated!
is this what you want ?
model.models = function(args){
var noop = function(){};
var o = {};
var init = args.on_init || noop;
var data = args.data || {};
init();
//handle other initialization
//o.a = xx;
//o.b = xx;
//o.c = data.xxx;
//....
return o;
}
then you can use the new, and it can't appear syntax error
Did a lot of fiddling, came up with this:
var blueprint = function(args){
return {
data: args.data,
on_init: args.on_init,
create: function(args){
this.on_init();
return {
data: this.data,
whatever: function(){
console.log(args);
}
};
}
};
};
var notifs = new blueprint({
on_init: function(){
console.log('init');
},
data: {
test: 'test'
}
});
var res = notifs.create('test');
console.log(blueprint);
console.log(notifs);
console.log(res);
It comes out with a main function that works, the notifs function is customizable for each individual object type, then calling the create method will create the end method.
Boom!

Categories

Resources