angular4 material table - javascript

#Component({
selector: "app-dynamic-table",
template: `
`
})
export class DynamicTableComponent {
private _columns = [
{
name: "date",
show: true
}, {
name: "selected",
show: true
}, {
name: "id",
show: true
}, {
name: "location3",
show: false
}, {
name: "location4",
show: false
}, {
name: "location5",
show: false
}
];
get columns() { return this._columns; }
get displayedColumns(): string[] { return this._columns.map(c => c.name); }
}
the above code successfully able to hide and show the material table column,But I need to restrict the user to select less than 3 column of table and should show the alert message.Could you anyone look inti that?

You can keep click event on your checkbox, and check the limit of checkbox selection on that event and decide whether to proceed with the event or not. If you prevent that event to happen, then eventually it will not fire change event of checkbox
Code
checkLimit(event) {
// To make this SSR proof code, you could consider using Renderer to query DOM
let dom = event.currentTarget.querySelector('.mat-checkbox-input');
console.dir('Dom', dom, event.currentTarget)
if (!dom.checked && this.columns.filter(col => col.show).length >= 3) {
event.stopPropagation()
event.preventDefault()
alert('Cant select more than three column')
return
}
}
Html
<mat-checkbox *ngFor="let column of columns"
(click)="checkLimit($event)"
[(ngModel)]="column.show">
{{column.name | titlecase}}
</mat-checkbox>

Really appreciate Pankaj Answer. But it's not an angular way. Accessing DOM in component and checking without renderer2. Here is the angular way of achieving the same solution.
Component:
validate(index, column, event){
column.show = !column.show;
const matches = this.columns.filter(item=> item.show);
if(matches.length >= 3){
alert('not allowed');
event.stopPropagation();
event.preventDefault();
column.show = false;
}
}
Template :
<div *ngFor="let column of columns; let i=index;">
<input type="checkbox" id="{{i}}" name="feature{{i}}"
value="{{column.name}}" [checked]="column.show" ([ngModel])="column.show" (click)="validate(i, column, $event)">
<label for="feature{{i}}">{{column.name}}</label></div>
https://stackblitz.com/edit/angular-q2wrc4

Related

Is there any way to make the quill better-table uneditable?

