Why is not the value in the view-model updated? This library has certain similarities to other libraries that also use bind
in models, for example, Backbone. Both Backbone and Aurelia "recognize" when there has been a change in value in some of their attributes since they subscribe to events such as "input" or "change" depending on the type of html element, and therefore, that is where synchronize the changes with the view
. In your case, that is the only detail that is missing. But before entering into the solution, we must know some important details of the select2 library that have an impact on the Aurelia binding function.
Disadvantages of select2 .
As you have stated, the select2 library precedes a series of html elements that pretend to render their own design. Despite this, select2 manages to show the value of the original item <select>
and this can be confirmed when the form is submitted. However, an important factor is lost by putting this design first, and the events of click
and change
are no longer triggered in the original element. What does this mean? Here's an example:
<select name='capital' id="capital">
<option value="Santiago">Chile</option>
<option value="San José">Costa Rica</option>
<option value="Kingston">Jamaica</option>
</select>
We are going to add the listener
of event change
common and current:
var elemento = document.getElementById("capital");
elemento.addEventListener("change", mostrarCapital);
function mostrarCapital(){
console.log(elemento.value);
}
When executing the previous code in the browser, we can confirm that each time an option is selected, it shows us by console the new value of the select. Ok, now add to the select2 library:
var elemento = document.getElementById("capital");
elemento.addEventListener("change", mostrarCapital);
function mostrarCapital(){
console.log(elemento.value);
}
// iniciar select2
$('#capital').select2();
Goodbye events! Every time we select an option, nothing happens. It no longer shows the selected value in console. That's because select2 does not dispatch such events in the original element.
You can check this example online from link
Why is the original bind of <select>
lost?
It is not really lost, and according to the above, select2 is just another component that has as a reference the initial value of <select>
. So, every time we load the page what is produced is a one-way
bind, since in effect, select2 recognizes the initial value according to the bind of the model, but a view > view-model
flow will never occur, because the events of change (change) are never dispatched, and therefore, Aurelia never realizes the new value of <select>
.
Finally, the solution
There are two pieces of the puzzle missing for (1) bidirectional synchronization two-way
and (2) without losing the functionality of select2:
Dispatch the event change
programmatically over the <select>
element in question so that Aurelia does the rest. We do that with clase.lanzarEvento(e.target)
:
attached() {
var clase = this;
$("select").on("select2:select", function(e) {
clase.lanzarEvento(e.target);
});
$("select").select2();
}
With line clase.lanzarEvento(e.target);
we are manually launching a "change" event on the original <select>
. The following code contains the definition of the event:
lanzarEvento(elemento) {
// creamos el evento de cambio
var eventoDeCambio = document.createEvent('Event');
eventoDeCambio.initEvent('change', true, true);
// despachar el evento
elemento.dispatchEvent(eventoDeCambio);
}
At this point, Aurelia has been notified and will therefore end up supplementing the bind with a flow view > view-model
since it will extract the value found in the element and update it to the model.
You can see this behavior in the online example posted on link .
You will notice that in the previous example, when you click on the "Select Nissan" button, the select2 component is not updated. This is because, as we know, it is a component that has no bind, since the bind is directly associated with it (as it should be). So, how do we solve it? Unfortunately, the plnkr.co
tool does not allow me to push the used demo further, because it does not have the ObserverLocator
module that resolves this issue. With this module we can know when a change has been made on a given attribute. In other words, the flow would be as follows:
- Observando cambios sobre "auto"..
- ¿se ha encontrado un nuevo valor?
* si: actualizar el componente select2 (a nivel de interfaz)
* no: flujo normal
However, here I leave the reference code, at least the specific changes on what I just mentioned:
// injectar el módulo
import {ObserverLocator, inject} from 'aurelia-framework';
@inject(ObserverLocator)
export class App {
// se suscribe a los cambios del atributo "auto"
constructor(observerLocator) {
var suscription = observerLocator
.getObserver(this, 'auto')
.subscribe(this.actualizarComponenteAutos);
}
// marcar la opción correcta en select2
actualizarComponenteAutos(newValue, oldValue) {
$("#auto").val(newValue).trigger("change");
}
The source code can be seen in full at link
Conclusion
I know the answer is very broad, but there were details that I did not want to ignore, especially because I wanted to illustrate how and when a bind one-way
and two-way
were given.