Is there an option to NOT use a theme in formly? - javascript

I want to use tailwind to do all my styling and don't want to add extra libraries to my code.
Here is a simple example of what I've come up with:
https://stackblitz.com/edit/angular-ivy-uxrxrc?file=src%2Fapp%2Fformly-component%2Fformly-component.ts
What I've done:
Created a simple component with formly inside of it with a basic input field. Angular seems to compile fine, but the formly doesn't seem to display any fields. It errors out with a Error: [Formly Error] There is no type by the name of "input".
I also can't seem to find any examples in the documentation that doesn't have a theme.

Formly provide a set of themes out of the box such material, bootstrap.. but that not prevent you to create your own theme, the only required is step is to define custom field type as documented in https://formly.dev/guide/custom-formly-field
to summarize here are the steps:
Create a custom field type with name input:
import { Component } from '#angular/core';
import { FieldType } from '#ngx-formly/core';
#Component({
selector: 'formly-datetimepicker',
template: `
<input [formControl]="formControl"></input>
`,
})
export class InputFieldType extends FieldType {}
define your field type through the NgModule declaration:
#NgModule({
declarations: [InputFieldType],
imports: [
....
FormlyModule.forRoot({
types: [
{ name: 'input', component: InputFieldType },
],
}),
],
})
export class AppModule {}
set type to type in your field config:
fields = [
{
key: 'name',
type: 'input',
},
]
still confused check Formly UI source code https://github.com/ngx-formly/ngx-formly/tree/master/src/ui

Related

Wrapping a story using decorator in storybook 5.2 - typescript / angular

