
Research
Security News
Malicious PyPI Package Exploits Deezer API for Coordinated Music Piracy
Socket researchers uncovered a malicious PyPI package exploiting Deezer’s API to enable coordinated music piracy through API abuse and C2 server control.
crs-binding
Advanced tools
Currently as BETA ---------------------------------------------------- # CRS Binding Engine
This is a lightweight but powerful binding engine.
There are two parts too the engine.
In the examples folder you will find several examples of both but for a more complete example you can look at the crs binding example application.
The first place to start is expressions as this is how you define your intent to the engine. There are two types of expressions.
Consider the following result expression.
const exp = "${firstName} ${lastName} is ${age} years old and lives at \"${address.street}\"";
We have a context object with the following properties:
The address property is actually a object with it's own properties, in the above example we want to use the street property.
When using the above expression with the context object it will give you the following string back.
John Doe is 20 years old and lives at "Somewhere"
Consider the following code
const exp = "${firstName} ${lastName} is ${age} old and lives at \"${address.street}\"";
document.querySelector("#result").innerText = crsbinding.expression.compile(exp).function({
firstName: "John",
lastName: "Doe",
age: 20,
address: {
street: "No Where"
}
});
crsbinding.expression.release(exp);
In the above example the result of this function is set to the innerText of a element.
One of the problems here is that we want to update the text if one of the properties changes.
This can be done using events.
Events can be used on any object that events enabled.
To enable a object to be event enabled use crsbinding.events.enableEvents(obj);
This is useful when you create custom classes that you want to make events enabled.
class Person {
get firstName() {
return this._firstName;
}
set firstName(newValue) {
this._firstName = newValue;
crsbinding.events.notifyPropertyChanged(this, "firstName");
}
get lastName() {
return this._lastName;
}
set lastName(newValue) {
this._lastName = newValue;
crsbinding.events.notifyPropertyChanged(this, "lastName");
}
constructor() {
crsbinding.events.enableEvents(this);
}
dispose() {
crsbinding.events.disableEvents(this);
}
}
Several things to note about the above example class.
So the above example is events enabled but we are not listening on any events yet.
We can do that using crsbinding.events.on
.
This function takes three parameters:
const john = new Person();
crsbinding.events.on(john, "firstName", () => document.querySelector("#name").innerText = john.firstName);
crsbinding.events.on(john, "lastName", () => document.querySelector("#lastName").innerText = john.lastName);
john.firstName = "John";
john.lastName = "Doe";
john.dispose();
When you no longer want to listen for changes you can remove the event listener using crsbinding.events.removeOn
.
On this function you need to provide the following parameters:
You can have multiple callbacks for a property change on a object for a given property.
This is why you need to provide the callback as part of the un-registering process.
It is thus imperative that wne you perform you cleanup you unregister the functions.
When using crsbinding.events.disableEvents
, all the events are automatically unregistered for you.
See the dispose function of the above example class.
What if you don't want to declare a class but achieve the same thing using an object literal.
The following code shows you how you can use observers to event enable a object literal, make changes and clean it up again.
const obj = crsbinding.observation.observe({
firstName: "name",
lastName: "lastName"
});
crsbinding.events.on(obj, "firstName", () => document.querySelector("#name").innerText = obj.firstName);
crsbinding.events.on(obj, "lastName", () => document.querySelector("#lastName").innerText = obj.lastName);
obj.firstName = "John";
obj.lastName = "Doe";
crsbinding.observation.releaseObserved(obj);
It is important to note that proxies are used when observing objects.
This means you have to assign it as observed as you will be working with the proxy from this point forward.
This often limits you from making custom elements event enabled but we also provide you with two classes that enables binding of elements.
We will look at these in more detail in the UI updates part.
We are not limited to simple property change events but can also use expressions.
What if you want to perform a action when certain condition is met, for this we can use crsbinding.events.when
.
The when function takes the following parameters:
const obj = crsbinding.observation.observe({
firstName: "name",
lastName: "lastName"
});
crsbinding.events.when(obj, "firstName == 'John' && lastName == 'Doe'", () => alert("We found him!"));
obj.firstName = "John";
obj.lastName = "Doe";
crsbinding.observation.releaseObserved(obj);
It is important to note that the condition is a standard Javascript syntax and is relative the object.
In the above example the context object has two properties, firstName and lastName.
You will note that the expression does not use "this" but instead uses a path relative the the context.
The same was true for the very first expression where we had a composite object, address being a object property and the expression pathing was relative too the context.
If you are no longer interested in listening too the event you can un-register it again using crsbinding.events.removeWhen
.
Again it requires the three unregister parameters.
You can define multiple conditions on a object and multiple callbacks for the same condition.
UI updates uses the object observation but brings defined changes to the UI.
From this point forward we will be show examples of what that would look like.
Before we look at specifics we need to understand some terms used.
<input value.once="firstName" />
<input value.one-way="firstName" />
<input value.two-way="firstName" />
<input value.bind="firstName" />
bind is just a shorthand for two way binding but they do the same thing.
It is important to note that the binding expressions above sets the properties on the elements and not the attributes directly.
<div>${firstName} ${lastName}</div>
This copies the template literal syntax you are use to in javascript.
In some cases you want to bind to a event of a element and call a function when that event occurs.
<button click.call="doSomething">Do Something</button>
<button click.delegate="doSomething">Do Something</button>
Again there are two syntax options. Some prefer call, others prefer delegate, we don't judge :) use what you are comfortable with. In the above example no parameter is passed to the calling function, but there are times when you do want to provide parameters.
Here are some examples showing off parameters as part of the call.
<!-- standard parameters -->
<button click.call="doSomething(10, 'hello world')">Do Something</button>
<!-- send the MouseEvent on -->
<button click.call="doSomething($event)">Do Something</button>
<!-- send the MouseEvent on and a standard parameter-->
<button click.call="doSomething(10, $event)">Do Something</button>
All the above examples work with delegate also.
We can use the if
binding expression on attributes to affect the behaviour of those attributes based on a condition.
Here are some examples:
<div hidden.if="isVisible != true">Hello World</div>
In this example the presence of the attribute is determined by the condition If isVisible is true, the hidden attribute is removed. if isVisible is false, the hidden attribute is added.
<div data-title.if="isVisible == true ? true">Hello World</div>
Here we are setting the data-title attribute's value too true if the condition passes. If the condition fails the attribute is removed.
<div data-title.if="isVisible == true ? true : false">Hello World</div>
Now we are not removing the attribute, instead we are setting the attribute value depending on the condition
We can bind to arrays and create elements on the UI based on those arrays using for
.
<div>
<template for="person of persons">
<div>
<h2>Personal</h2>
<input value.bind="person.firstName" />
<input value.bind="person.lastName" />
<h2>Contacts</h2>
<div>
<template for="contact of person.contacts">
<input value.bind="contact.cell">
<span>${contact.cell}</span>
</template>
</div>
<hr />
</div>
</template>
</div>
The context object has a observed array called people.
The above example repeats this template for each person in that array.
Note that the binding expression has changed a little where we now use the person as the path to start our expressions.
Each person has 0 to N contacts so here we want to print the contacts list for each person also.
This means that we have a for
operating in another for
.
The div elements the templates are in is important.
If the collection is cleared this container is emptied and the new array drawn.
Don't put content you want to keep in this container along with the template.
The array must also be an observed item.
You can use crsbinding.observation.observe
to observe the array.
When you add new items or remove items, the UI will be updated.
this.persons = crsbinding.observation.observe([
{
firstName: "First Name 1",
lastName: "Last Name 1",
contacts: crsbinding.observation.observe([
{
cell: "Cell 1"
},
{
cell: "Cell 2"
}
])
}
]);
##Bindable Element
Bindable element is a web component base class that is event enabled.
That means you can listen to events on the element as you would with a normal class and use it as a binding context.
Important aspect to remember with bindable element is that it provides two functions.
One for getting property values in a property and another to set a property value.
These functions will take care of notifying property change for those properties when set.
See the following examples.
get items() {
return this.getProperty("items");
}
set items(newValue) {
this.setProperty("items", newValue);
}
If you don't use setProperty to get the property value, the events will not fire and you will need to call notifyPropertyChanged yourself.
The bindable element class can be found in the file "crs-bindable-element.js";
It uses standard ES6 modules.
import {BindableElement} from"./../../node_modules/crs-binding/crs-bindable-element.js";
In bindable element you must also populate the properties static getter with the property names available for updateUI.
static get properties() {
return ["items"];
}
When unsure when to use it, just add any property name that you are binding too.
Bindable element will try and load the HTML from a external file. You need to tell it where to find that file using the html property.
get html() {
return import.meta.url.replace(".js", ".html");
}
If you do override the connectedCallback, do not add the super but remember to add the following code after you are done.
await crsbinding.parsers.parseElements(this.children, this);
crsbinding.expression.updateUI(this);
this.dispatchEvent(new CustomEvent("ready"));
As you can see from the above await the connected callback needs to be async. If you want to prevent this, you need to override the connectedCallback function.
You can register dom events in a bindable element so that you don't need to manage the addEventListener.
You use the registerEvent and unregisterEvent and unregisterEvent to add and remove dom events.
this.registerEvent("click", this.click.bind(this))
On the disconnected callback of the bindable element it will release all these events for you. This means that if you have events that live for the lifespan of the element you can register them when you want and the system will clean them up for you when the component is done.
##View base
View base is much like bindable element in that it provides you with base features.
This class is used when you want to create a view that can be a bindable context.
It comes with two properties too take note of:
It is also event enabled and provides the getProperty and setProperty functions to use in your property getters and setters.
The "ViewBase" can be found in the crs-view-base.js file.
Though the view base is not technically a component it does also have:
You can use them in the same way as you would a custom element.
When using view base and creating a new instance you must provide the element it will represent. ViewBase is used by systems like crs-router. You will need to mange the connected and disconnected callbacks manually if you are not using crs-router. These lifecycle events are important for getting the parsing of the element underway.
When you create a view using viewbase the element style will be set to hidden. When you call the connectedCallback function it will set that back to visible again.
Remember to set the properties static property
If viewbase is not a custom element you will need to add a dummy function getAttribute that returns null or what value you require.
static get properties() {
return ["items"];
}
const view = new ClassUsingViewBase(document.body);
##Old value vs New value
Sometimes you want to know what the old value was.
Crs binding does not track what the previous values was.
If you want that kind of feature use the setter of your property to mange that for you.
For example:
class Test {
get name() {
return this._name;
}
set name(newValue) {
this._oldName = this._name;
this._name = newValue;
}
}
This way you can track the old value of only the items you care about.
Notify property changed will check on the context if a function maching a convention exists and if it exists that function will be called.
This is used to be notified simply when a property changes value;
The convention is ${propertyName}Changed
;
If i had a property called name and in my binding context i wanted a function to be called each time that property changes, create a function with the name "nameChanged". If that function exists it will be called each time the "name" property changes and is notified using notifyPropertyChanged
There are two ways to deal with style classes on a element.
This example shows how we can bind to the classList and use the class attribute at the same time.
<div class="italic" classlist.one-way="myClasses">Is Active Status</div>
myClass on the binding context can be a string value or a array of strings.
italic will be there regardless of what you set myClasses too.
this.myClasses = this.isActive == true ? "blue": ["red", "bold"];
You can also perform conditional binding on the class list.
This is done using the "if" syntax.
<div classlist.if="isActive == true ? ['green', 'italic'] : 'red'">Is Active Status</div>
<div classlist.if="isActive == true ? 'green'">Is Active Status</div>
These two examples show the conditional binding with
If you don't want to use classes, you can also set a single style property.
<div style.background.one-way="background">Test</div>
This behaves the same way as if you wrote the following javascript:
element.style.background = value
When you set the background property on the binding context the value will style property will be set accordingly.
You can also use conditional binding here to set the style property.
<div style.background.if="isActive == true ? 'blue' : 'red'">Is Active Status</div>
<div style.background.if="isActive == true ? 'blue'">Is Active Status</div>
FAQs
zero dependency binding engine
The npm package crs-binding receives a total of 6 weekly downloads. As such, crs-binding popularity was classified as not popular.
We found that crs-binding demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncovered a malicious PyPI package exploiting Deezer’s API to enable coordinated music piracy through API abuse and C2 server control.
Research
The Socket Research Team discovered a malicious npm package, '@ton-wallet/create', stealing cryptocurrency wallet keys from developers and users in the TON ecosystem.
Security News
Newly introduced telemetry in devenv 1.4 sparked a backlash over privacy concerns, leading to the removal of its AI-powered feature after strong community pushback.