i have some free days between projects so i decided to learn typescript.
Therefore i want to do a repository factory. The idea is simple, there is some API where i can access models crud actions. It is nice solution to have one generic repository for buisnes models. But still get the model class from CRUD methods.
What is the correct way to do it ? Can someone help me solve this? How to do it right ?
What i want to achive is:
var factory = new RepositoryFactory($resource, 'http://api.com');
var repo = factory.getRepository(User);
var user = repo.getAll();
I starded to do somethink like this:
IEntity:
'use strict';
export interface IEntity {
id: number;
}
IRepository:
'use strict';
import {IEntity} from "./IEntity";
export interface IRepository<T extends IEntity> {
getAll(params:Object): T[];
getById(id:number): T;
create(data:Object): T;
update(id:number, data:{id:number}): T;
remove(id:number): boolean;
}
RepositoryFactory
'use strict';
import {IEntity} from "./IEntity";
import {Repository} from "./Repository";
export default class RepositoryFactory {
protected $resource:any;
protected url:string;
constructor($resource:any, url:string) {
this.$resource = $resource;
this.url = url;
}
public getRepository<T extends IEntity>(model:T):Repository {
return new Repository(this.$resource, this.url, model)
}
}
`Repository`:
'use strict';
import {IRepository} from "./IRepository";
import {IEntity} from "./IEntity";
export default class Repository<T extends IEntity> implements IRepository<T> {
protected $resource:any;
protected resource:any;
protected url:string;
protected model:T;
constructor($resource:any, url:string, model:T) {
this.$resource = $resource;
this.url = url;
this.model = model;
this.resource = this.getResource(model.path);
}
getAll(params:Object):T[] {
let results = this.resource.query((typeof params === 'undefined' ? {} : params), this.onSuccess);
return this.returnPromise(results);
}
getById(id:number):T {
let model = this.resource.get({id: id}, this.onSuccess);
return this.returnPromise(model);
}
create(data:Object):T {
let model = new this.resource(data);
return model.$save().then(this.onSuccess);
}
update(id:number, data:Object):T {
data.id = id;
var model = new this.resource(data);
return model.$update().then(this.onSuccess);
}
remove(id:number):boolean {
var data = {id: id};
var model = new this.resource(data);
return model.$delete().then(this.onSuccess);
}
protected getResource(path:string) {
return this.$resource(this.url + path, {id: '#id'}, {
'update': {
method: 'PUT'
},
'get': {
method: 'GET'
},
'save': {
method: 'POST'
},
'query': {
method: 'GET'
},
'remove': {
method: 'DELETE'
},
'delete': {
method: 'DELETE'
}
});
}
protected onSuccess(response:any) {
if (this.checkPropertyExistence(response, 'data')) {
if (response.data instanceof Array) {
let results = response.data;
for (var key in results) {
if (results.hasOwnProperty(key)) {
results[key] = new this.model(results[key]);
}
}
return results;
} else {
return new this.model(response.data);
}
}
return response;
}
protected transformRequest(obj:Object) {
var str = [];
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
}
}
return str.join("&");
}
protected returnPromise(object:Object) {
return object.$promise.then(function (result) {
return result;
});
}
protected checkPropertyExistence(obj:Object, paths:string|string[]) {
for (var i = 0; i < paths.length; i++) {
if (!obj || !obj.hasOwnProperty(paths[i])) {
return false;
}
obj = obj[paths[i]];
}
return true;
}
}
User:
'use strict';
import {IEntity} from "./IEntity";
export default class User implements IEntity {
id:number;
name:string;
static _path:string = '/users';
constructor(id:number, name:string) {
this.id = id;
this.name = name;
}
static get path():string {
return this._path;
}
}
Hey guys i have menage to created working example for you.
The example shows how to create repository factory with typescript.
I have prepared this DEMO FIDDLE where you can press RUN on the right side, and the page with single button will appear. After clicking the button console.log will display getAll method results.
In example i have mocked data, simply to show that factory works. If anyone wants to improves it, feel free and welcome to do it!
How it works?
//create factory, you can do it in an abstract controller class
//and later extends controller by it so you can easy get access to repository
var factory = new RepositoryFactory();
//inject path for $resource (unused in example)
//and your model.entity namespace
var repo = factory.getRepository('/users', 'User');
//call repo method
var users = repo.getAll({});
When using angular create RepositoryFactory as a service. Thats all. You may also want to inject $resource to get the right data from API.
And here is a full example code:
interface IEntity {
id: number;
}
class Entity implements IEntity {
private _id:number;
private _name:string;
private _birth:Date;
constructor(parameters: {id:number, name:string, birth:Date}) {
this._id = parameters.id;
this._name = parameters.name;
this._birth = parameters.birth;
}
get id():number {
return this._id;
}
set id(value:number) {
this._id = value;
}
get name():string {
return this._name;
}
set name(value:string) {
this._name = value;
}
get birth():Date {
return this._birth;
}
set birth(value:Date) {
this._birth = value;
}
}
class RepositoryFactory {
public getRepository<T extends IEntity>(path:string, model:string):IRepository<T> {
return new Repository<T>(path, model)
}
}
interface IRepository<T extends IEntity> {
getAll(params:Object): T[];
getById(id:number): T;
create(data:Object): T;
update(id:number, data:{id:number}): T;
remove(id:number): boolean;
}
class Repository<T extends IEntity> implements IRepository<T> {
protected path:string;
protected model:string;
constructor(path:string, model:string) {
this.path = path;
this.model = model;
}
getAll(params:Object):T[] {
let results = [
{id:1, name: 'rafal', birth:new Date()},
{id:2, name: 'szymon', birth:new Date()},
{id:3, name: 'mateusz', birth:new Date()},
];
let entities= [];
for (var key in results) {
if (results.hasOwnProperty(key)) {
let entity = Object.create(window[this.model].prototype);
entity.constructor.apply(entity, new Array(results[key]));
entities.push(entity);
}
}
return entities;
}
getById(id:number):T {
let object = {id:id, name: 'test', birth:new Date()};
var entity = Object.create(window[this.model].prototype);
entity.constructor.apply(entity, new Array(object));
return entity;
}
create(data:Object):T {
var entity = Object.create(window[this.model].prototype);
entity.constructor.apply(entity, new Array(data));
return entity;
}
update(id:number, data:Object):T {
var entity = Object.create(window[this.model].prototype);
entity.constructor.apply(entity, new Array(data));
return entity;
}
remove(id:number):boolean {
return true;
}
}
var factory = new RepositoryFactory();
var repo = factory.getRepository('/users', 'Entity');
var users = repo.getAll({});
Related
I am pulling data database,
My response class
sealed class Response<out T>{
object Loading: Response<Nothing>()
data class Success<out T>(val data: T): Response<T>()
data class Error(val message: String): Response<Nothing>()
}
Where should I handle the incoming data like this?
First approach
View Model
class DataViewModel (private val useCase: UseCase, ) : ViewModel() {
private val _dataState = mutableStateOf("")
val dataState = _dataState
val loading = mutableStateOf(false)
private val _eventFlow = MutableSharedFlow<UIEvent>()
val eventFlow = _eventFlow.asSharedFlow()
fun getData() {
viewModelScope.launch {
useCase.getData().collect { response ->
when(response){
is Response.Error -> {
_eventFlow.emit(UIEvent.ShowSnackBar(response.message))
loading.value=false
}
is Response.Loading -> loading.value=true
is Response.Success -> {
_dataState.value = response.data
loading.value=false
_eventFlow.emit(UIEvent.Success)
}
}
}
}
}
sealed class UIEvent {
data class ShowSnackBar(val message: String) : UIEvent()
object Success : UIEvent()
}
}
Screen
#Composable
fun DataScreen(dataViewModel: DataViewModel) {
val scaffoldState = rememberScaffoldState()
LaunchedEffect(key1 = true) {
dataViewModel.eventFlow.collectLatest { event ->
when (event) {
is DataViewModel.UIEvent.ShowSnackBar -> {
scaffoldState.snackbarHostState.showSnackbar(event.message)
}
is DataViewModel.UIEvent.Success -> {
"Do something"
}
}
}
}
Scaffold(
scaffoldState = scaffoldState,
content = { EditProfileContent(dataViewModel.dataState.value) }
)
}
Second Approach
View Model
class DataViewModel (private val useCase: UseCase, ) : ViewModel() {
private val _dataState = mutableStateOf<Response<Data>>(Response.Loading)
val dataState = _dataState
fun getData() {
viewModelScope.launch {
useCase.getData().collect { response ->
_dataState.value = response
}
}
}
}
Screen
#Composable
fun DataScreen(dataViewModel: DataViewModel) {
val scaffoldState = rememberScaffoldState()
when(val dataState = dataViewModel.dataState.value){
is Response.Error -> {
LaunchedEffect(key1 = true, block = {scaffoldState.snackbarHostState.showSnackbar(dataState.message)}) }
is Response.Loading -> { Loading() }
is Response.Success -> {
Scaffold(
scaffoldState = scaffoldState,
content = { EditProfileContent(dataState.data) }
)
// Do something else
}
}
}
The first approach seems much easier to me, especially when I'm pulling in more than one data, but when I search, the second approach is usually used in most places.
What's wrong with using the first approach? Which is the best practice?
Hi i need to get input values from ng-repeat, i am having Item class like as follows
public class Item
{
private String _DesignId;
public String DesignId
{
get { return _DesignId; }
set { _DesignId = value; }
}
private String _DesignName;
public String DesignName
{
get { return _DesignName; }
set { _DesignName = value; }
}
private String _Unit;
public String Unit
{
get { return _Unit; }
set { _Unit = value; }
}
private String _Price;
public String Price
{
get { return _Price; }
set { _Price = value; }
}
}
Here my angularjs
$scope.saveAllValues = function() {
$scope.mylist = {};
angular.forEach($scope.invoice.items , function(value, key){
var objclsu = {
"DesignId" : value.DesignId,
"DesignName" : $scope.ctrl.DesignName,
"Unit" : value.unit,
"Price" : value.price,
};
$scope.mylist.push({objclsu});
});
Here I can not use push. objclsu is not an array. Here how to add objclsu into $scope.mylist.
Here my web method i am using generic list as a parameter
[WebMethod()]
public static void SaveList(List<Item> mylist)
{
}
var httpreq = {
method: 'POST',
url: 'Requirementapi/SaveList',
headers: { 'Content-Type': 'application/json; charset=utf-8', 'dataType': 'json' },
data: { mylist : $scope.mylist }
}
$http(httpreq).success(function (response) {
callmsg(" invoice Saved successfully.");
});
How can I post a list from view to controller using ajax? I have this code in client side:
$(".js-save").click(function (e) {
e.preventDefault();
var button = $(this);
var lstERegistroVenta = [];
var ERegistroVenta = new Object();
ERegistroVenta.IdProducto = 1;
ERegistroVenta.IdInventario = 2;
ERegistroVenta.MontoProducto = 12.5;
ERegistroVenta.CantidadProducto = 1;
ERegistroVenta.MontoTotalSinIGV = 20.5
ERegistroVenta.MontoTotal = 23.5
lstERegistroVenta.push(ERegistroVenta);
$.ajax({
dataType: 'json',
type: 'POST',
url: "/API/Inventario/Venta/1",
data: JSON.stringify({ lstERegistroVenta: lstERegistroVenta }),
success: function () {
toastr.success("Correcto");
},
error: function () {
toastr.error("Error");
}
});
});
When I try to pass the data I receive only empty list. In server side I have this API
[HttpPost]
[Route("API/Inventario/{Venta}/{IdProducto}")]
public IHttpActionResult AsignarProducto(int IdProducto,List<ERegistroVenta> lstERegistroVenta)
{
return Ok();
}
public class ERegistroVenta
{
public int IdProducto { get; set; }
public int IdInventario { get; set; }
public double MontoProducto { get; set; }
public int CantidadProducto { get; set; }
public double MontoTotalSinIGV { get; set; }
public double MontoTotal { get; set; }
}
First of all, I wouldn't suggest calling an API method directly from a View. Instead, call a controller method which should internally call the API method. The code for this would look something like below.
$.ajax({
dataType: 'json',
type: 'POST',
url: "/TestController/TestMethod",
data: { "IdProducto" : "1", "lstERegistroVenta": lstERegistroVenta },
success: function () {
toastr.success("Correcto");
},
error: function () {
toastr.error("Error");
}
});
[HttpPost]
public void TestMethod(int IdProducto,List<ERegistroVenta> lstERegistroVenta)
{
// Your Logic Here
}
I found a solution. Maybe my mistake was that I didn't send an anonymous list of objects.
Take a look to this:https://kwilson.io/blog/post-an-array-of-objects-to-webapi-using-jquery/
My solution below:
$(".js-save").click(function (e) {
e.preventDefault();
var button = $(this);
var lstERegistroVenta = [];
var ERegistroVenta = new Object();
ERegistroVenta.IdProducto = 1;
ERegistroVenta.IdInventario = 2;
ERegistroVenta.MontoProducto = 12.5;
ERegistroVenta.CantidadProducto = 1;
ERegistroVenta.MontoTotalSinIGV = 20.5
ERegistroVenta.MontoTotal = 23.5
lstERegistroVenta.push(ERegistroVenta);
$.ajax({
dataType: 'json',
method: 'POST',
url: "/API/Inventario/Venta/1",
data: { '': lstERegistroVenta },
success: function () {
toastr.success("Correcto");
},
error: function () {
toastr.error("Error");
}
});
});
And in server side:
[HttpPost]
[Route("API/Inventario/{Venta}/{IdProducto}")]
public IHttpActionResult AsignarProducto(int IdProducto,List<ERegistroVenta> lstERegistroVenta)
{
// Logic Here
}
Hope this helps to another user.
I'm new to angular and trying to make a call to perform crud operations from angular to a newly created mvc webapi controller... cannot get around the 404 not found on the get right now. Looking for some guidance from the pros, thanks!
Angular:
angular.module('PersonApp').service('PersonService', ['$http', function` ($http) {
var PersonService = {};
var urlBase = '/api/PersonApi/';
PersonService.getPersons = function () {
console.log(urlBase);
return $http.get(urlBase);
};
PersonService.upsertPerson = function (person) {
return $http.post(urlBase, person); //must have ,person !!!!!!!!!!
};
PersonService.removePerson = function (id) {
return $http.delete(urlBase + id);
};
return PersonService;
}]);
PersonApi.cs api:
public class PersonApi : ApiController
{
TutorialDataEntities ef_Db = new TutorialDataEntities();
Repository.Repository PersonRepository = new Repository.Repository();
[Route("api/PersonApi/{person}")]
[HttpPost]
public HttpResponseMessage UpsertPerson([FromBody]DTOPerson person)
{
if (ModelState.IsValid) // and if you have any other checks
{
var result = PersonRepository.UpsertPerson(person);
formatDate(result.birthDate);
return Request.CreateResponse(HttpStatusCode.Created, result);
}
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
[Route("api/PersonApi/{id}")]
[HttpDelete]
public HttpResponseMessage RemovePerson([FromUri]int id)
{
PersonRepository.RemovePerson(id);
return Request.CreateResponse(HttpStatusCode.OK);
}
[Route("api/PersonApi/")]
[HttpGet]
public HttpResponseMessage GetPersons()
{
var result = PersonRepository.GetAllPersons();
foreach (var person in result)
{
formatDate(person.birthDate);
}
if (result == null)
return Request.CreateResponse(HttpStatusCode.NotFound);
return Request.CreateResponse(HttpStatusCode.OK, result);
}
private DateTime formatDate(DateTime date)
{
return Convert.ToDateTime(date.ToString("MM/dd/yyyy"));
}
}
My solution explorer:
WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Routeconfig.cs
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
I started to learn angularjs and trying to put together Angularjs+Breezejs+EntityFramework.
Here is my context cs file:
public class GrantsDbContext : DbContext {
public GrantsDbContext() {
Database.SetInitializer<GrantsDbContext>(new MigrateDatabaseToLatestVersion<GrantsDbContext, Configuration>());
}
public DbSet<OrgItem> OrgList;
}
OrgItem is just model-class (contained: ID, FullName, ShortName), and I set automigrate, and write default connection string in Web.config
That's breeze controller cs file:
[BreezeController]
public class DbController : ApiController {
private EFContextProvider<GrantsDbContext> _contextProvider = new EFContextProvider<GrantsDbContext>();
[HttpGet]
public String Metadata() {
return _contextProvider.Metadata();
}
[HttpPost]
public SaveResult SaveChanges(JObject saveBundle) {
return _contextProvider.SaveChanges(saveBundle);
}
[HttpGet]
public IQueryable<OrgItem> OrgList() {
return _contextProvider.Context.OrgList;
}
}
Here is angular service, that created manager for breeze:
angular.module('app').factory("entityManagerFactory", ["breeze", emFactory]);
function emFactory(breeze) {
new breeze.ValidationOptions({ validateOnAttach: false }).setAsDefault();
var serviceName = "breeze/db";
var metadataStore = new breeze.MetadataStore();
var provider = {
newManager: newManager
};
return provider;
function newManager() {
var mgr = new breeze.EntityManager({
serviceName: serviceName,
metadataStore: metadataStore
});
return mgr;
}
}
That's my controller that called query:
angular.module("app")
.controller("OrgCtrl", ['entityManagerFactory', OrgCtrl]);
function OrgCtrl(entityManagerFactory) {
var vm = this;
vm.orgs = [];
var manager = entityManagerFactory.newManager();
var orgsQuery = new breeze.EntityQuery("OrgList").select("ID, FullName, ShortName");
manager.executeQuery(orgsQuery).then(succesCallback).catch(failCallback);
function succesCallback(data) {
vm.orgs = data.result;
}
function failCallback(error) {
console.log(error);
}
}
When I firstly started the app, database was created with one table '_MigrationHistory' and then I got the error: 'Error: cannot execute _executeQueryCore until metadataStore is populated.'
'localhost:49934/breeze/db/Metadata' is available (200 OK).
All libraries updated with nuget.
Help please to solve this problem!
Thanks!