I'm coming from storybook pre 5.2 using storiesOf whereby if i wanted to wrap my component I would use template.
EG
.add('Test', () => ({
component: TestComponent,
template: `
<div class="wrapper">
<test-component></test-component>...
In 5.2 the recommended way to write stories has changed and describes how to use decorators to achieve the same outcome https://storybook.js.org/docs/basics/writing-stories/#decorators. However I am using angular and struggling to find a solution as there are only react and vue examples. Both of which use specific functions / components
In Vue projects you have to use the special component <story/> instead of the function parameter storyFn that is used in React projects
Using template as in the older spec I have tried the following
As an initial test that template works
export const Test = () => ({
component: TestComponent,
template: `Expecting just this text`
});
Outcome: See the text 'Expecting just this text'
Using <TestComponent>
export const Test = () => ({ component: TestComponent,
template: <div class="wrapper"><TestComponent></TestComponent></div>
});
Outcome: Blank screen with Template parse errors:
'CheckboxComponent' is not a known element: suggesting use of `schemas: [CUSTOM_ELEMENTS_SCHEMA]
Using <test-component>
export const Test = () => ({
component: TestComponent,
template: `<div class="wrapper"><test-component></test-component></div>`
});
Outcome: Blank screen with Template parse errors: 'CheckboxComponent' is not a known element: suggesting use of schemas: [CUSTOM_ELEMENTS_SCHEMA]
For both 2 & 3 I tried adding
export const Test = () => ({
component: TestComponent,
addDecorator: moduleMetadata({
schemas: [CUSTOM_ELEMENTS_SCHEMA]
}),
template: `[tried both templates from 2 & 3]`
});
Outcome: Same errors appeared again
Could someone shed some light on how this would accomplished in typescript and where I am going wrong - thanks.
Found the way to do it in 5.2 with the new story format. Fixed the Template parse errors by adding [CUSTOM_ELEMENTS_SCHEMA] and declaring the component.
I'm also using the docs addOn https://github.com/storybookjs/storybook/tree/master/addons/docs and added the capability for this.
I've included both storiesOf https://storybook.js.org/docs/formats/storiesof-api/ and the Component Story Format (CSF) https://storybook.js.org/docs/formats/component-story-format/ incase anyone else runs into difficulty.
storiesOf API
import { TestComponent } from './test.component';
import { storiesOf, moduleMetadata } from '#storybook/angular';
import { CommonModule } from '#angular/common';
import { CUSTOM_ELEMENTS_SCHEMA } from '#angular/core';
storiesOf('Elements|Test', module)
.addParameters({ // only needed for docs add-on
component: TestComponent,
componentSubtitle: 'Subtitle'
// docs: { page: null } uncomment this to disabled docs
})
.addDecorator(
moduleMetadata({
imports: [CommonModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [TestComponent]
})
)
.add('Test', () => ({
component: TestComponent,
template: `<div class="test">test text<app-test></app-test></div>`
}));
CSF
import { TestComponent } from './test.component';
import { moduleMetadata, } from '#storybook/angular';
import { CUSTOM_ELEMENTS_SCHEMA } from '#angular/core';
export default {
title: 'Elements|Test',
component: TestComponent, // only needed for docs add-on
parameters: { // only needed for docs add-on
componentSubtitle: 'Subtitle.'
// docs: { page: null } uncomment this to disabled docs
},
decorators: [
moduleMetadata({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [TestComponent]
})
]
};
export const Test = () => ({
component: TestComponent,
template: `<div class="text">test text<app-test></app-test></div>`
});

ng-select not updating in Angular 2

hello i am new in angular 2
i can make formGroup in add in ng-select controll and predefine value added.
that is perfectly.
but when button click then new value push in ng-select but ng-select not updating .
here my plunker
https://plnkr.co/edit/Hwfk1T2stkiRcLTxuFmz
//our root app component
import {Component, OnInit, NgModule, ViewChild} from '#angular/core';
import {BrowserModule} from '#angular/platform-browser';
import {FormControl, FormGroup, ReactiveFormsModule} from '#angular/forms';
import {SelectModule} from 'ng-select';
#Component({
selector: 'my-app',
template: `
<h1>ng-select demo app</h1>
<form style="padding:18px;max-width:800px;"
[formGroup]="form">
<div style="margin:5px 0;font-weight:600;">Single select example</div>
<ng-select
[options]="options0"
[multiple]="false"
placeholder="Select one"
formControlName="selectSingle"
>
</ng-select>
<button (click)="pushValue()">Click</button>
<div>Events:</div>
<pre #preSingle>{{logSingleString}}</pre>
</form>`
})
export class App implements OnInit {
form: FormGroup;
multiple0: boolean = false;
options0: any[] = [];
selection: Array<string>;
#ViewChild('preSingle') preSingle;
logSingleString: string = '';
constructor() {
this.options0.push({"label":'test',"value":'Test'});
console.log("Object:::"+JSON.stringify(this.options0));
}
ngOnInit() {
this.form = new FormGroup({});
this.form.addControl('selectSingle', new FormControl(''));
console.log("Object:::"+JSON.stringify(this.options0));
}
pushValue()
{
console.log("pushValue call.");
this.options0.push({"label":"test","value":"Test"});
console.log("Object:::"+JSON.stringify(this.options0));
}
}
#NgModule({
imports: [
BrowserModule,
ReactiveFormsModule,
SelectModule
],
declarations: [ App ],
bootstrap: [ App ]
})
export class AppModule {}
where is wrong ???
you can use Array.slice() to update to array instance in order to let angular detect the change of array.
this.options0 = this.options0.slice();
Looking at ng-select source code i noticed
ngOnChanges(changes: any) {
if (changes.hasOwnProperty('options')) {
this.updateOptionsList(changes['options'].isFirstChange());
}
so in order to update options list you should fire ngOnChanges. It can be done by creating new reference to options0
this.options0 = this.options0.concat({"label":"test","value":"Test"});
or
this.options0 = [...this.options0, {"label":"test","value":"Test"}];
Modified Plunker
Change Detection
Ng-select component implements OnPush change detection which means the dirty checking checks for immutable data types. That means if you do object mutations like:
this.items.push({id: 1, name: 'New item'})
Component will not detect a change. Instead you need to do:
this.items = [...this.items, {id: 1, name: 'New item'}];
This will cause the component to detect the change and update. Some might have concerns that this is a pricey operation, however, it is much more performant than running ngDoCheck and constantly diffing the array.

Angular: Creating plugins for 3rd party packages (libraries)

I created Angular Library (ngx-wig) and I would like to provide an ability to extend its functionality by using plugins.
What would be the best place to declare plugin in Angular? (may be something like myLibModule.forRoot(..)) and what type of instance should be plugin itself?
I solved same issue for AngularJs just by adding module for each plugin in which I register plugin by using configProvider of main module. Don't really like this solution because plugin registers itself, but it should be responsibility of applications where library is used.
UPDATE: related issue is opened on github here.
I think you can provide users to use component as a plug-in. This component has to extends you abstract base plugin component.
For example clear-styles plugin could look like
#Component({
selector: `nw-clear-styles-button`,
template: `
<button (click)="clearStyles($event)"
[disabled]="editMode || disabled"
class="nw-button clear-styles" title="Clear Styles">
Clear Styles
</button>`
})
export class NwClearStylesButtonComponent extends Ng2WigPluginComponent {
constructor() {
super();
}
clearStyles() {
const div = document.createElement('div');
div.innerHTML = this.content;
this.contentChange.emit(div.textContent);
}
}
format plugin
#Component({
selector: `nw-formats-button`,
template: `
<select class="nw-select"
[(ngModel)]="format"
(ngModelChange)="execCommand('formatblock', format.value)"
[disabled]="editMode || disabled">
<option *ngFor="let format of formats" [ngValue]="format">{{ format.name }}</option>
</select>
`
})
export class NwFormatButtonComponent extends Ng2WigPluginComponent {
formats = [
{name: 'Normal text', value: '<p>'},
{name: 'Header 1', value: '<h1>'},
{name: 'Header 2', value: '<h2>'},
{name: 'Header 3', value: '<h3>'}
];
format = this.formats[0];
constructor() {
super();
}
}
where Ng2WigPluginComponent is abstract base class provided by your library:
export abstract class Ng2WigPluginComponent {
execCommand: Function;
editMode: boolean;
content: string;
editModelChange: EventEmitter<boolean> = new EventEmitter();
contentChange: EventEmitter<string> = new EventEmitter();
}
So users can easily use declared in base class properties.
To register such plugins we can use mentioned by you forRoot method. For that you need to
1) configure you library module like follows:
ng2wig.module.ts
#NgModule({
...
})
export class Ng2WigModule {
static forRoot(entryComponents: CustomButton[]) {
return {
ngModule: Ng2WigModule,
providers: [
Ng2WigToolbarService,
{provide: NG_WIG_CUSTOM_BUTTONS, useValue: entryComponents},
{provide: ANALYZE_FOR_ENTRY_COMPONENTS, multi: true, useValue: entryComponents},
]
};
}
}
where
NG_WIG_CUSTOM_BUTTONS is your global library token to recognize provided plugins inside library
ng2wig-toolbar.service.ts
#Injectable()
export class Ng2WigToolbarService {
constructor(#Optional() #Inject(NG_WIG_CUSTOM_BUTTONS) customButtons: CustomButton[]) {
if (customButtons) {
customButtons.forEach(plugin => this.addCustomButton(plugin.pluginName, plugin.component));
}
}
ANALYZE_FOR_ENTRY_COMPONENTS is angular global token to be able to load plugins dynamically
2) Declare NwClearStylesButtonComponent in declarations array of your AppModule module
3) Pass it to the Ng2WigModule.forRoot method
Ng2WigModule.forRoot([
{ pluginName: 'clear-styles', component: NwClearStylesButtonComponent },
{ pluginName: 'format', component: NwFormatButtonComponent }
])
And then main you task will be to dynamically generate your component by using ComponentFactoryResolver and ViewContainerRef (see ng2wig-plugin.directive.ts in plunker below)
Plunker Example

