ngx Datatable update header dynamically - angularjs

I am using the component ngx-datatable in my angular app and I am trying to update the header texts dynamically.
What I was trying was the following:
<ngx-datatable-column prop="day_1" name="{{day_1_header}}">
and updating the day_1_header property dynamically, but when I do so the change is never reflected.
I have also tried adding a ViewChild and changing the name directly like so:
HTML:
<ngx-datatable-column #dataTable1 prop="day_1" name="{{day_1_header}}">
TS:
#ViewChild('dataTable1') dataTable1;
[..]
this.dataTable1.nativeElement.name = "test";
When I check the properties of my dataTable1 object the new name is set.
So can anyone tell me how to rerender/sync the datatable headers?
Thanks!

Damn...
Tried forever, asked the question and found a solution right away.
It worked by adding an explicit header-template to the column like this:
<ngx-datatable-column>
<ng-template let-column="column" ngx-datatable-header-template>
{{day_1_header}}
</ng-template>
</ngx-datatable-column>
Maybe it helps someone someday.

I have no idea why the other answer (which seems pretty direct and logical) didn't work for me. Maybe breaking change in higher version.
This GitHub issue gives another perspective on how to achieve it.
There's also this example in the source code that shows it.
Basically, you'll need the custom template for the column header
<ngx-datatable
class="material"
[rows]="rows"
[columns]="columns"
[columnMode]="ColumnMode.force"
[headerHeight]="50"
[footerHeight]="50"
rowHeight="auto"
>
</ngx-datatable>
<!-- custom column header template -->
<ng-template #hdrTpl let-column="column"> <strong>Fancy</strong>: {{ column.name }} !! </ng-template>
Then access it in your component class with #ViewChild() decorated property.
export class TemplateRefTemplatesComponent {
#ViewChild('hdrTpl', { static: true }) hdrTpl: TemplateRef<any>;
rows = [];
columns = [];
ColumnMode = ColumnMode;
constructor() {
}
ngOnInit() {
// You'll need to map over all columns to use it in all
this.columns = this.columns.map(columns => ({...columns, headerTemplate: this.hdrTpl});
}
}

Related

How to access Another Module's Content and Presentation Items after 2sxc v10.20+

Here is code that worked up through 2sxc 10.9.1. Though I am able to get the CmsBlock for the TabID, ModuleID and get that to .Render(), I need more. Here is the old code. Not sure it makes any difference, but this View is using the normal Link content-type and is running in an older version of the Content App (appx 3.03=ish). 2sxc has been upgraded and is now 11.22.0 LTS.
I have removed unnecessary stuff, so I doubt this runs as is...
#using ToSic.Razor.Blade
#using ToSic.SexyContent.Environment.Dnn7
#{
var Helpers = CreateInstance("_Helpers.cshtml");
// Display the items from the Manage Links module, we go in 'sideways'
// this gives us just the Content items with their Presentations settings, etc.
var sxci = Factory.SxcInstanceForModule(3360, 606); // ModuleID of Manage Links
var dyn = Factory.CodingHelpers(sxci);
var allLinks = dyn.AsDynamic(dyn.Data["Default"]);
}
#* other stuff *#
<div class="row co-documents justify-content-center align-items-center">
#foreach (var linkItem in allLinks) {
var linkInfo = Helpers.LinkInfos(linkItem.Link, linkItem.Window, linkItem.Icon);
string iconStyle = linkItem.IconStyle ?? "fas";
int linkColumns = (int)linkItem.Presentation.Columns;
string linkIconAlign = linkItem.Presentation.IconAlign;
string linkIconBGColor = linkItem.Presentation.IconBGColor;
#* other stuff *#
}
</div>
So the easy thing to figure out was how to get the module as a CmsBlock which I can Render() as is (below), but what I need to do instead is get proper access to the List of Content Items AND their Presentation data (like above, allLinks).
ToSic.Sxc.Dnn.Factory.CmsBlock(606, 3360).Render();
What am I missing? How can I get access to the other module's data like I was doing before? In this case, I do this in 3 different places on the website. So to outline this in English, I have a module that the client manages a few special links that get displayed in MegaMenus, other special nav, and directly on a couple of pages. In each place they render differently. In their "home" module, where they get edited, they just look boring like this:
I realize its something like this:
var allLinks = something1.AsList(something2.Data["Default"]);
I understand that something2 is an app instance, but how do I create it in the context of the other module?
And what is something1 nowadays? And how do instantiate it? Looks like its a new ToSic.Sxc.Code.DynamicCode() but I can't figure out how to construct that in a way that I can use or doesn't just throw errors.
Thanks in advance for any insight!!
Okay, it took a little testing, trial and error. And also I missed that DynamicCode() was a Method of the Factory class. In retrospect it does seem easy now.
So first you get the BlockBuilder
var block = Factory.CmsBlock(606, 3360);
Then you get the DynamicCode instance (Code.DnnDynamicCodeRoot) from that
var dc = Factory.DynamicCode(block);
And then things are normal
var allLinks = AsList(dc.Data["Default"]);
The rest of the code works like it did before; I can foreach through the links with Header (renamed from ListContent) and Presentation (now Content.Presentation) working just as expected.
The above answer works fine if you are inside the C# Razor template of the 2sxc View. But what if you are outside, for example in a Razor template for a DDR Menu?
Same two steps as above (get the block and the dc), but then you do NOT have access to AsList() or the App. Thankfully, you already have DynamicCode, so you could just get all the records in the Bibliography content-type like this:
<ul>
var items = dc.AsList(dc.App.Data["Bibliography"]);
foreach (var item in items)
{
<li>#item.EntityTitle</li>
}
</ul>
So once you've got your dc you've got access to all the usual 2sxc toys.

