Input field blurs when inline editing in ng-repeat - javascript

I am trying to do inline editing on a table of data (See the plunkr)
<table class="table table-bordered">
<tr ng-repeat="data in dataset" >
<td ng-repeat="(key, value) in data" >
<div class="key-block">
<strong >{{key}}</strong>
</div>
<div class="val-block" inline-edit="data[key]" on-save="updateTodo(value)" on-cancel="cancelEdit(value)">
<input type="text" on-enter="save()" on-esc="cancel()" ng-model="model" ng-show="editMode">
<button ng-click="cancel()" ng-show="editMode">cancel</button>
<button ng-click="save()" ng-show="editMode">save</button>
<span ng-mouseenter="showEdit = true" ng-mouseleave="showEdit = false">
<span ng-hide="editMode" ng-click="edit()">{{model}}</span>
<a ng-show="showEdit" ng-click="edit()">edit</a>
</span>
</div>
</td>
</tr>
I can see in many places that we have to use a . in ng-model inside ng-repeat to avoid the scope issue. As I dont know the key already I am doing like data[key] for the model.
The input field blurs after I enter a single character.

The behavior you described is normal. If you look closely you will see that both the input value and the directive are bound to the same object i.e data[key]. When you change the value of the text input the model get updated ultimately triggering a refresh of the directive and you are back to the "list" view.
One easy solution to fix this is to use an intermediate variable between the directive and the input value and update the model only when the save button is clicked. Something like that :
//Directive
scope.newValue = null;
scope.edit = function() {
scope.editMode = true;
scope.newValue = scope.model;
$timeout(function() {
elm.find('input')[0].focus();
}, 0, false);
};
//Template
<input type="text" on-enter="save()" on-esc="cancel()" ng-model="newValue" ng-show="editMode">
You can see a modified plunker here.

Related

Use data of ng-repeat

I can display a table of users from my database on my web application using ng-repeat. I can add and delete directly from the web application but now I'm trying to update informations about those users. I would like to click on a button on the row of the user (each rows display informations for one user, 1 row = 1 user) when I clicked on this button I would like to make a form with input fields filled with actual values.
I can only get informations about my users by clicking on this button but I don't know how to "send" informations to this form.
My table of users :
<tr ng-repeat="user in users">
...
</tr>
But something like this is not working at all :
<form>
<label>Name</label>
<input type="text" id="up_name" ng-model="user.name"/>
<label>Age</label>
<input type="text" id="up_age" ng-model="user.age"/>
...
</form>
If you are using this synthax, your form have to be in your ngRepeat. It is not the best way to do it, as you will have a form for user.
I would suggest you something different. In your controller, set an edit() function:
$scope.edit = function(user) {
$scope.editedUser = user;
}
When clicking a user in your table, call the edit() function:
<tr ng-repeat="user in users" ng-click="edit(user)">
...
</tr>
You can now edit in the form the editedUser object:
<form ng-if="editedUser">
<label>Name</label>
<input type="text" id="up_name" ng-model="editedUser.name"/>
<label>Age</label>
<input type="text" id="up_age" ng-model="editedUser.age"/>
...
</form>
What you can do is the following :
<tr ng-repeat="user in users" ng-init="selectedUser = null">
<td> {{ user.name }}</td>... <td ng-click="selectedUser = user"> edit </td>
</tr>
<div ng-if="selectedUser">
<form>
<label>Name</label>
<input type="text" id="up_name" ng-model="user.name"/>
<label>Age</label>
<input type="text" id="up_age" ng-model="user.age"/>
...
</form>
</div>
I think that you are talking about a sort of master-detail ui pattern.
Here it is a public plunker that will solve that kind of problem
Insert the both input and span HTML directive in <td> and use ng-switch : ng-switch-when & ng-switch-default to display only one field.
<td class="sorting_1" ng-switch="mode">
<input type="text" class="form-control small" ng-switch-when="edit" id="edit" ng-model="edit.username">
<span ng-switch-default id="item.username">{{item.username}}</span>
</td>
You need to write a custom directive for it.Reason for writing custom directive is the value of ng-switch will associate with individual instead of global.
In the last <td> tag add : which will contain edit and update buttons:
<td ng-switch="mode">
<button class="btn btn-success btn-xs edit" ng-switch-when="edit" ng-
click="updateItem(edit, index)">
<i class="fa fa-floppy-o"></i>
</button>
<button class="btn btn-success btn-xs" ng-switch-default ng-
click="editItem(item)">
<i class="fa fa-pencil-square-o "></i>
</button>
</td>
JS
$scope.editItem = function(oldData) {
$scope.edit = angular.copy(oldData);
$scope.mode = "edit";
}
$scope.updateItem = function(data, index) {
$scope.$emit('update', data, index);
$scope.mode = "default";
}
The value of input-box will be updated using
$scope.edit = angular.copy(oldData); into editItem() function.
With the use of event emitters modify the main object.
$scope.$on('update', function(event, data, index) {
angular.copy(data, $scope.items[index]);
});
use angular.copy to deep clone value instead of passing value as a reference.
Check http://codepen.io/sumitridhal/pen/YVPQdW

