How to remove the selected tags tag-input ngx-chips from code behind? - ngx-chips

When i try to clear the model , it works first time but then it just doesn't clear the tags.
HTML
<tag-input theme='bootstrap' id="test" [ngModel]="tagsSelected" [onlyFromAutocomplete]="true" [clearOnBlur]="true" [maxItems]= maxProductModels (onAdd)="onModelAdded($event)" (onRemove)="onModelRemoved($event)">
<tag-input-dropdown [showDropdownIfEmpty]="true" placeholder=""
[autocompleteItems]="sampleData">
</tag-input-dropdown>
</tag-input>
In the code behind , i am trying to clear the model in my typescript file
this.tagsSelected = [];
This works for the first time but later i doesn't seem to be working

Not sure how you are clearing your tagsSelected, but first I would suggest to make tagsSelect 2way binding [(ngModel)]="tagsSelected"
<tag-input theme='bootstrap' id="test" [(ngModel)]="tagsSelected" [onlyFromAutocomplete]="true"
[clearOnBlur]="true" [maxItems]= "5" (onAdd)="onModelAdded($event)" (onRemove)="onModelRemoved($event)">
<tag-input-dropdown [showDropdownIfEmpty]="true" placeholder=""
[autocompleteItems]="sampleData">
</tag-input-dropdown>
</tag-input>
<button (click)="clear()">Reset</button>
<button (click)="pop()">pop</button>
<button (click)="add()">add</button>
Component:
import {Component, OnInit} from '#angular/core';
import {TagModel} from 'ngx-chips/core/accessor';
#Component({
selector: 'app-ngx-chips',
templateUrl: './ngx-chips.component.html',
styleUrls: ['./ngx-chips.component.css']
})
export class NgxChipsComponent implements OnInit {
tagsSelected: any [] = [];
sampleData: any;
constructor() {
this.sampleData = [
{display: 'A1', value: 'aA4'},
{display: 'A2', value: 'aA3'},
{display: 'A3', value: 'aA2'},
{display: 'A4', value: 'aA1'},
];
}
clear = () => {
this.tagsSelected = [];
}
add = () => {
const display = 'x_' + this.tagsSelected.length;
this.tagsSelected.push({display, value: display});
}
pop = () => {
this.tagsSelected.pop();
}
ngOnInit(): void {
}
onModelAdded = ($event: TagModel) => {
console.log(`$event`, $event);
}
onModelRemoved = ($event: TagModel) => {
console.log(`$event`, $event);
}
}

Related

Dynamicly add a Button that is linked to an action

