Use data of ng-repeat - javascript

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

Related

Most efficient way to bind data in a table into a JavaScript array?

I have a table that is generated from server side on a partial and rendered using ajax. Because of this I lose binding.
So my only option is to map everything to a JavaScript array and post it.
Is there a way to map everything using jQuery without having to iterate every row then every column and look for each input one by one?
Are there any other options? Perhaps I have to use something like Vue?
Here is my partial:
#model Project.Areas.Robotics.Models.ViewModels.DefectRecordViewModel
<form method="post">
<card>
<card-header icon="fas fa-thumbs-down" title="Peças NOK">
</card-header>
<card-body>
<div class="form-row">
<div class="form-group col-md-6">
<h4>
<span class="">
<b><i class=""></i>Registo</b> #Model.ProductionRecordId
</span>
</h4>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label asp-for="References"></label>
<select id="references-nok" asp-items="Model.References" class="custom-select custom-select-sm" onchange="selectReferences(this)">
<option value="">--</option>
</select>
</div>
</div>
<div class="table-responsive">
<table id="tblDefects" class="table table-sm" style="width:100%;">
<thead class="bg-olive">
<tr>
<th>#Html.DisplayNameFor(model => model.DefectCodes[0].DefectCodeId)</th>
<th>#Html.DisplayNameFor(model => model.DefectCodes[0].Quantidade)</th>
<th>#Html.DisplayNameFor(model => model.DefectCodes[0].Detalhes)</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.DefectCodes.Count(); i++)
{
<tr>
<td>
#Html.DisplayFor(model => model.DefectCodes[i].DefectCode)
<input type="hidden" asp-for="DefectCodes[i].DefectCodeId" />
<input type="hidden" class="reference-holder" asp-for="DefectCodes[i].ReferenceId" />
<input type="hidden" asp-for="DefectCodes[i].DefectCodeId" />
</td>
<td>
<input asp-for="DefectCodes[i].Quantidade" class="form-control form-control-sm" />
<span asp-validation-for="DefectCodes[i].Quantidade" class="text-danger"></span>
</td>
<td>
<input asp-for="DefectCodes[i].Detalhes" class="form-control form-control-sm" />
<span asp-validation-for="DefectCodes[i].Detalhes" class="text-danger"></span>
</td>
</tr>
}
</tbody>
</table>
</div>
</card-body>
<card-footer>
<div class="form-row">
<div class="form-group col-md-6">
<button type="button" data-url="#Url.Page("Index","AddDefectCode")" onclick="addDefectRecord(this)" class="btn btn-success btn-sm"><i class="fas fa-plus"></i> Adicionar</button>
<button type="button" onclick="hideDetails()" class="btn btn-danger btn-sm"><i class="fas fa-times"></i> Cancelar</button>
</div>
</div>
</card-footer>
</card>
</form>
I am unable to understand clearly what is Your problem but if You have large number of records and You don't want to load complete at once or want to reload your table without reload or all functionality like paging searching and filter you can use data table plugin
with server side record manipulation if this you are question you can reply this post so i can give you more example or i will give you some example .for your further understanding .
other wise you can elaborate your question with some screen shot so i can understand your roots of problem and give the best possible answers .
Is there a way to map everything using jQuery without having to iterate every row then every column and look for each input one by one?
Since you've configured all the <input/> with well-formed names, you can build a FormData directly.
var formdata = new FormData(someFormElement);
For example, add a submitDefectCode function to send an ajax request as below:
<script>
function submitDefectCode(element){ // the element is the `<button/>` that is clicked
var url = element.getAttribute("data-url");
var form = document.querySelector("form"); // get the `<form>` element
var formdata = new FormData(form);
$.ajax({
method:"post",
url: url,
contentType: false,
processData: false,
data: formdata,
success:function(resp){
console.log(resp); // now you get the response
}
});
}
</script>
And trigger this function when clicking the button:
<button type="button" data-url="#Url.Action("Index","AddDefectCode")"
onclick="event.preventDefault(); submitDefectCode(this);"
class="btn btn-success btn-sm">
<i class="fas fa-plus"></i> Adicionar
</button>
Finally, make your server side code to receive the form data with a [FromForm] attribute:
[HttpPost("/AddDefectCode")] // change this route as you need
public IActionResult TestPost([FromForm]DefectRecordViewModel model)
{
// ... now you get the model automatically
return Json(model);
}
Demo:
Here's a demo screenshot when posting data with multiple table rows:
In case you want to build a javascript array manually, you can use the FormData.entries():
var array= [];
for(var pair of formData.entries()) {
array.push({key: pair[0], value:pair[1]});
}
I used ajax unobtrusive to submit the form on the partial. The model is automatically binded on serverside, no need to do anything manually.

how to get previous state on button click in asp.mvc