PrimeNG select datatable cell

I am working on a project using Angular 4 and PrimeNG where I need to be able to double click on a cell, selected it and open a dialog to do some modifications on the cell's underlying data.
As far as I can see from the documentation, currently there is no way to accomplish this. What is the best way to handle this situation?
Thanks.
So after some playing around I came up with a solution, which being far from perfect serves the purpose while we wait for the folks from PrimeNG (which I love, btw) to include this functionality.
The first issue was to determine which cell the user double-clicked on. I did that by having all column's templates in a div which I can get a reference to:
<p-dataTable #grd [value]="view"
(onRowDblclick)="editTemplate($event)"
(onRowClick)="clearSelection($event)">
<p-column field="SomeFieldName" header="Header" [editable]="false">
<ng-template let-col let-data="rowData" pTemplate="body">
<div [id]="col.field" class="cell-content">
<div [innerHTML]="data[col.field]" class="center-parent-screen"></div>
</div>
</ng-template>
</p-column>
All columns I am interested in handling on double click are wrapped in the div with class cell-content. Also notice the id attribute. It is set to match the field. Then in the onRowDblclick event:
editTemplate(e: any) {
let target = e.originalEvent.toElement.closest('div.cell-content');
if (target && target.id) {
let td = target.closest('td');
if (td) {
td.style.backgroundColor = 'darkorange';
td.style.color = 'white';
}
let fieldValue = e.data[target.id];
//do something with this data
}
}
The key here is the id attribute. Once we have that now we know which cell was clicked and we can proceed to do what we need to do. Also notice that I get a reference of the parent TD element and set the background and the color of the cell. Once you are finished working with it, you can clear the formatting to go back to normal.
You can also use the onRowClick event to clear the selection like so:
clearSelection(e: any) {
let target = e.originalEvent.toElement.closest('div.cell-content');
if (target && target.id) {
let td = target.closest('td');
if (td) {
td.style.backgroundColor = 'white';
td.style.color = 'black';
}
}
}
I know manipulating the DOM directly is not the way to go, but until we get the new version of PrimeNG that includes this functionality, this will do, at least for me.
Please let me know if you have a better way of doing this.

Mutating array within an array (Polymer iron-list)