On connect, I want to add some markup that is connected to an action within the same controller. Is this possible? How is it done?
This is what I am trying so far in my tricksmods controller:
addEmbedButton() {
const buttonHTML = '<button type="button" class="trix-button" data-trix-attribute="embed" data-action="click->tricksmods#showembed" title="Embed" tabindex="-1">Embed</button>'
this.buttonGroupBlockTools.insertAdjacentHTML("beforeend", buttonHTML)
}
showembed(){
console.log('showembed')
}
The markup is added, but the showembed action is not fired on click.
I worked this out. I needed to use the toolbarElement of the trix editor to get at my button and dialog.
import { Controller } from "#hotwired/stimulus"
import Trix from 'trix'
import Rails from "#rails/ujs"
export default class extends Controller {
static get targets() {
return [ "field" ]
}
connect() {
this.addEmbedButton()
this.addEmbedDialog()
this.eventListenerForEmbedButton()
this.eventListenerForAddEmbedButton()
}
addEmbedButton() {
const buttonHTML = '<button type="button" class="trix-button tricks-embed" data-trix-attribute="embed" data-trix-action="embed" data-action="click->tricks#showembed" title="Embed" tabindex="-1">Embed</button>'
this.buttonGroupBlockTools.insertAdjacentHTML("beforeend", buttonHTML)
}
addEmbedDialog() {
const dialogHTML = `<div class="trix-dialog trix-dialog--link" data-trix-dialog="embed" data-trix-dialog-attribute="embed" data-tricks-target="embeddialog">
<div class="trix-dialog__link-fields">
<input type="text" name="embed" class="trix-input trix-input--dialog" placeholder="Paste your URL" aria-label="embed code" required="" data-trix-input="" disabled="disabled">
<div class="trix-button-group">
<input type="button" class="trix-button trix-button--dialog" data-trix-custom="add-embed" value="Add">
</div>
</div>
</div>`
this.dialogsElement.insertAdjacentHTML("beforeend", dialogHTML)
}
showembed(e){
console.log('showembed')
const dialog = this.toolbarElement.querySelector('[data-trix-dialog="embed"]')
const embedInput = this.dialogsElement.querySelector('[name="embed"]')
if (event.target.classList.contains("trix-active")) {
event.target.classList.remove("trix-active");
dialog.classList.remove("trix-active");
delete dialog.dataset.trixActive;
embedInput.setAttribute("disabled", "disabled");
} else {
event.target.classList.add("trix-active");
dialog.classList.add("trix-active");
dialog.dataset.trixActive = "";
embedInput.removeAttribute("disabled");
embedInput.focus();
}
}
eventListenerForEmbedButton() {
this.toolbarElement.querySelector('[data-trix-action="embed"]').addEventListener("click", e => {
this.showembed(e)
})
}
eventListenerForAddEmbedButton() {
this.dialogsElement.querySelector('[data-trix-custom="add-embed"]').addEventListener("click", event => {
console.log('embeddy')
const content = this.dialogsElement.querySelector("[name='embed']").value
if (content) {
let _this = this
let formData = new FormData()
formData.append("content", content)
Rails.ajax({
type: 'PATCH',
url: '/admin/embed.json',
data: formData,
success: ({content, sgid}) => {
const attachment = new Trix.Attachment({content, sgid})
_this.element.editor.insertAttachment(attachment)
_this.element.editor.insertLineBreak()
}
})
}
})
}
//////////////// UTILS ////////////////////////////////////////////////////
get buttonGroupBlockTools() {
return this.toolbarElement.querySelector("[data-trix-button-group=block-tools]")
}
get buttonGroupTextTools() {
return this.toolbarElement.querySelector("[data-trix-button-group=text-tools]")
}
get buttonGroupFileTools(){
return this.toolbarElement.querySelector("[data-trix-button-group=file-tools]")
}
get dialogsElement() {
return this.toolbarElement.querySelector("[data-trix-dialogs]")
}
get toolbarElement() {
return this.element.toolbarElement
}
}

How to change text in sidebar dynamically based on component I click on?

