This is a migrated thread and some comments may be shown as answers.

Custom command Duplicate Row

23 Answers 897 Views
Grid
This is a migrated thread and some comments may be shown as answers.
Shahar
Top achievements
Rank 1
Shahar asked on 04 Jul 2013, 07:07 AM
I have a grid with edit : popup.
I want to add a custom command that will open the popup editor in adding new row  mode
but will have the values of the current record (like duplicate row).

23 Answers, 1 is accepted

Sort by
0
Dimiter Madjarov
Telerik team
answered on 05 Jul 2013, 06:50 AM
Hello Shachar,


A custom approach to achieve this would be the following - use a boolean flag value which will indicate whether the currently added row is added by the standard "Add new item" button or by the custom command. In the custom command event handler you should retrieve the dataItem for the current row, set the flag to true and manually call the addRow method of the Grid to add the new row. Finally in the edit event handler (which is fired each time a record is added or edited) you should check the flag and if it's true, manually set the values of the editor fields with the ones from the current row. Here is a short example, which demonstrates the approach.

E.g.
{ command: { text: "Add New Custom Row", click: addNewCustomRow}, width: "140px" }

var isCustomAdd = false;
var customRowDataItem;
 
function addNewCustomRow(e) {
    e.preventDefault();
 
    customRowDataItem = this.dataItem($(e.currentTarget).closest("tr"));
    isCustomAdd = true;
    this.addRow();
}

function edit(e) {
    if (isCustomAdd && e.model.isNew()) {
        isCustomAdd = false;
 
        //set the values of the pop up editor fields
        $(e.container).find("[name='ProductName']").val(customRowDataItem.ProductName);
        ...
    }
}

I hope that this information was helpful for you. I wish you a great day!

 

Regards,
Dimiter Madjarov
Telerik
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Shahar
Top achievements
Rank 1
answered on 16 Jul 2013, 09:26 AM

Hi Dimiter,
I tried the solution you provided.
It worked fine and I populated all the items in the popup window.

The problem is when I click the update button
only items that were changed are submited.

Thanks

0
Dimiter Madjarov
Telerik team
answered on 17 Jul 2013, 12:20 PM
Hello Shachar,


Sorry for the inconvenience. I missed a small aspect of the approach in my previous answer. The change event of the edit fields should be triggered in order to notify the model binding.
E.g.
function edit(e) {
    if (isCustomAdd && e.model.isNew()) {
        isCustomAdd = false;
        $(e.container).find("[name='ProductName']").val(customRowDataItem.ProductName).change();
    }
}

 

Regards,
Dimiter Madjarov
Telerik
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Aaron
Top achievements
Rank 1
answered on 30 Oct 2013, 09:13 PM
I am wanting to accomplish the same thing as Shachar, the only difference is that two of the fields that I want to populate, or "duplicate" are cascading drop down items.  For example, I have a grid with three columns: Category, Subcategory, Content.  Category and Subcategory are cascading dropdown list that retrieve data via JSON.  I want to have these automatically set to the same as the row I want to duplicate.  I tried to see if this works with the code you have provided here but the drop down list are not set appropriately.  I am thinking that maybe the edit event is being called before the Category dropdown list is populated.  Also, the Subcategory is usually not populated until there has been an item selected in the Category drop down.
0
Dimiter Madjarov
Telerik team
answered on 31 Oct 2013, 01:27 PM
Hi Aaron,


You are correct, when the edit event of the Grid is triggered, the DropDownLists are not populated yet, which means that their value cannot be set yet. In general this is a custom scenario, but a sample approach that you could try would be to use a separate object to store the values in the edit event.
E.g.
var ddlValues = [];
 
function edit(e) {
    ...
    ddlValues[0] = ...;
    ddlValues[1] = ...;
    ddlValues[2] = ...;
}

Then you could bind to the dataBound events of the three DropDownLists and manually set the values there after the data is populated.
E.g.
function dataBound1(e) {
    this.value(ddlValues[0]);
}
 
function dataBound2(e) {
    this.value(ddlValues[1]);
}
 
function dataBound3(e) {
    this.value(ddlValues[2]);
}

