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
Related
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.
Would like to ask what is wrong with the functions bellow, the first iteration works without any problem but, after the grid is refreshed, when protractor tries to move to the next cell, it gives me this error message:
Failed: stale element reference: element is not attached to the page document
The idea of this is to read the 5th column (or 4th if you consider stating from 0), check each row that contains the value "true", if contains true then do the operation of editing the row, clicking in a checkbox and saving (everything is performed at the line)
Each element has a unique ID related to status code so that why I am "not using rows" in the function (only to get the text from the line and then pass as string to complete the ID of the element)
**Every time that the value is changed and saved, the table gets a refresh
function resetGoodItemStatus(siteToResetValues){
var cellsConform = element.all(by.css('#datatableDir tr td:nth-of-type(5)'));
var conformCounter = 0;
selectValueDropDown(siteToResetValues)
cellsConform.each((eachCell) => {
eachCell.getText().then((cellText) => {
switch (cellText)
{
case 'true':
element(by.id(conformCounter+'-1')).getText().then(function(value){
element(by.id('btnEdit-US.'+value)).click();
element(by.xpath("//*[#editable-checkbox=\"scopeValue.getConformMapping(scopeValue.getDataRow("+'\'US.'+value+"\')).conform\"]/../span/span/input")).click();
element(by.id('btnSubmit-US.'+value)).click();
})
default:
browser.sleep(100)
}
conformCounter += 1
});
});
}
HTML before editing the line:
<tr role="row" class="odd">
<td id="0-0" class="ng-scope">
<form editable-form="" name="scopeValue.rowforms['US.DAM']" onaftersave="scopeValue.saveData('US.DAM',0)" ng-show="scopeValue.rowforms['US.DAM'].$visible" class="form-buttons form-inline ng-pristine ng-valid ng-hide" style="">
<button type="button" class="btn btn-xs btn-trans kni kni-check-circle text-info" id="btnSubmit-US.DAM" ng-disabled="scopeValue.rowforms['US.DAM'].$waiting" ng-click="scopeValue.rowforms['US.DAM'].$submit()">
</button>
<button type="button" class="btn btn-link kni kni-x-circle-slim" ng-disabled="scopeValue.rowforms['US.DAM'].$waiting" id="btnCancel-US.DAM" ng-click="scopeValue.cancelData('US.DAM',0)">
</button>
</form>
<div class="buttons" ng-show="!scopeValue.rowforms['US.DAM'].$visible">
<button type="button" class="btn btn-xs btn-trans kni kni-edit-circle text-info" ng-click="scopeValue.rowforms['US.DAM'].$show()" id="btnEdit-US.DAM">
</button>
</div></td>
<td id="0-1" class="ng-scope sorting_1">DAM</td>
<td id="0-2" class="ng-scope">US.DAM</td>
<td id="0-3" class="ng-scope">Generic Damaged Code</td>
<td id="0-4" class="ng-scope">
<span editable-checkbox="scopeValue.getConformMapping(scopeValue.getDataRow('US.DAM')).conform" e-name="conform" e-form="scopeValue.rowforms['US.DAM']" e-required="" class="ng-scope ng-binding editable">false</span>
</td></tr>
HTML after clicking at the Edit button:
<td id="0-4" class="ng-scope">
<span editable-checkbox="scopeValue.getConformMapping(scopeValue.getDataRow('US.DAM')).conform" e-name="conform" e-form="scopeValue.rowforms['US.DAM']" e-required="" class="ng-scope ng-binding editable editable-hide">false</span>
<span class="editable-wrap editable-checkbox ng-scope">
<span class="editable-controls"><input type="checkbox" name="conform" required="required" class="editable-input ng-pristine ng-empty ng-invalid ng-invalid-required ng-touched" ng-model="$data" style="">
<div class="editable-error ng-binding ng-hide" ng-show="$error" ng-bind="$error" style=""></div>
</span>
</span>
</td>
Thank you for your time!
One simple way is to get text of all cells in the 5th column into text array, then iterate the text array, the array index is equivalent to the table row index.
Once cell text is equal true, use row index to find the table row. The rest elements can be found within the table row.
Because in each iteration, below code will find all table rows from page again, should not happen Stale Exception
function resetGoodItemStatus(siteToResetValues){
var cellsConform = element.all(by.css('#datatableDir tr td:nth-of-type(5)'));
selectValueDropDown(siteToResetValues);
cellsConform.getText().then(function(conforms) {
// conforms is a string Array, each one is the text of one cell of 5th column
conforms.forEach(function(conform, rowIndex) {
if(conform === 'true') {
var row = element.all(by.css('#datatableDir tr').get(rowIndex);
row.element(by.css('button[id^="btnEdit-US"]')).click();
row.element(by.css('input[type="checkbox"]')).click();
row.element(by.css('button[id^="btnSubmit-US"]')).click();
browser.sleep(3000)
}
});
});
}
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'm making a table.
I've made my rows editable with: ng-click="todo.editing = !todo.editing"
index.html
<tr ng-repeat="todo in todos>
<td>
<div ng-hide="todo.editing">{{ todo.id }} </div>
<div ng-show="todo.editing"><input type="id" ng-model="todo.id" /></div>
</td>
<td>
<div ng-hide="todo.editing">{{ todo.text }}</div>
<div ng-show="todo.editing"><input type="text" ng-model="todo.text" /></div>
</td>
</tr>
<button class="btn btn-warning btn-sm ng-scope" ng-click="todo.editing = !todo.editing"><span class="glyphicon glyphicon-pencil"></span></button>
I've made a button to add new rows to the table:
index.html
<button type="button" class="btn btn-default btn-block " ng-click="addRow($storage.todos)">New record</button>
script.js
$scope.addRow = function (arr) {
console.log(arr);
arr.push({'id':$scope.id, 'text': $scope.text});
};
Now I want to expand my addRow() function so I can add a new row and at the same time position myself at that row, and also put me in edit mode.
The problem with position is that I am using pagination. Let's say I am at page one in pagination and the newly row comes at page four. I want to automatically get there.
Can somebody give me a hint? Thanks for your time.
Just make editing property value true during pushing.
Like this
arr.push({'id':$scope.id, 'text': $scope.text,editing:true});
I created a fiddle:
https://jsfiddle.net/0t1trq6m/
addRow() method now looks like:
$scope.addRow = function () {
console.log("test");
var e = new Entry();
e.id = $scope.todos.length;
e.text = $scope.content;
$scope.todos.push(e);
}
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.