Treeview doesn't work well in angularjs - javascript

My purpose:
I want to show treeview data in table. I use Angularjs and treeview to realize this feature. However, when the JS script get the data and give the data to treeview, the data will show for just a second and disappear.
html code like this:
<tbody id="understanding-content-table-body">
<tr ng-repeat="(i, log) in understandingLogs">
<td ng-repeat="item in log track by $index">{{item}}</td>
<td>
<div id="tree-{{i}}"></div>
</td>
</tr>
</tbody>
And my JS code like this:
for (var i = 0; i < tmp.length; ++i) {
var id = '#tree-' + i;
$(id).treeview({ data: tmp[i], levels: 0, showBorder: false, icon: "glyphicon glyphicon-minus" });
}
tmp is the data that want to show.
After debugging, I found that when the the JS script finished, another angularjs code will be executed, and then the treeview in the table will disappear. I think
that the treeview doesn't find the "tree-0", "tree-1"... because the html don't ready. And I execute the command
$('#tree-0').treeview({ data: [{text:123}], levels: 0, showBorder: false, icon: "glyphicon glyphicon-minus" });
then the data will be shown in the table.
So my question is that, could I execute the JS script after the html is finished?
By the way, I have tried the
angular.element(document).ready(function () {});
and
$('document').ready(function (){});
It doesn't work.

Related

How to remove clicked row while sending parametr to a function? Knockout

I have some complication with service removing. I have function that removes service on the server but I have to reload page to update table. I found way how to remove row by click-binding but there is the issue beacuse I can only remove row or get ID for delete service from server NOT both. :/
This is example of code that removes service on the server but doesn't remove table row.
HTML:
<table id="serviceView" class="fixed_header" border: 1>
<thead>
<tr>
<th>Name</th>
<th>Adress</th>
<th>Notification</th>
</tr>
</thead>
<tbody data-bind="foreach: services">
<tr>
<td data-bind="text: name"></td>
<td data-bind="text: address"></td>
<td data-bind="text: serviceId"></td>
<td ><button data-bind="click: $parent.DeleteService.bind(this, serviceId)">Remove</button></td>
</tr>
</tbody>
</table>
JS:
self.services = ko.observableArray([]);
self.lastCheck = ko.observable();
$.getJSON("http://localhost:55972/api/status", function (data) {
self.services(data.services);
self.lastCheck = data.lastCheck;
}); //////This is loading data to the table from server
self.DeleteService = function (serviceId) {
$.ajax({
type: "GET",
url: "http://localhost:55972/api/services/remove/" + serviceId,
}).done(function () {
self.services.remove(serviceId)
})
};
This is example of code that removes table row
When I use click-binding like this:
<button data-bind="click: $parent.DeleteService">Remove</button>
And change delete function to this:
self.DeleteService = function (serviceId) {
self.services.remove(serviceId)
$.ajax({
type: "GET",
url: "http://localhost:55972/api/services/remove/" + serviceId,
}).done(function () {
// here I want to remove row but i doesnt goes here without service ID.
})
};
It removes row but instead serviceId I got [object, object] in the URL.
Can you help me with it ? I got idea to use jquery to just update the table but it's seems unnecessarily complicated for me when I can use knockout.
I know the solution is not that hard but I'am just unable to solve it..... -_-
I'am sorry for taking time with this bullshit but this is my first real project and I'am so desperate at this point beacuse I have lot of things to do and I'am stucked on this.
In your Js code, you can try this:
self.services = ko.observableArray([]);
self.lastCheck = ko.observable();
$.getJSON("http://localhost:55972/api/status", function (data) {
self.services(data.services);
self.lastCheck = data.lastCheck;
}); //////This is loading data to the table from server
var serviceIdRemoved;
self.DeleteService = function (serviceId) {
serviceIdRemoved = serviceId; // now you can do whatever you need more with this value
$.ajax({
type: "GET",
url: "http://localhost:55972/api/services/remove/" + serviceId,
}).done(function () {
self.services.remove(serviceId)
})
};
With this way of work you can user the content of the variable and donĀ“t loose it. Also if you get [Object, Object], you can:
console.log(serviceId) // to see the content in the console.
JSON.stringify(data) //to see the content in html
This source could help you to understand it better.
The [object, object] you are seeing is actually the data and event objects which are secretly added to the JS function parameters by Knockout. If you want to add your own parameter to the click binding then you should do it like this:
<button data-bind="click: function(data, event) { $parent.DeleteService(serviceId, data, event) }">Remove</button>
You can then define your JS function as follows:
self.DeleteService = function (serviceId, data, event) {
[code here...]
}
You can read up on the exact details of it in the excellent Knockout documentation here:
http://knockoutjs.com/documentation/click-binding.html
It's about half-way down under the heading that reads Note 2: Accessing the event object, or passing more parameters

