I use ExtJs 4.1 and DeftJs.
Some class is defined with a constructor like that:
Ext.define( 'A.helper.Report', {
config: {
conf : null,
viewMain : null
},
constructor: function( oConfig ) {
this.conf = oConfig.conf;
this.viewMain = oConfig.viewMain;
this.initConfig( oConfig );
}
...
Now, I create several instances of this class like that:
var class1 = Ext.create( 'A.helper.Report', {
conf: someValue,
viewMain: someObject
} );
var class2 = Ext.create( 'A.helper.Report', {
conf: otherValue,
viewMain: otherObject
} );
When using these instances, although giving them different oConfig data, both class1 and class2 now have the data of the 2nd oConfig.
So when calling this.conf in both instances, I get someValue.
How can I keep the data of already created instances?
Solution:
I wrote
Ext.define( 'A.helper.Report', {
...
references: {}
...
and put my instances in there, overriding old instances.
Switched to references: null helped.
...
Be careful to not messing around with prototype objects...
Rewritten answer
You are doing it the wrong way.
See this working JSFiddle
Ext.define('A.helper.Report', {
config: {
conf : null,
viewMain : null
},
constructor: function(cfg) {
this.initConfig(cfg);
}
});
var class1 = Ext.create('A.helper.Report',{
conf: 66,
viewMain: 'Bear'
});
var class2 = Ext.create('A.helper.Report',{
conf: 88,
viewMain: 'Eagle'
});
class1.getConf(); // 66
class1.getViewMain(); // Bear
class2.getConf(); // 88
class2.getViewMain(); // Eagle
Related
I'm having an issue with some code I wrote that's utterly stumped me.
The main JSX tutorial available at the JSX Github Page has an example class called Point, which looks like:
class Point {
var x = 0;
var y = 0;
function constructor() {
}
function constructor(x : number, y : number) {
this.set(x, y);
}
function constructor(other : Point) {
this.set(other);
}
function set(x : number, y : number) : void {
this.x = x;
this.y = y;
}
function set(other : Point) : void {
this.set(other.x, other.y);
}
}
That class has a clear example of a multiple constructor types which I'm familiar from my C++ days. It even has a defined copy constructor, which I think is great.
However, if I got and create a similar class for use by me:
export default class MutableDataStore {
constructor() {
this.data = [];
this.settings = {};
}
//Copy constructor
constructor(other : MutableDataStore) {
this.data = other.data.slice();
this.settings = Object.assign({}, this.settings);
}
//...Other functions omitted
}
I get the following error in my webpack build:
ERROR in ./src/stores/helper-classes/mutabledatastore.jsx
Module build failed: SyntaxError: Duplicate constructor in the same class (8:1)
I'm completely stumped by this, since I can't find anything similar on the web about this, unless it seems to be a transient issue.
My webpack.config.js is:
var webpack = require("webpack");
var path = require("path");
var src = path.resolve(__dirname, "src");
var app = path.resolve(__dirname, "app");
var config = {
entry: src + "/index.jsx",
output: {
path: app,
filename: "javascript.js"
},
module: {
loaders: [{
include: src,
loader: "babel-loader"
}]
}
};
module.exports = config;
and my babel presets are es2015 and react.
Any help would be appreciated!
As loganfsmyth said in the comments, there can only be one constructor in an ES6 class. You can get the desired effect by either checking if other is set in the construct or by providing a default value for the parameter
export default class MutableDataStore {
constructor(other : MutableDataStore) {
this.data = other ? other.data.slice() : [];
this.settings = other ? Object.assign({}, other.settings) : {};
}
//...Other functions omitted
}
// or
export default class MutableDataStore {
constructor(other : MutableDataStore = { data: [], settings: {} }) {
this.data = other.data.slice();
this.settings = Object.assign({}, other.settings);
}
//...Other functions omitted
}
As a side not, I think you might have intended the copy constructor to copy the settings from other, not this.
I'm a huge fan of angular but it's got some tricky concepts with extremely nuanced differences between them and this is one of them.
I just want to create an class that I can use to create custom objects in my Angular controllers and factories. It surely shouldn't be that hard but I can't figure out how to do it. I want to have a custom, ResultSet class which I can instantiate to create instances of ResultSet. However for the life of me I can't figure out the correct syntax of factory v. service to use.
This is all I want:
ResultSet = function(dataSet){
this.filter = function(){
# filters and returns dataSet
# ...
}
}
and then I want to be able instantiate an instance of ResultSet inside a controller etc:
MyApp.controller('pageCtrl', ['ResultSet', (ResultSet) ->
# ...
rs = ResultSet.new(dataToFilter)
How can I create a service that allows me to create instances of my custom object?
It seems more correct to use an Angular Service rather than a Factory since a service returns an instance of an object (which is exactly what I want). But I can't figure out how to do this...
How would I use a service to declare my custom ResultSet class and then how would I instantiate an instance from it?
Maybe you were looking for something like this:
.factory('User', function (Organisation) {
/**
* Constructor, with class name
*/
function User(firstName, lastName, role, organisation) {
// Public properties, assigned to the instance ('this')
this.firstName = firstName;
this.lastName = lastName;
this.role = role;
this.organisation = organisation;
}
/**
* Public method, assigned to prototype
*/
User.prototype.getFullName = function () {
return this.firstName + ' ' + this.lastName;
};
/**
* Private property
*/
var possibleRoles = ['admin', 'editor', 'guest'];
/**
* Private function
*/
function checkRole(role) {
return possibleRoles.indexOf(role) !== -1;
}
/**
* Static property
* Using copy to prevent modifications to private property
*/
User.possibleRoles = angular.copy(possibleRoles);
/**
* Static method, assigned to class
* Instance ('this') is not available in static context
*/
User.build = function (data) {
if (!checkRole(data.role)) {
return;
}
return new User(
data.first_name,
data.last_name,
data.role,
Organisation.build(data.organisation) // another model
);
};
/**
* Return the constructor function
*/
return User;
})
From this post by Gert Hengeveld.
myApp.factory('ResulSet', function() {
function ResultSetInstance(dataSet) {
this.filter = function(){
// ...
}
}
return {
createNew: function(dataSet) {
return new ResultSetInstance(dataSet);
}
};
});
and then
myApp.controller('pageCtrl', function(ResultSet) {
var someData = ...;
var rs = ResultSet.createNew(someData);
}
Edit (from the question asker)
On experimenting with this further I found that you didn't even need to have the createNew method.
myApp.factory('ResultSetClass', function() {
ResultSetClass = function(dataSet) {
this.filter = function(){
// ...
}
}
return ResultSetClass
});
works just fine and then you can call new ResultSetClass(args).
Note for those using Coffeescript
Coffeescript will return the last variable or method in your class instance so if you are using coffeescript (as a general rule), it's imperative to return this at the end of the class definition
myApp.factory 'ResultSetClass', () ->
ResultSetClass = (dataset) ->
this.filter = () ->
# do some stuff
return this
return ResultSetClass
If you don't return this explicitly then you'll find that when you call
myApp.factory 'ResultSetClass', () ->
ResultSetClass = (dataset) ->
this.filter = () ->
# do some stuff
then you'll simply be left with the last thing the coffeescript returns which is the filter method.
I recently has do do something like that because I wanted to implement a factory of class instance, and being able to configurate my instances and benefit from Angular Dependency injection. I ended up with something like that
// Implem
export class XAPIService {
private path: string;
/* this DO NOT use angular injection, this is done in the factory below */
constructor(
private seed: XAPISeed,
private $http: ng.IHttpService,
private slugService: SlugService
) {
const PATH_MAP: Map<Y, Z> = new Map([
['x', id => `/x/${id}`],
['y', id => `/y/${id}`],
]);
this.path = PATH_MAP.get(this.seed.type)(this.seed.id);
}
list() {
/* implem that use configured path */
return this.slugService
.from(this.path + `/x`)
.then(url => this.$http.get<IX>(url))
.then(response => response.data)
}
}
export type IXAPIFactory = (s: XAPISeed) => XAPIService;
export function XAPIFactory(
$http: ng.IHttpService,
myService: SlugService
) {
'ngInject';
return (seed: XAPISeed) =>
new XAPIService(seed, $http, myService);
}
// angular
angular.module('xxx', [])
.factory('xAPIFactory', XAPIFactory)
// usage in code
export class XsController implements ng.IComponentController {
/* #ngInject */
constructor(
private xAPIFactory: IXAPIFactory,
) {}
$onInit() {
this.xService = this.xAPIFactory({ id: 'aaabbbaaabbb', type: 'y' });
return this.xService.list()
.then(xs => {
this.xs = xs;
})
}
}
E.g. in angularJS I may use the following construction:
myApp.factory('MyFactory', function(injectable) {
return function(param) {
this.saySomething = function() {
alert("Param=" + param + " injectable=" +injectable);
}
};
});
This can later be used like this:
function(MyFactory) {
new MyFactory().saySomething();
}
When the function passed to the method factory gets invoked, the param injectable is caged and will further be available to new instances of MyFactory without any need to specify that parameter again.
Now I want to use TypeScript and obviously I want to specify that my MyFactory is newable, and has a function saySomething. How could I do this elegantly?
I could write something like this:
class MyFactory {
constructor(private injectable, private param) {}
saySomething() {
alert(...);
}
}
myApp.factory('myFactory', function(injectable) {
return function(param) {
return new MyFactory(injectable, param);
}
});
But this changes the API:
function(myFactory) {
myFactory().saySomething();
}
I wonder if it could be more elegant, because I like how the "new" expresses quite clearly that a new unique object is created and this object creation is the whole purpose of the factory.
** Edit: TypeScript >= 1.6 supports class expressions and you can now write things like:
myApp.factory(injectable: SomeService) {
class TodoItem {
...
}
}
** Original answer:
I have the same problem: with AngularJS and ES5, I enjoy dependency injection not polluting constructors and be able to use the new keyword.
With ES6 you can wrap a class inside a function, this is not yet supported by TypeScript (see https://github.com/Microsoft/TypeScript/issues/307).
Here what I do (MyFactory is now class TodoItem from a todo app to be more relevant):
class TodoItem {
title: string;
completed: boolean;
date: Date;
constructor(private injectable: SomeService) { }
doSomething() {
alert(this.injectable);
}
}
class TodoItemFactory() {
constructor(private injectable: SomeService) { }
create(): TodoItem {
return new TodoItem(this.injectable);
}
// JSON from the server
createFromJson(data: any): TodoItem {
var todoItem = new TodoItem(this.injectable);
todoItem.title = data.title;
todoItem.completed = data.completed;
todoItem.date = data.date;
return todoItem;
}
}
// In ES5: myApp.factory('TodoItem', function(injectable) { ... });
myApp.service('TodoItemFactory', TodoItemFactory);
class TodosCtrl {
// In ES5: myApp.controller('TodosCtrl', function(TodoItem) { ... });
constructor(private todoItemFactory: TodoItemFactory) { }
doSomething() {
// In ES5: var todoItem1 = new TodoItem();
var todoItem1 = this.todoItemFactory.create();
// In ES5: var todoItem2 = TodoItem.createFromJson(...)
var todoItem2 = this.todoItemFactory.createFromJson(
{title: "Meet with Alex", completed: false}
);
}
}
This is less elegant than with ES5 and functions (and not using classes with TypeScript is a no go) :-/
What I would like to write instead:
#Factory
#InjectServices(injectable: SomeService, ...)
class TodoItem {
title: string;
completed: boolean;
date: Date;
// No DI pollution
constructor() { }
saySomething() {
alert(this.injectable);
}
static createFromJson(data: string): TodoItem {
...
}
}
#Controller
#InjectFactories(TodoItem: TodoItem, ...)
class TodosCtrl {
constructor() { }
doSomething() {
var todoItem1 = new TodoItem();
var todoItem2 = TodoItem.createFromJson({title: "Meet with Alex"});
}
}
Or with functions:
myApp.factory(injectable: SomeService) {
class TodoItem {
title: string;
completed: boolean;
date: Date;
// No constructor pollution
constructor() { }
saySomething() {
alert(injectable);
}
static createFromJson(data: string): TodoItem {
...
}
}
}
myApp.controller(TodoItem: TodoItem) {
class TodosCtrl {
constructor() { }
doSomething() {
var todoItem1 = new TodoItem();
var todoItem2 = TodoItem.createFromJson({title: "Meet with Alex"});
}
}
}
I could write something like this
This is what I do
Can I create a TypeScript class within a function
No it needs to be at the top level of the file or in a module. Just FYI if were able to create it inside a function the information would be locked inside that function and at least the type info would be useless.
What's the reason for instantiating multiple instances of MyFactory? Would you not want a single instance of your factory to be injected into your dependent code?
I think using the class declaration you provided will actually look like this once injected:
function(myFactory) {
myFactory.saySomething();
}
If you are really needing to pass a constructor function into your dependent code, then I think you will have to ditch TypeScript classes, since they can't be defined inside of a function which means you would have no way to create a closure on a variable injected into such function.
You do always have the option of just using a function in TypeScript instead of a class. Still get the strong typing benefits and can call 'new' on it since it is still a .js function at the end of the day. Here's a slightly more TypeScriptiffied version:
myApp.factory('MyFactory', (injectable: ng.SomeService) => {
return (param: string) => {
return {
saySomething: () {
alert("Param=" + param + " injectable=" +injectable);
}
};
};
});
I'm having a bit of an issue with a class extending the Backbone.Model.
Using the following class …
class Turtles extends Backbone.Model
idAttribute: "_id"
legs: [0,1,3,5]
urlRoot: '/turtles'
module.exports = Turtles
grunt.js is throwing this error when linting.
[L21:C18] 'Turtles' is already defined.
function Turtles() {
The output of the compile js file looks like this:
(function() {
var Turtles,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
Turtles = (function(_super) {
__extends(Turtles, _super);
function Turtles() {
return Turtles.__super__.constructor.apply(this, arguments);
}
Turtles.prototype.idAttribute = "_id";
Turtles.prototype.points = [0, 1, 3, 5];
Turtles.prototype.categories = ['story', 'tech', 'design', 'bug'];
Turtles.prototype.urlRoot = '/cards';
return Turtles;
})(Backbone.Model);
module.exports = Turtles;
}).call(this);
This output is very similar to some views I have extended using class Application extends Backbone.View, so I'm not sure why this model would be failing linting when all my views and collections arent.
That all being said, replacing class Turtles extends Backbone.Model with Turtles = Backbone.Model.extend works find and causes no errors.
Just wondered if anyone has had experience with this before, or perhaps can spot an issue.
Thanks
qx.ui.mobile.list.List seems not to support the qx.data.controller.List.
Is there another way to add a delegate for createItem? I tried to add the create delegation on both classes, but createItem was not invoked.
Here is an example, how to use databinding with lists:
/**
* Creates a list and returns it.
*/
__createListDataBindings : function() {
var self = this;
var list = new qx.ui.mobile.list.List({
configureItem : function(item, data, row)
{
var stopCount = self.getListData().getLength()-row;
item.setTitle("Stop #"+stopCount);
item.setSubtitle(data);
}
});
this.bind("listData", list, "model");
return list;
},
ListData is a class property:
properties :
{
// overridden
listData :
{
init : new qx.data.Array(),
nullable : true,
event : "updateListData"
}
},