i have a view in which i want to show textbox and button to save and edit the database table if in the table id comment is null i want to show textbox to enter the comment and if comment is there in the table then show the comment, but when user clicks the second(edit) button i want that textbox and button should reappear in that place, i put an if condition to check if my table column is null then show textbox and button to enter data into that column and i have second button to get back the textbox and buttons with the comment text.
My code is here
#if (item.Comments == null)
{
<div class="form-inline comments">
<textarea class="form-control commentText" placeholder="Enter Your Comment"></textarea>
<button type="submit" data-cid="#item.RoomId" class="btn btn-primary save"><i class="fas fa-save h3"></i></button>
<button type="submit" data-eid="#item.RoomId" class="btn btn-secondary edit"><i class="fas fa-edit h3"></i></button>
</div>
}
else
{
<div class="commentText">
#Html.DisplayFor(modelItem => item.Comments)
</div>
}
$('.save').click(function () {
var cmt = $('.commentText').val();
$.post("#Url.Action("AddComment", "ReceptionHk")", { id: $(this).data("id"), newComment: cmt });
});
$('.edit').click(function () {
$('.comments').show();
$('.commentText').hide();
});
My Controller Method
public void AddComment(int id, string newComment)
{
var roomcmt = db.SingleOrDefault<Room>(id);
if (ModelState.IsValid)
{
roomcmt.Comments = newComment;
var r = db.Update(roomcmt);
}
}
How to show the textbox and two button again when edit button is clicked
Please help me with this
Change your code in the following way:
A: Remove the line #if (item.Comments == null) and make the view display the creation section by default.
B: For creating/editing the comments have your code something like this:
<div class="form-inline comments">
<textarea class="form-control commentText" placeholder="Enter Your Comment"></textarea>
<button type="submit" data-cid="#item.RoomId" class="btn btn-primary save"><i class="fas fa-save h3"></i></button>
</div>
C: And then add a scope validation on the list as:
#if(item.Comments!=null)
{
<display your list here>
}
For displaying the comments, instead of statically displaying the comments use a table and do a foreach loop on the comments in the table to create rows with 2 columns in it as Comment,Edit.
Ex:
<table>
#foreach(var comment in item.Comments)
{
<tr>
<td>#comment.Text</td>
<td><button data-id="#comment.RoomId">Edit</button></td>
</tr>
}
</table>
and on the click of the Edit button call a javascript function that will call the retrieval of that comment and populate your editing fields after which you can later use it to update.
Your view will look something like this:
<div class="form-inline comments">
<textarea class="form-control commentText" placeholder="Enter Your Comment"></textarea>
<button type="submit" data-cid="#item.RoomId" class="btn btn-primary save"><i class="fas fa-save h3"></i></button>
</div>
#if(item.Comments!=null)
{
<table>
#foreach(var comment in item.Comments)
{
<tr>
<td>#comment.Text</td>
<td><button data-id="#comment.RoomId">Edit</button></td>
</tr>
}
</table>
}
Rest: implement your JS function to retrieve and create the comments.

Using ng-model to insert data into arrays

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

Show specific index input and hide others

I am stuck in a problem.
i have added a label and input in ng-repeat .when the user clicks the edit i want to show the input and hide the label and its working fine.but when the user clicks new button,it shows the new input but its not working.
Html
<tr ng-repeat="personalDetail in personalDetails">
<td> <label ng-if="personalDetail.Sname!=''" for="settings" > {{personalDetail.Sname}}</label>
<input ng-hide="$index" type="text" ng-model="personalDetail.Sname" />
</td>
<td><label data-val="{{personalDetail.Settings}}" for="desc" >{{personalDetail.Settings}}</label>
<input Style="display:none" data-val="{{personalDetail.Settings}}" ng-model="personalDetail.Settings" type="text" value="{{personalDetail.Settings}}" />
</td>
<td>
<span style="cursor: pointer" ng-click="Edit(personalDetail.Settings,$index)" class="glyphicon glyphicon-edit"></span>
<span style="cursor: pointer; padding-left:10px;" ng-click="Remove($index)" class ="glyphicon glyphicon-trash" ></span>
</td>
</tr>
Here is the angular
$scope.addNew = function (personalDetails) {
$scope.personalDetails.push({
'Sname': "",
'Settings': "",
});
I created a JSFiddle for this.
try this JSFiddle
JS:
var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
$scope.personalDetails = [ {'Sname': "firsName",
'Settings': "firstSettings"},
{'Sname': "secondName",
'Settings': "secondSettings"},
{'Sname': "thirdName",
'Settings': "thirdSettings"},];
$scope.addNew = function () {
$scope.personalDetails.push({
'Sname': "new",
'Settings': "new",
});}
});
html
<table>
<tr ng-repeat="personalDetail in personalDetails">
<td> <label ng-show="!personalDetail.edit || personalDetail.edit=false"
for="settings" > {{personalDetail.Sname}}</label>
<input ng-show="personalDetail.edit" type="text" ng-
model="personalDetail.Sname" />
</td>
<td><label data-val="{{personalDetail.Settings}}" for="desc" >
{{personalDetail.Settings}}</label>
<input Style="display:none" data-val="{{personalDetail.Settings}}" ng-
model="personalDetail.Settings" type="text" value="
{{personalDetail.Settings}}" />
</td>
<td>
<button style="cursor: pointer" ng-
click="personalDetail.edit=true">edit</button>
</td>
</tr>
</table>
<button style="cursor: pointer" ng-click="addNew()" >new</button>
This design only works with very simple cases.
I recommend to separate view from input. In the table you show the data.
Input form
Alt1: Put a simple form above the table with the name and setting as input fields. And a Save and Cancel button. The user can start adding directly.
Keep the edit button but when you press on it. Populate form above with the current rows data. An change the Save button Update.
Alt2: Use ng-repeat-start and ng-repeat-end to create an expandeble row for edit. Inside ng-repeat-end you can place a form. You will get a lot of room for validation messages.

Input field blurs when inline editing in ng-repeat

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.

Categories