Angular generic function handler not update component properties - javascript

I have a component that has 2 checkboxes that in each change event - aside text is changed.
I want to avoid double code so I created a function that receive two arguments the chackbox model and text to change but it seems that the value are passed by value.
Is it possible to pass it by reference? if not - what it the best-practice solution?
<input type="checkbox" id="aa" [(ngModel)]="checkboxOne" (change)="genericOnChange(checkboxOne,textOne)">
{{textOne}}
<input type="checkbox" id="nn" [(ngModel)]="checkboxTwo" (change)="genericOnChange(checkboxTwo,textTwo)">
{{textTwo}}
export class AboutComponent implements OnInit {
constructor() { }
checkboxOne = false;
checkboxTwo = false;
textOne = '';
textTwo = '';
ngOnInit() {}
genericOnChange(checkboxModel,componentProperty){
if(checkboxModel){
componentProperty ="pew pew pew!"
}
else{
componentProperty ="laser gun off!"
}
}
}
Please see stackbliz (about.component)

It’s passing the value because there isn’t a reference, it’s a primitive (you don't have a pointer like you would in C). You could create an object, or you could pass the variable name in as a string and do:
this[myVariable]. I would recommend the former (an object), as you could have a checkbox object with both checkboxes as properties:
checkboxes = {one: true, two: false}

You could create a wrapper object around your checkboxProperties, and pass that reference as a single parameter to your genericOnChange(...) function
genericOnChange(chk:CheckboxProperties){
if(chk.checked){
chk.name ="pew pew pew!"
}
else{
chk.name ="laser gun off!"
}
}
Now you are modifying a property of the object, and not a primitive string. Your changes will be reflected on the template as you are expecting.
Full example below:
https://stackblitz.com/edit/angular-basic-tutorial-r7eaup?file=app%2Fabout%2Fabout.component.ts

Related

Class with grouped properties in deeper levels

I'm trying to build a complex class where I want to group properties, making the instantiated object have multiple layers, instead of every property being at root level.
So far, the only way I've found to do this is by making a class with the properties to group, and then in a "parent" class add a property of the class I built.
The problem here though is that two properties not sharing the same class can't communicate with each other.
There are ways around this, but I find them all very hacky and looking bad. One would be to create a hidden element, and store data in there that a property from another class can read.
Another would be to create static properties, but then, unless you do some major work with that property, you can only have one object created from the parent class, as it'll be the same no matter the instantiation of the class.
Very basic example:
class A {
constructor(prop1){
this.property = prop1;
}
}
class B {
constructor(prop2){
this.property = prop2;
}
}
class C {
constructor(prop1, prop2){
this.PropertyA = new A(prop1);
this.PropertyB = new B(prop2);
}
}
let obj = new C(1, 1);
console.log(obj.PropertyA.property);
In this example, the property from class A can't get a value from property in class B.
So, my question is, is there another way of building the class C to keep the levels of hierarchy in the object?
I use the class structure because I like how it looks. It looks far more readable to me than the prototype structure, and I'm not building an object directly, as I would like to instantiate more of them.
It feels like I have forgotten things I've looked at to try to do this, but I'm sure it'll come to me soon enough after I post this.
Sooo...
I worked a bit on a static-solution, and basically made a private static property to hold a unique id per instantiated object, with the key-value pairs I want to be able to share between the different classes. This should only expose the methods to either set or get those values. The only requirement is that all the classes needs to be constructed with the object ID, so they can get the right value.
I understand that people will roll their eyes at my infantile tries to break the actual points of classes and such, but it works for me anyway in this specific circumstance anyway.
I'm sure there a multitude of ways to update it to ensure it runs more smoothly, but I think it works for most cases at the moment.
The code made in example code:
"use strict";
class A {
#id
#testProp
constructor(id){
this.#id = id;
this.#testProp = 10;
}
get TestProp(){ return this.#testProp + C.getSharedProp(this.#id, "BValue")};
set TestProp(newValue) { this.#testProp = newValue; C.setSharedProp(this.#id, "AValue", this.#testProp) };
}
class B {
#id
#testProp
constructor(id){
this.#id = id;
this.#testProp = 10;
}
get TestProp(){ return this.#testProp + C.getSharedProp(this.#id, "AValue")};
set TestProp(newValue) { this.#testProp = newValue; C.setSharedProp(this.#id, "BValue", this.#testProp) };
}
class C {
#id
constructor(){
this.#id = Math.random().toString(36).substr(2, 9);
this.PropertyA = new A(this.#id);
this.PropertyB = new B(this.#id);
}
static #sharedProps = {};
static getSharedProp(charId, valueName) {
if(!charId){
throw "Must supply character ID";
}
if(!valueName){
throw "Must supply name of value to return";
}
if(!(charId in this.#sharedProps)){
throw "Character ID not found";
}
if(!(valueName in this.#sharedProps[charId])){
throw valueName + "-element not found";
}
return this.#sharedProps[charId][valueName];
}
static setSharedProp(charId, valueName, value) {
if(!charId){
throw "Must supply character ID";
}
if(!valueName){
throw "Must supply name of value";
}
if(!(charId in this.#sharedProps)){
this.#sharedProps[charId] = [];
}
if(!(valueName in this.#sharedProps[charId])){
this.#sharedProps[charId][valueName] = -1;
}
if(!value){
console.warn("Value not supplied of " + valueName + ". Not updating extant value");
}else{
this.#sharedProps[charId][valueName] = value;
}
}
}
let obj = new C();
obj.PropertyA.TestProp = 20;
obj.PropertyB.TestProp = 5;
console.log(obj.PropertyA.TestProp); //should be 25; 20 from its own class and 5 from foreign class-object
console.log(obj.PropertyB.TestProp); //should be 25; 5 from its own class and 20 from foreign class-object

How to serialize an object and cast it back to the same class as the original object

I'm new to JavaScript so bear with me if what I'm asking is not "how you do it in JavaScript". Advice on other approaches are welcome.
I have a class named State and I need need to serialize objects of that class using JSON.stringify(). The next step is to deserialize them back into an objects. However, my class uses setters and getters.
The problem that I'm facing is that after I deserialized those objects the setters and getters seem to be gone. I just cannot figure out how I can properly turn serialized objects back into objects of that class so that they behave exactly the same as objects that are created using new directly.
In another language I would cast those objects into State objects. I cannot find a JavaScript mechanism that seems to work that way.
The code looks as follows:
class State {
constructor(href) {
this.url = href;
}
set url(href) {
this._url = new URL(href);
this.demoParam = this._url.searchParams.get("demoParam");
}
get url() {
return this._url;
}
set demoParam(value) {
let param = parseInt(value, 10);
if(isNaN(param)) {
param = 2;
}
console.log("Setting 'demoParam' to value " + param);
this._demoParam = param;
}
get demoParam() {
return this._demoParam;
}
toJSON() {
let stateObject = {};
const prototypes = Object.getPrototypeOf(this);
for(const key of Object.getOwnPropertyNames(prototypes)) {
const descriptor = Object.getOwnPropertyDescriptor(prototypes, key);
if(descriptor && typeof descriptor.get === 'function') {
stateObject[key] = this[key];
}
}
return stateObject;
}
}
let originalState = new State(window.location.href);
let newState1 = JSON.parse(JSON.stringify(originalState));
newState1.demoParam = 12;
let newState2 = Object.create(State.prototype, Object.getOwnPropertyDescriptors(JSON.parse(JSON.stringify(originalState))));
newState2.demoParam = 13;
let newState3 = Object.assign(new State(window.location.href), JSON.parse(JSON.stringify(originalState)));
newState3.demoParam = 14;
let newState4 = Object.setPrototypeOf(JSON.parse(JSON.stringify(originalState)), State.prototype);
newState4.demoParam = 15;
I would expect that everytime I set the demoParam property of a newStateX object I'd see a console log message. However. I only see it twice, i.e. for every new State(window.location.href) statement.
I have used the answer of this question. However, it does not work as expected.
when you serialize an object you trigger the toString or the toJSON method of your class' instance and end up with just a "dumb" JSON representation of your enumerable attributes.
if you want to recreate an instance that behaves like it did prior to serialisation, you will need to set an extra key/value pair in your toJSON function like ___internalType: 'state' and then later use eg. a switch statement to recreate your specific class with the new MyClass(serializedData)and passing in your serialised instance. Within the constructor of your class, you set all the attributes you need and voilà, you have your "old" instance again
/edit: to clarify the reason why your console logs aren't showing up is because you are not recreating an instance of your class but just creating a new plain Object.
You can use Object.assign to copy plain object data into an "empty" new instance of the class along the lines of this code:
function cast(o) {
if (!o._cls) return o;
var _cls = eval(o._cls);
return Object.assign(new _cls(), o);
}
In JavaScript i personally like to avoid using classes for my data objects. TypeScript offers some better opportunities to solve this problem, one of these is TypedJSON:
https://github.com/JohnWeisz/TypedJSON

Vue-Form-Generator schema is not reactive to computed properties

I have computed property in my data this.coinPairingOptions that needs to render its radio buttons based on some of the other fields in this schema. I have reduced the amount of code in the file to save space.
data: function () {
return {
schema: {
{model: "symbolPair", type: "radios", label: "Pair with", values:
this.coinPairingOptions, required: true}
},
computed: {
coinPairingOptions() {
console.log("computing coinPairingOptions")
let coin = this.model.symbol.toUpperCase();
let options = [];
if (this.model.exchange === 'Coinbase') {
options = this.getCoinbasePairs
} else if (this.model.exchange === 'Binance') {
options = this.getBinancePairs
} else {
}
console.log(options.get(coin));
return options.get(coin);
},
}
In the dev tools I can see the computed property changing to the correct values however it is not changing in the data. Apparently, this is appropriate behavior, but what is a way around this? I have tried putting {{this.coinPairingOptions}} in the html and it errors because it's a computed property with not value initially.
Any help would be appreciated!
You can't use computed property in data, because data evaluates before the computed properties did.
You can use a watcher to achieve the intended result. Have a look at the documentation, you can add the argument immediate to trigger the callback immediately with the current value of the expression.
Computed properties are already accessible in the template by using {{}}. You don't need to put a this in front of the computed.

Dynamically Access Model with View

I have the following script in my ASP.NET MVC Core View:
window["dataSet1"] = [];
#foreach (var item in Model.SelectedOptions)
{
foreach (var item2 in Model.MyChartData)
{
// ".mph" exists in 'MyChartData' model
// This works... but it's not dynamic
#:window["dataSet1"].push(['#item', #item2.mph);
// How can I get a dynamic variable determine
// what field to retrieve from the model?
// Example values for 'SelectedOptions' are:
// > "mph", "kph", "m/s", "knots"
// I'd like this to work...
#:window["dataSet1"].push(['#item', #item2.#item);
}
}
Instead of creating if else statements for each possible 'SelectedOptions' value (mph, kph, m/s, knots etc.), is it possible to simply use a variable to reference a specific object within my model?
The data I get back from my attempt is:
window["dataSet1"].push(['mph', MyProject.Models.MyChartData.mph]);
Rather than a value from my model, for example:
window["dataSet1"].push(['mph', 15.16451]);
You can solve it in c# adding a property using reflection to get the value or a simple case
Example:
#:window["dataSet1"].push(['#item', #item2.correctvalue( item );
In the item2 class:
public decimal correctvalue( propName ) {
return this.GetType().GetProperty(propName).GetValue(this, null);
}
Or more simple:
public decimal correctvalue( propName ) {
if (propName = "mph") {
return this.mph;
}
else ...
}
Keep in mind that you should validate the propName or the reflection will error. More info on using reflection to get the property value
The solution I created, whilst whacking it into a static class so it can be easily accessed in future.
public static object GetPropertyValue(this object myProperty, string propertyName)
{
return myProperty.GetType().GetProperties()
.Single(pi => pi.Name == propertyName)
.GetValue(myProperty, null);
}

Ember.js computed property not firing

I have a computed property thats not firing when a checkbox is checked. I just need to switch a property's value from 1 to 0 if its checked.
App.Address = Ember.Object.extend({
shipType: 1,
shipType: function() {
var type = this.get('shipType');
if (type === 1) {
type = 0;
return type;
} else {
type = 1;
return type;
};
}.property('shipCommerical')
})
And in my template:
<label>{{view Ember.Checkbox checkedBinding='shipCommerical'}} Check Me </label>
I have other computed properties located in the same place and in the same way. The only difference is they are text fields and not checkboxes. Does that make a difference?
Well, you've got ship commercial spelled incorrectly, but it appears you have it spelled incorrectly in both places. Additionally you have a recursive loop where your computed property needs itself, I'm going to assume you meant to use shipCommercial instead of shipType inside the computed property.
Template
<label>{{input type='checkbox' checked=shipCommercial}} Check Me </label>
Properties
shipCommercial:true,
shipType: function() {
var shipCommercial = this.get('shipCommercial');
return shipCommercial ? 0 : 1;
}.property('shipCommercial')
Example
http://emberjs.jsbin.com/zimopise/1/edit

Categories