Make properties lazy - javascript

I was reading the article on best practices here. And i came across the following lines:
A developer might attempt to set a property on your element before its definition has been loaded. This is especially true if the developer is using a framework which handles loading components, inserting them into to the page, and binding their properties to a model.
And the proposed solution to solve this problem was:
_upgradeProperty(prop) {
if (this.hasOwnProperty(prop)) {
let value = this[prop];
delete this[prop];
this[prop] = value;
}
}
I have been trying the understand the scenario in which this would happens, and try to understand how this fragment of code solves this problem. I have trying to find any reference material around but wasnt able to find anything similar like this.
Please could someone explain this scenario and what problem are we trying to solve here.

Web Components doesn't fully initialize your element until you call customElements.define('custom-tag', CustomElement); however, any <custom-tag> exists in the DOM as an HTMLUnknownElement as soon as the page renders. So in the period of time between when the page renders and when you call customElements.define(...), it's possible for someone to call something like:
document.querySelector('custom-tag').someProperty = someValue
which would modify the property of the not-yet initialized CustomElement.
Why would this happen?
I think this would most likely come up as a side-effect of using Web Components with a frontend framework (Angular, Vue, etc). These frameworks often have initialization code that happens after render, and there may be situations where a user may not have sufficient control to prevent the framework from initializing before Web Components.
How does the code fragment solve the problem?
The fragment function, _upgradeProperty() is meant to be called within the connectedCallback(), which is called after the Web Component has been fully defined and attached to an existing element. If you have any custom setter in your class, like:
class CustomElement {
set someProperty(value) {
this._someProperty = value.toLowerCase();
}
}
Then it's possible the property was set before the setter existed, meaning the raw value was saved directly to the instance's someProperty property, instead of being converted to lowercase and saved to _someProperty. Deleting the property and reassigning it after the setter has been defined ensures that the value is properly processed (in this case, made lowercase and saved in the right location).

Related

How to tell if a Svelte component is entirely static content?

