If i run this code, i get an error, Cannot find namespace 'NodeJS'.
public exportExcel(jsonData: any[], excelFileName: string): void {
//Excel Title, Header, Data
const header: string[] = Object.keys(jsonData[0]);
const data = jsonData;
//Create workbook and worksheet
let workbook = new Workbook();
let worksheet = workbook.addWorksheet(excelFileName);
//Add Header Row
let headerRow = worksheet.addRow(header);
// Cell Style : Fill and Border
headerRow.eachCell((cell, number) => {
cell.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FFFFFF00' },
bgColor: { argb: 'FF0000FF' }
}
cell.border = { top: { style: 'thin' }, left: { style: 'thin' }, bottom: { style: 'thin' }, right: { style: 'thin' } }
})
// Add Data and Conditional Formatting
data.forEach((element) => {
let eachRow = [];
header.forEach((headers) => {
eachRow.push(element[headers])
})
if (element.isDeleted === "Y") {
let deletedRow = worksheet.addRow(eachRow);
deletedRow.eachCell((cell, number) => {
cell.font = { name: 'Calibri', family: 4, size: 11, bold: false, strike: true };
})
} else {
worksheet.addRow(eachRow);
}
})
...
ERROR in node_modules/exceljs/index.d.ts(1648,34): error TS2503: Cannot find namespace 'NodeJS'.
Solution targetting Angular8+ Projects:
This is a known bug, caused due to the incompatibility of exceljs with the version of #types/node. I faced similar issue with Angular 10.
2 possible solutions exists:
Recommended: Update your tsconfig.app.json file with "types": ["node"]
If you are okay without having type support, then you can simply use the below import:
import * as Excel from "exceljs/dist/exceljs.min.js";
Just Open index.d.ts file from yours exceljs node_modules
and replace this line
dictionary: Buffer | NodeJS.TypedArray | DataView | ArrayBuffer; // deflate/inflate only, empty dictionary by default
with this
dictionary: Buffer | DataView | ArrayBuffer; // deflate/inflate only, empty dictionary by default
and then just ng serve
Related
Using SwiftUI - Xcode 14.2 - iOS 16.0
I have tried different search tutorials to create a search file for my project but am unable to find out how to select the item in the search file and place that selected item in a textfield in another file. I have searched this site for other posts, i tried searching through Google, YouTube, etc...
In File 1, I have a textfield that that has a prompt 'start typing' and when selected, it directs you to the Search file to select the item you want, so it can be placed in place of the prompt.
File 1 (where the textfield is needed to paste the selected item):
VStack {
NavigationLink(destination: NameSearch()) {
TextField("Name", text: .constant(""), prompt: Text(" Start typing ")
.foregroundColor(.blue))
.multilineTextAlignment(.leading)
.padding()
}
}
Once I click on the 'start typing' prompt, it navigates to NameSearch.swift file, as seen below.
NameSearch.swift:
import SwiftUI
struct NameSearch: View {
let name = [
"Jane", "George", "Sam", "Henry", "Sally", "Liz", "John"
]
#State private var searchText = ""
var body: some View {
NavigationStack {
VStack {
// Search view
SearchBarView(searchText: $searchText)
List {
// Filtered list of names
ForEach(name.filter{$0.hasPrefix(searchText) || searchText == ""}, id:\.self) {
searchText in Text(searchText)
}
}
.navigationBarTitle(Text("Search Name"))
.resignKeyboardOnDragGesture()
}
}
}
}
struct NameSearch_Previews: PreviewProvider {
static var previews: some View {
Group {
NameSearch()
.environment(\.colorScheme, .light)
NameSearch()
.environment(\.colorScheme, .dark)
}
}
}
extension UIApplication {
func endEditing(_ force: Bool) {
self.windows
.filter{$0.isKeyWindow}
.first?
.endEditing(force)
}
}
struct ResignKeyboardOnDragGesture: ViewModifier {
var gesture = DragGesture().onChanged{_ in
UIApplication.shared.endEditing(true)
}
func body(content: Content) -> some View {
content.gesture(gesture)
}
}
extension View {
func resignKeyboardOnDragGesture() -> some View {
modifier(ResignKeyboardOnDragGesture())
}
}
struct SearchBarView: View {
#Binding var searchText: String
#State private var showCancelButton: Bool = false
var onCommit: () ->Void = {print("onCommit")}
var body: some View {
HStack {
HStack {
Image(systemName: "magnifyingglass")
// Search text field
ZStack (alignment: .leading) {
if searchText.isEmpty { // Separate text for placeholder to give it the proper color
Text("Search")
}
TextField("", text: $searchText, onEditingChanged: { isEditing in
self.showCancelButton = true
}, onCommit: onCommit).foregroundColor(.primary)
}
// Clear button
Button(action: {
self.searchText = ""
}) {
Image(systemName: "xmark.circle.fill").opacity(searchText == "" ? 0 : 1)
}
}
.padding(EdgeInsets(top: 8, leading: 6, bottom: 8, trailing: 6))
.foregroundColor(.secondary) // For magnifying glass and placeholder test
.background(Color(.tertiarySystemFill))
.cornerRadius(10.0)
if showCancelButton {
// Cancel button
Button("Cancel") {
UIApplication.shared.endEditing(true) // this must be placed before the other commands here
self.searchText = ""
self.showCancelButton = false
}
.foregroundColor(Color(.systemBlue))
}
}
.padding(.horizontal)
.navigationBarHidden(showCancelButton)
}
}
Question 1: How do I hide all the names from showing in the list so that I just see the search bar and the cancel button and an empty list?
Question 2: Once I type the name I am looking for, it should pop up and I want to select name - how can I do this?
once I type the name in search bar, it appears in the empty list
I select that name
it then takes me back to File 1
replaces the 'start typing' prompt with the name i just selected in the Search file.
Question 3: I have noticed in the Search file, I am getting a warning with the following code. How can I resolve it?
extension UIApplication {
func endEditing(_ force: Bool) {
self.windows
.filter{$0.isKeyWindow}
.first?
.endEditing(force)
}
}
The warning that appears is:
'windows' was deprecated in iOS 15.0: Use UIWindowScene.windows on a
relevant window scene instead
Firstly, thank you for providing a working example of your code.
As you're building for iOS 15+, you should probably be using the .searchable modifier rather than rolling your own.
The 2021 WWDC video introducing this feature is here https://developer.apple.com/wwdc21/10176
Some new features from 2022 here: https://developer.apple.com/wwdc22/10052
I am trying to remove rows inside a ForEach. Removing the last row always throws an index out of range exception. Removing any other row does not.
ForEach(Array(player.scores.enumerated()), id: \.element) { index, score in
HStack {
if self.isEditSelected {
Button(action: {
self.player.scores.remove(at: index)
}, label: {
Image("delete")
})
}
TextField("\(score)", value: self.$player.scores[index], formatter: NumberFormatter())
}
}
I have tried using ForEach(player.indices...) & ForEach(player.scores...), but see the same problem.
Looks to me like the crash happens here self.$player.scores[index], as hardcoding the index to any value other that the last row is working.
Does anyone know how to fix this? Or if there is a better approach.
Here is fix
ForEach(Array(player.scores.enumerated()), id: \.element) { index, score in
HStack {
if self.isEditSelected {
Button(action: {
self.player.scores.remove(at: index)
}, label: {
Image("delete")
})
}
TextField("\(score)", value: Binding( // << use proxy binding !!
get: { self.player.scores[index] },
set: { self.player.scores[index] = $0 }),
formatter: NumberFormatter())
}
}
Based on #Asperi answer
public extension Binding where Value: Equatable {
static func proxy(_ source: Binding<Value>) -> Binding<Value> {
self.init(
get: { source.wrappedValue },
set: { source.wrappedValue = $0 }
)
}
}
You can use this as follows:
TextField("Name", text: .proxy($variable))
Xcode 13.0 beta introduced a new way to establish two-way-bindings between the elements of a collection and the views built by ForEach / List.
This method fixes the crash related to deleting the last row.
struct Score: Identifiable {
let id = UUID()
var value: Int
}
struct Player {
var scores: [Score] = (1...10).map {_ in .init(value: Int.random(in: 0...25))}
}
struct BindingTest: View {
#State private var player = Player()
var body: some View {
List {
ForEach($player.scores) { $score in
HStack {
TextField("\(score.value)", value: $score.value,
formatter: NumberFormatter())
}
}
.onDelete { player.scores.remove(atOffsets: $0)}
}
}
}
I have a morris chart that compares different students statistics. I also have a modal in which I can add a new student and the graph should update with new student statistics. After adding, the graph is getting updated but only when I refresh the whole page. How would I update the page without refreshing?
component.ts
ngOnInit() {
this.getData();
}
getData() {
this.http.get('url')
.subscribe(data => {
const graphData = data.stathistory;
const graphDataArr = [];
let currentChar = 'a';
for (const graphdata in graphData) {
const curDate = graphdata.replace(/(\d{4})(\d{2})(\d{2})/g, '$1-$2-$3');
const graphdataObj = { 'y': curDate };
for (const element in graphData[graphdata]) {
graphdataObj[currentChar] = Number(graphData[graphdata][element].rank);
currentChar = this.nextChar(currentChar);
}
graphDataArr.push(graphdataObj)
currentChar = 'a';
}
const yKeysArr = [];
for (let i = 0; i < data.domains.length; i++) {
yKeysArr.push(currentChar);
currentChar = this.nextChar(currentChar);
}
this.generateChart(graphDataArr, data.names, yKeysArr);
});
}
generateChart(graphRanks = [], names = [], yKeys = []) {
this.graphData = graphRanks;
this.graphOptions = {
xkey: 'y',
ykeys: yKeys,
labels: names,
resize: true,
parseTime: false,
pointSize: 0,
};
}
addStudent(name) {
this.http.post('url', {
name: name,
})
.subscribe(response => {
this.getData();
}
);
}
html
<div *ngIf = 'graphData' mk-morris-js [options]="graphOptions" [data]="graphData" type="Line" style="height: 500px; width: 100%;">
**code for modal dialog**
<button type="button" class="btn btn-primary" (click)="addStudent(name)">
Please let me know if more info is needed.
This looks fine. I would suggest you to add console.log(graphRanks); just before this.graphData = graphRanks; to ensure that the new data is loaded when expected. By the way your button calls the function addDomain(name) while in your script the function name is addStudent(name).
I would recommend that you make your graphData an observable and use the async pipe in your html. Something like this:
graphData$ = this.http.get('url').pipe(
map(x => // do stuff with x here)
)
Then, in your html you can make:
[graphData]="graphData$ | async"
Here is a good post by Todd Motto on the ng-if piece:
https://toddmotto.com/angular-ngif-async-pipe
EDIT:
If you don't want to make your graphData an observable - you could probably use a switchMap in you addStudent function.
addStudent(name) {
this.http.post('url', {
name: name,
})
.pipe(
switchMap(x => this.getData())
}
);
}
I finally got it working. I tried to clear the morris chart and generate the chart with new data. So, whenever there is a data change, it would clear the graph and redraw the graph with new data.
Clearing the chart
document.getElementById('idofthegraph').innerHTML = '';
This would draw the chart again
this.generateChart(graphDataArr, data.names, yKeysArr);
I need to add a splitPane textArea to my Griffon app.
I cannot seem to find an example of the proper syntax and way to do this.
Can anyone help me out??
Here is my view so far:
=================================================================================
package test1
import griffon.util.GriffonNameUtils as GNU
import java.beans.PropertyChangeListener
application(title: 'Test1',
//preferredSize: [600, 300],
pack: true,
locationByPlatform: true,
iconImage: imageIcon('/griffon-icon-48x48.png').image,
iconImages: [imageIcon('/griffon-icon-48x48.png').image,
imageIcon('/griffon-icon-32x32.png').image,
imageIcon('/griffon-icon-16x16.png').image]) {
borderLayout()
panel(constraints: WEST,
border: titledBorder(title: 'Platform')) {
migLayout()
buttonGroup(id: 'platform')
def radioButtonConverter = { String title, v -> v ? title : model.deviceType }
for (data in model.deviceTypes) {
radioButton(data.title, buttonGroup: platform, constraints: 'wrap',
selected: bind('deviceType', target: model,
converter: radioButtonConverter.curry(data.title), value: data.selected))
}
}
panel(constraints: EAST,
border: titledBorder(title: 'Path Browser')) {
migLayout()
controller.griffonClass.actionNames.each { name ->
button(getVariable(name + 'Action'),
constraints: 'growx, wrap')
}
}
panel(constraints: CENTER, id: 'devicePanel',
border: titledBorder(id: 'devicePanelBorder', title: 'No Devices')) {
noparent {
model.addPropertyChangeListener('deviceType', { e ->
model.deviceTypes.each{ d-> d.selected = false }
model.deviceTypes.find{ d -> d.title == e.newValue }.selected = true
devicePanelBorder.title = e.newValue
devicePanel.layout.show(devicePanel, e.newValue)
devicePanel.repaint() // force redraw
} as PropertyChangeListener)
}
cardLayout()
for(data in model.deviceTypes) {
// we set the title as the page's constraints -> simplifies bookkeeping
// in the PropertyChangeListener registered above
panel(constraints: data.title) {
gridLayout(cols: 2, rows: (data.devices.size()/2))
data.devices.each { device ->
checkBox(device.name, selected: bind(value: device.selected, target: device, 'selected'))
}
}
}
}
panel(constraints: SOUTH) {
riverLayout()
buttonGroup(id: 'execute', constraints: 'center')
button('Build XML', buttonGroup: execute)
button('Run', buttonGroup: execute)
button('Exit', buttonGroup: execute)
}
panel(constraints: NORTH) {
riverLayout()
label('TWC Companion Device Test Tool', constraints: 'center')
}
}
============================================================================================
Thanks!!
ironmantis7x
As shown by SwingPad (https://github.com/griffon/griffon/blob/master/src/dist/samples/SwingPad/griffon-app/views/griffon/samples/swingpad/SwingPadContent.groovy) using splitPane is as simple as
splitPane(resizeWeight: 0.5f) {
label('Left component')
label('Right component')
}
Have a look at the View section of the Griffon Guide to learn more about nodes
http://griffon.codehaus.org/guide/latest/guide/views.html#specialNodes
The following link has pointers to all nodes that can be used with SwingBuilder
http://groovy.codehaus.org/Swing+Builder
Lastly, you can launch SwingPad ($GRIFFON_HOME/samples/SwingPad) and play with live nodes. This application includes a list of all nodes (Help -> Node List) plus a very basic node name completion feature.
My chart shows up well but the two lines are of the same color. How do I specify different colors for the two lines? Here is my code (fragment) so far:
config.pointIndex = null;
config.areaPoints = new Array();
config.areaPoints[0] = pointsopens;
config.areaPoints[1] = pointsclicks;
var plotLinesopen = createPlotlines(pointsopens);
var plotLinesclick = createPlotlines(pointsclicks);
var options = {
chart : { renderTo : 'areaChart' },
colors: [
'#4572A7',
'#AA4643'
],
xAxis: {
plotLines1: plotLinesopen,
plotLines2: plotLinesclick
},
series : [ data.pointsopens, data.pointsclicks ]
};
if (length > 100) {
options.plotOptions = {
area : {
lineWidth: 1,
marker : { radius : 1 }
}
};
}
options = jQuery.extend(true, {}, areaChartDefault, options);
charts.area = new Highcharts.Chart(options);
Thank you.
PS, my code is now:
config.pointIndex = null;
config.areaPoints = new Array();
config.areaPoints[0] = pointsopens;
config.areaPoints[1] = pointsclicks;
var plotLinesopen = createPlotlines(pointsopens, '#AAAAAA');
var plotLinesclick = createPlotlines(pointsclicks, '#DDDDDD');
var options = {
chart : { renderTo : 'areaChart' },
xAxis: {
plotLines: [plotLinesopen, plotLinesclick]
},
series : [ data.pointsopens, data.pointsclicks ]
};
if (length > 100) {
options.plotOptions = {
area : {
lineWidth: 1,
marker : { radius : 1 }
}
};
}
options = jQuery.extend(true, {}, areaChartDefault, options);
charts.area = new Highcharts.Chart(options);
but it still gives me two dark blue plotlines. The createPlotlines function looks like so:
function createPlotlines(points, colour) {
// Create plotlines from point data
colour = typeof colour !== 'undefined' ? colour : '#CCCCCC';
alert ('colour=='+colour);
var plotLines = [];
var middleYval = 0;
for (var i in points) {
middleYval = Math.max(middleYval, points[i].y);
if (points[i].l) { // l property is true if label should be on for this point
plotLines.push({
color: colour,
id: 'plotline'+i,
value: points[i].x,
width: 1,
});
}
}
return plotLines;
}
Do you mean different colors for the xAxis and yAxis? I only see that this would make one of each. You can definitely set the colors of the axis lines independently.
See this: example, ref
EDIT:
For plotLines you can use this:
demo
If you take a look the reference you'll see that there's no attr named colors.
Other problem is that there's no attr plotLines1 and plotLines2 as you can see here
Solution: If you want to change a plot line color your have to pass your plotlines thrue an array, like the following and set theire color:
var options = {
chart : { renderTo : 'areaChart' },
xAxis: {
plotLines: [{
color: '#4572A7', //
width: 2,
value: 5.5,
id: 'plotline-1'
}, {
color: '#AA4643',
width: 2,
value: 8.5,
id: 'plotline-2'
}]
},
series : [ data.pointsopens, data.pointsclicks ]
};
Live example
Update1:
You're returning an array of objects in this function createPlotlines and then you put this array inside other array. That's the problem.
Replace your function to the following:
function createPlotlines(points, colour) {
// Create plotlines from point data
colour = colour || '#CCCCCC';
var middleYval = 0;
for (var i in points) {
middleYval = Math.max(middleYval, points[i].y);
if (points[i].l) { // l property is true if label should be on for this point
return {
color: colour,
id: 'plotline'+i,
value: points[i].x,
width: 1,
};
}
}
}
ok, here is what I needed, lineColor:
$data = array(
"name" => $name,
"color" => '#00FFFF',
"lineColor"=> '#00FFFF',
"data" => $rows
);
that code goes in a local function called compileChartData in my php.
Thanks for the effort and sorry for the mixup.
you can change plotLines to target single line.
Or else you can change the whole Yaxis or Xaxis
xAxis: {
type: 'datetime',
gridLineColor: '#EEEEEE'
},
yAxis: {
title: {
text: 'Calls'
},
gridLineColor: '#EEEEEE'
}