Gbuomprisco / ng2-material-select

Angular 2 Material-like Select Component
35 stars 14 forks source link

ERROR in Ng2SelectModule is not an NgModule #36

Open mhmo91 opened 7 years ago

mhmo91 commented 7 years ago

I know this has been reported before, but i just need ASAP clarification if the problem is bug with the module implementation or it's an ng-cli / webpack bug?

I'm on those versions "angular-cli": "1.0.0-beta.24", "@angular/compiler-cli": "^2.3.1", "typescript": "^2.0.10", "@angular/common": "^2.3.1",

thanks

mhmo91 commented 7 years ago

i have created a rep with the error https://github.com/mhmo91/ERROR-in-is-not-an-NgModule

also, check my comment on this thread https://github.com/angular/angular-cli/issues/3426#issuecomment-268955509

playground commented 7 years ago

@mhmo91 did you get this resolved? I'm having the same issue.

mhmo91 commented 7 years ago

@playground I didn't, my solution was shifting to another component. here's the alternatives 1- Material Select: currently at the moment it the best one outthere but doesn't support multiselect https://material.angular.io/components/component/select 2- ng2 select https://www.npmjs.com/package/angular2-select 3- Another ng2 select
https://www.npmjs.com/package/ng2-select I do hate it when someone create something for the opensource community and doesn't maintain it!

Gbuomprisco commented 7 years ago

Hi @playground,

the reason is you may be trying to compile it with ngc? This component does not support this yet, but I will work on it as soon as I can.

@mhmo91 I understand the frustration, but:

So believe me, if I could I'd write OSS all the time, but this world expects me to pay the bills too, so.

mhmo91 commented 7 years ago

@Gbuomprisco I totally understand ur point but with great power comes great responsibility 💃 i'm sorry if i sounded rude i didn't mean that ^^, anywayyss, thanks for ur work anyway :))

Gbuomprisco commented 7 years ago

@mhmo91 no worries :) I'll keep the thread up to date if there are news

playground commented 7 years ago

@Gbuomprisco thanks for the note and your work. Do you have an idea of the timeline?

Meanwhile @mhmo91, I'm trying out https://material.angular.io/components/component/select, but having trouble setting the default value and when select from the dropdown, the model(item.redirectCode) is not getting updated. It seems to work with older version of cli. Any idea? Here is my template.

<md-select placeholder="Enter redirect code" name="redirectCode" [(ngModel)]="item.redirectCode" ngModelOptions="{trackBy: '$value.id'}"> <md-option ngValue="code" *ngFor="let code of codes" ngSelected="item.redirectCode === code"> {{code}}

mhmo91 commented 7 years ago

@playground replace ngValue with [value] i'm not sure about the ngModelOptions, where did u get this from :D for more info, check the examples tab

playground commented 7 years ago

Thanks @mhmo91 that works.

yyi17 commented 7 years ago

@Gbuomprisco @playground

I have solved it by adding @NgModule decorator to node_modules/ng2-material-select/dist/src/ng2-select.module.d.ts

`import { NgModule } from '@angular/core';

@NgModule({ declarations: [ Ng2SelectModule ] }) export declare class Ng2SelectModule { }`

playground commented 7 years ago

It works.

playground commented 7 years ago

@lmsac did something get changed? Today webpack failed to compile because ERROR in Ng2SelectModule is not an NgModule

yyi17 commented 7 years ago

@playground I guess the reason is that there is something missing, such as metadata, required by the angular compiler. Finally I downloaded the source code of this project and add it to my app directly (not as a dependency). The source code was changed as following:

ng2-select.module.ts

import { NgModule } from '@angular/core';
import { Ng2Select } from './ng2-select';
import { ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';

import { Ng2DropdownModule } from 'ng2-material-dropdown';

@NgModule({
    imports: [
        CommonModule,
        ReactiveFormsModule,
        Ng2DropdownModule
    ],
    declarations: [
        Ng2Select
    ],
    exports: [
        Ng2Select
    ]
})
export class Ng2SelectModule {}

ng2-select.ts

import {
    Component,
    Input,
    Output,
    forwardRef,
    EventEmitter,
    ViewChild,
    ViewEncapsulation
} from '@angular/core';

import { Selectable } from './decorators/selectable';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { Ng2Dropdown } from 'ng2-material-dropdown';
import { DropdownStateService } from 'ng2-material-dropdown/dist/src/modules/services/dropdown-state.service';
import { SelectAccessor } from './accessor';

import { equal } from '../equals/equals';

const CUSTOM_SELECT_VALUE_ACCESSOR = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => Ng2Select),
    multi: true
};