I currently have an iron-list within another iron-list. The parent's data comes from a firebase-query element, and the child's data is computed from each parent item. The db structure and code looks a bit like this:
DB: [
category1: [
itemId1: {
price: 10,
title: "title"
}
]
]
<iron-list id="categoryList" items="{{categories}}" multi-selection as="category">
<template>
<div class="category-holder">
<iron-list id="{{category.$key}}" items="{{_removeExtraIndex(category)}}" as="item" selection-enabled multi-selection selected-items="{{selectedItems}}" grid>
<template>
<div class$="{{_computeItemClass(selected)}}">
<p>[[item.title]]</p>
<p>[[item.price]]</p>
</div>
</template>
</iron-list>
</div>
</template>
</iron-list>
After selecting any number of items, the user can tap on a fab to batch edit the price. This is where I'm having issues. I can't figure out how to access the correct child iron-list in order to call list.set...I'm currently trying the following very nasty method:
var categories = this.$.categoryList;
var categoryItems = categories.items;
(this.selectedItems).forEach(function(item) {
var index = item.itemId;
categoryItems.forEach(function(itemList, categoryIndex) {
if (itemList[index]) {
categories.set('item.' + categoryIndex + '.price', 10);
}
}, this);
}, this);
I'm iterating over the selected items in order to extract the item index and then iterating over the parent iron-list data (categoryItems) in order to check if the given item exists in that subset of data. If so, then I use the category index and attempt to call set on the parent iron-list using the given path to access the actual item I want to edit. As expected, this fails. Hopefully I've made myself clear enough, any help would be appreciated!
EDIT #1:
After much experimenting, I finally figured out how to correctly mutate the child iron-list:
(this.selectedItems).forEach(function(item) {
var list = this.$.categoryList.querySelector('#' + item.category);
var index = list.items.indexOf(item);
list.set(["items", index, "price"], 30);
}, this);
A couple of things worth noting. I'm using querySelector instead of the recommended this.$$(selector) because I keep running into a "function DNE" error. But now I have another problem...after calling the function, the value gets updated correctly but I get the following error:
Uncaught TypeError: inst.dispatchEvent is not a function
Here's a picture of the full error message:
I see the light, hopefully someone can help me out!
OK, I'll take a shot at this. I think the following happens, and I guess this based on how dom-repeat works:
var categories = this.$.categoryList;
var categoryItems = categories.items;
You take the variable that the iron-list is based on, but setting one array to another just creates a reference in javascript. As soon as you update categoryItems, you also update this.$.categoryList.items. When you later sets the new value, iron-list will do a dirty check and compare all subproperties, and because they are equal (because ... reference), the iron-list wont update the dom.
What you should do is to make sure it's a totally new copy and the way of doing that is to use JSON.parse(JSON.stringify(myArray)).
Further on, one major flaw I see in your code is that you're using querySelector to select an element, and then manipulate that. What you should do is to use this.categories and only that variable.
So your method should look something like:
// Get a freshly new array to manipulate
var category = JSON.parse(JSON.stringify(this.categories);
// Loop through it
category.forEach(category) {
// update your categoryList variable
}
// Update the iron list by notifying Polymer that categories has changed.
this.set('categories', category);

In Typescript/Angular 2 how to find an object in an array by a property of the object

I've been doing some searching and there seem to be a few possible solutions.
First one that will probably work: Filtering an array in angular2
but I find using a pipe and a for loop not ideal.
My idea was using an interface.
This would be a typescript solution. Problem here is defining the collection.
public covers = COVERS;
public images = VECTORS; // a BIG image collection
imagesByCoverType: CoverVector = {}; // Array is made if
// I use Vector[]; but I wanted to use interface.
// images are correctly filtered to the relevant ones.
ngOnInit() {
this.imagesByCoverType = this.images.filter(
image => image.type === 'book-cover');
}
// defined outside of the component class of course.
interface CoverVector {
[book_id: number]: Vector;
}
<li *ngFor="let cover of covers")>
<p>{{cover.title}}</p>
<!-- Here I would like to print out a url to an image of an image object -->
<!-- cover has a property book_id, and also a
chapter_id because the book is subdivided -->
<!-- I was thinking something like this: -->
<p>{{imagesByCoverType[cover.id].url}}</p>
</li>
So I want to access an object in an array by using an interface. How do I do this? Considering also that I have a filtered array of vectors that it should go over.
Recap for clarity:
I want:
A big collection of data that has a unique identifier connected to an 'interface' or find method.
This interface should make it possible to input this unique id and access with it the desired object.
Then all properties of this object should be accessible. Or actually only the url in this case. Point being: it should be there, not just the interface property, but any desired object property.
Then all this elegantly wrapped up in a Angular 2 ngIf statement.
I wouldn't have thought this in-array-find-by-object-property thing would be so hard but it's been a struggle.
Solution I am currently using
It's beyond me why I have to resort to a entire for loop just to access one element I already know the identifier of - from a for loop above it - but I used a for loop using a custom pipe.
// Angular 2
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'cover'
})
export class CoverVectorPipe{
transform(value: any, cover_id: number) {
return value.filter(
(item: any)=> item.aim_id === cover_id);
}
}
Any help solving this using an interface is still welcome.
Also I am wondering if this isn't computationally expensive.