This custom approach may cause another issue i.e. the dataBound event of the first DropDownList may be triggered before the ddlValues array is populated. If that is the cause, you could set it's AutoBind option to false and manually trigger the loading of the data at the end of the edit event.

I wish you a great day!

Regards,
Dimiter Madjarov
Telerik
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Aaron
Top achievements
Rank 1
answered on 31 Oct 2013, 05:46 PM
I took you approach here and it definitely is populating the drop down list how I would like for it to.  However, it is not updating the record when I select update because the Category and Subcategory properties of the object being passed back are not set.
0
Dimiter Madjarov
Telerik team
answered on 01 Nov 2013, 09:59 AM
Hello Aaron,

As stated in the previous post, the change event should also be triggered, so the model is aware that the value has been changed. After further testing I stated that a timeout would be required too. 

To demonstrate the complete approach I prepared a sample solution for you. You could find it attached to my post.

Regards,
Dimiter Madjarov
Telerik
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Aaron
Top achievements
Rank 1
answered on 01 Nov 2013, 04:16 PM
Dimiter,
This got me to where I needed to be.  Thank you.
0
Paige
Top achievements
Rank 1
answered on 02 Dec 2015, 08:12 PM

I'm having an issue with this solution.   

Once I append the .Name parameter to the textbox control on the popup template in order to be referenced for the copy function to work, that same textbox it is blank when I try to do a normal edit in the popup template. 

0
Dimiter Madjarov
Telerik team
answered on 03 Dec 2015, 09:24 AM

Hello Paige,

Using the Name() method in the editor should not be needed in this case. The name attribute will be automatically generated to be the same as the name of the edited property. Let me know if that is the case on your end too?

Regards,
Dimiter Madjarov
Telerik
 
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
 
0
Mark
Top achievements
Rank 1
answered on 22 Sep 2017, 06:10 PM

Hi Dimiter,

I am trying to implement this technique (you'd think that duplicate row would be a standard grid function as it's a common data entry need) and am stuck.

The popup gets executed but with no data.  My troubleshooting shows that the edit(e) function is not getting executed.  

My question is: what triggers the edit(e) function?

Also, what uses the customRowDataItem object?  

Thanks

0
Mark
Top achievements
Rank 1
answered on 25 Sep 2017, 08:58 PM

OK, to answer my noob questions: 

You have to add the event to the grid's event array for edit(e) to fire.

The edit(e) function is consuming customRowDataItem in the val() extension.  

This technique does work.

0
Insad
Top achievements
Rank 2
answered on 26 Sep 2017, 02:12 PM

Hi,

It looks like the solution above works but I have found some issues with it;

1 - the $(e.container).find("[name='ProductName']").val(customRowDataItem.ProductName); only works on text fields, not on special widgets like numericaltextbox, calender etc.

It would be nicer to take a more MVVM approach, so I use

e.model.set('ProductName', self.customRowDataItem.ProductName);

Which works for the numericals and dates as well.

2 - After pressing my Copy and Edit button (in the edit event function) I change the ProductName and to my surprise I can see it changing in the grid as well?? After pressing the Update button I see the changed ProductName 2 times in the grid!

3 - Pressing Edit on the second one with the duplicate name, followed by pressing Cancel changes the name in the grid back?

4 - Pressing Edit on the first one, followed by pressing Cancel removes the entire (new) record??

5 - I have added the ID to the grid and this also has strange values during the above points 3 and 4

 

To give a visual example look at this DoJo Sample: Copy and Edit record

 

Can u advise on what I am doing wrong here?

 

Regards,

Insad

0
Stefan
Telerik team
answered on 28 Sep 2017, 07:49 AM
Hello, Insad,

Thank you for the provided suggestions and the example.

I made a modification to the example in order to only shown the copy items as a copy instead of both items. This will keep the name of the original item. This will also resolve the issue listed in point 4.

As for point 3 ("Pressing Edit on the second one with the duplicate name, followed by pressing Cancel changes the name in the grid back"), this is expected and can be observed in all of our demos. This occurs because the item is not saved in the database and is without an ID. Then when the cancel button is clicked, this is calling cancel changes and it is reverting all unsaved changes for the dataItem.

Please inspect the modified example and advise if additional issues are observed:

http://dojo.telerik.com/AkanIR

