Angular call function in ng-click defined in object - javascript

Got a question about Angularjs and it's ng-repeat/ng-click workings.
So on this system that I'm working on there is a lot of code re-used for datatables, and I'm trying to make a generic template/service to remedy this. Now I'm running into a problem where we have multiple buttons with their own function calls on being clicked.
I've so far got this setup:
My column object defined as so:
var columns = [
{
identifier: "id",
type: "text"
},
{
identifier: "type",
type: "text"
},
{
identifier: "label",
type: "text"
},
{
identifier: "actions",
type: "button",
multi: true,
content: [
{
icon: "fa-globe",
events: {
click: $scope.openMapModal
}
},
{
icon: "fa-list",
events: {
click: $scope.openGroupModal
}
}
]
}
];
And my HTML as following:
<tr ng-repeat="row in table.data" ng-model-instant>
<td ng-repeat="column in table.columns" ng-if="column.type === 'text'">
{{TableService.getByString(row, column.identifier)}}
</td>
<td ng-repeat="column in table.columns" ng-if="column.type === 'button' && column.multi">
<a ng-repeat="button in column.content" class="btn fa {{button.icon}}" ng-click="button.events.click(row)"></a>
</td>
</tr>
Just for completeness, my TableService.getByString and a small table data set:
(note that columns defined above is set by a function into the table object, and I did not include it in the object).
var table = {
data: [
{
id: 0,
label: "foo",
type: "bar"
},
{
id: 1,
label: "one",
type: "bar"
},
{
id: 2,
label: "foo",
type: "two"
}
]
}
function getKeyObj(obj, key) {
var retVal = {
"key": key,
"obj": obj
};
if (retVal.key.indexOf('.') > -1) {
var keyParts = retVal.key.split('.');
retVal.key = keyParts.pop();
while (keyParts.length && (obj = obj[keyParts.shift()])) ;
retVal.obj = obj;
}
return retVal;
}
function getByString(obj, key) {
var ret = getKeyObj(obj, key);
return ret.obj[ret.key];
}
Now the problem that I'm encountering is that my functions are not being called in my ng-click's whenever I click on the buttons.
I've also tried it with settings the function as a string in my column object, but it didn't work either.
Am I going in the right direction with this or do I need to rethink my generalization, and ifso, what alternative is there?

Are you sure $scope.openMapModal is defined at the time you define the columns array? Could it be that you are assigning undefined to the click property instead of a reference to the function?

Related

Document.getElementsByClassName has 0 length but has elements inside

