Good Day,
I have an ASP.NET MVC app that I'm working on and have a partial view with one row of data.
<div class="row paymentRow">
<div class="col-xs-4">Additional Invoices</div>
<div class="col-xs-8"><input type="text" style="width: 100%"/></div>
</div>
I have a button that when clicked, it adds additional rows to the DOM after the after the last div with the class "row paymentRow".
<div class="btn-group" role="group" aria-label="...">
<button type="button" id="Add">Add Row</button>
<button type="button" id="Ok">Ok</button>
<button type="button" id="Cancel">Cancel</button>
</div>
The jQuery to add the additional row works:
$(function() {
$("#Add").click(function(e) {
e.preventDefault();
var row = '<div class="row paymentRow">' +
'<div class="col-xs-4"> </div>' +
'<div class="col-xs-8"><input type="text" style="width: 100%"/></div>' +
'</div>';
$("div.modalUploadWidth div.row:last").after(row);
});
});
My question is:
Is there a cleaner way to represent the HTML that is being dynamically constructed and assigned to row? I'm not a big fan of magic strings like this. Not only that, but there will be multiple instances of where I need to inject javascript into the DOM.
I know that I can put the string into a Resource and access it from there. I also know that Handlebars can do this by storing the javascript template into an external file and binding the contents of the external file to the DOM.
I'm trying to find alternatives I may be overlooking.
TIA,
coson
Client side binding library like KnockOut JS would be more appropriate to make dynamic controls on client side. Here goes a simple Knockout JS Sample - https://dotnetfiddle.net/fmwTtJ
<!DOCTYPE html>
<html lang="en">
<head>
<!-- JS includes -->
<script src="//code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script src="//ajax.aspnetcdn.com/ajax/knockout/knockout-3.1.0.js"></script>
</head>
<table>
<thead>
<tr>
<th>Last Name</th>
<th>First Name</th>
</tr>
</thead>
<tbody data-bind="foreach: persons">
<tr>
<td><input data-bind="value: LastName"/></td>
<td><input data-bind="value: FirstName"/></td>
</tr>
</tbody>
</table>
<button data-bind="click: $root.addPerson ">Click</button>
<script>
var ViewModel = function() {
var self = this;
self.persons = ko.observableArray([]);
self.addPerson = function(){
self.persons.push(new Person('Last Name','First Name'));
};
}
var Person = function(lastName, firstName) {
var self = this;
self.LastName = ko.observable(lastName);
self.FirstName = ko.observable(firstName);
};
vm = new ViewModel()
ko.applyBindings(vm);
</script>
</html>
When you click on the button, it will add a new row -
//take existing row (you've said there is always at least one row)
$(".paymentRow").
//take last (maybe there s already more than one)
last().
// create deep copy
clone().
//append it as last element to the parent element
appendTo(".modalUploadWidth");
Related
I am trying to add the rows dynamically for one of the variables which is of type String array in my db. But it only saves the last value entered in the row rather than saving all of them in an array. Below is my view code:
<div class="row" ng-class='{red:true}'>
<label for="remedy">Remedy</label>
</div>
<input name="remedy" id="remedy" ng-model="error.remedy" required>
<br/>
<div class="row" ng-class='{red:true}'>
<a href="#!/errorcreate" class="btn btn-primary btn-small" ng-click="addRemedyRow()" ng-class='{red:true}'>Add Row</a></div>
<br/>
<table style="width:100%">
<thead>
<tr>
<th ng-class='{red:true}'>Remedy</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="rowContent in remedyrows">
<td>{{rowContent.remedy}}</td>
</tr>
</tbody>
</table>
{{error.remedy}}
<button type="submit" class="btn btn-default">Create</button>
Cancel
And this is the code in javascript:
$scope.remedyrows = [];
$scope.addRemedyRow = function() {
$scope.remedyrows.push({
remedy: $scope.error.remedy
});
Below is the output I am receiving (in a screenshot):
I added dsdfg as second row and my final error.remedy value just shows dsdfg rather than showing an array of both values : [wdssdsd,dsdfg]. error is the main document of which remedy is one of the fields of type String array.
Any ideas on how to achieve this?
Instead of error.remedy, which is used as holder for future remedyrow, use intermediate variable output for displaying results and sending them to the server:
Javascript:
$scope.output = $scope.remedyrows.map(function(x) { return x.remedy; });
$http({data: $scope.output, method: 'POST', url: url});
HTML:
{{output | json}}
you could have achieved it by following way:
$scope.remedyrows = [];
$scope.output;
$scope.addRemedyRow = function() {
$scope.remedyrows.push({
remedy: $scope.error.remedy
});
$scope.output = $scope.remedyrows.toString();
}
and in html
{{output}}
I have a button that pops up an Angular UI Bootstrap popover, using a template.
You can view it in this pen
The popover template is a form with a table containing a series of text fields with ng-models:
<script type="text/ng-template" id="filterPopoverTemplate.html">
<div class="filters">
<form>
<table>
<tbody>
<tr>
<td><input type="text" size="5" ng-model="filterHsCodeRestricted"></td>
<td>HS Code Restricted</td>
</tr>
<tr>
<td><input type="text" size="5" ng-model="filterHsCode10"></td>
<td>HS Code 10</td>
</tr>
<tr>
<td><input type="text" size="5" ng-model="filterCOD"></td>
<td>COD</td>
</tr>
</tbody>
</table>
<div class="filter-buttons">
<button tabindex="0" class="btn btn-default btn-xs" ng-click="applyFilters()">Apply</button>
<button class="btn btn-default btn-xs" ng-click="resetFilters()">Reset</button>
</div>
</form>
</div>
</script>
I have a "reset" button which calls a function that I want to reset all the ng-models to empty strings:
$scope.resetFilters = function () {
$scope.filterHsCodeRestricted = '';
$scope.filterHsCode10 = '';
$scope.filterCOD = '';
};
However, if I type something into the field and click "reset", the model is not being set to the empty string. I've done this elsewhere and it works, just not inside a popover template, so I assume it has something to do with the fields being in a popover ng-template. How do I "access" those?
The problem is that you're using the model without the DotRule or controller-as-syntax.
The whole problem was already explained by Pankaj Parkar in this question.
So, to make it work, you have to create a new object, ex:
$scope.model = {};
Then, build your ng-model's like this:
ng-model="model.filterCOD"
And so on..
The problem with your code is :
You need to define another ng-controller inside your filterPopoverTemplate.html
app.controller('poptemp', function($scope) {
$scope.resetFilters = function() {
$scope.filterHsCodeRestricted = '';
$scope.filterHsCode10 = '';
$scope.filterCOD = '';
$scope.filterPOE = '';
$scope.filterECCN = '';
$scope.filterItemCondition = '';
};
});
Check the corrected code here
I'll try and state what im trying to do and hope it makes sense (i only learned this last week!). When clicking the delete button that i create, i would like the content associated along with it to go down into a panel body i created in my HTML page with a class name of 'panelAdd'. Any help is much appreciated as i am quite new. Thanks for reading. Ill put the HTML first
HTML
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.5/darkly/bootstrap.css" crossorigin="anonymous" />
<link rel="stylesheet" type="text/css" href="css/style.css" />
<title>To Do List</title>
</head>
<body>
<h3 class="header">
<strong>To Do List</strong>
</h3>
<table class="table table-responsive myTable col-xs-offset2" id="myTable">
<thead>
<th>Complete?</th>
<th>Task to Complete</th>
<th>Time to Complete?</th>
<th>Remove?</th>
</thead>
<tbody>
<tr>
</tr>
<tr>
<td><input type="checkbox" class="newCheck col-xs-offset-2" value=""></td>
<td><input type="text" class="newWord" placeholder="New task"></td>
<td><input type="text" class="newTime" placeholder="How long do you have?"></td>
<td><button class="btn btn-primary buttonAdd">Add Task</button></td>
</tr>
</tbody>
</table>
<footer>
<div class="panel panel-success">
<div class="panel-heading">
<h3 class="panel-title">Completed!</h3>
</div>
<div class="panel-body"></div>
</div>
</footer>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.4.js"></script>
<script type="text/javascript" src="js/add.js"></script>
<script type="text/javascript" src="js/remover.js"></script>
</body>
</html>
Add button
$(document).ready(function (){
$(".btn-primary").on("click", function(e) {
e.preventDefault();
var newWord, newRow, wordTd, newCheck, deleteButton, deleteTd;
var isDuplicate;
newWord = $(".newWord").val();
newTime = $(".newTime").val();
newCheck = $(".newCheck").val();
var newRow = $("<tr>");
var newCheck = $("<input>").attr("type", "checkbox").attr("class", "newCheck").attr("data-state", "not-checked");
var wordTd = $("<td>").append(newWord).before();
var timeTd = $("<td>").append(newTime).before();
var deleteButton = $("<button>").addClass("btn btn-danger buttonRemove").append("Remove");
var deleteTd = $("<td>").append(deleteButton);
newRow.append(newCheck).append(wordTd).append(timeTd).append(deleteTd).before();
$("tbody").append(newRow);
$("#newWord").val("")
});
});
$(document).on("click", ".newCheck", function(){
if($(this).prop("checked") === true){
$(this).parent().attr("class", "done");
}
else{
$(this).parent().removeClass();
}
});
Remove Button
$(document).ready(function (){
$(document).on("click",".btn-danger", function(){
$(this).parents("tr").remove();
});
});
FIDDLE
Remove Button
$(document).on("click",".btn-danger", function(){
$t = $(this).closest('tr').find('td')[0];
$(this).parents("tr").remove();
$('.panel-body').append($t);
});
});
What you can do is grab the content you want to insert and append it in the target panel in this case .panel-body. See the fiddle above which adds the task name to the Completed list.
Do you expect like this.
Fiddle Sample
Code snippets:
$(document).on("click",".btn-danger", function(){
var removed = $(this).parents("tr").remove();
$(".panel-body").append('<div class="panelAdd"></div>').append(removed);
});
Let me know if this helps!
DEMO
You can just use .detach and .appendTo on click event of your remove button as below:
$(document).on("click",".btn-danger", function(){
var detachedRow=$(this).parents("tr").detach(); //detach and store it as reference
detachedRow.find('input[type="checkbox"]').remove();
//I hope you don't need checkbox when task is complete so removing it from that row
detachedRow.appendTo($('.panel .panel-body #myTableCompleted tbody'));
append it to your completed panel
});
Note : The .detach() method is the same as .remove(), except that
.detach() keeps all jQuery data associated with the removed elements.
This method is useful when removed elements are to be reinserted into
the DOM at a later time.
I have also added the table structure in your .panel-body to get the same UI look and have removed column for checkbox from the same and it is as below:
<div class="panel-body">
<table class="table table-responsive myTable col-xs-offset2" id="myTableCompleted">
<thead>
<th>Task to Complete</th>
<th>Time to Complete?</th>
<th>Remove?</th>
</thead>
<tbody>
</tbody>
</table>
</div>
Note - I think there might be other requirements too like only checked
checkbox to be added to that completed panel-body etc., and if yes
there will be a minor change in the delete code
I have an app that has buttons in a table. The button that is clicked should theoretically Hide the table and change some data. When the button is outside of the table it works fine, but inside it fails. Here is my code.
HTML:
<body link="white" vlink="white">
<pin>Site ID</pin>
<center>
<h1>Site Finder</h1>
<button ng-click="'x=0','y=0'">test</button>
<div ng-controller="firstCtrl">
<input type="text" ng-model="search" border="3" placeholder="Please enter site name..." ng-hide="hideAttr"/>
<div link="white" vlink = "white"><button id="btn2" ng-click="hideAttr = !hideAttr" ng-hide="!hideAttr">Back To Search</button></div>
<table border="1" width="100%" ng-hide="hideAttr">
<thead>
<tr>
<td><center>Name</center></td>
<td>Carrier</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="site in SiteLocs | filter : search">
<td>
<button id="btn1" ng-click="hideAttr = !hideAttr">
{{site.name}}
</button>
</td>
<td>
{{site.carrier}}
</td>
</tr>
</tbody>
</table>
</div>
</center>
<div id="map-canvas" ng-show="!hideAttr"></div>
</body>
</html>
JS:
(function(){
$scope.hideAttr = true;
});
Why wont the button work in a table?
That button is contained within an ng-repeat directive. ng-repeat creates it's own child scopes, so what is actually happening is you're creating a new $scope variable on the child scope called hideAttr and setting it. A couple work arounds:
Define a function in your controller and call that - Angular will look up to the parent and find the method
Use $parent in your ng-click method: ng-click="$parent.hideAttr = !$parent.hideAttr"
As #tymeJV pointed out the ng-repeat creates new scopes. When you are changing a primitive value on the child scope it creates a copy that hides the parent attribute. In the controller you should have an object that has the primitive attribute you want to change i.e.
$scope.tableAttrs = { hide: false };
and inside the ng-repeat markup you would use:
<div ng-hide="tableAttrs.hide">something to hide</div>
<button ng-click="tableAttrs.hide = !tableAttrs.hide">hide</button>
heres a blog post explaining it (related to forms but same idea, the child scope hides the primitive value) http://zcourts.com/2013/05/31/angularjs-if-you-dont-have-a-dot-youre-doing-it-wrong/
I slimmed down my actual code but I can't get this work. I am using knockoutjs and bootstrap with inline knockout templates. I use to just put a bunch of input's inside a div but I changed it to a table for alignment reasons. I know the property names are correct and the javascript console doesn't show any errors at all for bad variables or binding issues. I am putting the foreach in a TR tag instead of the TBODY tag because I don't know how many checkboxes I will have every time and I don't want them in rows exactly, just one TR element and a bunch of TD cells inside that one TR tag for now. How can I make this work??
<div id="Counties" class="well well-large checkbox inline">
<table class="table table-condensed">
<tbody>
<tr data-bind="foreach: { data: counties
}">
<td><input type="checkbox" data-bind="attr: { value: $data.sid }" />$data.name
</td>
</tr>
</tbody>
</table>
</div>
Here are my viewModels :
function searchVm() {
var self = this;
self.counties = ko.observableArray([]); //array of jurisItem
}
function jurisItem(name, sid) {
var self = this;
self.name = name;
self.sid = sid;
}
Edit :
I also tried this based on knockoutjs documentation and it doesn't work. I know I can do this in other ways using jquery but I would prefer knockout template syntax...
<table class="table table-condensed">
<tbody>
<tr>
<!-- ko foreach: $root.counties -->
<td>
<input type="checkbox" data-bind="attr: { value: $data.sid }" />$data.name
</td>
<!-- /ko -->
</tr>
</tbody>
</table>
I am not sure what are You trying to do. I made some sample.
html:
<div id="Counties" class="well well-large checkbox inline">
<table class="table table-condensed">
<tbody>
<tr data-bind="foreach: counties">
<td>
<input type="checkbox" data-bind="attr: { value: sid }" />
<span data-bind="text: name" />
</td>
</tr>
</tbody>
</table>
</div>
javascript:
$(function () {
var SearchVm = function () {
var self = this;
self.counties = ko.observableArray([]);
};
var JurisItem = function (name, sid) {
var self = this;
self.name = name;
self.sid = sid;
}
var item1 = new JurisItem("TestName1", "TestSid1");
var item2 = new JurisItem("TestName2", "TestSid2");
var searchViewModel = new SearchVm();
searchViewModel.counties.push(item1);
searchViewModel.counties.push(item2);
ko.applyBindings(searchViewModel);
})
Does this work for You:
http://jsfiddle.net/PVMjy/41/