Regards,
Stefan
Progress Telerik
Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Insad
Top achievements
Rank 2
answered on 28 Sep 2017, 01:59 PM

Hello Stefan,

Thanks for your answer, I saw what I did wrong.

I noticed that the data only is created and is not updated to the server, so I assume that is because the server is for demo purposes only and will not handle the updates to it.

But how I can project this to my real life app is not clear to me.

To make you understand what I am facing I will make it a bit clear what the idea is;

I have an app that has an RFQ system in it.

When the user comes to the RFQ page a grid is shown with the current RFQs. For this there is a service that provides only the information for the grid and the ID in the database.

The user can Add/Edit and Delete RFQs.

When adding a new RFQ a window pops up with a tabstrip and several pages with the RFQ information that can be entered. When the user presses Save the data is stored via a service in the database and the RFQ grid is refreshed. For this I do a dataSource.add({}) to add a new datarecord, init some values and copy it into the viewModel and show the window. In the onSave the data is copied from the viewModel into the dataSource vars and a dataSource.sync() is called. Note that it is not possible to bind the datasource to the window due to incompatible fields (don't ask ;)).

When the user presses edit on a RFQ the same window pops up with the data of the selected RFQ in it and the user can change the data and press Update to store the RFQ. For this I do a dataSource.read() to get the data from the server, copy it into the viewModel and show the window. In the onUpdate the data is copied from the viewModel into the dataSource vars and a dataSource.sync() is called.

The delete just calls a delete service that takes the ID and the RFQ is dropped in the back-end.

What I need to do next is adding the Copy and Edit functionality that creates a new RFQ and takes the data from the selected RFQ, changing some values and present it in the window as if it was an existing record, with the difference that there is a Save button that stores the new data and the Cancel button forgets the info.

What I came up with so far is that when the user presses the Copy and Edit button, a flag is set, the window pops up as if it was a new record, only with a difference: I read the data from the RFQ selected (dataSource.fetch()), change some values and call the dataSource.add(sourceRFQ). From here I continue with copying the (new) data to the viewModel and when the user presses Save the data is sync'd.

Is this something (in simple words) that should work or do I need to take a different path for this?

Any thoughts are welcomed.

 

Best regards,

 

Insad

 

0
Insad
Top achievements
Rank 2
answered on 28 Sep 2017, 02:47 PM

I forgot to mention the problem I encounter with my described solution.

When I do the sync(), after the Copy and Edit -> enter data -> press Save, the UPDATE of the dataSource is fired, but there is no CREATE done, so the server responds that you cannot update data that is not there (in simple words, the data is not created, so why is the update called?)

 

Best regards,

Insad

0
Konstantin Dikov
Telerik team
answered on 02 Oct 2017, 11:39 AM
Hi Insad,

The suggested approach by my colleague Stefan calls the Create correctly in the dojo examlpe. Could you please try to modify the example to replicate the issue and to better match your scenario? 

Meanwhile, please ensure that the "id" field is set in the dataSource`s schema.


Regards,
Konstantin Dikov
Progress Telerik
Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Mark
Top achievements
Rank 1
answered on 03 Oct 2017, 10:27 PM

I have found one small logic issue with this code; it happens with the definition of the isCustomAdd variable. 

In the case where you are not duplicating a row, the conditional:

if (isCustomAdd && e.model.isNew()) {..} will throw an exception, until isCustomAdd is defined.  

This will keep the Cancel and Update popup buttons from working properly.  My workaround is to capture the error, that fixes it, but if someone has a better idea for defining that variable, that would be great. 

try {
    if (isCustomAdd && e.model.isNew()) {
        isCustomAdd = false;
        e.model.set("Performance_ID", customRowDataItem.Performance_ID);
        e.model.set("Type", customRowDataItem.Type);
        e.model.set("Text", customRowDataItem.Text);
    }
} catch (err) { }

 

0
Stefan
Telerik team
answered on 04 Oct 2017, 08:18 AM
Hello, Mark,

Thank you for sharing this with the Kendo UI community. It is highly appreciated.

In general, the "isCustomAdd" property should be declared inside the ViewModel and this should not create a scenario where the isCustomAdd is undefined.

Regards,
Stefan
Progress Telerik
Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Insad
Top achievements
Rank 2
answered on 15 Nov 2017, 01:54 PM

Hi again,

 

sorry for the delay but I was put on a high prio project for a short moment.

 

I've been playing around and my problem comes down to a really elementary problem.

Look at the following an please tell me how I can get it to work:

var myViewModel = kendo.observable({
    className: 'myViewModel'
    , myDs: rfqsDataSource      // rfqsDataSource implements all CRUD operations
     
    ...
     
    //
    // Read and copy data
    //
    , doMyCopy: function (sourceId)
    {
        var self = this;
         
        self.myDs.read(sourceId)
                .then(function () {
                    // Copy dataSet
                    var dataSet = self.myDs.at(0);
 
                    // Cancel read flags?
                    self.myDs.cancelChanges();
 
                    // Invalidate the ID field
                    dataSet.set('ID', null);
                     
                    // Add new dataset, copying data from the saved dataset
                    var newDataSet = self.myDs.add(dataSet);
 
                    // Alter the other fields
                    newDataSet.set('Description', 'Brand new day!');
                     
                    // Sync (CREATEing) the new record
                    self.myDs.sync();
                });
    }
 
    ...
});

 

However, instead of calling the CREATE method of the datasource, the UPDATE method is called??

To me this indicates that it appears that the read flags are still active, disregarding the cancelChanges() and therefor the UPDATE method is called instead of the CREATE method?

Best regards,

 

Insad

0
Stefan
Telerik team
answered on 17 Nov 2017, 08:29 AM
Hello, Insad,

Could you please integrate this scenario in one of the Dojo, so we can inspect it?

I implemented a similar approach in one of the Dojos, but the Create was called as expected:

http://dojo.telerik.com/AkanIR/2

Please ensure that the null ID is set to the id property on the dataItem.

Regards,
Stefan
Progress Telerik
Try our brand new, jQuery-free Angular components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Insad
Top achievements
Rank 2
answered on 17 Nov 2017, 01:25 PM

I found that initializing the new dataset with the seperate entities, excluding the ID field, works okay in my situation.

Initializing with the entire dataset triggers the update to happen instead of the create.

 

My data model is somewhat more complex than the demo in the DoJo; I heve several nested items an  an array of nested items.

Possibly this, somehow, triggers the flag to call the update instead of the create.

 

For now I have fixed it by calling the add() something like

01.// Copy data read
02.var dataSet = self.dataSource.at(0);
03.// Clear read flags
04.self.dataSource.cancelChanges();
05. 
06.// Invalidate the ID field
07.dataSet.set('ID', null);
08. 
09.// Create new record with data in it
10.self.lastDataItem = self.dataSource.add({
11.                                    Description: 'Kopie van ' + dataSet.Description
12.                                    , FinishingTypeIds: dataSet.FinishingTypeIds
13.                                    , FinishingTypes: dataSet.FinishingTypes
14.                                    , DeliveryTypeID: dataSet.DeliveryTypeID
15.                                    , PackagingTypeID: dataSet.PackagingTypeID
16.                                    , PaperDeliveryTypeID: dataSet.PaperDeliveryTypeID
17.                                    , PrepackTypeID: dataSet.PrepackTypeID
18.                                    , ProductSizeID: dataSet.ProductSizeID
19.                                    , ProductTypeCategoryID: dataSet.ProductTypeCategoryID
20.                                    , ProductTypeID: dataSet.ProductTypeID
21.                                    , Quantity: dataSet.Quantity
22.                                    , Version: 0
23.                                    , Entrys: dataSet.Entrys
24.                                });                                             // Add new record
0
Stefan
Telerik team
answered on 20 Nov 2017, 11:27 AM
Hello,

I'm glad to hear that the desired result is achieved.

Please let a know if additional assistance is needed on this matter.

Regards,
Stefan
Progress Telerik
Try our brand new, jQuery-free Angular components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
Tags
Grid
Asked by
Shahar
Top achievements
Rank 1
Answers by
Dimiter Madjarov
Telerik team
Shahar
Top achievements
Rank 1
Aaron
Top achievements
Rank 1
Paige
Top achievements
Rank 1
Mark
Top achievements
Rank 1
Insad
Top achievements
Rank 2
Stefan
Telerik team
Konstantin Dikov
Telerik team
Share this question
or