How do I change ng-class on window resize? - css

I have functionality where when I shrink the screen below a certain size and click on a table row it reveals other items in that table row. (white screen on left).
Then when I expand the window I want all table rows to return to normal, but I have a bug where if it's open already it won't close.
I tried setting the $scope.showIt variable to true/false, but it only seems to have an effect if it's the opposite of the initial $scope.showIt value. How do I change all the ng-classes in every td when the screen is a certain size?
This is my html:
<tr class="table-body "ng-repeat="service in services | orderBy:myOrderBy | filter:search">
<td align="center" valign="middle" ng-click="showIt = dropDetails(showIt)" ng-class="{'name-show':showIt, 'name-show-disabled':!showIt}">{{ service.name }}</td>
<td ng-class="{'show':showIt, 'show-disabled':!showIt}">{{ service.description }}</td>
<td ng-class="{'show':showIt, 'show-disabled':!showIt}">{{ service.prices }}</td>
</tr>
And in my controller:
$scope.dropDetails = function(showIt){ //This function works well.
console.log(window)
console.log(window.innerWidth)
if(window.innerWidth < 760){
return !showIt;
}else{
return showIt;
}
}
var w = angular.element($window);
var resizeId=0;
$scope.showIt = true;
w.bind('resize ', function (a) {
clearTimeout(resizeId);
resizeId = setTimeout(function(){
if(w[0].innerWidth < 760){
}else{
$scope.showIt = true; //Does nothing.
$scope.$apply();
}
}, 500);
})

Do use Dot Rule while defining ng-model in Angular view. So that will help you to follow prototypal inheritance while refering to there property.
The reason here is, you have used ng-repeat(which does create prototypically inherited child scope while rendering template each time on view). Here the same thing you need to do. Define model like $scope.model and inside that define showIt property. Rather you should put that showIt on each of you element of services, by which you handle each event individually.
Markup
<tr class="table-body" ng-repeat="service in services | orderBy:myOrderBy | filter:search">
<td align="center" valign="middle"
ng-click="service.showIt= dropDetails(service.showIt)"
ng-class="{'name-show':service.showIt, 'name-show-disabled':!service.showIt}">
{{ service.name }}
</td>
<td ng-class="{'show':service.showIt, 'show-disabled':!service.showIt}">
{{ service.description }}
</td>
<td ng-class="{'show':service.showIt, 'show-disabled':!service.showIt}">
{{ service.prices }}
</td>
</tr>
Now question remains how to hide all the services, so that would be very easy. Loop through each services collection element & make showIt variable as false.
angular.forEach($scope.services, function(service){
service.showIt = false;
})
Also you are using custom event on DOM, I'd suggest you to put this code in directive. So that can get reusable too.

Related

How to effectively use foreach in laravel-livewire in a table when the data dynamically changes?

