Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for angular2 forms #63

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

gergelybodor
Copy link

No description provided.

@gergelybodor
Copy link
Author

Hi! I coded a working example of form implementation for your select2 component!
We are actually using this code in one of our web-app.
Please review our changes!

@achimha
Copy link
Contributor

achimha commented Feb 24, 2017

Would be nice if this PR got merged. A similar previous one is still open?

@gergelybodor
Copy link
Author

Yes. The other one seems incomplete to me. I tried using that in a project and didn't work.

Copy link
Contributor

@achimha achimha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not use that (there is a bug anyway where this was used) but instead a closure as callback so this points to the right context.

@@ -97,6 +112,8 @@ export class Select2Component implements AfterViewInit, OnChanges, OnDestroy, On
}

this.element.on('select2:select select2:unselect', function () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this whole that business and use a closure:

this.element.on('select2:select select2:unselect', () => {

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. I will correct it.

@achimha
Copy link
Contributor

achimha commented Feb 24, 2017

I think this PR is problematic and should probably not be applied. The writeValue method is called early at initialization and it has its own select2 init call in it which means every control gets initialized twice and this breaks things. I can work around it like this:

private isSelect2Initialized() {
        return this.element.hasClass('select2-hidden-accessible');
}

writeValue(newValue: any) : void {
        this.renderer.setElementProperty(this.selector.nativeElement, 'value', newValue);
        if (!this.isSelect2Initialized()) {
            return;
        }
        this.element.trigger('change.select2');

        this.onChange(newValue);
        this.onTouched();
        this.valueChanged.emit({
            value: newValue,
            data: this.element.select2('data')
        });
    }

@achimha
Copy link
Contributor

achimha commented Feb 24, 2017

Another point is that I believe we should not return just the value (i.e. a string). This often means information loss. Instead we should return an object with the value and the data associated.

that.valueChanged.emit({
value: that.element.val()
});
});
}

writeValue(newValue: any): void {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't look right to me, initializing the select2 here. It duplicates code and this method is called too soon for initializing it anyway.

this.valueChanged.emit({
value: this.element.val(),
data: this.element.select2('data')
Copy link
Author

@gergelybodor gergelybodor Feb 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had to remove this line. Got error while submitting form.
TypeError: Cannot read property 'select2' of undefined at HTMLSelectElement.<anonymous> (new.select2.component.ts?246f:123) at HTMLSelectElement.dispatch (eval at webpackJsonp.407.module.exports (scripts.bundle.js:1), <anonymous>:5201:27) at HTMLSelectElement.elemData.handle (eval at webpackJsonp.407.module.exports (scripts.bundle.js:1), <anonymous>:5009:28) at Object.trigger (eval at webpackJsonp.407.module.exports (scripts.bundle.js:1), <anonymous>:8171:12) at HTMLSelectElement.eval (eval at webpackJsonp.407.module.exports (scripts.bundle.js:1), <anonymous>:8239:17) at Function.each (eval at webpackJsonp.407.module.exports (scripts.bundle.js:1), <anonymous>:368:19) at jQuery.fn.init.each (eval at webpackJsonp.407.module.exports (scripts.bundle.js:1), <anonymous>:157:17) at jQuery.fn.init.trigger (eval at webpackJsonp.407.module.exports (scripts.bundle.js:1), <anonymous>:8238:15) at Select2.eval (eval at webpackJsonp.407.module.exports (scripts.bundle.js:1), <anonymous>:2082:21) at Select2.Observable.invoke (eval at webpackJsonp.407.module.exports (scripts.bundle.js:1), <anonymous>:637:20) at Select2.Observable.trigger (eval at webpackJsonp.407.module.exports (scripts.bundle.js:1), <anonymous>:631:12) at Select2.trigger (eval at webpackJsonp.407.module.exports (scripts.bundle.js:1), <anonymous>:5472:19) at Results.eval (eval at webpackJsonp.407.module.exports (scripts.bundle.js:1), <anonymous>:5298:12) at Results.Observable.invoke (eval at webpackJsonp.407.module.exports (scripts.bundle.js:1), <anonymous>:637:20) at Results.Observable.trigger (eval at webpackJsonp.407.module.exports (scripts.bundle.js:1), <anonymous>:631:12)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's because it is called when the select2 control has not been constructed yet.

I think the ControlValueAccessor implementation is not working as it should, this needs to be re-thought. Right now it doesn't improve the situation I fear.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what would be the value of this.element.select2('data') ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole record as e.g. returned via AJAX. If my AJAX returns something like this:

{ id: 1, text: 'foo', extraField: true, anotherField: 'bar' }

Then this record would be returned by this.element.select2('data').

Copy link
Author

@gergelybodor gergelybodor Feb 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, your reasoning is perfectly valid.
So far we haven't ran into this kind of use case.
We usually just set the id the same as the text value, so when we select an item, we basically get the object we wanted the get. Haven't used additional fields at all.
How about we go with the much simpler version now, and worry about returning more complex objects later?

@gergelybodor
Copy link
Author

Another point is that I believe we should not return just the value (i.e. a string). This often means information loss. Instead we should return an object with the value and the data associated.

This PR is about adding angular2 form support for your component. I really don't care what you want to return in your output. That shouldn't be the subject of this PR.

@achimha
Copy link
Contributor

achimha commented Feb 24, 2017

The question is what the "value" of a select2 in a form should be. Just the string? What if the item has much more than its text? How would you get hold of the object? element.val() is just a subset of element.select2('data').

Therefore I am not sure this is the right approach.

@gergelybodor
Copy link
Author

The value input is of type string or string array as far as i know. When you initialize your form with select2 in it, you initialize it with a string or string array. When you change the form or submit it, the form control contains the current value of the select2, that is a string or string array.
So I dont really understand your statement "What if the item has much more than its text?"
You get the Select2OptionData object's id value out of the form, and that is perfectly fine by me.

@achimha
Copy link
Contributor

achimha commented Feb 25, 2017

The question is -- should there be a separate API to get the complete record or should the main API be able to handle it? Look at the following use case:

$('#myselect2').data('select2').trigger('select', {
  data: { id: 1, text: 'foo', myproperty: false }
});

This invocation you need for an AJAX Select2 to set a value programmatically.

Isn't it true that most people use Select2 for showing/selecting data via AJAX? The local data use case is is of course much simpler but is this why people go for a complex control like Select2? I am worried about modelling the API for just one use case.

We could extend the current API to take string|string[]|Object and then make the appropriate Select2 call when an object is passed.

@gergelybodor
Copy link
Author

I just don't think passing any other object other than the selected values into a form is practical. If you need any other fields of the Select2OptionData object, just write a simple get function.
I also don't see any reason to not just returning a string or string array to the form. I don't recall ever having any kind of information loss.
If you have any sugestions of how to implement passing an object to the form, I would be happy to modify this PR, but otherwise I believe this code works as is.

@pralhadstha
Copy link

Is there any other method that helps in solving the issue of formControlName not supporting by this package? When will this pull request be merged??

@keyvhinng
Copy link

keyvhinng commented Jun 8, 2017

I know that it may not be the proper place for questions but I need to make some modifications to ng2-selet2's source code for a custom behavior. What is the process to modify the source code and build it ? If I run the 'build' script defined in package.json I have the following issue:
error TS2339: Property 'amd' does not exist on type '{ (): JQuery; (it: IdTextPair): JQuery; (method: "val"): any; (method: "val", value: any, trigger...'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants