How to check csv column header name in client controller script of the widget?

Virendra K
Kilo Sage

Hi All,

I have requirement to check the attached csv file's 1st column header using client controller script in the widget.

I am trying to use below code and changed the name of the header name of the csv file but its not working.

 

$scope.header = angular.element('#header').val();
        if ($scope.header != 'CI Name' ) {
            alert('VIR ci name passed ');
            $scope.title ="Invalid file format: ";
            $scope.message = "Ensure the 'CI Name' column is the first column in the file.";
            return false;
        }
 
I am not an expert in portal (html/angular script). Please help me to achieve this.
 
Thanks in advance.
 
Regards,
Virendra

  

1 ACCEPTED SOLUTION

@Virendra K 

this is a sample working code for your validation

I am checking if first column is Name

you can enhance it further based on your requirement.

HTML:

<label for="csvUpload">Upload CSV file:</label>
<input type="file" id="csvUpload" accept=".csv" onchange="angular.element(this).scope().validateCSV(event)" />

<div ng-if="error" style="color: red; margin-top: 10px;">
  {{ error }}
</div>

Client Controller:

function($scope) {
  $scope.error = "";

  $scope.validateCSV = function(event) {
    let file = event.target.files[0];
    $scope.error = "";

    if (!file) return;

    let reader = new FileReader();

    reader.onload = function(e) {
      let contents = e.target.result;
      let lines = contents.split(/\r\n|\n/);

      if (lines.length === 0) {
        $scope.$apply(() => $scope.error = "CSV file is empty.");
        return;
      }

      // Get first line (header)
      let headers = lines[0].split(',');

      if (headers[0].trim() !== "Name") {
        $scope.$apply(() => $scope.error = "First column header must be 'Name'.");
        return;
      }

      $scope.$apply(() => $scope.error = "CSV file is valid.");
    };

    reader.readAsText(file);
  };
}

Server:

(function() {
  data.error = "";

  // Server script doesn't need to process file here, validation done client-side
})();

Output:

csv validate 1st column widget.gif

I hope you will mark my response as correct as I shared a working solution which you can enhance further based on your developer skills and customer requirements

Regards,
Ankur
Certified Technical Architect  ||  9x ServiceNow MVP  ||  ServiceNow Community Leader

View solution in original post

11 REPLIES 11

Here is a snippet html template and controller you can start from but getting it to your specs might require a little bit of understanding of angular and the oob widgets. You can put this in a catalog item as a widget variable and test it out.

 

The logic you are talking of could be put in the setAttachments callback function passed to the nowAttachmentHandler.

<!-- from oob sc cat item widget -->
<div ng-if="true" class="wrapper-md row no-margin" role="region" data-label="Attachments"
    aria-label="${Attachments}">
    <div
        ng-class="{'flex-center attachment-height': c.isNative == 'true', 'flex-start': c.isNative != 'true'}">
        <div ng-if="!submitting && !submitted" style="font-weight:normal;cursor:default;margin-bottom:2rem;">
            <sp-attachment-button ng-if="::!showDragAndDrop()" modal="true" supported-extensions="exe, bat, sh"
                required="{{data.mandatory_attachment}}"></sp-attachment-button>
            <sp-attachment-button ng-if="::showDragAndDrop()" modal="true"
                required="{{data.mandatory_attachment}}"
                ng-class="{'hidden-xs': false, 'hidden-sm': true, 'hidden-md': true, 'hidden-lg': true}"></sp-attachment-button>
            <span class="fa fa-asterisk mandatory" ng-if="data.mandatory_attachment"
                ng-class="{'mandatory-filled': data.mandatory_attachment && (data.attachment_submitted || attachments.length > 0)}"
                style="vertical-align:super" aria-hidden="true"></span>
            <span ng-class="{'attachment-text' : options.native_mobile == 'true'}" aria-hidden="true">${Add
                attachments}</span>
        </div>
    </div>
<div ng-if="::showDragAndDrop()" class="panel panel-{{options.color}} b drag-and-drop-area"
        ng-class="{'hidden-xs': true}" aria-hidden="true">
        <sp-attachment-picker on-file-pick="dropFiles($files)"></sp-attachment-picker>
    </div>
    <span ng-if="attachmentUploadInProgress">${Uploading attachments}
        <div class="sp-loading-indicator la-sm" style="color:black;display:inline">
            <div></div>
            <div></div>
            <div></div>
        </div>
    </span>
    <now-attachments-list template="sp_attachment_single_line"></now-attachments-list>
</div>
api.controller = function ($scope, nowAttachmentHandler, spUtil, spAttachmentUpload, $timeout, cabrillo, spModal) {
	var c = this;
	c.isNative = cabrillo.isNative()
	$scope.table = $scope.page.g_form.recordTableName;
	$scope.guid = $scope.$parent.c.getAttachmentGuid();
	$scope.data.maxAttachmentSize = 24;
	var ah = $scope.attachmentHandler = new nowAttachmentHandler(setAttachments, appendError);
	ah.setParams($scope.table, $scope.guid, 1024 * 1024 * $scope.data.maxAttachmentSize);
	$scope.showDragAndDrop = function () {
		if (true)
			return true;
		else
			return false;
	}

	function setAttachments(attachments, action) {
		if (!angular.equals($scope.attachments, attachments))
			$scope.attachments = attachments;
		if (action === "added") {
			
			console.log(attachments[0]);
		}
		if (action === "renamed") {
			// attachment renamed logic
		}
		if (action === "deleted") {
			// attachment deleted logic
		}
		spUtil.get($scope, {
			action: "from_attachment"
		});

	}
	function appendError(error) {
		spUtil.addErrorMessage(error.msg + error.fileName);
	}

	$scope.dropFiles = function (files) {
		if (files && files.length > 0) {
			$scope.attachmentUploadInProgress = true;
			$scope.totalFilesBeingUploaded++;
			spAttachmentUpload.uploadAttachments($scope.attachmentHandler, files);
		}
		$timeout(function () {
			if ($scope.attachmentUploadInProgress != false)
				spUtil.addInfoMessage($scope.data.attachmentUploadProgressMsg);
		}, 2000);
		$scope.$on('attachment.upload.idle', function () {
			$scope.attachmentUploadInProgress = false;
			$scope.totalFilesBeingUploaded = 0;
		});
	};
	$scope.confirmDeleteAttachment = function (attachment) {
		if (c.isNative) {
			if (confirm("delete attachment?")) {
				$scope.data.attachment_action_in_progress = true;
				$scope.attachmentHandler.deleteAttachment(attachment);
			}
		} else {
			spModal.confirm("delete attachment?").then(function () {
				$scope.data.attachment_action_in_progress = true;
				$scope.attachmentHandler.deleteAttachment(attachment);
			});
		}
	}
};

 

prajaktajga
Tera Expert

Hello @Virendra K ,

I am also not much expert in portal ,I found something related which you can try once 

1.angular.element('#header').val() does not read inside a file
2.Browsers do NOT expose CSV content via <input type="file">

3.You must read the file using JavaScript FileReader first.


1.In widget HTML
<input type="file" id="csvFile" accept=".csv" />

2.In client controller use FileReader to read the CSV
$scope.validateCSV = function() {
var fileInput = document.getElementById('csvFile');
var file = fileInput.files[0];

if (!file) {
alert("Please upload a file first.");
return false;
}

var reader = new FileReader();

reader.onload = function(e) {
var text = e.target.result;

// Split first line (header row)
var firstLine = text.split('\n')[0].trim();

// Read the first column header
var firstColumnHeader = firstLine.split(',')[0].trim().replace(/"/g, '');

console.log("First Column Header:", firstColumnHeader);

if (firstColumnHeader !== 'CI Name') {
$scope.title = "Invalid file format";
$scope.message = "Ensure the 'CI Name' column is the first column in the CSV.";
alert($scope.message);
$scope.$apply();
return false;
} else {
alert("CSV is valid!");
}
};

reader.readAsText(file);
};

3.Add a button to trigger the validation
<button class="btn btn-primary" ng-click="validateCSV()">Validate CSV</button>