Angular - Table with dynamic rows without ng-repeat? - angularjs

I have an app I am working to re-create in angular. One section is a list of items that currently has 140 different items. Each item has a table with 1-4 rows, and differing numbers of columns (usually 6-20).
The way the information works, we really need to show all the items to start. However, the processing time to render the tables using ng-repeat, as well as the massively exponential number of scopes is prohibitive, especially since some users will have older computers (although likely running FF or Chrome, so I'm not worried about IE issues).
I have all the data locally in a json array, and I can format it as needed. Of course, there is filtering, so things will be shown/hidden all over. But the data in the tables is one time bound, and doesn't change.
I'd really prefer to make this something that is in a template. I could either loop it in a javascript array, or create the table data myself that way. However, in angular, templates are not allowed to have logic, so I can't do that.
I'd prefer to do everything rendering client side for speed, so I'd prefer not to have to call up the server to render it.
I could also pre-render it in the json, and use sanitize I believe, but that seems back door and against the separation of interests which is core to angular.
What would be the best practices in this case? I think I've tried about everything, and nothing within angular seems perfomant enough for this case. I'd prefer not to have to use paging, since the users would want to scroll multiple filtered long lists. Any suggestions?
Edit: Now with some sample code:
<table id="{{::item.name}}}" class="table table-striped table-bordered table-hover table-condensed table-responsive text-center" >
<thead>
<tr>
<td ng-repeat="header in ::item.tableHeader">{{::header}}</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="diff in ::item.tableInfo track by $index">
<td ng-repeat="cell in ::diff track by $index">{{::cell}}</td>
</tr>
</tbody>
</table>
I used arrays because the order matters, and objects don't always follow the order. Again, with over 140 items, 1-4 rows with 10-20 columns, the scopes become quite costly.

Related

How do models work in CakePHP 3?

Recently I was given a work on a project that is being made in CakePHP 3. The project has some basic function working already and I was asked to create some new functionalities.
My understanding of PHP and MVC comes from CodeIgniter, where I have used Active Record to work with the database.
In CI, the Model was just Model. Here in Cake, I see an Entity, Table and Behavior. I somehow understood this (for me) new concept by asking around my colleagues. Now I just want to finish my assignment without asking them anything further.
So, it is a project management portal. As an Admin, you log in and you manage projects. People then come and join the project.
There are two tables I need to use:
users Table with info such as firstname, lastname, phone, address etc.
users_projects Table that stores user_id and report_id [they are Foreign Keys to users Table and reports Table respectively]
My task is to create a page where you can see who has joined in a particular project in a paginated table fashion with info from Users table such as name, surname, phone, etc..
I have prepared a new Controller called ReportsController in which I have created new participants method, so when you visit /admin/reports/participants/$project_id, it gives you the table with users joined in particular project.
ReportsController
public function participants($project_id = null)
{
$this->loadModel('Projects');
$participants = $this->Projects->selectAllProjectParticipantsByID($project_id);
foreach ($participants as $participant){
echo $participant->users[0]->firstname;
//dump($participant);
}
//dump($participants);
}
ProjectsTable
public function selectAllProjectParticipantsByID($project_id){
// return $this->find()->where(['project_id' => $project_id]);
// return $this->find()->where(['project_id' => $project_id]);
return $this->find('all')->contain([$project_id]);
}
Reports/participants template
<div class="col-xs-9">
<h3><?= __('Participants Report for') ?>
<small><?= __('Participants Report') ?></small>
</h3>
<table class="table table-bordered" cellpadding="0" cellspacing="0">
<thead>
<thead>
<tr>
<!-- <th colspan="2">--><?//= $this->Paginator->sort('id') ?><!--</th>-->
<th><?= $this->Paginator->sort('firstname', __('First Name')) ?></th>
<th><?= $this->Paginator->sort('lastname', __('Last Name')) ?></th>
<th><?= $this->Paginator->sort('phone', __('Phone')) ?></th>
<th><?= $this->Paginator->sort('address', __('Address')) ?></th>
<th><?= $this->Paginator->sort('personType', __('Person Type')) ?></th>
<th><?= $this->Paginator->sort('status', __('Status')) ?></th>
<th class="actions text-right"><?= __('More Info') ?></th>
</tr>
</thead>
<tbody class="sortable" data-url="<?= $this->Url->build(['controller' => 'QuestionSets', 'action' => 'reorder']) ?>">
</tbody>
</table>
<ul class="pagination">
</ul>
The result I get so far is following:
Error PrintScreen png
Now I have been reading about CakePHP's Pagination, Data Model, Templating etc for 16 hours now but I still am unable to get the results, which is really frustrating since I know I would have done this in another framework in a matter of minutes.
I best learn by example, sometimes I get lost by reading all the techy words in documentations, so you helping me is the best way you can teach me something.
Well, this question looks like little broad but still I want to point out following things:
Codeigniter vs CakePHP
Just as you mentioned as CakePHP covers many things/features,learning curve of cakephp is not as easy as CodeIgniter. And I would say you need to spend little more time for CakePHP documentation (CakePHP Cookbook).
One of the key advantages of CakePHP is that it provides ready to use templates. When you start to understand CakePHP relations and conventions, you will certainly feel more comfortable.
And I bet when you understand things, you can do much things with CakePHP than you do with CodeIgniter in easier fashion.
Where to start ?
Have you ever done CakePHP blog tutorials? If not this is the best place to start CakePHP. When you finished you will get basics things in CakePHP well. If you escape these basics things it would be hard to understand cakephp conventions,relations and MVC. See here (Cakephp3 blog tutorial.)
The things you did in CodeIgniter model,is basically done with table objects (eg.UsersTable.php) in CakePHP. These objects provide access to collections of data. They allow you to save new records, modify/delete existing ones, define relations, and perform bulk operations.
Talking about the error:
You need to know about what is contain and what to contain:
$this->find('all')->contain([$project_id]);
You need to contain tables (like Users) not the any field of tables as you did above. Again you will understand these things when you complete blog tutorial and which is pretty straightforward.

Date greater than the current time in Angular

I'm having to move a project from rails to angular, Much to my disgrace.
How would i go about changing this
<% if souporder.datefrom > Time.now %>
into angular terms?
heres my code
<tr ng-repeat="soup in souporders">
<td>{{soup.id}}</td>
<td>{{soup.soup}}</td>
<td>{{soup.datefrom}}</td>
<td>{{soup.dateto}}</td>
</tr>
Edit
This is in my view
<div ng-repeat="soup in souporders">
<tr ng-if="date == soup.datefrom">
And this is in my controller
$scope.date = new Date();
You clarified in the comments that you only need this calculated on page refresh, which is pretty simple and doesn't require much angular magic.
On initializing the view's controller, simply set a variable whether or not the order date is in the future or not and expose it on the scope. Then bind directly to it, and use the bindonce functionality {{:myvariable}} to improve performance because it won't be changing anyway.
Depending on your needs, you can even have the server calculate that value and return it as a property of each order in your JSON array of orders. (I'm guessing a bit at what you're doing, but you can adapt the answer if you're not doing what I think you're doing.)
Update: I just realized that perhaps your objective is not to do something special with each row based on the date, but to filter the list based on the date. Most of my answer still stands, but you can forget about the bindonce spiel. What you need is called an angular filter on ng-repeat and is done using the pipe (|) character. You can read all about it by searching the topic. There are examples in the angularjs docs. But basically, instead of:
<tr ng-repeat="soup in souporders">
<td>{{soup.id}}</td>
<td>{{soup.soup}}</td>
<td>{{soup.datefrom}}</td>
<td>{{soup.dateto}}</td>
<td>{{:soup.active}}</> <!--do something nice here instead of just displaying true/false --->
</tr>
You might want:
<tr ng-repeat="soup in souporders | filter:{current: true}">
...etc
</tr>
Either on the server or on the client, when you load the data from the server, you can add a boolean property to the items in the souporders array that says whether it's current or not.
What this accomplishes:
You have a filtered list of items based on a property that does NOT change dynamically and requires a page reload to update. But it's fast (but not the fastest it could be if you really need to go crazy - don't prematurely optimize, you'll know what to do when the time comes to do it, and that time isn't now).
Why it's not that great:
It isn't dynamic and it isn't angular-y or declarative. But based on your requirements, it's a solution. I'd spend some time considering if you want this to be dynamic, and if not, why the items that aren't showing are even returned from the server. Are they used elsewhere on the page?