I am trying to make a timeline website. I would like to click on a Interval component (such as Russo-Persian War in the code below) and a text-description to show on the sidebar. There is currently no text description set. The sidebar is currently set to "Lorem Ipsum". How can I dynamically change the text in the sidebar based on the Interval component I click on? Thank you.
Here is my code:
Sidebar.vue
<div id="side-bar">
<hr />
{{text}}
</div>
</template>
<script>
import Vue from 'vue'
export default Vue.extend({
data() {
return {
text: `Lorem Ipsum`,
}
},
methods: {
},
})
</script>
Helper.js
export class Mark {
name = ''
tags = []
constructor(date) {
this.year = date
}
}
/* An interval consist of two marks (dates) in time-history */
export class Interval {
from = null
to = null
subIntervals = null
name = ''
i18n = null
tags = []
constructor() {
if (arguments.length === 1) {
let data = arguments[0]
this.from = new Mark(data.from)
this.to = new Mark(data.to)
if (data.subIntervals !== undefined) {
this.subIntervals = data.subIntervals
}
this.i18n = {
messages: {
es: { message: { title: data.title } }
}
}
} else {
this.from = arguments[0]
this.to = arguments[1]
}
}
}
/* A 'Timeline' consist of a 'name' and a
* property 'events', which consists of an ordered list
* of 'Interval's AND/OR 'Mark's (or even other 'Timeline's) */
export class Timeline {
events = []
name = ''
tags = []
constructor(_name) {
this.name = _name
}
}
Data.js
import { Interval, Timeline, century } from './Helper'
...
new Interval({
title: '1800s',
from: 1800,
to: 1899,
})
let lower = new Timeline()
let ninteenthcentury = new Timeline('1900s')
ninteenthcentury.events.push(
new Interval({
title: 'Russo-Persian War',
from: 1804,
to: 1813,
}),
new Interval({
title: 'Russo-Turkish War',
from: 1806,
to: 1812,
})
(etc...)
)
lower.events.push(
ninteenthcentury
)
export { lower }
Format.vue
<template>
<div class="interval"
:style="{
width: width + 'px',
marginLeft: marginLeft + 'px',
display: width < 51 ? 'none' : 'flex'
}"
:title="title">
<span v-on:click= "" class="name" >{{$t('message.title')}}</span>
<!-- sub intervals -->
<div class="lane" v-if="data.subIntervals">
<interval
v-for="(subInterval, index) in data.subIntervals"
:key="index"
:data="subInterval"
:left="last(index)"
:ratio="ratio" />
</div>
</div>
</template>
<script>
import Vue from 'vue'
export default Vue.extend({
name: 'interval',
props: ['data', 'ratio', 'left'],
i18n: {
},
created: function() {
if (this.data.i18n) {
this.$i18n.setLocaleMessage('es', this.data.i18n.messages.es)
}
},
methods: {
last(index) {
if (index > 0) {
return this.data.subIntervals[index - 1].to.year
}
return this.data.from.year
},
},
computed: {
marginLeft() {
if (this.left) {
return Math.abs(this.left - this.data.from.year) * this.ratio
}
return 0
},
width() {
return (this.data.to.year - this.data.from.year) * this.ratio
},
title() {
return this.$i18n.t('message.title') + ` (${this.data.from.year},${this.data.to.year})`
},
}
})
</script>
First of all you should consider migrating your helper to vuex, using store features will help you in every thing.
You will have all intervals in one array and, for instance, have a prop in your store for currentInterval that changes every time you click an interval on the timeline. You can achieve this by dispatching a vuex action to the store that will mutate the state of your prop, ex currentInterval.
In the component where you show the current interval data you will ...mapGetters from the store that will always return that prop currentInterval of the store.
More information about store management here: enter link description here

Set value programmatically ng-select

[items] : {id: number, label: string }[]
[bindValue]: id
[bindLabel]: label
([ngModel)] : number[]
I want to bind default data but there is no label.(picture)
And if i change bindValue to label then these square will show up the label
Manually selecting works fine.
i add this line of code to add default selected items
this.heroForm.get('selectedCitiesIds').patchValue([1,2]);
check this example
<form [formGroup]="heroForm">
<div class="form-group">
<label for="state">City</label>
<ng-select *ngIf="isCitiesControlVisible"
[items]="cities"
bindLabel="name"
bindValue="id"
labelForId="state"
[multiple]="true"
placeholder="Select cities"
clearAllText="Clear"
formControlName="selectedCitiesIds">
</ng-select>
<br>
<button (click)="toggleCitiesControl()" class="btn btn-sm btn-secondary">Show/Hide</button>
<button (click)="clearCities()" class="btn btn-sm btn-secondary">Clear</button>
</div>
</form>
and
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup } from '#angular/forms';
#Component({
selector: 'forms-multi-select-example',
templateUrl: './forms-multi-select-example.component.html',
styleUrls: ['./forms-multi-select-example.component.scss']
})
export class FormsMultiSelectExampleComponent implements OnInit {
heroForm: FormGroup;
isCitiesControlVisible = true;
cities: any[] = [
{ id: 1, name: 'Vilnius' },
{ id: 2, name: 'Kaunas' },
{ id: 3, name: 'Pavilnys (Disabled)', disabled: true },
{ id: 4, name: 'PabradÄ—' },
];
constructor(private fb: FormBuilder) {
}
ngOnInit() {
this.heroForm = this.fb.group({
selectedCitiesIds: []
});
this.heroForm.get('selectedCitiesIds').patchValue([1,2]);
}
toggleCitiesControl() {
this.isCitiesControlVisible = !this.isCitiesControlVisible;
}
clearCities() {
this.heroForm.get('selectedCitiesIds').patchValue([]);
}
}

ag-grid in angular5 row inline edit