I'm working on a static site generator where I'd like to be able to support both reactive JavaScript interaction and standard load-a-fresh-page-into-the-browser hyperlinks. It occurred to me that something like Svelte might be a good fit for this; I could use the server-side rendering support to generate HTML for all my pages, and then I could compile and ship JavaScript components with hydratable: true to support the dynamic features.
One issue I thought of with this approach is that most of my project's components will be entirely static content: just HTML and hyperlinks, without any state or event handlers, and I won't change the props except when I generate a new HTML file for a different page. If I naively generate JavaScript to hydrate all those components at page load time, I could end up with a much larger bundle (and more work done at runtime) than I actually need.
Does Svelte offer any way to optimize this situation? Can I somehow check if a component is a pure function of its props so I can avoid hydrating it if I don't need to? Or is the compiler smart enough to do that for me?
This is a good question that we don't currently have a simple answer for.
It is possible to determine whether an individual component has values that can change — svelte.compile(...) returns an object with a vars property, which is an array of all the values inside the component. Inspecting this array will tell you which values are never reassigned to or mutated. (It won't tell you if a component has event handlers that have side-effects but which don't affect state, which would also be necessary to determine whether a component is entirely static. That's information that we could add in a future 3.x release.)
But it's only half the story. Consider a component that declares a name prop...
<script>
export let name;
</script>
<h1>Hello {name}!</h1>
...and which is used in your app like so:
<Greeting name="world"/>
As far as the compiler is concerned when it compiles the <Greeting> component, the name value could change at any moment, so it's unsafe to treat it as entirely static. But if it could understand your app more holistically, it would be able to replace {name} with world, which would have various benefits.
When hydrating, Svelte assumes that there could be a discrepancy between the existing DOM and what's supposed to be there. In many situations it would be safe to assume otherwise, and skip checking subtrees it knew to be static, which would obviate the need to include them in the generated JS.
As a compiler, Svelte is unusually well-positioned to be able to take advantage of these techniques, but it's work that we haven't undertaken yet. Ideally we'll be able to upgrade the compiler in such a way that your apps will get smaller without anything needing to change. If you're keen to start experimenting with what's possible in the meantime, then the vars property returned from svelte.compile(...) (and also the ast property, I suppose) is the place to start.

JavaScript - Executing code when property modified / Data binding

I'm trying to build a simple binding framework in JavaScript. I want to have a model with getters and setters that keeps everything consistent, i.e. updating one property can affect others and then JavaScript that then binds that to fields in the page.
The problem I'm having is I need to add some extra code to the setters on the model when I bind so that updating a property executes code that's already in the setter to keep the model consistent and then update the bound input(s).
I thought about renaming the existing property and adding a new property with the original name which could set the original property and update the input but I can't see a way to rename the field.
Is there any way to either rename a property with a getter/setter, modify a setter or get the code in a setter so I can copy that?
Alternatively, is there any other way to achieve what I'm trying to do?
I'm trying to keep separation between business logic (the model) and the UI (the html) without requiring the person writing the model to have to think about the UI / binding at all.
I also don't want to use any of the big libraries like Angular or Knockout as they're a lot of code to include in the page for pretty limited requirements, plus this is a project we've been developing / maintaining for 20 years+ so we don't want to be using a library that has a history of massive breaking changes (Angular).
We currently need to support IE10/11 and modern versions of Chrome / Edge / Firefox / Safari (iOS and Mac). However, if there's something that doesn't support all of these, we're open to doing a "nice" way with a "nasty" fallback until we can drop IE support.
If it makes any difference, we are using TypeScript to write the JavaScript.
Edit:
Since submitting my question, I've found this. It says you can rename a property with:
Object.defineProperty(o, new_key, Object.getOwnPropertyDescriptor(o, old_key));
delete o[old_key];
This is working for renaming simple variable type properties but not for renaming properties with getters and setters. I'm not sure why, although my properties with getters / setters return false when I do hasOwnProperty on them.
Edit2:
Turns out that TypeScript was adding the properties against the prototype instead of the object and this is why they weren't accessible. I called Object.getOwnPropertyDescriptor(o.__proto__, old_key) instead and this gave me the descriptor I needed.
I've found that you can edit / extend the setter by doing the following:
var propDescriptor = Object.getOwnPropertyDescriptor(model, key);
propDescriptor.set = function (value) {
setter.call(model, value);
//Do extra stuff here
}
Object.defineProperty(model, key, propDescriptor);
This means that the property still maintains consistency while allowing you to extend it to update the bound input field.

How to bind to view model object properties?

We are building a form-based app that has a complex object with many levels of nested properties.
So far, I have created a simple experiment with a single view model with one object. The experiment has fields that are bound to object properties, which successfully display the data. However, when changing the fields, the object does not seem to be updated.
What should I do to make sure form input propagates throughout the view model and into the template?
You need to use getter method in app.js as below,
get swaggerString() {
console.log(this.swagger);
const swaggerStringified = JSON.stringify(this.swagger);
return swaggerStringified;
}
In your HTML, change method to property,
${swaggerString}
Updated your GIST,
https://gist.github.com/anonymous/3b85820d66c2dfbf0f770208a7c8b63f
Hope this helps!
What are you planning to do in the real app?
The accepted answer only answers how to solve your problem as posted in the gist. I'm guessing your real app doesn't need to display JSON data.
If you are just wanting to display deeply nested object properties, then that is simple, you simply bind to those properties themselves. See here: https://gist.run/?id=5af5c22be4b49c0e3fef327e3d8b986b
<pre>
{
"name": "${swagger.name}",
"version": "${swagger.version}"
}
</pre>
You can even go arbitrarily deep in to an object tree, e.g. ${foo.bar.baz.ball.foop}.
The thing to understand is that Aurelia observes for changes to whatever you tell it to observe. When you tell it to simply observe an property that is an object, it can only watch for changes to the property itself. This means it will only see a change if you assign a different object to the property. It does not watch every property on the object for changes for performance reasons (and also due to Object.observe being cancelled).
All hope is not lost, though. Please respond with some specifics and I'll try to help you out better.

Getter / Setter or direct reference?

Learning Ember.js and have a reasonable understanding of getters and setters (accessors) through Ruby and Java.
In Ember/Javascript, I seem to have a very serious lack of understanding. For instance in my controllers/models, I don't have a clue whether to use object.set(property,value) or refer them directly object.property = 'value'
As an example, in my earlier question (How to get Model values in Controller), part of working answer was to use object.name instead of object.get('name'). It worked but I miss the basic understanding.
Would appreciate some clarifications.
The rule is: You should always use .get()/.set() when you are in your .js-files. When you are in your templates (.hbs or other) you should not (you can't do it).
If you access a property via myObj.myProp it will work for regular properties, but computed ones won't. If you set a property via myObj.myProp you can still get the value back, but bindings and observers won't be notified that it has changed and won't updated properly.
This is a design decision by the Ember team which allows for efficient bindings/observers instead of doing dirty-checking of all bound/observed properties (which is what Angular currently does).
I made a small jsbin showing this. Three values are bound initially, the button then changes one and logs it into the console (so make sure to have the console open), the binding isn't updated but the value can be retrieved. It then tries to get a computed property via myObj.myProp which returns undefined and then the regular way.
http://emberjs.jsbin.com/cipapaxevi/2/
Also, as a side note. If you want a property thats on a child object to what you have you can access it via myObj.get('myProp.myOtherProp') instead of doing myObj.get('myProp').get('myOtherProp'). It saves you from the worry that myProp could return null or undefined.
That's the way Javascript works and it's one of its disadvantages. It doesn't have public/private proprieties nor methods. Therefore, we should rely on good comments in the code.
I am not an expert in Ember, but when I face the similar problems in JS I usually look at the source code of the library to see how it's constructed and then make a decision. That's why probably js libraries are usually shipped with both min and dev versions.
However, if the object has special methods to access its properties then they are there for a reason, so use them instead of the direct access to the properties. As I said above, in Js you can't make properties be private or protected.

Elegant way to notice JavaScript object attribute changes

I am working on a JavaScript application with a server-side component. The aim is to have multiple different objects on a canvas that are synchronized (i.e., have the same appearance) between multiple browsers. The synchronization is done by the server-side component which broadcasts the individual changes to all browsers. Whenever an object changes, it has to notify the server about which will then take care of notifying the other browsers.
The objects on the canvas are represented by JavaScript objects whose attributes determine the appearance for the user. Of course, not all of the attributes are important for the appearance. Hence, only changes of important attributes have to be transmitted to the other browsers. There are different 'classes' of objects, but all objects 'inherit' from a common 'superclass' (I know, the class inheritance terminology doesn't really work in JavaScript, but in this case, I think it is easier that way).
Now, I have some trouble to send the client-server notifications in an elegant way. Currently, I have setter-methods for all the important attributes of the different objects. These setter-methods call a function which sends the notifications to the server.
I don't really like that solution, since it introduces much boilerplate code. Ideally, when I create a new object, I would like to be able to just specify the important attributes an be done with it. The logic that takes care of monitoring the changes of these attributes could be inside the 'superclass'. But I have no idea how to implement this. Maybe there is a way to build the setters dynamically at runtime? Or maybe there is some other way I did not think about?
If anyone can think of a solution to this problem, I would be glad to hear about it.
Thanks in advance!
Edit:
I had to revoke the accepted answer (creating getters and setters dynamically via Object.defineProperty) since though I thought it would solve my problem, it doesn't really. I now get notified when a property is changed via direct attribute assignment which fires the setter, e.g.:
SynchronizedObject.importantProp = 'someValue';
But as I noticed, in many cases the importantProp is some more complex object. And those objects are usually updated via the getter, not the setter.
SynchronizedObject.importantProp.x = 'someValue';
As far as I can see, I have no chance to notice changes done in this way with my dynamic getters/setters implementation. To use it, I have to change the way I am accessing my objects. Something that works:
prop = SynchronizedObject.importantProp;
prop.x = 'someValue';
SynchronizedObject.importantProp = prop;
That way, the setter is used and everything works out fine. But this feels really awkward and I don't want to have to think about the synchronization every time, I set a property. So it seems to me, the solution is not really usable.
Can anyone help?
How about one set function?
MyObj.prototype.set = function(key, value) {
this[key] = value;
// do other things
};
You could combine this with an EventEmitter implementation to make it easy to observe changes.
This is exactly what __defineSetter()__ is intended to support. Check out John Ressig's JavaScript Getters and Setters blog post for some good examples. It would be pretty simple to fire off an event from inside a setter.
You may want to consider the MeteorJS framework if wheel reinvention is not really your bag.

Categories