I have a quill editor with a quill-better-table module. I want it to be uneditable at certain times, so I set it to readOnly. This works for buttons and text, but the table is still editable. The context menu (operationMenu) is also available.
Is there any way to make the better-table uneditable?
const quill = new Quill('#editor-wrapper', {
theme: 'snow',
readOnly: this.readOnly || false,
modules: {
table: false, // disable table module
'better-table': {
operationMenu: {
items: {
unmergeCells: {
text: 'Another unmerge cells name'
}
}
},
toolbar: {
container: [
['tableCreate'], // custom button for create table
],
handlers: {
'tableCreate': () => this.addCreateTableBtnEvent()
}
},
}
}
})
addCreateTableBtnEvent: function () {
const table = quill.getModule('better-table');
table.insertTable(2, 2);
}
Maybe it's not an elegant solution and the modification must be taken into account if I upgrade quill-better-table.js in the future, but so far it works.
I edited the quill-better-table.js and put in checks whether the Quill editor is editable or not.
If it's not editable than there is no content menu or column tool on the quill-better-table.
You can skip any function of the quill-better-table in this way.
...
menuInitial(_ref)
{
let {
table,
left,
top
} = _ref;
var editable = this.quill.root.getAttribute("contenteditable")
if ( editable === 'true' )
{
this.domNode = document.createElement('div');
this.domNode.classList.add('qlbt-operation-menu');
..

Pop up message not await for user response and moving on on onclick function

I have this logic on changing radio-button selection, if the user made some changing I am showing a message. if he confirm it will enter Onconfirm, else - Onreject.
1 issue -> the change of the radio button happens before the message show.
2 issue -> one reject I want to cancel the choice he made and to undo to his last choise - whice not happenning.
please help me with this!!
radio button
<div class="right" *ngFor="let type of types">
<p-radioButton name="treesDetailsType" [(ngModel)]="oneselectedType" formControlName="selectedType" (onClick)="onChangeType(type,$event)" class="treeDetails" value="{{type.id}}" label="{{type.desc}}" [disabled]="isReadOnly && type.id != data.selectedType"></p-radioButton>
</div>
the function of onclick
onChangeType(type, $event) {
let isFormTouched = this.isFormTouched(type);
if (isFormTouched) {
this.messagingService.showConfirmById(44, () => {
this.onConfirm()
}, () => {
this.onReject($event);
});
}
else
this.onchangedTrue(type); //this set some validators for the choice
}
on reject
#HostListener('click', ['$event']) onReject($event) {
event.stopImmediatePropagation();
//whatever written here its not happens before the change !!!!!
console.log(event);
}
----edited after the perfect suggestion from #Eliseo
askConfirm(value: any) {
let isFormTouched = this.isFormTouched(value);
if (isFormTouched) {
this.messagingService.showConfirmById(44, () => {
this.oneselectedType = value;
this.fg.controls.selectedType.setValue(value);
}, () => {
this.radios.forEach(x => {
x.writeValue(this.oneselectedType);
})
},
);
}
else {
this.oneselectedType = value;
this.onchangedTrue(value);
}
}`
the code work perfectly without the condition
--edited - on get the value from the server and patch it - the radio button lost
There a problem in my code (the another answer). Really I'm not pretty sure the reason, so I create a function like
redraw()
{
const value = this.form.value.type;
this.radios.forEach((x) => {
x.writeValue(value)
});
}
So, my function "ask" becomes like
ask(value: any) {
this.confirmationService.confirm({
message: 'Do you want to choose ' + value + '?',
header: 'Choose Confirmation',
icon: 'pi pi-info-circle',
key: 'positionDialog',
accept: () => {
this.form.get('type').setValue(value);
},
reject: () => {
this.redraw()
},
});
}
This allow me, when change the form, call to the function redraw. If I has a function
getForm(data: any = null) {
data = data || { type: 1, prop: '' };
return new FormGroup({
type: new FormControl(data.type),
prop: new FormControl(data.prop),
});
}
I can do some like
loadData(id: number) {
this.dataService.getData(id).subscribe((res: any) => {
this.form = this.getForm(res);
//it's necesary call to the function this.redraw
this.redraw()
});
}
newData() {
this.form = this.getForm();
//it's necesary call to the function this.redraw
this.redraw()
}
See in the this stackblitz what happens if we don't call to this.redraw() (just comment the lines)
1.-Select "new York" and say that you don't want it
2.-Click the button to load user
As "user" has the type 3 -"new York", the radio buttons looks like that it's not selected.
Yes is an ugly work-around, but for now I can not imagine another solution
Well there're another approach, that is change the value as usually and if we say that we want not the value, return the old value
askAfterChange(value:any)
{
const oldValue=this.form2.value.type;
this.form2.get('type').setValue(value)
this.confirmationService.confirm({
message: 'Do you want to choose ' + value + '?',
header: 'Choose Confirmation',
icon: 'pi pi-info-circle',
key: 'positionDialog',
accept: () => {
},
reject: () => {
this.form2.get('type').setValue(oldValue);
},
});
}
The "key" is split the [(ngModel)] in [ngModel] and (ngModelChanged)
//NOT WORK yet
<p-radioButton ... [ngModel]="selectedType"
(ngModelChange)="askConfirm($event)">
askConfirm(value: any) {
this.confirmationService.confirm({
message: 'Are you sure do you want '+value+'?',
header: 'Delete Confirmation',
icon: 'pi pi-info-circle',
accept: () => {
this.selectedType=value
},
reject: () => {
},
key: "positionDialog"
});
}
Well the problem is that the element still show the value selected How resolved? The first is get our p-radio buttons using ViewChildren, so we are give a template reference variable (the same to all the buttons) see the #radio
<div *ngFor="let type of types" class="p-field-radiobutton">
<p-radioButton #radio ...
(ngModelChange)="ask($event)"
[ngModel]="oneselectedType" ></p-radioButton>
</div>
//get the "radio buttons"
#ViewChildren('radio', { read: RadioButton }) radios!: QueryList<RadioButton>
constructor(private confirmationService: ConfirmationService) { }
ask(value: any) {
this.confirmationService.confirm({
message: 'Do you want to choose this?',
header: 'Choose Confirmation',
icon: 'pi pi-info-circle',
key: 'positionDialog',
accept: () => {
//if accept
this.oneselectedType = value
},
reject: () => {
//else, we loop over all the "radios"
this.radios.forEach(x => {
//and force is checked
x.writeValue(this.oneselectedType);
})
}
});
}
If you're using reactive Forms, you can also use a [ngModel] (ngModelChange) in the way, see that the model is myForm.get('selectedType').value
<p-radioButton ... [ngModel]="myForm.get('selectedType').value"
(ngModelChanged)="askConfirm($event)"
[ngModelOptions]="{standalone:true}"
>
And change in askConfirm
askConfirm(value: any) {
this.confirmationService.confirm({
...
accept: () => {
this.form.get('oneselectedType').setValue(value)
},
reject: () => {
this.radios.forEach(x => {
//and force is checked
x.writeValue(this.form.value.oneselectedType);
})
},
key: "positionDialog"
});
}
a simple stackblitz
Well, In the stackblitz I hard-code the value of the formGroup. Generally we has a service so we can
1.-Define our Form
form=new FormGroup({
selectedCity:new FormControl(),
selectedColor:new FormControl(),
prop:new FormControl()
})
//And in ngOnInit
this.dataService.getData().subscribe(res=>{
this.form.patchValue(res)
})
Or 2.-simple declare our form
form:FormGroup
//and in ngOnInit
use in ngOnInit
this.dataService.getData().subscribe(res=>{
this.form=new FormGroup({
selectedCity:new FormControl(res.selectedCity),
selectedColor:new FormControl(res.selectedColor),
prop:new FormControl(res.prop)
})
})
If we need a default value, we can give the value at first
(the stackblitz has in code this options)

angular-slickgrid, trigger the cell edit on select editor change event

I am using angular-silkgrid with angular 7. I am using inline editing feature. I am using single select editor for inline edit. Currently I want to achieve this functionality:
As soon as user click on the editable field , the select drop down will be visible.On select any option from select dropdown the inline mode should exist and column value should be updated.
currently I need to click on other field to exit from inline mode(I want to achieve this on select option select)
editor: {
// display checkmark icon when True
enableRenderHtml: true,
// tslint:disable-next-line:max-line-length
collection: [{
value: 1,
label: 'Sucessful'
}, {
value: 2,
label: 'Unsucessful'
}],
model: Editors.singleSelect, // CustomSelectEditor
elementOptions: {
autoAdjustDropPosition: false,
onClick: (event, rr) => {
// here i want to write some code which can trigger to grid to start update
}
}
}
Thanks All for the answers. I have solved my issue as below:
editor: {
enableRenderHtml: true,
collection: [{ value: CCLStaus.Sucessfull, label: 'Sucessful' }, { value: CCLStaus.UnSucessfull, label: 'Unsucessful' }],
model: Editors.singleSelect,// CustomSelectEditor
elementOptions: {
onClick: (event) => {
const updateItem = this.angularSilkGrid.gridService.getDataItemByRowIndex(this.rowInEditMode);
updateItem.status = +event.value;
this.angularSilkGrid.gridService.updateItem(updateItem, { highlightRow: false });
this.angularSilkGrid.gridService.renderGrid();
}
}
}
Generally,
grid.getEditorLock().commitCurrentEdit()
will commit and close the editor.
Also, any of
grid.navigateRight()
grid.navigateLeft()
grid.navigateDown()
grid.navigateUp()
grid.navigateNext()
grid.navigatePrev()
will commit and exit gracefully. In the select2 editor, you'll notice:
this.init = function () {
...
// Set focus back to select2 element on closing.
$input.on('select2:close', function (e) {
if ((e.params.originalSelect2Event && e.params.originalSelect2Event.data)
|| e.params.key === 9) {
// item was selected with ENTER or no selection with TAB
args.grid.navigateNext();
} else {
// closed with no selection
setTimeout(function () {
$input.select2('focus');
}, 100);
}
});
};
this.destroy = function () {
$input.select2('destroy');
$input.remove();
};
, noting that args.grid.navigateNext() will commit and close the editor, including calling the destroy() method at the appropriate time.
From the Angular-Slickgrid Editor Example there's a checkbox in the example to auto commit and that is a setting to you need to enable in your Grid Options
this.gridOptions = {
autoEdit: true,
autoCommitEdit: true,
}
The lib will internally call grid.getEditorLock().commitCurrentEdit() like Ben wrote in his answer, in Angular-Slickgrid you can just set the autoCommitEdit flag that I added.

Tinymce insert/edit image fields on pop up are not editable(focused) inside vuetify's dialog

I am aware with the tweaks required for enabling focus inside the tinymce pop up when in bootstrap modal.
But currently I am working with a vuetify dialog. Which does'nt seem to focus the pop up fields of tinymce.
I have gone through this question but it does not work in context to vuetify
TinyMCE 4 links plugin modal in not editable
Below is my code I have removed some methods just for clean up and have kept basic things that I have tried in mounted event & editor init.
<no-ssr placeholder="Loading Editor..">
<tinymce
id="content"
:toolbar2="toolbar2"
:toolbar1="type=='BASIC'?'':toolbar1"
:plugins="plugins"
:other_options="other_options"
v-model="content"
#input="handleInput"
v-on:editorInit="initCallBack"
/>
</no-ssr>
</template>
<script>
//https://dyonir.github.io/vue-tinymce-editor/?en_US
export default {
props: {
value: { type: String },
type: { type: String }
},
data() {
return {
content: this.value,
plugins: this.getPlugins(),
toolbar2: "",
toolbar1: this.getToolbar1(),
other_options: {
menubar: this.getMenubar(),
height: "320",
file_browser_callback: this.browseFile,
auto_focus: '#content'
}
};
},
mounted(event) {
// window.tinyMCE.activeEditor.focus();
// window.tinymce.editors["content"]
console.log(this.$el, event);
let list=document.getElementsByClassName("mce-textbox");
for (let index = 0; index < list.length; ++index) {
list[index].setAttribute("tabindex", "-1");
}
// if ((event.target).closest(".mce-window").length) {
// e.stopImmediatePropagation();
// }
// this.$refs.refToElement ..$el.focus())
// this.el.addEventListener('focusin', e => e.stopPropagation());
},
methods: {
handleInput(e) {
this.$emit("input", this.content);
},
initCallBack(e) {
window.tinymce.editors["content"].setContent(this.value);
window.tinymce.editors["content"].getBody().focus();
// console.log(this.$refs);
// const focusable = this.$refs.content.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')
// focusable.length && focusable[0].focus()
document.getElementById("content").addEventListener("onfocusin", console.log("fucssed"));
// tinymce.activeEditor.fire("focus");
this.$el.querySelector(".mce-tinymce").addEventListener('focusin', e =>{ e.stopImmediatePropagation();console.log('event',e)});
const element = this.$el.querySelector(".mce-tinymce");
let _this=this;
if (element)
this.$nextTick(() => {
element.focus();
console.log("FOCUSED",element,_this);
// element.stopImmediatePropagation();
});
// window.tinyMCE.activeEditor.focus();
// console.log(this.$el,e);
// if ((e).closest(".mce-window").length) {
// e.stopImmediatePropagation();
// }
}
};
</script>```
I am using the component : https://github.com/dyonir/vue-tinymce-editor
But fields of the pop are not getting focussed/edited.
From vuetify 2.0 onwards there is a new prop 'retain-focus' which you can set to false in order to fix the above issue.
<v-dialog :retain-focus="false">
Tab focus will return to the first child of the dialog by default. Disable this when using external tools that require focus such as TinyMCE or vue-clipboard.
Edit:
Here is the link to retain-focus prop implementation GitHub:
https://github.com/vuetifyjs/vuetify/issues/6892
Modify the z-index of v-dialog:
Current:
z-index: 202
Modified:
<style>
.v-dialog__content {z-index: 203 !important;}
</style>
Do not forget the !important to give priority to style.

How to apply show less and show more on cells of a reactive table in meteor

document_table_Settings : function ()
{
return{
rowsPerPage: 5,
showNavigation: 'auto',
showColumnToggles: false,
fields: [
{key: 'para',label: 'Para',sortable: false},
{key: 'desc', label: 'Description',sortable: false},
{
key: 'rowId', label: 'Delete',sortable: false, fn: function (rowId, object) {
var html = "<button name='Del' id=" + rowId + " class='btn btn-danger'>Delete</button>"
return new Spacebars.SafeString(html);
}
},
{
key: 'rowId', label: 'Edit',sortable: false, fn: function (rowId, object) {
var html = "<button name='edit' id=" + rowId + " class='btn btn-warning'>Edit</button>"
return new Spacebars.SafeString(html);
}
}
]
};
}
I want to show description entries having show more and show less feature .As the description is long enough. so after 100 character it shows button to toggle.
If I understand you correctly, you are trying to only show the first 100 characters of the 'Description' column in the Reactive Table and then add some mechanism so that the user can click or rollover to see the entire 'Description' text.
There are a few ways to achieve this and I have provided two options below (in order of simplicity).
For a low tech rollover option, truncate the text to only show the first 100 characters, add an ellipsis (...) to the end of your text, then use the title property in a span element to show the full text on rollover.
First you will need to define a 'truncate' Template helper (I would make this a global helper so that you can use anywhere in your app).
Template.registerHelper('truncate', function(strValue, length) {
var len = DEFAULT_TRUNCATE_LENGTH;
var truncatedString = strValue;
if (length && length instanceof Number) {
len = length;
}
if (strValue.length > len) {
truncatedString = strValue.substr(1, len) + "...";
}
return truncatedString;
});
Then create a new Template for the column.
<template name="field_description">
<span title="{{data.description}}">{{truncate data.description}}</span>
</template>
And finally, change your Reactive Table configuration to use a Template.
fields: [
...,
{ key: 'desc', label: 'Description', tmpl: Template.field_description }
...,
];
For a slightly more complicated option, you can take a similar approach but add a clickable link that would show more or less detail. To get it to work you have to define a few Reactive Vars, define an event handler, and change your 'Description' Template accordingly. Here is a rough solution that should work.
Change your template like so.
<template name="field_description">
<span>{{truncatedDescription}}
{{#if showLink}}
{{linkState}}
{{/if}}
</span>
</template>
Then add the necessary logic to your field_description template (including an event handler).
import { Template } from 'meteor/templating';
import './field-description.html';
Template.field_descirption.onCreated(function() {
const MAX_LENGTH = 100;
this.description = new ReactiveVar(Template.currentData().description);
this.showMore = new ReactiveVar(true);
if (this.description.get().length > MAX_LENGTH) {
this.description.set(Template.currentData().description.substr(1, MAX_LENGTH));
}
this.showLink = () => {
return Template.currentData().description.length > MAX_LENGTH;
};
this.toggleTruncate = () => {
if (this.showMore.get()) {
this.description.set(Template.currentData().description);
this.showMore.set(false);
} else {
this.description.set(Template.currentData().description.substr(1, MAX_LENGTH));
this.showMore.set(true);
}
};
});
Template.field_descirption.helpers({
truncatedDescription: function() {
return Template.instance().description.get();
},
showLink: function() {
return Template.instance().showLink();
},
linkState: function() {
if (Template.instance().showMore.get()) {
return 'show more';
} else {
return 'show less';
}
};
});
Template.field_descirption.events({
'click .js-more-less'(event, instance) {
instance.toggleTruncate();
},
});
Lastly, make sure your Reactive Table config is still setup to use a Template for the field.
fields: [
...,
{ key: 'desc', label: 'Description', tmpl: Template.field_description }
...,
];
Note that the second option makes use of Meteor's Reactivity to solve the problem. Let me know if you need additional explanation on how the 2nd solution works.
That should do it!

Categories