Accessing Table in Partial View

I'm trying to edit a table by adding rows, but running into an issue with the the partial view not being fully rendered (This is my assumption)
I'm loading the partials into their divs via page load and ajax calls;
<div id="preTestSteps">
</div>
<div id="mainTestSteps">
</div>
<div id="postTestSteps">
</div>
Scripts;
$(document).ready(function() {
var testSuiteExecutionId = #(Model.TestSuiteExecutionId);
var testSuiteId = #(Model.TestSuiteId);
loadTestStepResultsPartialView(testSuiteExecutionId, testSuiteId, 1, "preTestSteps");
loadTestStepResultsPartialView(testSuiteExecutionId, testSuiteId, 0, "mainTestSteps");
loadTestStepResultsPartialView(testSuiteExecutionId, testSuiteId, 2, "postTestSteps");
});
function loadTestStepResultsPartialView( testSuiteExecutionId, testSuiteId, testStepType, divId) {
$.ajax({
type: 'POST',
url: '#Url.Action("DetailsTestStepResults", "TestSuiteExecutions")',
data: { 'testSuiteExecutionId': testSuiteExecutionId, 'testSuiteId': testSuiteId, 'testStepType': testStepType },
success: function(data) {
$("#" + divId).html(data);
}
});
In the partial view, the table has a unique ID which is accessed to append (view model is a list of viewmodels, using the first index is to get data which is unique for the list of logs);
<div id="#collapseStepItemName" class="collapse col-sm-12" role="tabpanel" aria-labelledby="headingOne">
<div class="card-body">
<table class="table" id="logTable_#Model[0].TestStepId#Model[0].MessageType">
<thead>
<tr>
<th width="5%"></th>
<th width="20% !important">Time</th>
<th width="75%">Message</th>
</tr>
</thead>
<tbody>
#foreach (var logEntry in Model)
{
<tr id="tableRow_#logEntry.TestStepId#logEntry.MessageType">
<td><img width="20" height="20" src="~/Content/Images/#HtmlUtilities.GetTestSuiteExecutionIconName(logEntry.LogType)" /></td>
<td><i>#logEntry.TimeStamp</i></td>
<td><i>#Html.Raw(HtmlUtilities.GetHtmlFormattedString(logEntry.Message))</i></td>
</tr>
}
</tbody>
</table>
</div>
The current test code (with hard coded tableID for the sake of testing) is the following;
var tableId = "logTable_" + 44 + "False";
var newRow = document.getElementById(tableId).insertRow();
newRow.innerHTML="<td>New row text</td><td>New row 2nd cell</td><td>Please work</td>";
The following error is thrown in the browser debug;
Uncaught TypeError: Cannot read property 'insertRow' of null
Is there a way to execute the script after the partial views are fully rendered? Or is this issue something else and not due to the views being loaded in?
I made sure the table appending script actually works by testing it on a table in the main view, and it worked as intended.
Since you're using jQuery, place this code inside document.ready function:
$(document).ready(function() {
// other stuff
var tableId = "logTable_" + #Model[0].TestStepId + #Model[0].MessageType;
var row = $('<tr>').append('<td>New row text</td><td>New row 2nd cell</td><td>Please work</td>');
$('#' + tableId).find('tbody').append(row);
});
If you insist using vanilla JS to add rows, make sure that all DOM objects are already loaded as given in example below:
document.addEventListener("DOMContentLoaded", function (ev) {
var tableId = "logTable_" + #Model[0].TestStepId + #Model[0].MessageType;
var newRow = document.getElementById(tableId).insertRow();
newRow.innerHTML="<td>New row text</td><td>New row 2nd cell</td><td>Please work</td>";
}
The reason behind insertRow has null value is that table DOM elements may not fully loaded when adding row script executes, hence row addition script should run when all required DOM elements are complete.
Demo example: JSFiddle

MVC Calling a function from a partial view is not working

This is my first MVC application, and this must be something simple but I've been trying to make this work for so many hours now..
What I want to do
I want to display a table in a partial view and be able to delete items from the parent view. The simple version looks something like this (the actual application is not about fruits):
What I have now
Partial View (_FruitList.cshtml)
<div id="listOfFruits">
<table class="table">
<tr>
<th class="active">Description</th>
<th class="active">Amount</th>
</tr>
#foreach(var item in Model)
{
<tr>
<td>#item.Description</td>
<td>#item.Amount</td>
<td><button class=".." onclick="d(#item.FruitID)">Delete</button></td>
</tr>
}
</table>
</div>
Parent View (Home.cshtml)
#section scripts
{
<script type="text/javascript">
$(document).ready(function (){
function d(id){
var url = "/Fruit/DeleteFruit/";
$.post(url, {id: id})
.done(function(response){
$("#listOfFruits").html(response);
});
}
});
</script>
}
#{Html.RenderPartial("_FruitList", Model.FruitList);}
Controller (FruitController.cs)
[HttpPost]
public ActionResult DeleteFruit(int id)
{
//Delete the selected fruit
FruitViewMode item = new FruitViewMode();
return PartialView(item.FruitList);
}
My Question
I can view the table with the fruit data in the parent view, but clicking the Delete button does not call the d function in the parent view.
(Javascript and JQuery should be working in the partial view because I've tested alert and addClass and they work fine)
I'm very new to this so it's very likely that I'm missing some basic stuff but what am I missing?
d() isn't declared in the global page scope, so it isn't found. declare it in the root of the <script> tag (i.e., not within a document.ready) to have access to it from the onclick
<script type="text/javascript">
function d(id){
var url = "/Fruit/DeleteFruit/";
$.post(url, {id: id})
.done(function(response){
$("#listOfFruits").html(response);
});
}
</script>
I believe in the past I had used a HTML.ActionLink for the same goal of deleting something by Id:
HTML.ActionLink method

I have a button in each row of a table: How can the OnClick handler tell which row it was called from?

The goal: ,,
So I've got a Table (Which is initialized as a JQuery DataTable). Each row contains a 'remove me' button, and when that button is pressed, I want to delete the row from the current table.
What I've tried:
tr = $(this).closest('tr');
$('.my-table-class').dataTable().fnDeleteRow(tr);
What happens
No matter what row I click on, the last row is deleted from the table is deleted, except if there's only one row in the table, in this situation a javascript error: "TypeError: j is undefined" is thrown from Jquery.dataTable.min.js. Both baffle me.
I can get the attributes of the right row - for example, If do something like: alert($(this).attr("data-name")); I click on John Smith's row, I'll see 'John Smith' in an alert box... so $(this) is the a tag, so why doesn't the .closest() method grab the right trtag?
My Questions:
How do I get 'this' row (the one which contained the button which was pressed) in order to delete it?
Any idea what's causing the 'TypeError: j is undefined" error when there's only one row in the table?
Details:
Here's the rendered (from .jsp) HTML table:
<table class="table my-table-class">
<thead><tr><th>Name</th><th> </th></tr></thead>
<tbody>
<tr>
<td>John Smith</td>
<td><i class="icon-plus"></i></td>
</tr>
<tr>
<td>Robert Paulson</td>
<td><i class="icon-plus"></i></td>
</tr>
<tr>
<td>Juan Sanchez</td>
<td><i class="icon-plus"></i></td>
</tr>
</tbody>
Here's how I initialize the tables as a Jquery DataTable:
$('.st-my-table-class').dataTable( {
"bInfo": true,
"aaSorting": [[ 0, "desc" ]], // sort 1st column
"bFilter": true, // allow search bar
"bPaginate": false, // no pagination
"sDom": '<"top"f>rt<"bottom"lp><"clear">' // (f)ilter bar on top, page (i)nfo omitted
} );
And here's the whole event handler:
$('.my-button-class').on("click", function(){
tr = $(this).closest('tr');
$('.my-table-class').dataTable().fnDeleteRow(tr);
});
I think This JSFIDDLE is much closer to what you wanted. Here is the basic code
$(function() {
var dataTable = $('.my-table-class').dataTable();
$( ".test" ).click(function() {
var row = $(this).closest('tr');
var nRow = row[0];
dataTable.dataTable().fnDeleteRow(nRow);
});
});
which I pulled from this resource here that explains in full detail on how it works. In short you need to select the node itself not the jQuery object. You can also use .firstlike so.
$( ".test" ).click(function() {
var row = $(this).closest('tr').first();
dataTable.dataTable().fnDeleteRow(row);
Note: I added the "This" text as I don't have the button style/icon.
As #Dawn suggested, you're passing a jQuery element to fnDeleteRow, which is expecting a HTML node.
Simply try:
$('.my-button-class').on("click", function () {
tr = $(this).closest('tr').get(0); // gets the HTML node
$('.my-table-class').dataTable().fnDeleteRow(tr);
});
Working JSFiddle: http://jsfiddle.net/so4s67b0/
First of all in the function it is need to declare a variable and assign our data table to it. Then take the selected row's index into another variable. After that remove the selected row from the data table. .draw(false) will be manage the pagination and no of rows properties in jquery DataTable.
$('.my-button-class').click(function () {
var tbl = $('.my-table-class').DataTable();
var index = tbl.row(this).index();
var row = $(this).closest("tr").get(index);
tbl.row(index).remove().draw(false);
});

Adding table rows from a Grails Template on Button Click

So, the _form.gsp template associated with my create.gsp creates an initial table from a template for the row as follows:
<table id="myTable">
<!-- define table headers here -->
<g:each var="i" in="${1..5}">
<g:render template="tableRow" model="['i': i]" />
</g:each>
</table>
What I'd like to do is add a button or a link underneath that table that let's you add five more rows, while keeping all the data you've entered in the form so far.
I can see how that's possible in "pure" javascript, but I'd basically have to repeat the _myTable.gsp HTML in my javascript file. I'd like to avoid that (DRY, etc.).
How can I do that?
Edit
So, I tried Gregg's solution (below). Here's what I came up with.
The Controller has an action:
def addMoreRows() {
println params
def i = params.rowNumber + 1
def j = i+5
println "i is set to " + i
render(template: "foapRow", bean:i, var:i, model: ['rowStart': i, 'rowEnd': j])
}
The create.gsp page calls the _form.gsp as normal, adding a rowStart and a rowEnd to the model.
create.gsp
<g:render template="form" model="['userId':userId, 'rowStart':1, 'rowEnd':5]"/>
*_form.gsp*, in turn, passes those parameters on to the row template, and creates a link to call the above controller action. It also has the javascript Gregg recommended:
<script type="text/javascript">
$("#addRowsLink").on("click", function(e) {
e.preventDefault();
$.get("/Controller/addMoreRows", function(html) {
$("#theTableInQuestion>tbody").append(html);
});
});
</script>
<table>
...
<g:render template="tableRow" model="['rowStart':1, 'rowEnd':5]"/>
</table>
<g:remoteLink id="addRowsLink" action="addMoreRows" update="theTableInQuestion" onSuccess="addRows(#theTableInQuestion, data, textStatus)" params="['rowNumber':data]">Add More Rows</g:remoteLink>
The *_tableRow.gsp* begins and ends with:
<g:each var="i" in="${rowStart..rowEnd}">
<tr>
...
</tr>
</g:each>
From a previous attempt, I have this function in my included javascript file:
function addRows(tableId, rowCode, status) {
$(tableId + ' tr:last').after(rowCode);
}
Right now, when I click the "Add More Rows" link, I still get taken to a new page, and it only has one row on it.
One possible solution. You're going to need to change your template so it does the looping:
GSP:
<table id="myTable">
<tbody>
<g:render template="tableRows" model="[loopCount:loopCount, moreData:moreData]" />
</tbody>
</table>
Template:
<g:each in="${loopCount}" var="idx">
<tr>
<td>.....</td>
......
</tr>
</g:each>
JavaScript:
$("#someButtonId").on("click", function(e) {
e.preventDefault();
$.get("/controller/someAction", function(html) {
$("#myTable>tbody").append(html);
});
});
Controller:
def someAction = {
// logic here
render template: "tableRows", model="[loopCount: 5, moreData:moreData]"
}
You could also submit all the data in your table to the server every time and refresh the entire page, adding logic to loop over some variable number of rows. But you would need to collect all that data on the server and make sure it gets put back in the request.
There's probably a dozen ways to do this so don't be surprised if you get that many answers. :o)

Categories