Value not changed back when canceled - javascript

This is in angular. There is a p-table that shows some data, like below:
When I clicked on the pencil icon to edit the table row, this pops up. I'll call this the edit dialog:
When I make any kind of edit in the text input box, the table row on p-table also change to the new value; example if I change the Download URL to "sss", the p-table also shows "sss" even though I haven't clicked the "Update" button yet. However, if I decide to cancel the change by clicking on the Cancel button, the new value ("sss") stays and the old value does not revert back. Any help on reverting it back is appreciated.
Here is how my code is currently set up: when I open the page the first time, it retrieves the data in the table from a database and display in the p-table. When I click on the pencil icon, it uses DynamicDialog to open the edit dialog that was created by another component.
Here's the typescript code to that it uses to open the edit dialog, which is called ModelDialogComponent:
this.ref = this.dialogService.open(ModelDialogComponent, {
data: {
record,
},
header: title,
width: '500px',
contentStyle: { 'max-height': '500px' },
baseZIndex: 10000,
dismissableMask: true,
})
ModelDialogComponent has five input text and two buttons. Each of the input text is create in this similar fashion. This is one of the html code for it:
<!--Applicationn Name-->
<div class="p-field p-grid" style="padding: 10px">
<label class="p-col-fixed" style="width: 150px">App Name </label>
<div class="p-col">
<input type="text" pInputText readonly [(ngModel)]="updateData.app" />
</div>
</div>
The Cancel button is tied to a function: (click) ="cancelUpdate()"
cancelUpdate() is this:
cancelUpdate(){
this.config.data.record = this.clonedVersion;
this.config.data.record = {...this.config.data.record};
this.updateData = this.clonedVersion;
this.updateData = {...this.updateData};
this.ref.close(this.clonedVersion);
}
Here is ModelDialogComponent init:
ngOnInit() {
this.updateData = this.config.data.record;
this.clonedVersion = {...this.updateData};
}
Canceling does not revert the value back to its original value, and how do I do that?
If you need more info, please let me know.

I figured out a way to get it to revert back. use onClose, and then get the original data from the database. This works good for now because the database is small. As it grows, I think I will need to figure out why cloning the table row and setting it back does not work. Here's my code for the solution I made:
this.ref.onClose.subscribe(() => {
this.messageService.add({
severity:'info',
summary: 'Canceled Editing',
detail:'User canceled edits',
sticky: false,
});
this.ToolVersionService.getAllVersion().subscribe((data) => {
this.rowData = data;
this.loading = false;
this.EditDetails = false;
},
(err) => {
console.error(err)
});
});

Related

Retain the selection in previous page when moved to next in ExtJS Combo box

