two-way bind with Select2 in aurelia.io

11

Question

How to implement a two-way link of an object to a VM in aurelia.io without losing the functionality of Select2 and maintaining the full link of the original, and also changes in the VM see reflected in the Select2 .

Context

I am working on a project where the aurelia.io library has been implemented, while the Select2 plugin is being implemented at various points in the project; The problem is that when you initialize the plugin, it replaces the em with a series of divs and other objects, thus losing the binding with the original , which remains hidden from view. the view, while working with the new artificial created by the Select2 plugin.

UPDATE 1

This is an example of what you want to achieve.

HTML Code:

<label>Seleccione la línea a la que pertenece la sublínea</label>    
<select value.bind="sublinea_seleccionada.LineaId">
    <option repeat.for="linea of Lineas" value.bind="linea.Id" selected.bind="$parent.sublinea_actual.LineaId == linea.Id">${Linea.Nombre}</option>
</select>
<button click.delegate="asignarLineaPorDefecto()">Seleccionar línea por defecto</button>

TypeScript Code:

export class Sublineas{

    Lineas: linea[] = [];
    sublinea_seleccionada: subLinea = new subLinea();   

    mostrarIdLineaSeleccionada() { 

         alert('Se ha seleccionado la Línea con Id ${this.sublinea_seleccionada.LineaId}'); 

    }

    asignarLineaPorDefecto(){

        this.sublinea_seleccionada.LineaId = 1;

    }

    attached() {

       var self = this;

        $("select").on("change", event => {

           self.mostrarIdLineaSeleccionada();

        });

        $("select").select2();

    }


}

UPDATE 2

I found this answer in SOen , but in the example provided by Select2 it only works in one-way, that is, it only updates in value in the view model but it is not updated with the changes in it.

    
asked by Matthew Joel Rodríguez Llanos 03.12.2015 в 02:53
source

2 answers

0

Link an object to a Select2 option?

I have a Select2 form like this:

<require from="./select2"></require>
<select select2="selected-values.two-way: selectedStuff" data-placeholder="Choose Stuff" multiple>
    <option repeat.for="thing of stuff">${thing.id}</option>
</select>

With which I can access selectedStuff , which is an arrangement of the ids of the things I have selected. However, I would like to get an arrangement of those things and not their ids. In my use case, it is not viable to search for something from your id.

Is there any way to force the object of the element to give me an arrangement of the things I need instead of an arrangement of its ids?

Answer:

The attribute value of the <option> element only accepts strings. Use the attribute model to store non-string elements:

<select select2 multiple value.bind="selectedThing2">
  <option repeat.for="thing of things" model.bind="thing">${thing.name}</option>
</select>

Here is plunker worked:

Here is the related documentation in aurelia:

Related questions:

  • How to change the css of color of select2 tags?
  • Using select2 with multiple choices and duplicate id values
  • Programmatic selection of select2 which retrieves its data via Ajax
  • Select2 with ajax, multiple and knockout binding not saving objects to selectedOptions
  • Preselect values in Select2 for ajax cases
  • Select2 4.0 - initialize with tag not in data list
  • Show 2 options and then "and x more" (select2)
  • select2 4.0 - TypeError: $ (...) .select2 (...) .select2 (...) is undefined
  • How do I get to select2 control to center itself?
  • Aurelia trying to load HTML from Select2?
answered by 27.01.2016 / 12:04
source
5

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.

        
    answered by 07.12.2015 в 04:05