Is content generated by ng-repeat and hidden by ng-if loaded before being removed?

I'm new to Angular and am implementing something that requires a large ng-repeat loop. Of all the things in the loop I'm only showing one however. I show it using ng-if (ng-if over ng-show so as to remove all other elements from the DOM). My question is: if I have considerable amounts of data inside the elements whos ng-if statements don't evaluate to true, for example some images or very big tables, is that data still downloaded by a user accessing the page with the ng-repeat and ng-if statements on? I.e If I had my ng-repeat repeating 10 times and each time generating a div containing a 100,000 row table, even though only one of the tables shown via the use of ng-if, are all of the other tables evaluated and so downloaded by the user? This is in the interest of bandwidth.
I hope that made sense. For illustration purposes here's an example
<div ng-repeat="foo in bars">
<div ng-if="foo.property == 1">
{{ foo.veryBigTable }}
</div>
</div>
You are actually mixing two parts of a web application i.e. your server side code and your client side angular code.
The answer to your question is Yes, i.e. the whole data will be downloaded from server irrespective of your ng-if condition.
Since you will be populating your data in scope element bars somewhere in your code which will contain all data returned from server and server does not know the conditional data you implemented in the HTML. Its up to the HTML code to display the displayable data based on ng-if condition.
So you need to modify your server code to only send that amount of data which is going to be displayed. This is the way you can save your bandwidth.
Hope, you got my point!