I have the ExtJs form combo which shows the values in the dropdown as checkbox and the value to select. I have used the pagination to list all the values with no of pages. I need the selected value to be retained in the current page even when we move to next or previous page and comes back to the same page(without selecting any thing in prev, next pages).
Code:
Ext.create('Ext.form.ComboBox', {
id: 'ageComboPickerReport',
name: 'ageComboPickerReport',
maxHeight: 150,
margin: '0 5 0 0',
width: 150,
emptyText: "Select tags",
listConfig: {
getInnerTpl: function (displayField) {
return '<div class="x-combo-list-item"><img src="" class="chkCombo-default-icon
chkCombo" /> {' + displayField + '}</div>';
},
autoEl: { 'q-name': 'rpt-age-criteria-list'
}, labelAlign: 'top',
pageSize: 25,
displayField: 'age', valueField: 'id', forceSelection: true, store: me.ageStore,
//Disable Typing and Open Combo
onFocus: function () {
if (!this.isExpanded) {
me.ageStore.load()
this.expand()
} this.getPicker().focus();
}
}),
Could any one tell me how to retain the selected value in the page when move to other page and comes back
I faced a very similar issue, in my case I had a grid with checkbox selection model, and I wanted to retain the selected rows during moving between pages. I am quite sure (although not 100%) that there is no built-in functionality for that in ExtJS. I can tell what I did, hope it helps, although you have to adopt it because it is not for ComboBox, but for Grid.
Since this is a paged store, not all of the records are loaded, only the ones that are currently on the displayed page. (I assume your store is remote because I see you call load on it.) So to achieve what you like, you need to:
keep track of the selected record ids to be able the reset selection on page,
keep track the items (records) themselves as well, since you will likely need them,
after a page is loaded and displayed, set the selection accordingly.
(This solution can have issues, so you need to be careful. Depending on the use case, it is possible for example that the user selects something, goes to another page and come back, but the previously selected row is no more available (it was deleted by someone else). You have to consider whether it affects you.)
The complete code is complicated and not general enough to share it, but I can outline the steps I did:
Set up two data entries in viewmodel to track the selected record ids and items (records):
data: {
multiSelection: {
ids: {},
items: [],
},
}
Add listeners to the grid's select and deselect events in the view:
listeners: {
select: 'onGridSelect',
deselect: 'onGridDeselect',
},
Create onGridSelect and onGridDeselect functions in the controller, and also add a isDataChanged variable to the controller to indicate whether the store was changed (it is changed on each paging). It is important because I will programatically change the selection, and I don't want my listeners to be executed in this case, only when the user interacts. Like this:
isDataChanged: false,
onGridSelect: function(selModel, record, index, eOpts) {
if (isDataChanged) {
return;
}
const viewModel = this.getViewModel(),
multiSelection = viewModel.get('multiSelection');
multiSelection.ids[record.getId()] = true;
Ext.Array.push(multiSelection.items, record);
},
onGridDeselect: function(selModel, record, index, eOpts) {
if (isDataChanged) {
return;
}
const viewModel = this.getViewModel(),
multiSelection = viewModel.get('multiSelection');
delete multiSelection.ids[record.getId()];
Ext.Array.remove(multiSelection.items, record);
},
Finally, add a listeners to the store to detect changes, this will be called every time the user moves between pages. This is a little tricky, because I need to access the grid from the store listeners which is not very ExtJS like, but I had to (store needs to be the grid's store:
store.on('datachanged', function(store, eOpts) {
// this part you have to figure out, my solution is way too specific
// for share
const grid = ...;
// this was important for me, if the store is really changed,
// deleted, added rows etc., I deleted the selection, but
// you can consider it
if (store.getUpdatedRecords().length > 0 ||
store.getRemovedRecords().length > 0 ||
store.getNewRecords().length > 0) {
// access the `viewmodel` and reset `multiSelection` data entries to
// default, I `return` here to skip the rest
return;
}
// disable listener
grid.getController().isDataChanged = true;
const selModel = grid.getSelectionModel(),
multiSelection = grid.getViewModel().get('multiSelection');
// deselect everything
selModel.deselectAll();
// get an array of the saved selection and find out, which
// record from the current page is in the saved multiSelection
const selected = [];
Ext.Array.each(grid.getStore().getRange(), function(record) {
if (multiSelection.ids[record.getId()]) {
Ext.Array.push(selected, record);
}
});
// apply selection to current page
selModel.select(selected);
// enable listener
grid.getController().isDataChanged = false;
}

add a new empty line in ui-grid

I'm trying to add a new empty line in ui-grid. I've tried looking in different tuto and example, but all that I found didn't reply to my spec, and I wasn't able to adapt it to what I'm looking for.
In fact I'm looking how to add a new empty line in an existing ui-grid neither using a button outside the grid nor a button in the rowfooter.
I'm looking to add a abutton in the grid like the + button shown in the screen shot below
or may be render automatically a new empty line when the rendering the ui-grid and a new one when all rows were filled.
I tried doing that using function in cell template but it's not working.
any help is really appreciated
The first option sounds like more of a CSS issue to me. Essentially, the add button would use some sort of font library containing a +, and you would need to position it in the corner of the grid. Perhaps looking at the grid footer would be a starting place. It sounds like you've seen the basics of creating an add row button here: http://ui-grid.info/docs/#/tutorial/112_swapping_data
The second option (render automatically a new empty line when the rendering the ui-grid and a new one when all rows were filled) requires a JavaScript approach.
The basic logic I followed is:
(Assume) Some data loads from somewhere in a backend (in this sample, it's a simulated load returning a promise as $http or $resource would).
After that data is loaded, we append a new row. We wait for the data first; otherwise we'd not be pushing the new row to the correct location.
Upon completion of the edit action, we set a timeout to ensure subsequent edits on other cells do not keep firing a new row. If the timeout is reached, we append a new row. If a subsequent edit action occurs, and a timeout promise exists (for adding a new row), we cancel it. Once no edit actions occur, and the timeout is reached, we push the new row.
To ensure that we are only taking action when our "extra row" is modified, when we create a row, a reference is maintained to the current row such that we can evaluate whether or not a received event is of interest (var newRowTimeoutPromise).
The core logic in code is below, with a sample implementation in Plnkr:
var extraRow = null,
addNewTimeoutMillis = 2000,
newRowTimeoutPromise = null;
loadData().then(function(data) {
$scope.gridOpts.data = data;
}).finally(function() {
// add initial empty row, and set our reference to it
extraRow = addEmptyRow($scope.gridOpts.data);
})
$scope.gridOpts = {
showGridFooter: true,
onRegisterApi: function(gridApi) {
$scope.gridApi = gridApi;
// listen for cell edit completion
gridApi.edit.on.afterCellEdit($scope, function(rowEntity, colDef, newValue, oldValue) {
// test if the edited row was the "extra row"
// otherwise, and edit to any row would fire a new row
// Set a timeout so we don't create a new row if the user has
// not finished their edit(s) on other fields
newRowTimeoutPromise = $timeout(function() {
if (rowEntity == extraRow) {
// add a new empty row, and set our reference to it
extraRow = addEmptyRow($scope.gridOpts.data);
newRowTimeoutPromise = null;
}
}, addNewTimeoutMillis);
})
// Listen for cell edit start, and cancel if we have a pending new
// row add. Otherwise, each time you finish the edit on a cell,
// this will fire.
gridApi.edit.on.beginCellEdit($scope, function(rowEntity, colDef, newValue, oldValue) {
if (newRowTimeoutPromise != null) {
$timeout.cancel(newRowTimeoutPromise);
}
})
}
};
http://plnkr.co/edit/IMisQEHlaZDCmCSpmnMZ?p=preview
I used jQuery to fetch and change style of specific cell elements of the cell template.
Here is a helpful Fiddle
Here is the controller script : -
var app = angular.module('app', ['ngTouch', 'ui.grid']);
app.controller('MainCtrl', ['$scope',
function($scope) {
$scope.gridOptions = {};
$scope.Add = function() {
$scope.gridOptions.data.push( { firstName: ' ',lastName:'',company:'' });
$(".ui-grid-coluiGrid").prevObject["0"].activeElement.style.display="none";
$(".ui-grid-cell")[$scope.gridOptions.data.length-2].style.display="inline";
};
$scope.gridOptions.onRegisterApi = registerGridApi;
function registerGridApi(gridApi) {
$scope.gridApi= gridApi
};
$scope.gridOptions.columnDefs = [{
name: 'firstName',
field: 'firstName',
}, {
name: 'lastNamer',
field: 'firstName'
}, {
name: 'ShowScope',
cellTemplate: '<button id="btb" ng-click="grid.appScope.Add()">+</button>'
}];
$scope.gridOptions.data = [{ yourdata}];
}
]);
To make it work properly 2 more things have to be done
Use cellContentEditable to make the rows editable
In order to disable display style of cell template button that appears on cells corresponding to rows of already existing data,you could use angular foreach or a for loop to iterate through these rows and disable style(I tried using renderContainers but it always returns the length of rendered rows outside Add functions as 0).
I have a working plunker over here.
http://plnkr.co/edit/Vnn4K5DcCdiercc22Vry?p=preview
In columnDefs, I have defined a separate column for add:
{
name: 'add',
displayName: '',
enableCellEdit: false,
enableColumnMenu: false,
width: '3%',
cellTemplate: '<div class="ui-grid-cell-contents" ng-click="grid.appScope.addRow()"><span ng-click="grid.appScope.addRow()">Add</span></div>'
}
And
$scope.addRow= function(){
var newlist = {"remarks":'',"testName":''};
$scope.gridOptions.data.push(newlist);
}
Update: A second plunker with bootstrap icons for add/remove
http://plnkr.co/edit/FjsA2r?p=preview

Change value in $rootScope

I have a table with a background like this: http://ast-ss.se/terrain/
You can navigate through the boxes by just clicking on them. I have also implemented the feature that random monsters, black dots, is generated at the map. You can navigate to the box and challenge the monster.
When I click on a box with a monster in it, I get the monster's specifications, lever, strength etc etc.
Here is the code snippet from my directive:
if(angular.element(this).hasClass('monster'))
{
var monster_info = angular.element(this).find("img").attr("title");
$http.get("lib/terrain.php", {params: { monster_data:monster_info}}).success(function(data) {
console.log(data);
$rootScope.$broadcast('mapInfo', data);
});
}
As you can see, I'm using $rootScope.$broadcast for this. Here is what I'm doing in my controller:
$scope.$on('mapInfo', function(event, mapInfo) {
$scope.mapInfo = mapInfo;
console.log($scope.mapInfo);
});
And here is my HTML:
<div id="leftcontent_top">
<ul>
<li>Vildmarken</li>
<li> | </li>
<li>Marknaden</li>
<li> | </li>
<li>Värdshuset</li>
</ul>
</div>
<div ng-bind-html="safeHtml(mapInfo)"></div>
<div ng-if="challengeMonster"><input type="submit" ng-click="doChallenge()" value="Utmana"></div>
This works fine. The information about the monster is printed out at the page. However, when you are in a box without a monster, I want to display information about the box you are in. Everytime you navigate to a new box, I want to print out information about that box. I have tried like this:
Directive:
angular.element('.click').click(function() {
if(angular.element(this).hasClass('monster'))
{
var monster_info = angular.element(this).find("img").attr("title");
$http.get("lib/terrain.php", {params: { monster_data:monster_info}}).success(function(data) {
console.log(data);
$rootScope.$broadcast('mapInfo', data);
});
}
else
{
test = angular.element(this).attr('id');
$rootScope.$broadcast('mapInfo', test);
angular.element('.click.dotted').empty();
angular.element('.click.dotted').removeClass('dotted');
if(!angular.element(this).hasClass('dotted'))
{
$rootScope.$broadcast('mapInfo', test);
angular.element(this).addClass('dotted');
angular.element(this).html($('<img src="images/dot.png">'));
}
}
});
As you can see, I have an else-statement with $broadcast, but the value of test is not printed out at the page. But if I click on a box with a monster in it, the information is printed out, but if I click in a empty box, nothing is printed out. As you can see in the controller, I run console.log($scope.mapInfo) and I can see that $scope.mapInfo contains the correct value.
Anyone?

Kendo UI Grid editable manual dataItem.set() slow/delay

I have an editable Kendo Grid that may have a column with a checkbox to change a boolean value. I have used this solution proposed by OnaBai that is working perfectly!
The only problem is that the checkbox value change is too slow. When user clicks it, it takes about 1 second to change. I realize that the dataItem.set() method is responsible by this delay.
My grid has a considerable amount of data. About 30-40 columns and 300+ lines. It is defined as follows:
$("#mainGrid").kendoGrid({
dataSource: dataSource,
pageable: false,
sortable: true,
scrollable: true,
editable: true,
autoBind: false,
columnMenu: true, // Cria o menu de exibição de colunas
height: getGridHeight(),
toolbar: [/* hide for brevity */],
columns: [/* hide for brevity */],
dataBound: function() { /* hide for brevity. */},
edit: function() { /* hide for brevity. */}
});
Another detail is that, when dataItem.set() is called, it calls dataBound() event but that is not causing the delay. Grid's edit() method is not being called on this process. I don't know if worths to post dataSource code.
I would suggest using the approach from this code library article when it comes to use checkboxes. It does not use the set methods of the model and still works the same way. Even with 2000 records on a single page CheckAll will work flawlessly.
I have found an alternative way for doing what OnaBai proposed and it's working better.
// This is the grid
var grid = $("#mainGrid").data("kendoGrid");
// .flag is a class that is used on the checkboxes
grid.tbody.on("change", ".flag", function (e)
{
// Get the record uid
var uid = grid.dataItem($(e.target).closest("tr")).uid;
// Find the current cell
var td = $(e.target).parent().parent();
// This opens the cell to edit(edit mode)
grid.editCell(td);
// This ones changes the value of the Kendo's checkbox, that is quickly shown,
// changed and then hidden again. This marks the cell as 'dirty' too.
$(td.find("input")[0]).prop("checked", $(e.target).is(":checked") ? "checked" : null).trigger("change").trigger("blur");
}
Should try something like this:
I'll set the column with the Edit button to look like this:
columns.Command(command => {command.Edit().HtmlAttributes(new { id = "btnEdit_" + "${Id}" }); }).Width(100).Hidden(true);
And have it where clicking into the first column (I have an image with a hyperlink) uses an onclick function to programmatically click the Edit button, click the checkbox, then click the Update button. Probably more "old school", but I like knowing it is following the order I would be doing if I were updating it, myself.
I pass in the object ("this"), so I can get the row and checkbox when it appears, the new status as 0 or 1 (I have some code that uses it, not really necessary for this demo, though, so I'm leaving that part out of my function for simplicity), and the ID of that item:
columns.Bound(p => p.IsActive).Title("Active").Width(100).ClientTemplate("# if (IsActive == true ) {# <a href=javascript:void(0) id=btnActive_${Id} onclick=changeCheckbox(this, '0', ${Id}) class='k-button k-button-icontext k-grid-update'><img style='border:1px solid black' id=imgActive src=../../Images/active_1.png /></a> #} else {# <a href=javascript:void(0) id=btnActive_${Id} onclick=changeCheckbox(this, '1', ${Id}) class='k-button k-button-icontext k-grid-update'><img style='border:1px solid black' id=imgActive src=../../Images/active_0.png /></a> #}#");
function changeCheckbox(obj, status, id) {
var parentTr = obj.parentNode.parentNode;
$('[id="btnEdit_' + id + '"]').click();
parentTr.childNodes[5].childNodes[0].setAttribute("id", "btnUpdate_" + id); // my Update button is in the 6th column over
parentTr.childNodes[0].childNodes[0].setAttribute("id", "chkbox_" + id);
$('[id=chkbox_' + id + ']').click().trigger("change");
$('[id=chkbox_' + id + ']').blur();
var btnUpdate = $('[id="btnUpdate_' + id + '"]');
$('[id="btnUpdate_' + id + '"]').click();
}
Code above assumes, of course, the checkbox is in the first column. Otherwise, adjust the first childNodes[0] on that chkbox setAttribute line to the column it sits in, minus one because it starts counting from zero.
I did a solution much like #DontVoteMeDown. But I have a nested grid (master / detail) so I get the parent grid from the event parameter. Also I just trigger a click-event on the checkbox.
$("#grid .k-grid-content").on("change", "input.chkbx", function (e) {
// Get the parent grid of the checkbox. This can either be the master grid or the detail grid.
var parentGrid = $(e.target).closest('div[data-role="grid"]').data("kendoGrid");
// Get the clicked cell.
var td = $(e.target).closest("td");
// Enter the cell's edit mode.
parentGrid.editCell(td);
// Find the checkbox in the cell (which now is in "edit-mode").
var checkbox = td.children("input[type=checkbox]");
// Trigger a click (which will toggle check/uncheck).
checkbox.trigger("click");
});

ExtJS Change Event Listener failing to fire

I was asked to post this as a question on StackOverflow by http://twitter.com/jonathanjulian which was then retweeted by several other people. I already have an ugly solution, but am posting the original problem as requested.
So here's the back story. We have a massive database application that uses ExtJS exclusively for the client side view. We are using a GridPanel (Ext.grid.GridPanel) for the row view loaded from a remote store.
In each of our interfaces, we also have a FormPanel (Ext.form.FormPanel) displaying a form that allows a user to create or edit records from the GridPanel. The GridPanel columns are bound to the FormPanel form elements so that when a record is selected in the GridPanel, all of the values are populated in the form.
On each form, we have an input field for the table row ID (Primary Key) that is defined as such:
var editFormFields = [
{
fieldLabel: 'ID',
id: 'id_field',
name: 'id',
width: 100,
readOnly: true, // the user cannot change the ID, ever.
monitorValid: true
} /* other fields removed */
];
So, that is all fine and good. This works on all of our applications. When building a new interface, a requirement was made that we needed to use a third-party file storage API that provides an interface in the form of a small webpage that is loaded in an IFrame.
I placed the IFrame code inside of the html parameter of the FormPanel:
var editForm = new Ext.form.FormPanel({
html: '<div style="width:400px;"><iframe id="upload_iframe" src="no_upload.html" width="98%" height="300"></iframe></div>',
/* bunch of other parameters stripped for brevity */
});
So, whenever a user selects a record, I need to change the src attribute of the IFrame to the API URL of the service we are using. Something along the lines of http://uploadsite.com/upload?appname=whatever&id={$id_of_record_selected}
I initially went in to the id field (pasted above) and added a change listener.
var editFormFields = [
{
fieldLabel: 'ID',
id: 'id_field',
name: 'id',
width: 100,
readOnly: true, // the user cannot change the ID, ever.
monitorValid: true,
listeners: {
change: function(f,new_val) {
alert(new_val);
}
}
} /* other fields removed */
];
Nice and simple, except that it only worked when the user was focused on that form element. The rest of the time it failed to fire at all.
Frustrated that I was past a deadline and just needed it to work, I quickly implemented a decaying poller that checks the value. It's a horrible, ugly hack. But it works as expected.
I will paste my ugly dirty hack in an answer to this question.
"The GridPanel columns are bound to
the FormPanel form elements so that
when a record is selected in the
GridPanel, all of the values are
populated in the form."
As I understand it from the quote above, the rowclick event is what actually triggers the change to your form in the first place. To avoid polling, this could be the place to listen, and eventually raise to your custom change event.
Here is the ugly hack that I did to accomplish this problem:
var current_id_value = '';
var check_changes = function(offset) {
offset = offset || 100;
var id_value = document.getElementById('id_field').value || '';
if ( id_value && ( id_value != current_id_value ) ) {
current_id_value = id_value;
change_iframe(id_value);
} else {
offset = offset + 50;
if ( offset > 2500 ) {
offset = 2500;
}
setTimeout(function() { check_changes(offset); }, offset);
}
};
var change_iframe = function(id_value) {
if ( id_value ) {
document.getElementById('upload_iframe').src = 'http://api/upload.php?id=' + id_value;
} else {
document.getElementById('upload_iframe').src = 'no_upload.html';
}
setTimeout(function() { check_changes(100); }, 1500);
};
It's not pretty, but it works. All of the bosses are happy.
If you took a moment to read the source, you would see that the Ext.form.Field class only fires that change event in the onBlur function

Categories

Resources