I want to know the best way I can give user inline edit in ag-grid on button click.
see the image below.
As per my requirement, if user clicks on edit icon, then ag-grid row goes in fullrow edit mode (able to do from documentation provided onag-grid.com) and at the same time, icons in 'Action' column changes to save and cancel icons. So, want to know how this can be done in Angular5. I need idea of dynamically changing this last column.
There's quite a few steps here that you'll need to implement.
Step 1: Create a custom renderer component
#Component({
selector: 'some-selector',
template: `
<span *ngIf="!this.isEditing">
<button (click)="doEdit()">Edit</button>
</span>
<span *ngIf=this.isEditing">
<button (click)="doSave()">Save</button>
<button (click)="doCancel()">Cancel</button>
</span>
`
})
export class MyRenderer implements ICellRendererAngularComp {
isEditing = false;
params: any;
agInit(params: any): void {
this.params = params;
}
doEdit() {
// we need to loop thru all rows, find the column with the renderer, and 'cancel the edit mode'.
// otherwise, we will end up with rows that has SAVE buttons appearing but not in edit mode
const renderersInOtherRows = this.params.api.getCellRendererInstances(this.params);
if( renderersInOtherRows && renderersInOtherRows.length > 0 ) {
for ( let i=0; i<renderersInOtherRows.length; i++) {
const wrapper = renderersInOtherRows[i];
if ( wrapper.getFrameworkComponentInstance() instanceof MyRenderer ) {
const foundRenderer = wrapper.getFrameworkComponentInstance() as MyRenderer;
if( foundRenderer.isEditing ) {
foundRenderer.doCancel();
}
}
}
}
this.isEditing = true;
this.params.api.startEditingCell( { rowIndex: this.params.node.rowIndex, colKey: 'some-col-field-name'});
}
doCancel() {
this.isEditing = false;
// restore previous data or reload
}
doSave() {
this.isEditing = false;
// do your saving logic
}
}
Step 2: Load the component
#NgModule({
imports:[
AgGridModule.withComponents([MyRenderer]),
// ...
],
declarations: [MyRenderer],
})
export class MyModule();
Step 3: Use the component
SuppressClickEdit = true, will prevent double/single click edit mode
#Component({
selector: 'my-grid',
template: `
<ag-grid-angular #grid
style="width: 100%; height: 500px;"
class="ag-theme-balham"
[rowData]="this.rowData"
[columnDefs]="this.columnDefs"
[editType]="'fullRow'"
[suppressClickEdit]="true"></ag-grid-angular>
`
})
export class MyGridComponent implements OnInit {
columnDefs = [
// ... other cols
{ headerName: '', cellRendererFramework: MyRenderer }
];
}
I was just looking for something similiar and so I thought I would share what I did to get this working. I am new to Angular so this may not be the best way.
This is in my component.html
<button (click)="onEdit()">edit button</button>
<button (click)="onSaveEdit()" *ngIf="!editClicked">save button</button>
<button (click)="onCancelEdit()" *ngIf="!editClicked">cancel</button>
This is in my component.ts
public params: any;
private editClicked;
constructor() {
this.editClicked = true;
}
agInit(params: any): void{
this.params = params;
}
onEdit() {
this.editClicked = !this.editClicked;
this.params.api.setFocusedCell(this.params.node.rowIndex, 'action');
this.params.api.startEditingCell({
rowIndex: this.params.node.rowIndex,
colKey: 'action'
});
}
onSaveEdit() {
this.params.api.stopEditing();
this.editClicked = !this.editClicked;
}
onCancelEdit() {
this.params.api.stopEditing(true);
this.editClicked = !this.editClicked;
}
Hope this helps steer you in the right direction.

Angular2 Service which create, show and manage it's inner Component? How to implement js alert()?

