In Typescript, how would I use a getter/setter for all the properties of an object? For example, I can have the following code:
class Div {
private _container: HTMLDivElement;
public get container() {
return this._container;
}
public set container(value) {
alert("Updated!");
this._container = value;
}
testDiv() {
this.container = <HTMLDivElement>document.createElement('div');
this.container.style.width = "100px";
}
}
var newDiv: Div = new Div();
newDiv.testDiv();
"Updated!" is alerted only once - when the div is first set (which, I guess, is really what it should be), but I want it to alert "Updated!" even when I'm setting a property on that object. Is there a way to do this, or should I come up with some kind of workaround?
Create an event system that triggers when your objects state changes. Do not allow direct access to properties. Funnel all activity through a controller that triggers state change as necessary.
Or look into using a framework that already does this. Backbone, Knockout, Angularjs etc.
Related
Is there any point in repeating this pattern for every property in JavaScript?
class Thing {
get myProp() {
return this._myProp;
}
set myProp(value) {
this._myProp = value;
}
}
I understand that getters/setters can be useful if you're doing additional work in the methods, but recreating the basic functionality here seems like needless repetition. If I instantiate, I can still manipulate the backing property (._myProp) directly, so I feel like I could just as easily leave these out and perform my assignment and access in the more typical, ad-hoc fashion.
I suppose you could argue that defining the interface this way (with the underscore-prefixed property name) signals to users that it's not meant to manipulate the property, but that seems like a flimsy reason for potentially dozens of these.
In compiled languages, it's common for people to do this. This is because in those languages, assigning to a field and invoking a setter may be identical to the programmer, but they compile to two completely different operations.
If I wanted to add a side effect for setting a field in a C# class, for example, and that field was being set directly instead of through a setter? Changing it to a setter would cause some issues. I wouldn't have to rewrite any of the consuming code, but I would have to recompile all of it. This is, of course, a huge problem if your public releases are compiled.
JavaScript is subject to no such considerations, though, so making everything into a getter/setter prematurely is kind of silly. If you see someone doing this, more than likely you're dealing with someone who learned the convention from another language and carried it into JavaScript, without thinking a whole lot about why.
Using an accessor property in the fashion you describe (set and retrieve a "background" data property) is virtually semantically identical to using a data property directly. There are some differences: the accessor property will exist on instance's prototype, rather than on the instance directly (though the instance will have the "background" data property), but this won't really affect anything unless you are doing advanced introspection on your class instances.
The only advantage is ease of modifying the code if you want to introduce more sophisticated accessor behavior in the future. If you forsee a need to add accessor behavior, use this pattern to save yourself time in the future.
Property accessors are useful to provide side effects or change original behaviour:
class Thing {
get myProp() {
console.log('myProp was read');
return this._myProp;
}
set myProp(value) {
if (!value)
throw new Error('myProp cannot be falsy');
this._myProp = value;
}
}
There is no point in myProp getter/setter pure abstraction:
class Thing {
get myProp() {
return this._myProp;
}
set myProp(value) {
this._myProp = value;
}
}
If I instantiate, I can still manipulate the backing property
(._myProp) directly,
If private states are what you are looking for you can still use a weak map.
(function(scope) {
"use strict";
const prop = new WeakMap();
scope.Foo = class {
constructor() {
prop.set(this, {});
Object.seal(this);
}
get bar() {
return prop.get(this)._bar;
}
set bar(value) {
return prop.get(this)._bar = value;
}
}
}(this))
const f = new Foo;
f.bar = "bar";
f._bar = "_bar";
console.log(f.bar);
console.log(f._bar);
get and setters are also useful when implementing MVC, you can trigger events on property change.
(function(scope) {
"use strict";
const prop = new WeakMap();
scope.Foo = class {
constructor() {
prop.set(this, {});
prop.get(this)._target = new EventTarget
Object.seal(this);
}
get bar() {
return prop.get(this)._bar;
}
set bar(value) {
prop.get(this)._bar = value;
prop.get(this)._target.dispatchEvent(new CustomEvent('change', {
detail: value
}));
}
addEventListener(event, listener) {
prop.get(this)._target.addEventListener(event, listener)
}
}
}(this))
const f = new Foo;
f.addEventListener('change', function(event) {
console.log("changed!", event.detail);
});
f.bar = "bar";
I've recently came across the following situation and looking for an advice / elegant solution of pretty tricky misbehave, IMHO.
Let's assume that I've created the following custom element definition in file bible-reference.js:
class BibleReference extends HTMLElement {
constructor() {
super();
}
get value() { return this._value; }
set value(newValue) { this._value = newValue; }
}
customElements.define('bible-reference', BibleReference);
We've defined our new element, and we have a value property getter/setter to interact with this element's value. Pay attention, that the getter/setter will be found on the element's prototype object, which is okay in itself, to be sure.
Now, let's review the following HTML layout that happened to be in my case:
<bible-reference id='elem-1'></bible-reference>
<script src="starter.js"></script>
<script src="bible-reference.js"></bible-reference>
The trick is in that starter.js content. If this script contains code below, things get broken:
let br = document.getElementById('elem-1');
br.value = 'some value';
The value is set on the non-upgraded yet element, therefore it is not the getter/setter that are being invoked, but just a new property is added to the element. Having this property set on the object itself, even after the upgrade is done the value's getter/setter are not accessible for this element instance anymore, since there is an (overriding) value property available before getting to the prototype.
I've solved it meanwhile by adding delete this.value; to the constructor, but... what a hack!
Any insights appreciated.
Another way to handle the issue would be to define the value property in the closure of the constructor().
<bible-reference id='elem-1'></bible-reference>
<script>
let br = document.getElementById('elem-1');
br.value = 'Genesis 1:1';
class BibleReference extends HTMLElement {
constructor() {
super()
var _value = this.value
Reflect.defineProperty(this, 'value', {
get: () => _value,
set: newValue => _value = newValue
})
}
}
customElements.define('bible-reference', BibleReference);
console.log(br.value)
</script>
But I guess you'll say it's a bad hack ;-) Anyway I'd prefer your delete solution.
I have a service that has a public property declared as follows:
public indiceUsuarios: IndiceUsuario[] = [];
After changing data and doing operations I reload that property with the following method in the same class:
private reloadIndiceLocal(indiceUsuarios: IndiceUsuario[]): void {
let self = this;
self.indiceUsuarios.length = 0;
self.indiceUsuarios.push(...indiceUsuarios);
}
I'm injecting this service into another component and referencing that property on the ngOnInit() using the following line this.indiceUsuarios = this.sesionService.indiceUsuarios; (this local property is not initialized before this). I'm then using this local property on this component's view. It shows perfectly fine and is changing when I add elements to the array.
The issue is that if I remove elements from the array they will keep showing (the view is not updating to reflect these changes).
Any ideas?
Well, In my case it turned out to be that I was relying on a singleton that was poorly written and wasn't acting as a singleton (effectively linking my view to an object with a different instance to the one I was changing). My bad.
I'm having trouble updating an array that is displayed as a list. I'm trying to make Vue detect the changes using $set() (as explained in the documentation), but I can't make it work.
Here's my code:
this.choices = this.currentScene.choices;
for (i = 0; i < this.choices.length; i++) {
choice = this.currentScene.choices[i];
choice.parsedText = this.parseText(choice.text);
this.choices.$set(i, choice);
}
Vue still doesn't update the view. What am I doing wrong? Thanks in advance!
Edit: Yes, "this" refers to the Vue instance.
It would definitely be useful to have a JSfiddle of your code, but I'm going to take a crack anyways.
I'm not sure you need to use that function to update the array, since as the documentation points out, its only when you need to change the index of the item.
JavaScript has a built in function called .map that takes a callback function and returns a new array with the callback applied to each item.
For example, you could translate your function to this, assuming that .parseText is a method on the Vue class.
var self = this; // so that we can access the Vue class inside map
this.choices = this.currentScene.choices.map(function(choice) {
choice.parsedText = self.parseText(choice.text);
return choice;
});
And Vue should pick up those changes.
You could use a computed property for this, so you never have to manually update the array. Anytime choices changes you would see the change reflected in this.parsedChoices:
computed: {
parsedChoices: function(){
return this.currentScene.choices.map(function(choice) {
choice.parsedText = this.parseText(choice.text);
return choice;
}.bind(this)); // bind Vue class as value of `this` inside func
}
}
I have a JavaScript MVC design, implemented using prototypes, where different items may be displayed in different ways as decided by the controller. For example an 'Event' item may be displayed with the views ViewTabs or ViewSimple. Class hierarchy:
ViewBase
- ViewTabs
-- EventViewTabs, which implements EventViewTabs.Validate
- ViewSimple
-- EventViewSimple, which implements EventViewSimple.Validate
Deciding whether to use EventViewTabs or EventViewSimple is done by a EventController. My problem is: I have a Validate method for checking inputs from the Event views, but this method is identical for the EventViewTabs and the EventViewSimple views. Where should I put Validate in order to avoid duplication? I cannot put it in ViewBase, as other items (e.g. User) also inherit from this class.
Seems I need multiple inheritance for this, but is there a smarter way to do it? I have a feeling I'm overlooking something obvious.
You're missing composition. Inheritance isn't the answer to all issues about code reuse to avoid copy-paste programming.
Let's say you've a View base prototype:
function View() { }
If you want this view to support validation, you can inject the validation dependency in the constructor function:
function View(validator) {
this.validator = validator;
}
View.prototype = {}; // A lot of functions here
That is, now any view which inherits View's prototype will have an associated validator. In other words: you don't need to derive two prototypes in your concrete view (you don't need and you can't do it anyway).
In the other hand, in terms of object-oriented programming, it wouldn't make sense to derive from Validator to create a View.
When you say a view has a validator, since you're using has as verb, you're talking about an association (a form of composition). Alternatively, when you say my main screen is as view, we're talking about an inheritance, because a specific view must be also a view, so it needs base view's members to act like a view.
Basically your validator could be tailor-made with the type it has to work with. In UML, it's called composition. I figure out your code as follows:
function Validator {}
Validator.prototype.validate = function(arg) {
//arg is no longer inputs
return true|false; //the ultimate output along with additional information;
}
function EventViewTabsValidator() {}
EventViewTabsValidator.prototype = Object.extend(Validator.prototype); //inheritance
EventViewTabsValidator.prototype.constructor = EventViewTabsValidator; //enforce the constructor to point to your derived type
EventViewTabsValidator.prototype.validate = function() {
var inputs = $('inputs');
var param = 'do some stuff specific to EventViewTabsValidator based on the inputs';
return Validator.prototype.validate.call(this, param); //pass param, not inputs
}
function EventViewSimpleValidator() {}
EventViewSimpleValidator.prototype = Object.extend(Validator.prototype); //inheritance
EventViewSimpleValidator.prototype.constructor = EventViewSimpleValdiator; //enforce the constructor to point to your derived type
EventViewSimpleValidator.prototype.validate = function() {
var inputs = $('inputs');
var param = 'do some stuff specific to EventViewSimpleValidator based on the inputs';
return Validator.prototype.validate.call(this, param); //pass param, not inputs
}
function EventViewTabs() {
this.validator = null; //see init
}
EventViewTabs.prototype.init = function() {
this.validator = new EventViewTabsValidator();
}
function EventViewSimple() {
this.validator = null; //see init
}
EventViewSimple = function() {
this.validator = new EventViewSimpleValidator();
}
Your could abstract up both types to a base EventView, which could expose this.validator.
Your instance of EventController will call:
var simple = new EventViewSimple();
simple.validator.validate();
var tabs = new EventViewTabs();
tabs.validator.validate();
Whatever the EventView instance, they implement their own specific validator that can be called in a generic way.
One approach is to use mixins to add the other behavior (this is the ruby approach, and is also used by react.js and react.rb) You can google for javascript+mixins and find some excellent tutorials like this one: http://raganwald.com/2014/04/10/mixins-forwarding-delegation.html
For your specific case validate (or perhaps validator) would be the mixin.
Why not to do something like this:
ViewBase
ViewBase
-EventValidator, which implements Validate
--ViewTabs
---EventViewTabs
--ViewSimple
---EventViewSimple.
Also consider to use composition over inheritance see this video