JSP dropdown, shortening results

So im trying to populate a dropdown with entries from a specific database. Right nwo it works, however it puts in the ENTIRE entry which has 10+ attributes, where i only need a couple of them. Is there any way to specify which columns get passed back and displayed in the dropdown?
<tr>
<TD><span class="required">*</span> CMS Group ID:</TD>
<td><form:select path="cmsGroupId">
<form:options items="${list}" itemValue="id" />
</form:select>
</td>
<td><form:errors path="cmsGroupId" cssClass="required" /></td>
</tr>
Ideally id like to only get the first 4 columns from here. (ID, Version, Name, Entity ID) but dont really know how to make it work. Ive found bits on doing a c:foreach loop but havent gotten that working right either...
Thanks!
Build the appropriate display values on the Java side, not in JSP.
Don't try to do this in the view layer–IMO it's not the right place for such logic.

Is there a generally accepted way to add meta information to array-elements indicating how to handle them?

I am using an array which contains the results of a database-query, which is later formatted as html (for a webapplication) or as csv (for import in a spreadsheet).
I want to attach information to the array-element which has some additional information about how the data of this element can be used.
For instance, array-element-data...
... can be displayed as a link: then I want the link information attached. The code which creates html from the array can use it to create a link.
... is a date of the form 2009-09-14: then I want to somehow flag it as being a date. If the usage is for a html-page, it can then be displayed somewhat more beautiful, e.g. Mo Sep 14 or Today; if the recipient is a csv it would be best to leave it.
Is there a best-practice way of solving this problem?
I did think of several possible solutions, but wanted to ask if someone already knows a "best practice". From possibly best to worst:
Store each array-element as custom-created object (Date,Linkable,Text...), instead of having the array-element as text. Possibly provide a default .to_string() method.
Make each array-element a hash, so that I could say a[5][7]['text'] or a[5][7]['link'].
Make different versions of the array, e.g. textArray[5][7], linkArray[5][7]
Creating the html as a start and just using the text-version seems like a bad idea, as the appearance differs depending on the usage (e.g. 2009-09-14 vs Mo Sep 14).
Or am I just asking the wrong question?
Unless you specify a language, (1) and (2) are basically the same. An object or a hash, what's the difference in a dynamic programming language other than perhaps syntax? In Lua everything's a dictionary.
(1)/(2) are usually preferred to (3), since they generally make copying an element along with its meta-data much easier. When sorting, for instance.
So without being specific to a language / environment, best practice in the absence of any special conditions is to somehow combine the meta-data and the element, and deal in the resulting datatype. You could do this by defining a new class to contain both, defining one more more subclasses of your original element type, using a generic pair, using a general-purpose dictionary, or just storing the meta-data in the original object (which would the be obvious approach in, say, Javascript).
As a general advice it's best if the data contains no information at all on how to represent itself.
Instead the part of the application creating the representation should have these settings in a separate data structure. Think of it as a XML file and various XSLT files creating different representations.
But in cases where this is not possible or when you have to merge the two information into one data structure for the actual conversion, I followed this rule of thumb:
Don't be to clever and do what's most natural in your language!
In Java and Delphi I always used the "custom object" variant, because one gets certain advantages like compile time checking.
In PHP I always used hashes because it's the more PHP-ish style.
I've done "Solution 3" sometimes, but I always regretted it. These structures tend to become a maintenance nightmare and you'll most likely run into synchronization isses, from a data point of view and also from a coding point of view.
A common approach in web frameworks would be to map records onto objects: one record from the database is read into one object, so your result is an Array of Objects. For different tables you need different classes. This is one of the building blocks for the Model View Controller (MVC) pattern used in many web frameworks.
For example in Ruby on Rails the Table users is handled by a Class User. You create both using a scaffold.
ruby script\generate scaffold user lastname:string link:string joined:date
Date, Boolean, String, Text, Decimal, Integer are distinct datatypes here. Unfortunately URLs are not, so I have to use String for the link.
You can read users from the database like this:
#u = User.find(77) # gives you one object
#list = User.find(:all) # gives you an array of User-objects
the attribute of the user object have the correct types to work with dates, numbers, etc:
if 100.days.ago < #u.joined then ....
Logic that is inherent to the data is implemented in the User class.
The list of users may be displayed in HTML using a view like this:
<h1>Listing Users</h1>
<table>
<tr>
<th>Lastname</th>
<th>Link</th>
<th>Joined on</th>
</tr>
<% #list.each do |user| %>
<tr>
<td><%=h user.lastname %></td>
<td><%= link_to "Homepage", user.link %></td>
<td><%=h user.joined %></td>
</tr>
<% end %>
</table>
Logic that is inherent to displaying the data is implemented in the view(s).
The knowledge which attribute of the object is to be treated as a link or a normal text resides in the view, not in the object itself.
Displaying / Outputting the same data as cvs is done by creating a cvs-view.

Resources