Angular 2: Multiple Form

I have a component which contains child components. Each child contains a form, built using FormBuilder.
I have included JADE template inside the components just for clarity.
Further I load the child components using the component router.
#Component({
selector: 'parent',
template: `
.detail-page
.detail-header
.ui.secondary.menu.inverted.orange
.item Inquiry Details
.right.menu
a.item((click)='add($event)')
i.plus.icon
a.item((click)='save($event)')
i.save.icon
.detail-content
router-outlet
.detail-footer
a.item([routerLink]="['Child1']")
a.item([routerLink]="['Child2']")
`
})
#RouteConfig([
{ path: '/child1', name: 'Child1', component: Child1, useAsDefault: true },
{ path: '/child2', name: 'Child2', component: Child2 }
])
export class Parent {
save(event) {
event.preventDefault();
// validate all child forms
???????
}
}
#Component({
template: `
form([ngFormModel]='childForm1')
input(type='text', [control]="field1")
input(type='text', [control]="field2")
`
})
export class Child1 {
constructor(
private _formBuilder: FormBuilder
) {
this.childForm1 = this._formBuilder.group({
field1: this.field1,
field2: this.field2
});
}
}
#Component({
template: `
form([ngFormModel]='childForm2')
input(type='text', [control]="field1")
input(type='text', [control]="field2")
`
})
export class Child2 {
constructor(
private _formBuilder: FormBuilder
) {
this.childForm2 = this._formBuilder.group({
field1: this.field1,
field2: this.field2
});
}
}
I need to validate the all the child forms when the save button is pressed.
As I understand only one child component is active/initialized at the end of the routing. Thus I cannot loop over the components to validate the forms.
What is the best approach to design such a component where it is not user friendly to have a long vertical form but have it broken down into manageable child form components?
Is there a way to re-use the components created?
I was thinking I can use the dynamic component loader but still I only get access to the current loaded component.
Your suggestions/help is much appreciated.
Thank You
You can add all forms at once and just show a selection of them at once using
[hidden]="someExpression"
You can still wrap the parts in components to keep the size of the template of the parent small, but validation only runs for on elements that actually exist in the DOM. (If you wrap parts of the form into components, then the forms in these components would be validated individually and you have to collect the results to get the status for the whole.)