/**
 * A component for entering a list of terms to be used with ngModel.
 */

@Component({
    selector: 'ng2-select',
    providers: [CUSTOM_SELECT_VALUE_ACCESSOR],
    styleUrls: ['./style.scss'],
    templateUrl: './template.html',
    encapsulation: ViewEncapsulation.None
})
@Selectable()
export class Ng2Select extends SelectAccessor {
    @Input() public placeholder: string;
    @Input() public options: any[] = [];
    @Input() public displayBy: string;
    @Input() public selectedDisplayBy: string;
    @Input() public identifyBy: string;
    @Input() public multiple: boolean = false;

    @Output() public onChange: EventEmitter<string> = new EventEmitter<string>();

    @ViewChild(Ng2Dropdown) public dropdown;

    public getSelectedValue(): any {
        if (this.multiple && this.value.length === 1) {
            return this.selectedDisplayValue(this.value[0]);
        } else {
            const index = this.options.findIndex(item => equal(this.value, item));
            return index >= 0 ? this.selectedDisplayValue(this.options[index]) : undefined;
        }
    }

    public selectedDisplayValue(item): string {
        return this.selectedDisplayBy ? item[this.selectedDisplayBy] : this.displayValue(item);
    }

    public displayValue(item): string {
        return this.displayBy ? item[this.displayBy] : item;
    }

    public get placeholderDisplay(): string {
        if (this.multiple && this.value.length > 1) {
            return `${this.value.length} items selected`;
        } else {
            return this.getSelectedValue() || this.placeholder;
        }
    }

    public isEqual(itemOne, itemTwo) {
        return this.identifyBy ? itemOne[this.identifyBy] === itemTwo[this.identifyBy] :
            equal(itemOne, itemTwo);
    }

    public isSelected(item): boolean {
        if (this.multiple) {
            return this.value.filter(value => this.isEqual(item, value)).length > 0;
        } else {
            return equal(this.value, item);
        }
    }

    ngOnInit() {
        const state = (<DropdownStateService>this.dropdown.state).dropdownState;

        state.onItemClicked.subscribe(item => {
            if (this.multiple) {
                this.toggle(item.value);
            } else {
                this.value = this.multiple ? this.value : item.value;
            }

            this.onChange.emit(this.value);
        });

        this.dropdown.onShow.subscribe(() => {
            if (!this.value) {
                return;
            }

            // focus selected element
            const index = this.findIndexValue(this.value);
            const item = this.dropdown.menu.items.toArray()[index];

            (<DropdownStateService>this.dropdown.state).dropdownState.select(item, false);
        });
    }
}

Ng2Select depends on ng2-material-dropdown, so add it by npm install. ng2-material-dropdown ^0.7.3

In Ng2Select, this.dropdown.state should be changed to (<DropdownStateService>this.dropdown.state).dropdownState, maybe because of api changes of ng2-material-dropdown.

