1

I've got an irritating problem with data binding using ng-model and button.

The principle of operation of my site:

  1. My HTML site displays a list of projects (loaded from external .json file).
  2. Each row has a button named Edit which displays a modal containing some <input type="text" filled with relevant data about project (like project.name, project.date etc.)
  3. Initial value of input is equal to object data (text-input called Name will contain project.name etc.)
  4. Object is modified only if you click Save button and confirm the operation (confirm(sometext) is okay). Closing the modal, not clicking the button or pressing cancel on confirmation box should prevent data from being updated.
  5. Editing input (let's say that project.name is "Project2" and I modify it by adding 3 numbers resulting in "Project2137"), closing modal and opening it again should result in "Project2" text inside input (because object wasn't modified, only input)

So far I understand that single text input should look like this

<input type="text" id="editName" class="form-control" ng-model = "project.name">

Using ng-model means that they are binded. That's what I know. However editing input means that object is updated as soon as I enter some data. I tried to fiddle with ng-model-options but I didn't find any possible solutions.

I tried to do it programmatically as well using

<input type="text" id="editName" class="form-control" value = {{project.name}}>
....
<button type="button" class="btn pull-right btn-primary btn-md" ng-click="edit(project)" data-dismiss="modal" >Save</button>

And function:

$rootScope.edit = function(project)
    {
        if(confirm("Are you sure to save changes?"))
            {
                 project.name = angular.element(document.getElementById('editName')).val();
             //  ...and so on with other properties

This solution is kinda close to what I wanted to achieve (object is updated only on confirm), but I faced another problem: input loads data from object only once at the beginning instead of each time the modal is opened which is against rule #5

Is there any way to fix this using either ng-model bind or custom function? Or maybe there is some other, easier way?

--EDIT--

Here I don't have any problem with saving the data using a button, everything works well and clicking Save is reflected in a projects list. (well until I hit a F5 key). The problem is that input text is not properly binded to project and that's what I want to fix.

  1. Sample data (pseudocode)

    project1.name = "Proj1" project2.name = "Proj2"

  2. I click an Edit button on row #1

  3. Text input displays "Proj1". Everything is fine.
  4. I change input by adding some random characters like "Proj1pezxde1"
  5. Text input is now "Proj1pezxde1"
  6. I do not click Save button.
  7. I close the modal.
  8. Project summary still displays "Proj1". Okay.
  9. I click an edit button on first row

10. Text input is "Proj1pezxde1" even though I didn't modify an object.

Text input should read data from object again (each time I open this modal) and thus display "Proj1"

That's the problem I want to fix. Sorry for being a little bit inaccurate.

Karatte
  • 25
  • 3
  • 9
  • Just bind the input to something other than project.name, and do project.name = theEditedValue when the user clicks Save. – JB Nizet Jan 07 '17 at 19:22
  • your question is needs to be updated with the actual edit code? – Aravind Jan 07 '17 at 19:23
  • `"Just bind the input to something other than project.name, and do project.name = theEditedValue when the user clicks Save"` @JBNizet But then I would need a some kind of function that is called each time the modal is opened to reset the content of input text and bind it to project.name again. – Karatte Jan 07 '17 at 19:26
  • 1
    Yes, but how is that difficult? `theEditedValue = project.name`. – JB Nizet Jan 07 '17 at 19:30
  • Looks promising but how do I display initial value with input text? Binding it to `editedValue` means that I can't use `value = project.name`and I can't do something like `editedValue = project.name` inside `edit(project)` because this function is called after clicking a button, not each time the modal is displayed – Karatte Jan 07 '17 at 19:33
  • 1
    Well, do it in the function that opens the modal, before opening it. – JB Nizet Jan 07 '17 at 19:44
  • I tried using `$( "#modal" ).on('shown', function(){ //something });` but this function doesn't have access to the `project` from `ng-repeat="project in projects` – Karatte Jan 07 '17 at 19:59
  • Ah, so you're using jquery? Why don't you use angular-ui-bootstrap: your would have a much more angular-friendly API to open your modals (and much more). – JB Nizet Jan 07 '17 at 20:17

4 Answers4

1

You can create a copy of the project object in modal controller and use this object to bind with the input element of the modal

$scope.copyProj = angular.copy($scope.project);

Assign the copy object properties to project only when save is clicked.

Deep
  • 9,594
  • 2
  • 21
  • 33
0

As per my understanding after reading the provided descriptions, you have a list of projects, which is being used as in an repeater and you want to bind each projects data to a Text box and a Button.

Have you tried initializing your Projects object following way?

$scope.projects = [
        { 'name': 'proj1', 'id': '1' },
        { 'name': 'proj2', 'id': '2' }
    ];

Then you can do something like below to show your data

<div ng-repeat="project in projects">
    <div>
       <input type="text" class="form-control" ng-model = "project.name">
       <button type="button" class="btn pull-right btn-primary btn-md" ng-click="edit(project)" data-dismiss="modal" >Save</button>
    </div>
</div>
UBK
  • 232
  • 4
  • 15
  • List of projects is displayed using `ng-repeat` but that's not an issue here since loading data from .json and displaying it works pretty well. Using your solution will provide two ways to update object data: by clicking a button or writing anything into input and losing focus on input (if I'm not mistaken) – Karatte Jan 07 '17 at 19:18
  • since you are using jquery modal, you need to pass the project object for which the modal is being opened before calling the open function and when you override the show and inside the on shown function you need to set the input type text elements value with the project.name. Please check [this thread](http://stackoverflow.com/questions/394491/passing-data-to-a-jquery-ui-dialog) to understand how to use data attribute and use it in the jquery modal. Or, better way is to use [angular ui bootstrap modal](https://angular-ui.github.io/bootstrap/#/modal). – UBK Jan 09 '17 at 18:45
  • 1
    I have tried something which you can check. It is based on the angular.copy which others have also suggested and it uses Angular UI bootstrap modal. [Here is the Plunker](https://plnkr.co/edit/L58KiSIHZqTwe3CVBQ3Q?p=preview) – UBK Jan 09 '17 at 19:25
0

The simplest way to do this in my opinion is using a second object that is a copy of the project, and after confirmation applying the changes to the original project object.

For example, a simple "pseudo code" of a controller:

function MyCtrl($scope) {
    $scope.projects = [...];
    $scope.currentProject = null;

    $scope.edit = function(project) {
        $scope.currentProject = angular.copy(project); // This will create a copy so the changes in $scope.currentProject will not reflect.
        // Open dialog with input bound to $scope.currentProject
        if (confirm) {
            // Assign all properties from currentProject to project
            angular.extend(project, $scope.currentProject);
        }
    }
}
Ron Dadon
  • 2,666
  • 1
  • 13
  • 27
0

So , as I understand from your question , you need to update the project data only if it is saved. To do that you can maintain a copy of the actual object which get updated only it is saved like below :

Here we are using angular.copy(), which does a deep copy of the source object.

$scope.original = {name : "xyz"};
$scope.project = angular.copy(original);

//Call this when the user confirms to save , here we are replacing the 
//original copy with the latest object that needs to be saved.

$scope.save = function () {
  $scope.original = angular.copy($scope.project);
}

//Call this when closing the modal or clicking cancel or when losing     
//focus, this will reset the changes to the original copy.

$scope.reset = function () {
  $scope.project = angular.copy(original);
}
Supradeep
  • 3,246
  • 1
  • 14
  • 28
  • Updating the data using either function or modal is not a problem, it works pretty well. I need to reset the contents of input text each time the modal is opened. – Karatte Jan 07 '17 at 19:30
  • Yeah, okay, by resetting, if you mean, emptying the input, just assign an empty object to the model object when modal is opened and if you are using form validation, set the form to pristine state. – Supradeep Jan 07 '17 at 19:32
  • Not emptying but displaying current object value like `name`. I may have used word "reset" incorrectly. – Karatte Jan 07 '17 at 19:35
  • Then in that case, you can do as i said in my answer above – Supradeep Jan 07 '17 at 19:45