angular.forEach(caseChangeRecord, function(change, key) {
if (caseChangeRecord != 'undefined') {
body += change.fieldName + ' : ' + change.newValue;
}
});
Actual Display Output:
A:BA:A:BA:B
Need to display Output:
A:B
A:B
A:B
body += change.fieldName + ' : ' + change.newValue + ' < br>';
if (caseChangeRecord != 'undefined') condition within your loop has no affect, since if caseChangeRecord is undefined, we will never enter the loop, in my example I assume that caseChangeRecord is an array (if it is an object, we can use Object.keys(caseChangeRecord) and get each item by its key).
Also, you probably need to use $sce service and its trustAsHtml() function:
var caseChangeRecord = [
{
fieldName: 'A1',
newValue: 'B1'
},
{
fieldName: 'A2',
newValue: 'B2'
},
{
fieldName: 'A3',
newValue: 'B3'
}
];
if (caseChangeRecord) {
$scope.body = $sce.trustAsHtml(caseChangeRecord.map(function (change) {
return '<p>' + change.fieldName + ' : ' + change.newValue + '</p>';
}).join(''));
}
then in your HTML you can use it like:
<body ng-controller="MainCtrl" ng-bind-html="body"></body>
it is not exactly your case, but I think this way you can solve your issue, if you need an exact solution for your case, update my plunker and I will try to help.
plunker: https://plnkr.co/edit/Mk1Fbguce7ctXlVN6qsf?p=preview
Related
I have to use AngularJS to build a dashboard and one of the components is a table.
Since I did not find relevant dependencies/libraries for angularjs (like tabulator or datatables), I am doing it myself.
Instead of using the native angular filter, I built a custom method, but I am not sure if I am following a good approach.
The main idea is that when I pull the data object (array of objects) via Ajax, I create both an "original" and a "current" data object,s and at the beginning, they are exactly the same of course.
Then I created an input field above every column heading and I linked the search function to the blur and keyup events (enter key).
When the search function is triggered, I start making changes to the "current" object. This way I can filter by multiple columns incrementally. I filter the data object using an awesome library called AlaSQL.
I also linked to a button the "reset" method, which simply makes the "current" object equal to the "original" object, and cleans up the input fields.
The point is, am I missing any best practices? Are there better ways to do so with AngularJS?
Any suggestions?
Thanks a lot.
HTML
<div ng-app="myApp">
<div ng-controller="divController">
<my-table></my-table>
</div>
</div>
JS
var app = angular.module('myApp', []);
app.controller('divController', function ($scope, $http) {
$scope.data = {};
$scope.data.current = null;
$scope.data.original = null;
$scope.filter = {
id: {
field: "id",
value: null
},
name: {
field: "name",
value: null
},
owner: {
field: "owner",
value: null
},
}
$scope.reset = function () {
console.log("reset");
$scope.data.current = $scope.data.original;
for (let prop in $scope.filter) {
$scope.filter[prop]["value"] = null;
}
}
$scope.filterExec = function (field, value) {
if (value) {
console.log(`Executing filter on field "${field.trim()}" by this value "${value.trim()}"`);
var filtered = alasql('SELECT * FROM ? where ' + field + ' LIKE "%' + value + '%"', [$scope.data.current]);
$scope.data.current = filtered;
}
}
$http.get("./workspaces_demo_obj.json")
.then(function (response) {
console.log(response);
$scope.data.original = response.data;
$scope.data.current = response.data;
});
});
app.directive('myTable', function () {
return {
template:
'<div>Total rows {{data.current.length}} <button ng-click="reset()">RESET</button></div>' +
'<table class="table table-responsive table-sm">' +
'<thead>' +
'<tr><th>Workspace ID</th>' +
'<th>Name</th>' +
'<th>Owner</th></tr>' +
'<tr><th><input ng-model="filter.id.value" ng-blur="filterExec(filter.id.field, filter.id.value)" ng-keydown="$event.keyCode === 13 && filterExec(filter.id.field, filter.id.value)" placeholder="Filter by id"></input></th>' +
'<th><input ng-model="filter.name.value" ng-blur="filterExec(filter.name.field, filter.name.value)" ng-keydown="$event.keyCode === 13 && filterExec(filter.name.field, filter.name.value)" placeholder="Filter by name"></input></th>' +
'<th><input ng-model="filter.owner.value" ng-blur="filterExec(filter.owner.field, filter.owner.value)" ng-keydown="$event.keyCode === 13 && filterExec(filter.owner.field, filter.owner.value)" placeholder="Filter by owner"></input></th></tr>' +
'</thead>' +
'<tbody>' +
'<tr ng-repeat="x in data.current">' +
'<td>{{ x.workspace_id }}</td>' +
'<td>{{ x.name }}</td>' +
'<td>{{ x.owner }}</td>' +
'</tr>' +
'</tbody>' +
' </table>',
restrict: 'E'
};
});
Is there a way to customize the tooltip in the Ext.grid.column.Action? I'd like to set the autoHide to false.
Thanks in Advance
You can by overriding or extending the ActionColumn.
You can see from the QuickTipManager docs that if you set a data item, data-hide="user" is the equivelent of autoHide=false.
The ActionColumn doesn't expose that functionality, it just uses the defaults, so we have to override the ActionColumns's defaultRenderer.
The defaultRenderer is a protected template function, so we can provide our own renderer and a custom config.
Start by copying the existing defaultRenderer source from the ActionColumn and then adding a few lines to handle our new config.
We can add a custom tooltipAutoHide config to the action config. Then in the defaultRenderer, we can read that config, defaulting to true, and render out data-hide="user" if tooltipAutoHide:false is set.
Here is an example. The relevant lines are
Read the config
//Get custom 'tooltipAutoHide' config from tip
tooltipAutoHide = item.tooltipAutoHide === false ? false : true;
Render out 'data-hide="user"' if false
// write data-hide=user == autoHide:false
(!tooltipAutoHide ? ' data-hide="user"' : '') +
In column definition, set tooltipAutoHide:true
{
xtype:'myactioncolumn',
enter code here items:[{
tooltip: 'Edit',
tooltipAutoHide: false
}]
}
Here is the full sample
Ext.define('Ext.ux.column.MyActionColumn', {
extend: 'Ext.grid.column.Action',
xtype: 'myactioncolumn',
defaultRenderer: function (v, cellValues, record, rowIdx, colIdx, store, view) {
var me = this,
scope = me.origScope || me,
items = me.items,
len = items.length,
i, item, ret, disabled, tooltip, altText, icon, glyph, tabIndex, ariaRole;
// Allow a configured renderer to create initial value (And set the other values in the "metadata" argument!)
// Assign a new variable here, since if we modify "v" it will also modify the arguments collection, meaning
// we will pass an incorrect value to getClass/getTip
ret = Ext.isFunction(me.origRenderer) ? me.origRenderer.apply(scope, arguments) || '' : '';
cellValues.tdCls += ' ' + Ext.baseCSSPrefix + 'action-col-cell';
for (i = 0; i < len; i++) {
item = items[i];
icon = item.icon;
glyph = item.glyph;
disabled = item.disabled || (item.isDisabled ? Ext.callback(item.isDisabled, item.scope || me.origScope, [view, rowIdx, colIdx, item, record], 0, me) : false);
tooltip = item.tooltip || (item.getTip ? Ext.callback(item.getTip, item.scope || me.origScope, arguments, 0, me) : null);
//
//Get custom 'tooltipAutoHide' config from tip
tooltipAutoHide = item.tooltipAutoHide === false ? false : true;
console.log(tooltipAutoHide);
altText = item.getAltText ? Ext.callback(item.getAltText, item.scope || me.origScope, arguments, 0, me) : item.altText || me.altText;
// Only process the item action setup once.
if (!item.hasActionConfiguration) {
// Apply our documented default to all items
item.stopSelection = me.stopSelection;
item.disable = Ext.Function.bind(me.disableAction, me, [i], 0);
item.enable = Ext.Function.bind(me.enableAction, me, [i], 0);
item.hasActionConfiguration = true;
}
// If the ActionItem is using a glyph, convert it to an Ext.Glyph instance so we can extract the data easily.
if (glyph) {
glyph = Ext.Glyph.fly(glyph);
}
// Pull in tabIndex and ariarRols from item, unless the item is this, in which case
// that would be wrong, and the icon would get column header values.
tabIndex = (item !== me && item.tabIndex !== undefined) ? item.tabIndex : me.itemTabIndex;
ariaRole = (item !== me && item.ariaRole !== undefined) ? item.ariaRole : me.itemAriaRole;
ret += '<' + (icon ? 'img' : 'div') +
(typeof tabIndex === 'number' ? ' tabIndex="' + tabIndex + '"' : '') +
(ariaRole ? ' role="' + ariaRole + '"' : ' role="presentation"') +
(icon ? (' alt="' + altText + '" src="' + item.icon + '"') : '') +
' class="' + me.actionIconCls + ' ' + Ext.baseCSSPrefix + 'action-col-' + String(i) + ' ' +
(disabled ? me.disabledCls + ' ' : ' ') +
(item.hidden ? Ext.baseCSSPrefix + 'hidden-display ' : '') +
(item.getClass ? Ext.callback(item.getClass, item.scope || me.origScope, arguments, undefined, me) : (item.iconCls || me.iconCls || '')) + '"' +
(tooltip ? ' data-qtip="' + tooltip + '"' : '') +
// write data-hide=user == autoHide:false
(!tooltipAutoHide ? ' data-hide="user"' : '') +
(icon ? '/>' : glyph ? (' style="font-family:' + glyph.fontFamily + '">' + glyph.character + '</div>') : '></div>');
}
return ret;
}
});
Ext.create('Ext.grid.Panel', {
title: 'Action Column Demo',
store: Ext.data.StoreManager.lookup('employeeStore'),
columns: [{
text: 'First Name',
dataIndex: 'firstname'
}, {
text: 'Last Name',
dataIndex: 'lastname'
}, {
xtype: 'myactioncolumn',
width: 50,
items: [{
iconCls: 'x-fa fa-cog',
tooltip: 'Edit',
tooltipAutoHide: false,
handler: function (grid, rowIndex, colIndex) {
var rec = grid.getStore().getAt(rowIndex);
alert("Edit " + rec.get('firstname'));
}
}]
}],
width: 250,
renderTo: Ext.getBody()
});
Here's a working Sencha Fiddle example.
Whenever I try to update medMins the function produces the correct results twice. However, Vue returns with
[Vue warn]: You may have an infinite update loop in a component render function.
I tried switching medMins to a computed property but got the same result. I was reading that the problem was that the component renders then I'm changing some component data during which some reactive data is changed causing it re-render... etc. Is there a way I can avoid this? Am I able to update medMins in this component or do I have to do some other way? Any help would be much appreciated.
Vue.component('day', {
//props: ['items'] or
props: {
dayofweek: {
type: Array,
required: true
},
name:{
type: String,
default: 'Blarg'
},
},
data: function() {
return {
medMins: 0
}
},
methods: {
updateMed: function(day) {
this.medMins += Number(day.endTimeMillis/60000).toFixed()-Number(day.startTimeMillis/60000).toFixed()
}
},
template: ''+
' <div>'+
' <h1>{{name}}</h1>\n' +
' <div class = "row">\n' +
' <div class ="col" v-for="day in dayofweek">{{day.activityType}}' +
' <div v-if="`${day.activityType}` == 45" v-on="updateMed(day)"></div>' +
' </div>' +
' </div>' +
' <h1>{{medMins}}</h1>'+
' </div>',
computed: {
}
});
Sounds like you just want a computed property for medMins. Something like this
// no need for "data" as far as I can see
computed: {
medMins () {
return this.dayofweek.reduce((medMins, { activityType, endTimeMillis, startTimeMillis }) => {
if (activityType == 45) {
medMins += Number(endTimeMillis/60000).toFixed()-Number(startTimeMillis/60000).toFixed()
}
return medMins
}, 0)
}
},
template: `
<div>
<h1>{{name}}</h1>
<div class = "row">
<div class="col" v-for="day in dayofweek">
{{day.activityType}}
</div>
</div>
<h1>{{medMins}}</h1>
</div>
`
This will produce a number for medMins that calculates your totals for all the 45 activity types.
In Vue world, v-on is an event listener but you need to mention which type event you need to listen.
Let's say if it is click event then
v-on:click="updateMed(day)"
Hope this helps!
I need a cell table with more than one value. I made a construct for ui-grid options, a custom template for the cell and few css lines. It works and permit me to decide if value must stay in the same line or in more than one line.
Custom template:
var template =
'<div class="ui-grid-cell-contents crm-group">'+
'<div class="crm-group-row" ng-repeat="rowFields in col.colDef.fields">' +
'<div class="crm-group-col" ng-repeat="colField in rowFields" ng-if="row.entity[colField.name]">' +
'<span class="title" ng-if="colField.descr">'+
'{{colField.descr}}: '+
'</span>' +
'<span class="content">'+
//"{{row.entity[colField.name] }}"+ //without filter
"{{row.entity[colField.name] | colField.cellFilter}}"+ //<-- HERE doesn't work
'</span>' +
'</div>'+
'</div>' +
'</div>';
option structure:
{ field: 'group',
name: 'Group',
width: '**',
fields : [
[
{
name: "age",
descr: "Num",
cellFilter : "floatToFixed:'2'",
},
{
name: "email",
descr: "Email",
},
],
],
cellTemplate: template
},
Now i need to apply a possible different filter to each value. So i set it on the field structure. In this case i want apply a custom filter to the first field value.
Filter:
app.filter('floatToFixed', function () {
return function (input, number) {
console.log(input);
console.log(number);
if (!input)
return null;
if (!number)
return input;
else
return parseFloat(input).toFixed(parseInt(number));
}
});
But it doesn't work. Here the plunker.
Help is appreciate.
First of all, you have incorrect format after pipe | - name of the filter can't contain dot, what you could do is have a filter that applies filter from a string, that's the solution I can came up from top of my head:
http://plnkr.co/edit/by8vQBgeau0823akwQvu?p=preview
.filter('applyFilter', function($filter) {
return function(input, filter) {
var filterToApply = filter !== undefined ? filter.split(':') : undefined;
return filterToApply === undefined ? input : $filter(filterToApply[0])(input, parseInt(filterToApply[1], 10))
}
})
and then in html
"{{row.entity[colField.name] | applyFilter:colField.cellFilter}}"
I have JSON object like below
{
"txt_inc_Application": {
"EWS": true,
"EWindow": true
},
"txt_inc_IncidentType": {
"Brand Damage": true,
"Internal failure": true
}
}
And I am using angular.forEach to get the values
$scope.filterFormula=function() {
angular.forEach($scope.filters, function(filterObj , filterIndex) {
angular.forEach(filterObj, function(value , key) {
console.log(value+"--"+key)
})
})
}
How can i get "txt_inc_Application" and "txt_inc_IncidentType" in the loop?
Also when call the angular function in html like below why it is getting executed twice?
{{filterFormula()}}
The first parameter to the iterator in forEach is the value and second is the key of the object.
angular.forEach(objectToIterate, function(value, key) {
/* do something for all key: value pairs */
});
In your example, the outer forEach is actually:
angular.forEach($scope.filters, function(filterObj , filterKey)
var obj = {name: 'Krishna', gender: 'male'};
angular.forEach(obj, function(value, key) {
console.log(key + ': ' + value);
});
yields the attributes of obj with their respective values:
name: Krishna
gender: male