is there any way to bind ng-model to multiple input fields uniquely inside a directive?

In my Project i Got a Issue like.I need to bind the user hobbies in the text field.if the user comes with a single hobby he can directly enter the hobby that he has. but when he had multiple then he had to click add multiple hobbies button.that working fine when i am displaying input fields dynamically using directives.but the issue is the value that coming from ng-model for that input field is binding to all input fields.
Here is my code.
Thanks in advance!
these are the images
this is how i am getting
this is what i need
In HTML
<div>
<div id="showHobbyfield"></div>
<input type="number" class="form-control" placeholder="ADD HOBBIES"
ng-click="addHoby()">
</div>
In controller
$scope.addHoby= function(){
var compiledeHTML = $compile("<div my-hobby></div>")($scope);
$("#showHobbyfield").append(compiledeHTML);
};
$scope.addUser = function(){
$scope.Users= [];
var obj = {
userhobby : $scope.user.morehobies
};
$scope.Users.push(obj);
menuStorage.put($scope.Users);
//menustorage is service to store user in localStorage.
In directive
'use strict';
angular.module('myApp')
.directive('myHobby', function() {
return {
scope : false,
templateUrl: 'views/my-hobby.html'
};
});
this is template: my-hobby.html
<div class="input-group">
<input type="text" ng-model="user.morehobies" class="form-control" placeceholder="type your hobbies here">
<div class="close-icon">
<span class="glyphicon glyphicon-remove" style="padding-left: 6px;"> </span>
</div>
</div>
For this i would suggest some other way if its ok with you.
If your hobbies is coming in array, like
user.morehobies = ['Reading', 'Writing']
or create array for storing hobbies.
then inside directive you can pass that object in directive.
I will use ng-repeat inside directive.
<div class="input-group" ng-repeat="h in hobies">
<input type="text" ng-model="h" class="form-control" placeceholder="type your hobbies here">
<div class="close-icon">
<span class="glyphicon glyphicon-remove" style="padding-left: 6px;"> </span>
</div>
</div>
so whenever user clicks on "Add hobbies" then we can add empty string in hobbies object in directive.
and whenever user clicks on remove you can remove that item from array.

Validate inputs in table row with angular

I use this lib to create table on page.
Every row in this table can be edited. So if editMode is on, I show inputs at each column of row. Some of this inputs is required. I want to show red text "Required" or just red border if required field is empty.
But problem - it's simple when I use form. But in my case I can't use forms.
This answer isn't good for me, cause every row must have unique form-name for correct validation.
Example : https://jsfiddle.net/r8d1uq0L/147/
<div ng-repeat="user in users">
<div name="myform-{{user.name}}" ng-form>
<input type="text" ng-model='user.name' required name="field"/>
<span class="error" ng-show="myform.field.$error.required">Too long!</span>
</div>
</div>
<div>
<button ng-click="add()">
Add
</button>
</div>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.users = [{name:"1"}, {name:"2"}];
$scope.add = function(){
$scope.users.push({});
}
});
No need to create a dynamic form name, as ng-repeat does create new child scope on each iteration while drawing template on browser. So just keep name as name="innerForm" and use innerForm.field.$error.required to have validation over it.
<div ng-repeat="user in users">
<div name="innerForm" ng-form>
<input type="text" ng-model='user.name' required name="field"/>
{{myform[$index]}}
<span class="error" ng-show="innerForm.field.$error.required">Too long!</span>
</div>
</div>
Forked Fiddle

how can I validate multiple ng-repeated ng-form