I tried to find a way for having and manage an angular2 Component in a Service but with no success:
I need to create:
AlertService{
alertConfirm(msg): Promise;
}
alertConfirm will prompt an Confirmation window with 2 buttons (Ok, Cancel) and will return users' choise as a Promise.
In General, the idea is to implement the famous JavaScript alert() method
but with a designed UI window and with also a cancel button.
The method will return a Promise with a response of user's choice: "OK" or "Cancel".
I tried to find a way for holding an "anonymous" component, AlertComponent, in AlertService:
AlertComponent{
showMsgConfirm(msg): Promise;
}
The Promise will be set with a response when user close prompt window or click "OK" or "Cancel".
The question:
How to make "AlertService" to have an inner "AlertComponent" which can be managed by it's "alertOK" method?
I mean, I didn't find a way for "alertConfirm" to call "showMsgConfirm" method and to return it's Promise as a response.
for example, calling from main app component:
this.alertService.alertConfirm("Save changes?").then(res => {
if(res.ok){console.log("Can be saved");
}, err=> { });
Any ideas for this?
Thanks,
Update:2 different ideas for solution, but with no sucess to manage the AlertComponent:
import { Injectable, ViewContainerRef, ReflectiveInjector, ComponentFactoryResolver, ComponentRef } from '#angular/core';
import { AlertComponent } from './../components/modales/AlertComponent/AlertComponent.component';
#Injectable()
export class AlertService {
constructor(private componentFactoryResolver: ComponentFactoryResolver) { }
public createAlertComp(vCref: ViewContainerRef): ComponentRef<any> {
let factory = this.componentFactoryResolver.resolveComponentFactory(AlertComponent);
/*
//Option 1:
// vCref is needed cause of that injector..
let injector = ReflectiveInjector.fromResolvedProviders([], vCref.parentInjector);
// create component without adding it directly to the DOM
let comp = factory.create(injector);
// add inputs first !! otherwise component/template crashes ..
comp.instance.model = modelInput;
// all inputs set? add it to the DOM ..
vCref.insert(comp.hostView);
return comp;
*/
//Option 2:
var componentRef: ComponentRef<AlertComponent> = vCref.createComponent(factory);
return null;
}
}
And the answer is... :
The Service:
_counter is used for each modal to have a unique name.
comp.instance.close is a property of inner component for subscribing for EventEmitter.
.
import { Injectable, ViewContainerRef, ReflectiveInjector, ComponentFactoryResolver, ComponentRef, EventEmitter } from '#angular/core';
import { CtmAlertComponent } from './ctmAlert/ctmAlert.component';
#Injectable()
export class AlertCtmService {
private _vcr: ViewContainerRef;
private _counter: number = 0;
constructor(private componentFactoryResolver: ComponentFactoryResolver, public viewRef: ViewContainerRef) {
console.log("AlertCtmService.constructor:");
//TODO: Consider appending to this.viewRef: "#alertCtmServiceContainer" as a Dom elemnt perent container which will hold all AlertModals:
// Maybe by:
// this.viewRef.element.nativeElement.insertAdjacentHTML('beforeend', '<div class="alertCtmServiceContainer"></div>');
this._vcr = this.viewRef;
}
public alertOK(alertMsg: string): EventEmitter<any> {
return this.createEventEmitterComponent("CtmAlertComponent", alertMsg, false);
}
public alertConfirm(alertMsg: string): EventEmitter<any> {
return this.createEventEmitterComponent("CtmAlertComponent", alertMsg, true);
}
private createEventEmitterComponent(componentName: string, alertMsg: string, isConfirm: boolean): EventEmitter<any> {
console.log("AlertCtmService.createEventEmitterComponent:");
switch (componentName) {
case "CtmAlertComponent":
default:
var _component = CtmAlertComponent;
break;
}
let factory = this.componentFactoryResolver.resolveComponentFactory(_component);
// vCref is needed cause of that injector..
let injector = ReflectiveInjector.fromResolvedProviders([], this._vcr.parentInjector);
// create component without adding it directly to the DOM
let comp = factory.create(injector);
// add inputs first !! otherwise component/template crashes ..
comp.instance.close.subscribe(resp => {
console.log("AlertCtmService.createEventEmitterComponent: comp.instance.close.subscribe: resp=" + resp.ok);
comp.destroy();
})
comp.instance.alertBodyMsg = alertMsg;
comp.instance.isConfirm = isConfirm;
comp.instance.nameId = "Modal" +(++this._counter).toString();
// all inputs set? add it to the DOM ..
this._vcr.insert(comp.hostView);
//return null;
return comp.instance.close;
}
public init(vCref: ViewContainerRef): ViewContainerRef {
this._vcr = vCref;
return this._vcr;
}
}
Inner Component:
Using Bootstrap for handling display of Modal in UI: modal('show') \ modal('hide').
.
import { Component, AfterViewInit, Input, ViewChild, ElementRef, Renderer, NgZone, EventEmitter} from '#angular/core';
#Component({
selector: 'ctm-alert',
styles: [``],
templateUrl: '/app/shared/alertCtm/ctmAlert/CtmAlert.component.html',
styleUrls: ['./app/shared/alertCtm/ctmAlert/CtmAlert.component.css'],
providers: []
})
export class CtmAlertComponent implements AfterViewInit {
public ModalIsVisible: boolean;
//private static subscriptions: Object = {};
//enums = Enums;
close = new EventEmitter();
public nameId = "";
private isOk = false;
alertBodyMsg: string = "";
isConfirm = false;
constructor() {
console.log("CtmAlertComponent.constructor:");
}
ngAfterViewInit() {
this.showModal();
var attrId = this.getIdAttr();
$('#' + attrId).on('hidden.bs.modal', function () {
debugger;
console.log('CtmAlertComponent: #licenseModal_XXX.on(hidden.bs.modal)');
this.submitStatus();
}.bind(this) );
}
showModal() {
this.ModalIsVisible = true;
var attrId = '#' +this.getIdAttr();
$(attrId).modal('show');
}
hideModal() {
this.ModalIsVisible = false;
var attrId = '#' + this.getIdAttr();
$(attrId).modal('hide');
}
getIdAttr(): string {
return "ctmAlertModal_" + this.nameId;
}
submitStatus() {
var resp = { ok: (this.isOk == true) };
this.close.emit(resp);
}
submitOk() {
this.isOk = true;
this.hideModal();
}
submitCancel() {
this.isOk = false;
this.hideModal();
}
}
App's Declaration:
unfortunately, we must declare the anonymus component in our main-app module.
We must add a declaration of entryComponents: [CtmAlertComponent],
.
import { CtmAlertComponent } from './shared/alertCtm/ctmAlert/ctmAlert.component';
#NgModule({
imports: [
BrowserModule,
HttpModule,
AppRoutingModule,
...
],
declarations: [
CtmAlertComponent,
AppComponent,
...
],
entryComponents: [CtmAlertComponent],
providers: [
...
],
bootstrap: [AppComponent],
})
export class AppModule { }
enableProdMode();
Modal UI:
this html template is based on bootstrap's UI:
.
<div class="ctmAlertModal modal fade in" [id]="getIdAttr()" role="dialog">
<div class="modal-dialog modal-lg" [ngClass]="{'modal-lg-6': true }">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header" style="">
<div class="pull-right" style="position: relative;">
<span class="fa fa-times-circle" aria-hidden="true" style="color: #949494"></span>
</div>
</div>
<div class="modal-body">
<div class="modal-body-msg">
{{alertBodyMsg}}
</div>
<div class="modal-body-buttons">
<div style="margin: 0 auto;" [style.width]="(isConfirm)? '165px' : '70px' ">
<button type="button" *ngIf="isConfirm" class="btn-submit pull-left btn-cancel" [ngClass]="{'disabled': false }" [disabled]="false" (click)="submitCancel()">
<!--<img alt="End-Training" class="centering-me2" src="../../../contents/training_state_stop_white.svg">-->
Cancel
</button>
<button type="button" class="btn-submit pull-right" [ngClass]="{'disabled': false }" [disabled]="false" (click)="submitOk()">
<!--<img alt="Resume-Training" src="../../../contents/training_state_play_white.svg">-->
OK
</button>
</div>
</div>
</div>
</div>
</div>
</div>
.
Usage::
for example:
.
this.alertCtmService.alertOK("Save changes???").subscribe(function (resp) {
console.log("alertCtmService.alertOK.subscribe: resp=" + resp.ok);
this.saveData();
}.bind(this) );
**
An example I built : https://plnkr.co/qc1ZM6
**
sources:
building-angular-2-components-on-the-fly-a-dialog-box-example
angular2-ngmodule