Undefined Values in Kendo Dropdownlist

I am using Telerik's dropdownlist in my MVC application View. I am facing two problems:
1) When I run my application, I find every value of kendo dropdownlist is "Undefined".
This is the code for my View:
#model IEnumerable<EulenMgrKendoUIMvcApplication.Dominio.Tablas.DelegacionProductoUsuario>
#(Html.Kendo().DropDownListFor(d=>d)
.Name("IdDelegacionProductoDrpDwn").HtmlAttributes(new { #style = "font-size:12px" })
.DataTextField("IdDelegacionProducto")
.DataValueField("IdDelegacionProducto")
**.BindTo((System.Collections.IEnumerable)ViewData["IdDelegacionProducto"]))**
This is my controller, where I populate the dropdownlist:
public class DelegacionProductoUsuarioController : Controller
public ViewResult List()
{
IEnumerable<DelegacionProductoUsuario> delegaciones = DelegacionProductoUsuario.GetAll();
**PopulateDelegacionProducto();**
return View(delegaciones);
}
private void PopulateDelegacionProducto()
{
List<Int64> IdDelegacionProductoList = new List<Int64>();
foreach( DelegacionProductoUsuario d in DelegacionProductoUsuario.GetAll()){
IdDelegacionProductoList.Add(d.IdDelegacionProducto);
}
ViewData["IdDelegacionProducto"] =IdDelegacionProductoList ;
}
}
>I am debugging the application and the controller is passing to the view the proper values,so I don't understand why it doesn't show them.
2) Second problem: I insert this Dropdownlist in one of the columns of a kendo grid with no success.
In it's place it appears a common label. Here is the code for my Grid, I mark in Bold the column where I try to show my dropdownList:
#(Html.Kendo().Grid(Model)
.Name("Grid")
.Columns(columns=>
{
columns.Bound(d => d.BorradoLogico).Title("Borrado logico");
columns.Bound(d => d.FTick).Title("Ftick");
**columns.Bound(d => d.IdDelegacionProducto).Title("IdDelegacionProducto").EditorTemplateName("IdDelegacionProductoDrpDwn");**
columns.Bound(d => d.IdUsuario).Title("IdUsuario");
})
How does that 'DelegacionProductoUsuario' class look like? Does it have property named 'IdDelegacionProducto' ? It looks like you have not set the dataValueField correctly.
As for the second question, where did you put that EditorTemplate (is it in the Shared/EditorTemplate or in a EditorTemplates folder? More info about editor template can be found here.
Dear Petur: thanks a lot for answering. On regard to your answer:
My class DelegacionProductoUsuario does have a property called IdDelegacionProducto. On regard to your question "where I place the EditorTemplate" , I don't understand what you mean, I place it in the view that Lists all of my DelegacionProductoUsuario . Please keep on helping me. Thanks a lot Petur.

Resources