I've been struggling with the foreach blade directive when combined with Laravel-Livewire and dynamically changing data rendered in an html table.
Specific scenario:
A table is presented to the user
Each row of the table includes a clickable icon that toggles a variable associated with that row
Toggling this icon visibly removes the row from the table
It sounds simple enough but I cannot get it to work correctly.
<?php
namespace App\Http\Livewire;
use Livewire\Component;
class UnreconciledServices extends Component
{
public $services;
public function render()
{
return view('livewire.unreconciled-services');
}
//
// receives an invoice number and changes the "reconciled" value for the $aaaServices record to true
//
public function test($a)
{
}
}
This is pretty simple - there is a function called "test" that accepts a variable (the function does nothing at all) and there is a collection of records called $services that has been passed in from the parent.
The blade file is as follows:
<div>
<table style="width:100%">
<thead>
<tr>
<th width="5%">
</th>
<th>
Service Date
</th>
<th>
Call #
</th>
<th>
Payment
</th>
</tr>
</thead>
<tbody>
#foreach ($services as $service)
{{-- <span wire:key="{{$service->invoice_number}}"> --}}
{{-- #if ($service->reconciled == false) --}}
<tr id="{{$service->invoice_number}}" class="text-center" style="{{ $service->reconciled == 1 ? 'display:none' : 'show'}}">
<td>
{{-- <input class="m-2" type="checkbox"> --}}
{{-- <span class="m-2"><i class="far fa-trash-alt text-red-500"></i></span> --}}
<span wire:key="{{$service->invoice_number}}" wire:click="test('{{$service->invoice_number}}')" class="m-2 cursor-pointer"><i class="lm-2 rm-2 fas fa-plus text-green-500"></i></span>
</td>
<td>
{{$service->invoice_date}}
</td>
<td>
{{$service->call_number}}
</td>
<td>
{{$service->service_price}}
</td>
</tr>
{{-- #endif --}}
{{-- </span> --}}
#endforeach
</tbody>
</table>
</div>
This is a simple table with 4 columns. The 1st is a clickable icon, the other 3 are data. Rows should only be visible if the "reconciled" boolean is 0.
When initially rendered, the appropriate records are displayed (there are 2 that qualify and 177 that do not). However, as soon as I click the icon - even when the function it links to has zero actual content - all records are suddenly visible except for the one that I've just hidden.
Looking at the html change when I click the icon, the tag changes from
<tr id="21031251674" class="text-center" style="display:none">
to
<tr id="21031253205" class="text-center" style="show">.
I have tried using livewire keys, though I'm not sure how this would affect the functionality since I'm not rendering components, but rather data driven html.
Surely someone else has encountered this and overcome. Please point me in the right direction.
Sorry for the delay. This whole thread should be deleted as bone-headed. But it was written in earnest...
When all was said and done, I was unclear on the differences b/w an html / ajax - based approach such as livewire and something more javascript-based such as Vue. I was expecting functionality that didn't exist in the framework as I was attempting to use it.
To solve the problem, I moved towards individual row items being components in and of themselves, using emit to communicate between components and adjust the backend data source.
Once I embraced the html / ajax approach and stopped trying to force / expect javascript functionality & dom manipulation then all was well. Just a mindset difference from what I am familiar with, and it took me some time to turn the corner.

Select and change a single table cell color per row with angular 6

I am trying to make a table in which I will be able to select a cell and change its color. Only 1 cell per row can be selected. When selected, its color should be red. When another is clicked, its color should change back to what it was before. I have been trying this for days, but no success. Only made it to change color of a whole row or column, but not a single cell. Can it be done?
Here is my template code:
<table id="table2" class="table table-bordered text-center">
<thead class="thead-light">
<th>Criteria</th>
</thead>
<tbody>
<tr *ngFor="let criter of rows;">
<td>{{ criter.criteria1 }}</td>
<td>{{ criter.criteria2 }}</td>
<td>{{ criter.criteria3 }}</td>
<td>{{ criter.criteria4 }}</td>
</tr>
<tbody>
</table>
As for CSS, it just needs this class (I think):
.on { background-color: "red"; }
That is what got ATM. If you need some more info let me know. Any help would be appreciated.
changeBG(val) { // on tr element
// val is event.target
let table = document.querySelector('#table2');
let selectedCells = table.getElementsByClassName('on');
if (val.tagName !== 'TD') {
return;
}
if (selectedCells.length) {
selectedCells[0].className = '';
}
val.className = 'on';
}
I don't know much about the angular dom interaction, but hopefully you can find the way through this

How to use ngClass with table in Angular

I have a table, where I want to declare ngClass (first row)
<table [ngClass]="{'before_forth': pageNumber < 4, 'forth': pageNumber = 4}">
<tr id="words" *ngFor="let item of itemsSource3; let i = index;">
<td *ngFor="let item2 of helper[i]; let j = index;">
<input class="resizedTextbox" name="{{i}}{{j}}"
[(ngModel)]="helper[i][j].value" (ngModelChange)="onValueChange(f,i)"/>
</td>
{{item}}
</tr>
</table>
And my CSS is
table.forth.td:nth-child(2){background-color: green}
table.forth.td:nth-child(5){background-color: green}
table.before_forth.td:nth-child(2){background-color: red}
table.before_forth.td:nth-child(5){background-color: red}
So, what I would like to do is to set the colour, which depends on the number of page (in my component I use this method
this.pageNumber = this.activatedRoute.snapshot.queryParams['page'];
and it worked fine in other cases ( I use it oft)
I guess the problem is in css, in this piece of code table.forth.td:nth-child, could you please tell me what would be the right way to access class within the table?
You should use pageNumber === 4 and not pageNumber = 4, because then you are just assigning the variable:
<table [ngClass]="{'before_forth': pageNumber < 4, 'forth': pageNumber === 4}">
The second problem is the css rule, you are using a .td class in there, even though it's nowhere used in the html. My guess is that you want to target the <td> element inside the table:
table.forth td:nth-child(2){background-color: green}
table.forth td:nth-child(5){background-color: green}
table.before_forth td:nth-child(2){background-color: red}
table.before_forth td:nth-child(5){background-color: red}
The third problem is the usage of {{item}} outside the <td> tag. This is invalid html and will result in unwanted behaviour, you should move this inside the td
Also, don't use id inside in combination with *ngFor. An element with an id is supposed to be unique throughout the entire page. Actually, it's better not to use id at all if you have no special use for it:
<table [class.before_forth]="pageNumber < 4" [class.forth]="pageNumber === 4">
<tr *ngFor="let item of itemsSource3; let i = index;">
<td *ngFor="let item2 of helper[i]; let j = index;">
<input class="resizedTextbox" name="{{i}}{{j}}" [(ngModel)]="helper[i}[j].value" (ngModelChange)="onValueChange(f,i)">
{{item}}
</td>
</tr>
</table>
The last point is, the proper spelling of 4th is fourth and not forth, but that's just an fyi ;)

How to highlight a selected row in *ngFor?

I couldn't find something that will help me to solve this issue in Angular2. I'd like to set a css class when I select a row. (without using jQuery)
<table class="table table-bordered table-condensed table-hover">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Website</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of companies" (click)="selectedCompany(item, $event)">
<td>{{item.name}}</td>
<td>{{item.email}}</td>
<td>{{item.website}}</td>
</tr>
</tbody>
</table>
I'm using Angular2 final release
There are plenty of solutions to do this, one of them is you can store the current company when clicked.
In the *ngFor you check if the current item is the currentCompany and you add the class highlighted or whatever class you wish if its the same company.
export class TableComponent {
public currentCompany;
public selectCompany(event: any, item: any) {
this.currentCompany = item.name;
}
}
And then on your template:
<tr *ngFor="let item of companies" (click)="selectCompany($event, item)"
[class.highlighted]="item.name === currentCompany">
--
Another solution if you wish to have multiple highlighted companies you can add a property highlighted to your item. Then on selectCompany() you just set the property to true. On your check you do [class.highlighted]="item.highlighted".
I know this was answered a while ago, but to expand on the accepted answer, you could also use [ngClass]="{'class_name': item.id === currentCompany }". The table hover may need to be removed as it may hide the background color change
<tr *ngFor="let item of companies" (click)="selectCompany($event, item)" [ngClass]="{'class_name': item.id === currentCompany }" >
Then css
.class_name{ background-color: yellow; }

Get link from text input field and then open it with DalekJS

I have an issue where the link I need to traverse to using DalekJS that is not clickable, it is rather copy/pastable. How can I retrieve a value from an input field in the browser and then use it in a test.open() call?
<table>
<tbody>
<tr>
<td>Link 1</td>
<td><input type="text" value="http://example.com/link-1" class="start-link"></td>
</tr>
<tr>
<td>Link 2</td>
<td><input type="text" value="http://example.com/link-2" class="start-link"></td>
<tr>
</table>
In the example above I would like DalekJS to dynamically run test.open('http://example.com/link-1'); during the progression of my test.
module.exports = {
'A test case': function (test) {
test.open('http://example.com/example-from-above.html')
.open('http://example.com/link-1') //This is the link I'm trying to retrieve dynamically.
.screenshot('image.png')
.done();
}
}
How can I accomplish this?
DalekJS does not allow reading values of HTML element synchronously and using them in the test scripts. If you need to interact with the content of the testing page, the execute method may help.
You can try setting the window location by a function "executed in the browser":
module.exports = {
'A test case': function (test) {
test.open('http://example.com/example-from-above.html')
// Open the URL from the first input element value
.execute(function () {
var input = document.querySelectorAll('.start-link')[0];
location = input.value;
})
// Wait until the browser opens the page
.waitFor(function () {
return location.pathname.indexOf('link-1') > 0;
})
.screenshot('image.png')
.done();
}
}

Resources