This question already has answers here:
Lazy getter doesn't work in classes
(3 answers)
Closed 4 years ago.
In this example I try to overwrite getter with cached value.
it doesn't works.
Is it possible to overwrite getter of a class instance?
class Module {
constructor(id) {
this.id = id
}
get element() {
const elm = document.getElementById(id)
if(!elm) throw new Error(`#${id} is not in document`)
delete this.element;
this.element = elm;
return elm;
}
}
You should use Object.defineProperty():
class Module {
constructor(id) {
this.id = id
}
get element() {
const elm = document.getElementById(this.id)
if(!elm) throw new Error(`#${id} is not in document`)
Object.defineProperty(this, 'element', {
value: elm,
});
return elm;
}
}
Related
This question already has answers here:
What is the 'this' pointer?
(8 answers)
How do the "->" and "." member access operations differ in C
(10 answers)
Closed 2 years ago.
I'm new to C++, I want to understand why I can't use the dot (.) operator here, since the variable is declared inside the class.
class SessionController {
private: SessionView view;
private: Session model;
public: SessionController (SessionView view, Session model) {
this.view = view;
this->model = model;
}
};
I'm getting an error at this.view = view; saying
Member reference type 'SessionController *' is a pointer; did you mean to use '->'?
In this case you can avoid the problem by setting the values directly in the initialiser list.
class SessionController {
private: SessionView view;
private: Session model;
public: SessionController (SessionView view, Session model) : view(view), model(model) {
}
};
Or if you really need to do it in the body.
class SessionController {
private: SessionView view;
private: Session model;
public: SessionController (SessionView pview, Session pmodel) {
view = pview;
model = pmodel;
}
};
I have a navigation property in a hierarchical model structure that causes an circular dependency error in angular 7 during serialization.
export class MyClass {
// this property should be ignored for JSON serialization
parent: MyClass;
childList: MyClass[];
}
I wonder if there is any build-in solution (ex. a decorator like this exists for Jackson: #JsonBackReference) to ignore the parent property at serialization (ex. during http.put).
Thanks a lot for any advice!
if you prefer to handle this with a decorator you can make your own like this one
function enumerable(value: boolean) {
return function (target: any, propertyKey: string) {
let descriptor = Object.getOwnPropertyDescriptor(target, propertyKey) || {};
if (descriptor.enumerable != value) {
descriptor.enumerable = value;
Object.defineProperty(target, propertyKey, descriptor)
}
};
}
and then mark property as not enumerable like this
class MyClass {
#enumerable(false)
parent: MyClass;
}
other option is to redefine toJSON behavior
MyClass {
...
public toJSON() {
const {parent, ...otherProps} = this;
return otherProps;
}
Let's say I want to get a data from Visual Studio TFS and the response (as json) is in this kind of format:
{
"Microsoft.VSTS.Scheduling.StoryPoints": 3.0,
// ......
}
There's dot in the property name. Reading from other questions I found out that I can read that json in typescript by using an interface like this
export interface IStory { // I don't think this kind of interface do me any help
"Microsoft.VSTS.Scheduling.StoryPoints": number
}
And then I can use the property with this syntax:
var story = GetStoryFromTFS();
console.log(story["Microsoft.VSTS.Scheduling.StoryPoints"]);
But I'd prefer not to call the property like this, since the intellisense won't able to help me finding which property I want to use (because I call the property using a string).
In C# there is a JsonProperty attribute which enable me to create a model like this:
public class Story
{
[JsonProperty(PropertyName = "Microsoft.VSTS.Scheduling.StoryPoints")]
public double StoryPoints { get; set; }
}
And then I can use the property this way:
var story = GetStoryFromTFS();
Console.WriteLine(story.StoryPoints);
This way the intellisense will able to help me finding which property I want to use.
Is there something like JsonProperty attribute in typescript? Or is there any other, better way, to achieve this in typescript?
You have many options. Just keep in mind that all of these options require you to pass the original data to the class that will access it.
Map the values.
class StoryMap {
constructor(data: IStory) {
this.StoryPoints = data["Microsoft.VSTS.Scheduling.StoryPoints"];
}
StoryPoints: number;
}
Wrap the data.
class StoryWrap {
constructor(private data: IStory) {}
get StoryPoints(): number { return this.data["Microsoft.VSTS.Scheduling.StoryPoints"] };
}
Build a decorator to map the data.
function JsonProperty(name: string) {
return function DoJsonProperty(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.get = function () {
return this.data[name];
}
descriptor.set = function (value) {
this.data[name] = value;
}
}
}
class StoryDecorator
{
constructor(private data: IStory) {}
#JsonProperty("Microsoft.VSTS.Scheduling.StoryPoints")
get StoryPoints(): number { return 0 };
}
I have a problem with org.primefaces.component.diagram, i want to add an action when click on any overlay or connector, i make this using jquery, but the problem is that there is no identifier for the connection, after search i was able to get the ids of the 2 end points of the connection but if there is many connection between the same points then i cannot distinguish between them, i tried to override the diagram and add "connectionId" attribute on the connection but i got an exception in the front end :
Uncaught ReferenceError: connectionId590236 is not defined at eval (eval at (jquery.js.xhtml?ln=primefaces&v=5.2:14), :1:1488)
screenshot
The closet solution would be is to use setId on Element in the DefaultDiagramModel creation.
An example would be as the following:
Element elementA = new Element("A", "20em", "6em");
elementA.setId("element-a");
Element elementB = new Element("B", "10em", "18em");
elementB.setId("element-b");
Element elementC = new Element("C", "40em", "18em");
elementC.setId("element-c");
...
Since PrimeFaces doesn't provide the control you are searching for, and the original component comes from jsPlumb, you may rely on that to achieve what you are looking for.
First make sure that the <p:diagram> has a widgetVar value, es. diagramWV
An example would be the following:
$(document).ready(function () {
//timeout makes sure the component is initialized
setTimeout(function () {
for (var key in PF('diagramWV').canvas.getAllConnections()) {
if (PF('diagramWV').canvas.getAllConnections().hasOwnProperty(key)) {
//Elemenets Events
// on source just once
$(PF('diagramWV').canvas.getAllConnections()[key].source).off('click').on('click', function () {
console.log($(this).attr('id'))
});
// on target just once
$(PF('diagramWV').canvas.getAllConnections()[key].target).off('click').on('click', function () {
console.log($(this).attr('id'))
});
//Connection Event
PF('diagramWV').canvas.getAllConnections()[key].bind("click", function (conn) {
console.log("source " + conn.sourceId);
console.log("target " + conn.targetId);
});
}
}
}, 500);
});
Note: The canvas property of the widgetVar is the current instance of jsPlumbInstance
Here's an online demo, and a small working example on github.
finally i found an acceptable solution :
-> add an label overlay on the connection and set the identifier on it.
org.primefaces.model.diagram.Connection conn = new org.primefaces.model.diagram.Connection(
EndPointA, EndPointB);
LabelOverlay labelOverlay = new LabelOverlay(connection.getId(), "labelOverlayClass", 0.3);
conn.getOverlays().add(labelOverlay);
-> then add JS function to handle on dbclick action on the connection and get the id from its related overlay using the classes "._jsPlumb_overlay" and "._jsPlumb_hover"
<p:remoteCommand name="connectionClicked"
actionListener="#{yourBean.onConnectionDoubleClick}" />
<script type="text/javascript">
var connectionId;
$('._jsPlumb_connector').on('dblclick', function(e) {
$('._jsPlumb_overlay._jsPlumb_hover').each(function() {
connectionId = $(this).text();
});
connectionClicked([ { name : 'connectionId', value : connectionId } ]);
});
});
</script>
-> finally in the bean you extract the id and do whatever you want
public void onConnectionDoubleClick() {
Map<String, String> params = FacesContext.getCurrentInstance()
.getExternalContext().getRequestParameterMap();
String connectionId = params.get("connectionId");
if(StringUtils.isBlank(connectionId))
return;
.........
I was able to add a click event to Overlay by extending the primefaces Overlay class. If you make a change to the toJS() class (taking heavy inspiration from the Primefaces LabelOverLay) then you can write your own overlay with the jsplumb overlay constructor. Here's my implementation of a ClickableLabelOverlay.
public class ClickableLabelOverlay implements Overlay {
private String label;
private String styleClass;
private double location = 0.5;
private String onClick;
public ClickableLabelOverlay() {
}
public ClickableLabelOverlay(String label) {
this.label = label;
}
public ClickableLabelOverlay(String label, String styleClass, double location, String onClick) {
this(label);
this.styleClass = styleClass;
this.location = location;
this.onClick = onClick;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String getStyleClass() {
return styleClass;
}
public void setStyleClass(String styleClass) {
this.styleClass = styleClass;
}
public double getLocation() {
return location;
}
public void setLocation(double location) {
this.location = location;
}
public String getOnClick() {
return onClick;
}
public void setOnClick(String onClick) {
this.onClick = onClick;
}
public String getType() {
return "Label";
}
public String toJS(StringBuilder sb) {
sb.append("['Label',{label:'").append(label).append("'");
if(styleClass != null) sb.append(",cssClass:'").append(styleClass).append("'");
if(location != 0.5) sb.append(",location:").append(location);
if(onClick != null) sb.append(",events:{click:function(labelOverlay, originalEvent){").append(onClick).append("}}");
sb.append("}]");
return sb.toString();
}
}
Put any javascript you want to execute inside of the onClick variable and it'll run when you click on the overlay. For convenience I added it to the set of default overlays for my diagram.
diagram.getDefaultConnectionOverlays().add(new ClickableLabelOverlay(...)
I've seen the error before, usually when we're trying to call a constructor for an object that the interpreter hasn't seen yet. Most likely a dynamic attempt at instantiation. However, I'm currently experiencing the error on a line of execution on Android devices only.
See the guilty function below -
public class XMLBuilder
{
private var elementClasses:Dictionary;
private var elements:Dictionary;
public function XMLBuilder()
{
elementClasses = new Dictionary();
elements = new Dictionary();
}
public function buildFromXML(xml:XML):void
{
elements = new Dictionary();
var element:StardustElement;
var node:XML;
for each (node in xml.*.*) {
//error thrown on line below//
element = StardustElement(new elementClasses[node.name()] ());
if (elements[node.#name] != undefined) {
throw new DuplicateElementNameError("Duplicate element name: " + node.#name, node.#name, elements[node.#name], element);
}
elements[node.#name.toString()] = element;
}
for each (node in xml.*.*) {
element = StardustElement(elements[node.#name.toString()]);
element.parseXML(node, this);
}
}
public function registerClass(elementClass:Class):void {
var element:StardustElement = StardustElement(new elementClass());
if (!element) {
throw new IllegalOperationError("The class is not a subclass of the StardustElement class.");
}
if (elementClasses[element.getXMLTagName()] != undefined) {
throw new IllegalOperationError("This element class name is already registered: " + element.getXMLTagName());
}
elementClasses[element.getXMLTagName()] = elementClass;
}
}
Where the object 'elementClasses' is a dictionary that contains a set of Classes. (And In this case it does, i've checked while debugging on both iOS & Android). And where evaluating 'elementClasses[node.name()] is Class' returns true.
Does anybody know of any quirks in AIR that would cause this to happen on Android only? Or anything in this function that jumps-out?
So, in AS3 XML node.name() will return a QName object. If you look at my registerClass(elementClass:Class), you will see that we're adding classes to the elementClasses dictionary using element.getXMLTagName() which returns a String.
Now, apparently in AIR iOS if we attempt a lookup on someDict[someQName], someQName.toString() or some kind of cast is attempted.
Whereas in AIR Android, it would appear that it attempts to use the QName object itself as the key.
I would say that the Android implementation has the expected behaviour, but it's definitely fishy to me...
Fixed code below. node.name().localName used instead essentially.
public function buildFromXML(xml:XML):void
{
elements = new Dictionary();
var element:StardustElement;
var node:XML;
for each (node in xml.*.*) {
element = StardustElement(new elementClasses[node.name().localName] ());
if (elements[node.#name] != undefined) {
throw new DuplicateElementNameError("Duplicate element name: " + node.#name, node.#name, elements[node.#name], element);
}
elements[node.#name.toString()] = element;
}
for each (node in xml.*.*) {
element = StardustElement(elements[node.#name.toString()]);
element.parseXML(node, this);
}
}
public function registerClass(elementClass:Class):void {
var element:StardustElement = StardustElement(new elementClass());
if (!element) {
throw new IllegalOperationError("The class is not a subclass of the StardustElement class.");
}
if (elementClasses[element.getXMLTagName()] != undefined) {
throw new IllegalOperationError("This element class name is already registered: " + element.getXMLTagName());
}
elementClasses[element.getXMLTagName()] = elementClass;
}