[![Firefox Console][1]][1]In my Vue app I am trying to use mdb-datatable, the table reads data() and sets the rows accordingly. I am setting the row data programmatically after my data is loaded with Ajax. In one column I need to add a button and it needs to call a function. I am trying to add onclick function to all buttons with "status-button" class but something weird happens.
When I print HtmlCollection it has a button inside, which is expected but I can't reach proceedButtons[0], it is undefined. proceedButtons.length also prints 0 length but I see the button in console.
I also tried to add onclick function but probably "this" reference changes and I get errors like "proceedStatus is not a function" it does not see anything from outer scope.
<mdb-datatable
:data="tableData"
:searching="false"
:pagination="false"
:responsive="true"
striped
bordered/>
export default {
name: "Applications",
mixins: [ServicesMixin, CommonsMixin],
components: {
Navbar,
Multiselect,
mdbDatatable
},
data () {
return {
statusFilter: null,
searchedWord: '',
jobRequirements: [],
applications: [],
options: ['Awaiting', 'Under review', 'Interview', 'Job Offer', 'Accepted'],
tableData: {
columns: [
{
label: 'Name',
field: 'name',
sort: 'asc',
},
{
label: 'Date',
field: 'date',
sort: 'asc'
},
{
label: 'Compatibility',
field: 'compatibility',
sort: 'asc'
},
{
label: 'Status',
field: 'status',
sort: 'asc'
},
{
label: 'Proceed Application Status',
field: 'changeStatus',
}
],
rows: []
}
}
}
fillTable(applications) {
let statusButtonId = 0;
applications.forEach(application => {
this.tableData.rows.push({
name: application.candidateLinkedIn.fullName,
date: this.parseDateFromDateObject(application.applicationDate),
compatibility: this.calculateJobCompatibility(application.candidateLinkedIn.linkedInSkillSet),
status: application.applicationStatus,
changeStatus: '<button type="button" class="btn-indigo btn-sm m-0 status-button"' +
' style="margin-left: 1rem">' +
'Proceed Status</button>',
candidateSkillSet: application.candidateLinkedIn.linkedInSkillSet
});
statusButtonId++;
});
},
addEventListenersToButtons() {
let proceedButtons = document.getElementsByClassName("status-button")
console.log(proceedButtons);
console.log(proceedButtons[0])
console.log(proceedButtons.item(0))
/*
proceedButtons.forEach(button => {
button.addEventListener("click",this.proceedStatus);
});
*/
},
[1]: https://i.stack.imgur.com/zUplv.png
From MDN:
Get the first element with a class of 'test', or undefined if there is no matching element:
document.getElementsByClassName('test')[0]
So undefined means no match, even if length is 0...
Since this is not an array, you do not get out-of-bounds exceptions.
https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName
Regarding Arrays
You can't index the list returned from getElementsByClassName.
You can turn it into an array though, and then index it.
ES6
let proceedButtons = document.getElementsByClassName("status-button")
const arr = Array.from(proceedButtons);
console.log(arr[0]);
Old School
const arr = []
Array.prototype.forEach.call(proceedButtons, function(el) {
arr.push(el);
});
console.log(arr[0]);

Binding array of object to Kendo grid popup multiselect

I'm trying to bind an array of id-value pairs to a kendo grid popup editor.
Got everything to work for creating a new record. Popup editor loads the custom editor and successfully submits the data to the controller.
The problem is when I try to edit records. The records displays properly in the row, but when I try to edit it, the multiselect does not hold the values.
Grid Markup
$("#ProjectSites-SubContract-grid").kendoGrid({
dataSource: {
type: "json",
schema: {
data: "Data",
total: "Total",
errors: "Errors",
model: {
id: "Id",
fields: {
DateOfContract: { type: 'date', editable: true },
DateOfCompletion: { type: 'date', editable: true },
AmountOfContract: { type: 'number', editable: true },
Contractor: { defaultValue: { id: "", name: "" } }
}
}
},
},
columns: [
{
field: "ScopeOfWork",
title: "Scope of Work",
template: "#=parseScopeOfWork(ScopeOfWork)#",
editor: scopeOfWorkEditor
},
]
});
});
Scope of Work editor
function scopeOfWorkEditor(container, options) {
$('<input data-text-field="name" data-value-field="id" data-bind="value:ScopeOfWork"/>')
.appendTo(container)
.kendoMultiSelect({
dataSource: {
data: [
#foreach (var scopeOfWork in Model.AvailableScopeOfWork)
{
<text>{ id : "#scopeOfWork.Value", name : "#scopeOfWork.Text" },</text>
},
]
}
});
parseScopeOfWork -
this method guys iterates through the object list and concats the name.
function parseScopeOfWork(scopeOfWork) {
var result = "";
for (var i = 0; i < scopeOfWork.length; i++) {
result += scopeOfWork[i].Name;
if (i < scopeOfWork.length - 1)
{
result += ", <br/>";
}
}
return result;
}
Here's a screenshot:
You're binding the SpaceOfWork to the new widget, but how that widget knows your Model ? I mean, just using data-bind doens't binds the model to the widget, it can't figure that by itself. I have two suggestions:
Set the value in the widget's initialization:
.kendoMultiSelect({
value: options.model.ScopeOfWork
Demo
Bind the model to the widget for good:
let $multiSelect = $('<input data-text-field="name" data-value-field="id" data-bind="value:ScopeOfWork"/>');
kendo.bind($multiSelect, options.model);
$multiSelect
.appendTo(container)
.kendoMultiSelect({ ...
Demo
Note: Edit the category cell in both demos to see the changes.

Getting id or name of folder in fuelux tree

I'm trying to get the id or name of the selected folder in a fuelux tree but couldnt manage to get it done.
My tree is a classic folder/file type tree and I want to be able to see the id of the folder when I click on a file.
this is my datasource for tree
var treeDataSource = new DataSourceTree({
data: [
{ name: 'Elektronik Belgelerim', type: 'folder', 'icon-class': 'blue', additionalParameters: { id: 'F1' } },
{ name: 'Gelen Kutusu', type: 'folder', 'icon-class': 'blue', additionalParameters: { id: 'F2' } },
{ name: 'Giden Kutusu', type: 'folder', 'icon-class': 'blue', additionalParameters: { id: 'F3' } },
{ name: 'Çöp Kutusu', type: 'folder','icon-class':'green', additionalParameters: { id: 'I1' } },
//{ name: 'Çöp Kutusu', type: 'item', 'icon-class': 'success', additionalParameters: { id: 'F4' } },
//{ name: 'Reports', type: 'item', additionalParameters: { id: 'I1' } },
//{ name: 'Finance', type: 'item', additionalParameters: { id: 'I2' } }
],
delay: 400
});
js function for tree begins like this inside tree-custom.js
var e = function (e, i) {
this.$element = t(e), this.options = t.extend({}, t.fn.tree.defaults, i), this.$element.on("click", ".tree-item", t.proxy(function (t) {
this.selectItem(t.currentTarget)
}, this)), this.$element.on("click", ".tree-folder-header", t.proxy(function (t) {
this.selectFolder(t.currentTarget)
}, this)), this.render()
};
and this is where I add the links under folders again inside trree-custom.js. Very primitive I know but that's all I can do with my current skillset. The part I added is between quotes. Rest came with beyondadmin theme and looks like usual fuelux.
selectFolder: function (e) {
//alert("testselectFolder");
//
//alert($('#myTree').tree({ dataSource: dataSource }));
var i, n, r, o = t(e),
s = o.parent(),
a = s.find(".tree-folder-content"),
l = a.eq(0);
//-----------------------------------------------
var li = $('<li>');
var TcgbLink = $('<a href=/E-Belge/Main/Folder/Inbox/?Type=1&DocumentTypeId=3>e-TCGB</div>' +"</br>");
var FaturaLink = $('<a href=/E-Belge/Main/Folder/Inbox/?Type=1&DocumentTypeId=4>e-Fatura</div>' + "</br>");
var Dolasim = $('<a href=>e-Dolasim Belgesi</div>');
li.append(FaturaLink);
a.append(li);
li.append(TcgbLink);
a.append(li);
li.append(Dolasim);
a.append(li);
//-----------------------------------------------
o.find(".fa.fa-folder").length ? (i = "opened", n = ".fa.fa-folder", r = "fa fa-folder-open", l.show(), a.children().length || this.populate(o)) : (i = "closed", n = ".fa.fa-folder-open", r = "fa fa-folder", l.hide(), this.options.cacheItems || l.empty()), s.find(n).eq(0).removeClass("fa fa-folder fa-folder-open").addClass(r), this.$element.trigger(i, o.data())
},
Now these links are being generated under all 4 folders. I want to be able to get the id (or name, preferably Id) of the folder so I can assign new Type parameters to querystring.
So far I tried to reach the id with this.data.id to no avail.
Instead of injecting the folder's children in the selectFolder callback, it is recommended to add the children via the dataSource callback (as in this example code: http://getfuelux.com/javascript.html#tree-usage-javascript).
The first argument to the dataSource is the "parent data" when you click on a tree node (with the second argument being the callback that you send the new array of child data).
This way you can use the selected event for getting your ID, because it gets the jQuery data passed to it.

jquery DataTables Editor: "select" field displays option value instead of label

I am using jquery's DataTables which is really working great. Then only problem I got is, that I am facing (in non-edit-view) the value of the select-field (which is an id). The user of course doesn't want to see the id of course.
Therefore I am looking for a possibility to configure that column in a way to show always the value of label property.
Here a some snippets:
$(document).ready(function() {
var table = $('#overviewTable').DataTable({
dom: "Tfrtip",
ajax: "/Conroller/GetTableData",
columns: [
{ data: "Id", className: "readOnly", visible: false },
{
data: "LoanTransactionId",
className: "readOnly readData clickable",
"fnCreatedCell": function(nTd, sData, oData, iRow, iCol) {
$(nTd).html("<a href='#'>" + oData.LoanTransactionId + "</a>");
}
},
{ data: "Id", className: "readOnly" },
{ data: "property_1", className: "readOnly" },
{ data: "Priority" },
{ data: null, className: "action readOnly", defaultContent: 'Info' }
],
order: [1, 'asc'],
tableTools: {
sRowSelect: "os",
sRowSelector: 'td:first-child',
aButtons: []
}
});
// data reload every 30 seconds
setInterval(function() {
table.ajax.reload();
}, 30000);
editor = new $.fn.dataTable.Editor({
ajax: "PostTable",
table: "#overviewTable",
fields: [
{
label: "Id",
name: "Id"
},
{
label: "Column 1",
name: "property_1"
},
{
label: "Priority",
name: "Priority",
type: "select",
options: [
{ label: "low", value: 0 },
{ label: "mid", id: 1 },
{ text: "high", id: 2 }
]
}
]
});
// Inline Edit - only those who are not readOnly
$('#overviewTable').on('click', 'tbody td:not(:first-child .readOnly)', function(e) {
editor.inline(this, {
submitOnBlur: true
});
});
How it looks in the display mode
How it looks in the edit mode
See the documentation on columns.render
You want to modify your column options for priority
Preferred Option: Your data source has a field with the priority as a string
This is the best option, as you don't want to have two places with this business logic. Keep it out of the client code.
Also, you will want to modify the editor as well so that the options used have been retrieved dynamically from the server to keep this business logic out of the client too. This is left as an exercise for the reader.
Since you don't provide details on what your data structure looks lik, I'm assuming it is an object, and it has an attribute priorityAsString so use the string option type for render.
columns: [
...
{
data: "Priority" ,
render: "priorityAsString",
},
Option 2) You write a function to map priority to string
Do this if you can't get the data from the server. But remember you will need to update many places when the priority list changes.
columns: [
...
{
data: "Priority" ,
render: renderPriorityAsString,
},
...
function renderPriorityAsString(priority) {
const priorityToString = {
0: 'low',
1: 'med',
2: 'high',
};
return priorityToString[priority] || `${priority} does not have a lookup value`;
}
"render": function ( data, type, full ) { return label;}

jQuery TextExt: Tags with custom data objects

I'm currently struggling with implementing a jQuery plugin to my site for tags (with custom data objects) with autocomplete. jQuery TextExt can be found here (http://textextjs.com/). I'm currently struggling with using custom data objects for each tag, which can only be chosen from what autocompletes. Based on this example (http://textextjs.com/manual/examples/tags-with-custom-data-objects.html) I'm trying to figure out how to return both "name" and "id" when a tag is chosen. Does anyone know how to achieve this or point me in the correct direction?
Perhaps the answer is somewhere in this example (http://textextjs.com/manual/examples/filter-with-suggestions.html)?
Here's what I have written, which isn't working (it only returns the name, I've tried adding 'item.id' to the functions but that didn't work for me either):
<script type="text/javascript">
jQuery(document).ready(function( $ ){
jQuery('#textarea').textext({
plugins: 'tags',
items: [
{ name: 'PHP', id: '1' },
{ name: 'Closure', id: '2' },
{ name: 'Java', id: '3' }
],
ext: {
itemManager: {
stringToItem: function(str)
{
return { name: str };
},
itemToString: function(item)
{
return item.name ;
},
compareItems: function(item1, item2)
{
return item1.name == item2.name;
}
}
}
});
})
</script>
Your itemManager code should probably look like this, you will need to store the suggestions in a custom array to look up their relevant ids in the stringToItem Method
itemManager: {
items: [], // A custom array that will be used to lookup id
stringToItem: function(str)
{
//Lookup id for the given str from our custom array
for (var i=0;i<this.items.length;i++)
if (this.items[i].name == str) {
id = this.items[i].id;
break;
}
return { name: str, id: id };
},
itemToString: function(item)
{
//Push items to our custom object
this.items.push(item);
return item.name ;
},
compareItems: function(item1, item2)
{
return item1.name == item2.name;
}
}

Categories