ng2-material-material' depends onequals, andequalsdepends onjkroso-type. The angular compiler doesn't supportrequire` function, so I downloaded source code of these two package, and changed them in typescript.

equals.ts

import { type } from '../jkroso-type/jkroso-type';

var types: any = {}

// (Number) -> boolean
types.number = function (a, b) {
    return a !== a && b !== b/*Nan check*/
}

// (function, function, array) -> boolean
types['function'] = function (a, b, memos) {
    return a.toString() === b.toString()
        // Functions can act as objects
        && types.object(a, b, memos)
        && equal(a.prototype, b.prototype)
}

// (date, date) -> boolean
types.date = function (a, b) {
    return +a === +b
}

// (regexp, regexp) -> boolean
types.regexp = function (a, b) {
    return a.toString() === b.toString()
}

// (DOMElement, DOMElement) -> boolean
types.element = function (a, b) {
    return a.outerHTML === b.outerHTML
}

// (textnode, textnode) -> boolean
types.textnode = function (a, b) {
    return a.textContent === b.textContent
}

// decorate `fn` to prevent it re-checking objects
// (function) -> function
function memoGaurd(fn) {
    return function (a, b, memos) {
        if (!memos) return fn(a, b, [])
        var i = memos.length, memo
        while (memo = memos[--i]) {
            if (memo[0] === a && memo[1] === b) return true
        }
        return fn(a, b, memos)
    }
}

types['arguments'] =
    types['bit-array'] =
    types.array = memoGaurd(arrayEqual)

// (array, array, array) -> boolean
function arrayEqual(a, b, memos) {
    var i = a.length
    if (i !== b.length) return false
    memos.push([a, b])
    while (i--) {
        if (!equal(a[i], b[i], memos)) return false
    }
    return true
}

types.object = memoGaurd(objectEqual)

// (object, object, array) -> boolean
function objectEqual(a, b, memos) {
    if (typeof a.equal == 'function') {
        memos.push([a, b])
        return a.equal(b, memos)
    }
    var ka = getEnumerableProperties(a)
    var kb = getEnumerableProperties(b)
    var i = ka.length

    // same number of properties
    if (i !== kb.length) return false

    // although not necessarily the same order
    ka.sort()
    kb.sort()

    // cheap key test
    while (i--) if (ka[i] !== kb[i]) return false

    // remember
    memos.push([a, b])

    // iterate again this time doing a thorough check
    i = ka.length
    while (i--) {
        var key = ka[i]
        if (!equal(a[key], b[key], memos)) return false
    }

    return true
}

// (object) -> array
function getEnumerableProperties(object) {
    var result = []
    for (var k in object) if (k !== 'constructor') {
        result.push(k)
    }
    return result
}

export function equal(a: any, b: any, memos?: any[]): boolean {
    // All identical values are equivalent
    if (a === b) return true
    var fnA = types[type(a)]
    var fnB = types[type(b)]
    return fnA && fnA === fnB
        ? fnA(a, b, memos)
        : false
}

jkroso-type.ts

var toString = {}.toString

/**
 * Return the type of `val`.
 *
 * @param {Mixed} val
 * @return {String}
 * @api public
 */

export function type(x) {
    var type = typeof x
    if (type != 'object') return type
    type = types[toString.call(x)]
    if (type == 'object') {
        // in case they have been polyfilled
        if (x instanceof Map) return 'map'
        if (x instanceof Set) return 'set'
        return 'object'
    }
    if (type) return type
    if (x instanceof Node) switch (x.nodeType) {
        case 1: return 'element'
        case 3: return 'text-node'
        case 9: return 'document'
        case 11: return 'document-fragment'
        default: return 'dom-node'
    }
}

export var types = {
    '[object Function]': 'function',
    '[object Date]': 'date',
    '[object RegExp]': 'regexp',
    '[object Arguments]': 'arguments',
    '[object Array]': 'array',
    '[object Set]': 'set',
    '[object String]': 'string',
    '[object Null]': 'null',
    '[object Undefined]': 'undefined',
    '[object Number]': 'number',
    '[object Boolean]': 'boolean',
    '[object Object]': 'object',
    '[object Map]': 'map',
    '[object Text]': 'text-node',
    '[object Uint8Array]': 'bit-array',
    '[object Uint16Array]': 'bit-array',
    '[object Uint32Array]': 'bit-array',
    '[object Uint8ClampedArray]': 'bit-array',
    '[object Error]': 'error',
    '[object FormData]': 'form-data',
    '[object File]': 'file',
    '[object Blob]': 'blob'
}

Finally, change app.module.ts

import { Ng2SelectModule } from '../vendor/ng2-material-select/ng2-select.module';
...
@NgModule({
  imports: [
    ...
    Ng2SelectModule
  ],
  ...
  bootstrap: [AppComponent]
})
export class AppModule { }

It works with ng build and ng build --prod. ng build uses webpack, so perhaps my solution is helpful to your issue.

ghost commented 7 years ago

This module is not compatible with default compiler that implements AOT. So it's a broken component as its missing its *.metadata.json file.

playground commented 7 years ago

@Cogitaria for immediate/temporary fix as suggested by @lmsac

I have solved it by adding @NgModule decorator to node_modules/ng2-material-select/dist/src/ng2-select.module.d.ts

`import { NgModule } from '@angular/core';

@NgModule({ declarations: [ Ng2SelectModule ] }) export declare class Ng2SelectModule { }`

ghost commented 7 years ago

@Gbuomprisco do you intend on fixing this and the other modules you created that are no longer valid modules? Otherwise, honestly, you should delete these repos and remove it (from being listed, which is easy to do). If somebody stumbles on it they may make the mistake of thinking it a) works, b) might be actively maintained. Neither of course is true (see above comments about responsibility, which are spot on).

Playground, thanks, but our automated build has no room for doctoring what are essentially binaries as a post-install job. I'm going to look for an active component that meets the basic Angular requirements for a valid component.

Gbuomprisco commented 7 years ago

Hi @Cogitaria,

yes - I will. When? I don't know - hopefully in the weekend, as my other modules are in good shape instead.

I don't agree with removing them, because:

Gbuomprisco commented 7 years ago

This should be hopefully solve with the latest version

playground commented 7 years ago

@Gbuomprisco Has this been fixed?