I have a variable form list and want to display validation error messages outside of the list elements.
consider this template:
<form name="gForm">
<table>
<tr ng-repeat="invoice in invoices" ng-form="invoiceForm">
<td><input name="amount" type="number" max="200" /></td>
</tr>
</table>
<div ng-show="gForm.invoiceForm.amount.$invalid">you must the money</div>
<button ng-click="addInvoice()">add invoice</button>
</form>
the validation error would only be displayed when the last ng-repeat is invalid. Put another way, gForm.invoiceForm points to the lastly created form in ng-repeat.
I've seen other questions related to this problem but they only suggest repeating the validation messages inside the ng-repeat. I need the message to be outside and displayed once only.
The way you have it, gForm.invoiceForm does refer to the last <tr> in ng-repeat.
If you want to display the error when any of the amounts is invalid, you can use gForm.$invalid. In fact there is no need to use ng-form="invoiceForm" unless there are more requirements not evident from the current question's code.
Another problem is that, in order for Angular to recognize the input and apply its directive (and its magic consequently), the ng-model directive is required as well.
Adding the ng-model directive and changing the condition to gForm.$invalid solves the problem:
...
<tr ng-repeat="invoice in invoices">
<td><input name="amount" type="number" max="200"
ng-model="invoice.amount" /></td>
</tr>
...
<div ng-show="gForm.$invalid">you must the money</div>
...
See, also, this short demo.
Are you looking for something like this? Yes you need to use ng-model, but also you need a unique name:
<div ng-app="pageModule"
ng-controller="parentCtrl">
<form name="gForm">
<table>
<tr ng-repeat="invoice in invoices" ng-form="invoiceForm">
<td>{{invoice.name}}: <input name="invoice.name" required type="number" max="200" ng-model="invoice.amount" /></th>
</tr>
</table>
<div ng-show="gForm.$invalid && showError">you must the money</div>
<button ng-click="addInvoice()">add invoice</button>
</form>
</div>
<script>
var pageModule = angular.module('pageModule',[])
.controller('parentCtrl',function($scope) {
$scope.invoices = [
{ name : 'ford' },
{ name : 'chevy' },
{ name : 'honda' },
]
$scope.showError = false;
$scope.addInvoice = function() {
$scope.showError = true;
console.log('add invoice');
}
})
</script>

Angular: on click, add input fields text to array

First off, starting out with this so a bit confused. I have a simple table, where the contents are pulled in from a database and injected into the DOM with Angular.
The table consists of an option, and its value. I would like the user to be able to edit the values, and then click a "save" button, where I make a http call to my back-end with the details.
I've got the basics working, clicking a button and the input fields replace the table cell content:
Clicking "Edit":
When clicking "Cancel", it reverts back - so this is all working.
So this bit I can't work out, is when I press update, I want to create an array (json?), where I can send somewhere using http.
I'd need something where each object in the array/json contains the "option" and the "value", so I can match these in the database.
My HTML:
<div ng-hide="loading" class="table-responsive">
<table class="table table-striped table-compact table-bordered">
<thead>
<tr>
<th>Option</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="setting in settings">
<td><< setting.option >> <i class="fa fa-question pull-right pointer" tooltip="<< setting.description >>" ></i></td>
<td ng-switch="status">
<input ng-switch-when="editable" type="text" class="form-control" value="<< setting.value >>" />
<span ng-switch-when="uneditable"><< setting.value >></span>
</td>
</tr>
</tbody>
</table>
</div>
<div ng-hide="loading" ng-switch="status">
<button ng-switch-when="uneditable" ng-click="edit()" class="btn btn-sm btn-info">Edit</button>
<button ng-switch-when="editable" ng-click="save()" class="btn btn-sm btn-success">Update</button>
<button ng-switch-when="editable" ng-click="cancel()" class="btn btn-sm btn-danger">Cancel</button>
</div>
And finally my ng controller:
app.controller('appSettingsController', function($scope, ApplicationSettings) {
$scope.settings = {};
$scope.loading = true;
$scope.status = 'uneditable';
// Grab data for table
ApplicationSettings.get()
.success(function(data) {
$scope.settings = data;
$scope.loading = false;
});
$scope.edit = function() {
$scope.status = 'editable';
$scope.updates = {};
};
$scope.cancel = function() {
$scope.status = 'uneditable';
};
$scope.save = function() {
// Construct Array/JSON of inputs
};
});
Anyone got any ideas? I have a feeling it's something to do with using ng-model?
Inside your table, the second column has the following input element tag when in edit mode:
<input ng-switch-when="editable" type="text" class="form-control"
value="<< setting.value >>" />
Firstly - I think the value attribute should be {{setting.value}} and not << setting.value >> - I can't imagine the latter giving the value in AngularJS.
Now, for your requirements. Instead of using the value attribute, you can use the ng-model attribute as you guessed.
With the ng-model attribute in place, the input should now be:
<input ng-switch-when="editable" type="text" class="form-control"
ng-model="setting.value" />
ng-model will take care of displaying the value for that input as well as due to two way data binding, the value entered into the input will be stored back into setting.value.
What this means is that, AngularJS automatically will update $scope.settings when you input something in the text box. You don't have to write any additional code to ensure that the value is put back. It's like the Update button was already clicked and data was saved - only that it wasn't clicked but data was still saved
The only downside to this is that when you click on cancel, the old values are no longer available (since the moment you type something into the text, the values are updated). You can store initial values of $scope.settings into another variable before switching to edit mode. That way, when you click cancel, you are still left with old values.

Categories