Angular 2: component with more than one dependency

The following code:
...
#Component({
selector: 'sitelink',
properties: ['route: route, title: title, isHome: home, href: href']
})
#View({
template: ''
})
export class SiteLink {
constructor(#Optional() #Host() #SkipSelf() parentFrame: AppFrame, #Optional() #Host() #SkipSelf() parentLink: SiteLink) {
}
...
gives me the following error during the translation from TypeScript to JavaScript
at line 32, file app-frame.ts Argument of type 'typeof SiteLink' is not assignable to parameter of type 'Type'.
Component({
selector: 'sitelink',
properties: ['route: route, title: title, isHome: home, href: href']
})
at line 36, file app-frame.ts Argument of type 'typeof SiteLink' is not assignable to parameter of type 'Type'.
View({
template: ''
})
Using only one constructor parameter works as expected. When removing the annotations from the parameter list, it does not work, too. After removing the #Component and #View annotations, the code compiles. So the error must be related to the #Component annotation.
Edit: Even if I replace the SiteLink parameter with another type (for example AppFrame), I get the same error.
I'm using alpha 36 and the latest d.ts files from the DefinitelyTyped repository.
Its possible to inject an instance of the class itself in the constructor by using a forward reference:
constructor(#Inject(forwardRef(() => SiteLink)) siteLink) {
this.name = nameService.getName();
}
See here for more details.
As Jesse Good wrote, this is an issue with angular's d.ts file, see https://github.com/angular/angular/issues/3972.
Replacing
interface Type extends Function {
new(args: any): any;
}
with
interface Type extends Function {
new(...args): any;
}
fixed it for me.

Categories

Resources