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.
Related
I'm rendering the contents of an array using ng-repeat like this-
<tr ng-repeat="option in eventCtrl.questionDetail.options track by $index">
<th scope="row">Option {{$index + 1}}</th>
<td>
<input type="text" ng-model="option" ng-change="eventCtrl.newlyAddedOptions[$index] = option" style="width:100%;">
</td>
<td>
<button type="button" ng-confirm-click="Are you sure to delete?" confirmed-click="eventCtrl.removeOption($index)" class="btn btn-light btn-sm">Delete</button>
</td>
</tr>
<button type="button" class="btn btn-dark" ng-click="eventCtrl.addOption()" id="addNewOption">+ Add New Answer Option</button>
On click of the button I'm insering an empty string into the questionDetail.options array so that I get an empty input field to insert my data.Controller functions looks like this-
myApp.controller('eventController',function(){
let dash=this;
dash.newOption ='';
//store all newoptions to this array before finally updating the
//question
dash.newlyAddedOptions = [];
dash.addOption = () =>{
dash.questionDetail.options.push(dash.newOption);
});
//add new options
dash.updateTheQuestion = () =>{
//add the newly added options in the questionDetail if any which will be finally updated
apiService.updatequestion(dash.params.qid,dash.questionDetail).then(function successCallBack(response) {
$rootScope.$broadcast("loader_hide");
alert(response.data.message);
});
}
Now when I insert data into the field and try to add another option the previously inserted field becomes blank beacuse the questionDetail.options array get rerendered again.However I've used ng-change to collect data and store it into the newlyAddedOptions array.
How do I change the empty strings pushed into array with the value that is retrieved with ng-model="option" so that I could directly push those into questionDetal.options array.
I know this good be done easily and I'm missing something.
Thank You in advance.
Edit:I was pushing an empty string because I wanted a blank input on clicking the add option where I can insert new option.This is mainly the edit question view where user can add an option or delete an option with the options that are coming from the database.
Plunkr-https://plnkr.co/edit/SLfy8qaz8LoHurwpVmw6?p=catalogue
Try this :
<tr ng-repeat="opt in eventCtrl.questionDetail.options track by $index">
<th scope="row">Option {{$index + 1}}</th>
<td>
<input type="text" ng-model="newlyAddedOptions[$index]" style="width:100%;">
</td>
<td>
<button type="button" ng-confirm-click="Are you sure to delete?" confirmed-click="eventCtrl.removeOption($index)" class="btn btn-light btn-sm">Delete</button>
</td>
</tr>
<button type="button" class="btn btn-dark" ng-click="eventCtrl.addOption()" id="addNewOption">+ Add New Answer Option</button>
https://codepen.io/supravi96/pen/bMmpOQ?editors=1010
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
I need to call the controller deleteMember so to when the user clicks on a button, the member is deleted.
//deleting a member
Members.controller('deleteMember',['$scope','$http',function($scope, $http){
$scope.deleteMember = function(member){
$scope.deleteMember="";
console.log(member);
var deleteMember=confirm("Sure you want to delete?");
if(deleteMember){
$http.post('PHP/deleteMember.php',member).success(
function(data){
console.log(data);
if (data){
console.log("Deletion successful"); //delete worked
}else{
console.log("Deletion not successful"); //delete did not work
}
});
};
};
}]);
HTML code:
<div class="col-md-2">
<td><button type="button" class="btn btn-warning">Delete</button><td> <!--on button click, the member will be deleted-->
</div>
Is there a way that I can write the name of the controller using HTML?
Thanks for the help :)
You could add the ng-click attribute to the button:
<button type="button" ng-click="deleteMember(member)" class="btn btn-warning">Delete</button>
In this example I assume that the member variable that is passed to the deleteMember method is already in the scope of this button. This would be the case if this button is rendered inside an ng-repeat directive.
For example:
<tr ng-repeat="member in members">
...
<td>
<button type="button" ng-click="deleteMember(member)" class="btn btn-warning">
Delete
</button>
<td>
</tr>
Also you should probably not shooting yourself into the foot by replacing the deleteMember function with a string because the next time you want to call this method it simply won't work:
$scope.deleteMember = "";
You can simply just call the function deleteMember using ng-click and make sure you pass in the member you want to delete as an argument. You are passing a member in your function as an argument in the controller, so one must also be passed via the HTML. You haven't shown it in your code but I assume you are using ng-repeat to do this.
<div class="col-md-2">
<td><button type="button" class="btn btn-warning" ng-click="deleteMember(member)">Delete</button><td> <!--on button click, the member will be deleted-->
</div>
You mention calling the controller in the HTML. If you mean your page isn't initiated with the 'deleteMember' controller then you can wrap that block of code using ng-controller to give you access to the deleteMember.
<div class="col-md-2" ng-controller="deleteMember">
<td><button type="button" class="btn btn-warning" ng-click="deleteMember(member)">Delete</button><td> <!--on button click, the member will be deleted-->
</div>
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.
I have a table which is populated with javascript, fetching data via ajax. I have a <tr> template which I clone and set the values within to the data retrieved via the ajax request. I'm using Twitter bootstrap's popovers, so that users can click on a cell, which pops the popover with an input/select etc prompting the user to change the cell value. The problem occurs when I am setting the value of those inputs. When I am building each row, I set the value of the input in the popover template with .val('xxx'), but when I log it/click the cell and view the popover, nothing is changed; conversely, if I set it with .attr('value', 'xxx'), it works just fine.. why would .val('xxx') not work?
Here's what the meat of it looks like..
Row template:
....
<td>
<div class="last-name popover-toggle">
<span class="vlabel">----</span>
<div class="popout">
<input type="text" name="last_name" value=""/>
<div class="popout-button-container">
<button type="button" class="btn btn-primary btn-small cancel-field">Cancel</button>
<button data-url="{{ url('edit_lead.json') }}" type="button" class="btn btn-primary btn-small save-field">Save</button>
</div>
</div>
</div>
</td>
....
Setting input value:
...
if (data['last_name']) {
$nRow.find('.last-name input[name=last_name]').val(data['last_name']);//.attr('value', data['last_name']);
$nRow.find('.last-name .vlabel').text(data['last_name']);
}
registerPopover($nRow.find('.last-name'), 'Last Name');
....
Register popover:
function registerPopover(el, title) {
var options = {
animation: false,
content: el.find('.popout').html(),
html: true,
trigger: 'manual'
};
if (typeof(title) != 'undefined') {
options['title'] = title;
}
el.popover(options);
}
Not sure what is your actual problem, question is not very obvious to me but there is a difference between attr and val. The attr method sets/change the attribute of an input/element in the source and you may see it using browser's inspection tool, on the other hand, val method sets/update the rendered property which is in the memory but it doesn't effect the original source/HTML in the browser's source code. For example, if you do something like this
HTML:
<input type = "text" name="txt" id = "txt" value = "Hello!" />
JS:
$('#txt').val('World!').clone().insertAfter($('#txt'));
And check the source code, then you'll see there is two input elements and both have value attribute Hello! but on the screen both have world!. Check this detailed answer.