Keep classes assigned with Angular when changing the state - css

HTML:
<div class="col-xs-4" ng-repeat="product in products" ng-click="selectItem($event)"> {{desc(product)}}</div>
In controller:
$scope.selectItem = function (event) {
$(event.target).addClass('greenLetter');
}
This works, the problem is when I need change state, if I go back to this controller, I miss greenLetter, how can I keep it?

The Angular way to do that would be using ngClass.
In the following code, clicking the element will set selected to the current product you are repeating over (using $index).
ngClass will be applied only if the selected element is the clicked one.
<div class="col-xs-4"
ng-class="{'greenLetter': selected == $index}"
ng-repeat="product in products"
ng-click="selectItem($index)">
$scope.selectItem = function(index) {
$scope.selected = index;
}

Related

How to use [ngClass] in a *ngFor Angular component without a local index keeper?

I'm using the following markup to mark the clicked component as active.
<div *ngFor="let menu of menus;"
(click)="onClick($event,menu.link)"
[ngClass]="{'active':menu.active}">
{{menu.title}}
</div>
The method handling the click is as follows.
onClick(target, link) {
target.active = !target.active;
this.router.navigate([{ outlets: { primary: [""], menus: [link] } }]);
}
It seems that the value of target.active goes from undefined to true to false to true etc. but the style doesn't get set. (I'm printing out the whole component to the console and can't see the addition of the class' name.)
Question: What am I missing in this approach?
NB, I know how to resolve it by approaching it from a different angle. I set up a local variable keeping the index and setting it like shown here. The aim of my question is to learn to achieve the requested behavior in a more like-a-bossy way.
target here:
onClick(target, link) {
target.active = !target.active; <------------
this.router.navigate([{ outlets: { primary: [""], menus: [link] } }]);
}
doesn't refer to menu, it refers to the event. But based on your ngClass directive:
[ngClass]="{'active':menu.active}">
You need to set active to menu variable and so it can be done like this:
<div *ngFor="let menu of menus;"
(click)="onClick(menu,menu.link)"
[ngClass]="{'active':menu.active}">
{{menu.title}}
</div>
Instead of passing in the $event, send it the actual menu object. Like this:
<div *ngFor="let menu of menus;"
(click)="onClick(menu)"
[ngClass]="{'active':menu.active}">
{{menu.title}}
</div>
And in the component:
onClick(menu) {
menu.active = !menu.active;
this.router.navigate([{ outlets: { primary: [""], menus: [menu.link] } }]);
}

Updating element CSS on PageA from button on PageB

I am using tabs for an app. I want a user button which when clicked on tab-detail.html to update the CSS of an element on its parent tab page tab.html
.controller('TabCtrl', function($scope,Tabs) {
$scope.tabs = Tabs.all() ;
// this populates the "tab.html" template
// an element on this page is: <span id="tab_selected_1">
// when user selects a listed item on tab.html
// it calls tab-detail.html
})
.controller('TabDetailCtrl', function($scope,$stateparams,Tabs) {
$scope.tabs = Tabs.get($stateparams.tabID) ;
// on tab-detail.html is a button <button ng-click="tabSelect()">
$scope.tabSelect = function(thisID) {
// update css on TabCtrl elementID
document.getElementById('tab_selected_1').style.color = "green" ;
}
})
The only way to get to tab-detail.html is via tab.html, thus tab.html must be loaded. But no matter what method I try I can't seem to find a way to access the element that is on another controller's page.
I have tried:
var e = angluar.element('tab_selected_1');
or
var e = angluar.element(document.querySelector('tab_selected_1') ;
e.style.color = "green" ;
The approach you are doing will never do a JOB for you as the DOM you want isn't available. You could achieve this by creating a sharable service that will maintain all of this variable in it and it will be used on UI. For ensuring binding of them your service variable should be in object structure like styleData OR you could also achieve this by creating angular constant.
app.constant('constants', {
data: {
}
});
Then you could inject this constant inside you controller & modify it.
.controller('TabCtrl', function($scope, Tabs, constants) {
$scope.constants = constants; //make it available constants on html
$scope.tabs = Tabs.all() ;
// this populates the "tab.html" template
// an element on this page is: <span id="tab_selected_1">
// when user selects a listed item on tab.html
// it calls tab-detail.html
})
.controller('TabDetailCtrl', function($scope,$stateparams,Tabs, constants) {
$scope.tabs = Tabs.get($stateparams.tabID) ;
$scope.constants= constants; //make it available constants on html
// on tab-detail.html is a button <button ng-click="tabSelect()">
$scope.tabSelect = function(thisID) {
// update css on TabCtrl elementID
$scope.constants.data.color = "green" ;
}
})
Markup
<div id="tab_selected_1" ng-style="{color: constants.data.color || 'black'}">
one way to do this is ....
1) Create a service
2) set a value to a variable in service on button click(tab-detail.html)
3) use that service variable value in tab.html
(Correction update at bottom)
#pankajparkar solution does work, however it does not work with IONIC as the IONIC Framework somehow overrides the DOM settings. Via the DOM Element inspector an see: style='color:green' being added inline to the ITEM/SPAN and can see the element defined as: element.style{ color: green}...but the color of the rendered HTML does not change....it stays black.
Further research shows this is somehow an IONIC problem as other users have the same problem. Other SOFs and blogs indicate that there appears to be a work around but I have yet to see it work.
The above is reformatted for others future use (even though it doesn't work with IONIC), thus I am still looking for a solution to work with IONIC:
.constant('constants', {
tabColors: {
curID:0,
},
})
.controller('TabCtrl', function($scope,Tabs,constants) {
$scope.constants = constants;
}
.controller('TabDetailCtrl', function($scope,$stateparams,Tabs,constants) {
$scope.constants = constants;
$scope.setItem= function(thisID) {
$scope.constants.tabColors.oldID = $scope.constants.tabColors.curID ;
delete $scope.constants.tabColors['tabID_'+$scope.constants.tabColors.curID] ;
$scope.constants.tabColors.curID = thisID ;
$scope.constants.tabColors['tabID_'+thisID] = 'green' ;
}
// HTML in Tab.html
<span id='tab_tabID_{{tab.tabID}}' ng-style="{color: constants.tabColors['tabID_'+tab.tabID] || 'black'}">
Some Text Here
</span>
//HTML in TabDetail.html
<button id="tab_button" class="button button-small button-outline button-positive" ng-click="setItem({{tab.tabID}});">
Select This Item
</button>
Correction: This method does work and does work with IONIC. The problem with IONIC is every element embedded within an ionic tag <ion-item>... <ion-nav>
...etc inherits its own properties from predefined classes...so you must either update the class (not optimal) or have ID tags on every element and/or apply CSS changes (using above method) to every child element. This is not optimal however it will work.
In my case my HTML actually looked like:
<span id='tab_tabID_{{tab.tabID}}' ng-style="{color: constants.tabColors['tabID_'+tab.tabID] || 'black'}">
<h2>Header Text Here</h>
<p>More text here</p>
</span>
The above CSS method works with this:
<span id='tab_tabID_{{tab.tabID}}'>
<h2 ng-style="{color: constants.tabColors['tabID_'+tab.tabID] || 'black'}">
Header Text Here
</h>
<p ng-style="{color: constants.tabColors['tabID_'+tab.tabID] || 'black'}">
More text here
</p>
</span>

Angular ng-show (or ng-if) not updating on value change

I have a simple example at plunker. I have an ng-show on one element and a select as another element. The select should toggle showing/hiding the other (input) element. Initially setting the select to Yes shows the other input element as expected. Then setting the select to No does toggle the scope value to false as expected, but does not hide the input element.
I've scoured the other posts related to this and the ones I found are around having or not having {{}} on the ng-show (I don't as it should be) or not having the value on $scope (which I do). I thought is may be a $scope.apply() issue, but then why does the 1st change to Yes work? Also adding the apply still does not make the No(false) work. What am I missing?
TIA!
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.conf = {};
});
You need to check ng-show="conf.is_ieee=='true'" instead of ng-show="conf.is_ieee". Check this plunker.
<div class="col-md-4" ng-show="conf.is_ieee=='true'">
<label class="form-label">IEEE Conference ID:</label>
<input type="text" class="form-control" name="ieee-id" ng-model="conf.ieee_id"/>
</div>
http://plnkr.co/edit/MXvuhSPB0ChJyDrvPI55?p=preview

Data binding of states between paper elements

Is it possible to bind a state (attribute) of a paper-checkbox [checked|unchecked] dynamically to an attribute like [readonly|disabled] inside a paper-input element? This is my implementation so far:
<template repeat="{{item in lphasen}}">
<div center horizontal layout>
<paper-checkbox unchecked on-change="{{checkStateChanged}}" id="{{item.index}}"></paper-checkbox>
<div style="margin-left: 24px;" flex>
<h4>{{item.name}}</h4>
</div>
<div class="container"><paper-input disabled floatingLabel id="{{item.index}}" label="LABEL2" value="{{item.percent}}" style="width: 120px;"></paper-input></div>
</div>
</template>
The behavior should be as follow:
When the user uncheck a paper-checkbox, then the paper-input element in the same row should be disabled and/or readonly and vice versa. Is it possible to directly bind multiple elements with double-mustache or do I have to iterate the DOM somehow to manually set the attribute on the paper-input element? If YES, could someone explain how?
Another way to bind the checked state of the paper-checkbox.
<polymer-element name="check-input">
<template>
<style>
#checkbox {
margin-left: 1em;
}
</style>
<div center horizontal layout>
<div><paper-input floatingLabel label="{{xlabel}}" value="{{xvalue}}" disabled="{{!xenable}}" type="number" min="15" max="200"></paper-input></div>
<div><paper-checkbox id="checkbox" label="Enable" checked="{{xenable}}"></paper-checkbox></div>
</div>
</template>
<script>
Polymer('check-input', {
publish:{xenable:true, xvalue:'',xlabel:''}
});
</script>
</polymer-element>
<div>
<check-input xenable="true" xvalue="100" xlabel="Weight.1"></check-input>
<check-input xenable="false" xvalue="185" xlabel="Weight.2"></check-input>
</div>
jsbin demo http://jsbin.com/boxow/
My preferred approach would be to refactor the code to create a Polymer element responsible for one item. That way, all of the item specific behaviour is encapsulated in one place.
Once that is done, there are a couple ways of doing this.
The easiest would be to simply create an on-tap event for the check box that toggles the value of a property and sets the disabled attribute accordingly.
<paper-checkbox unchecked on-tap="{{checkChanged}}"></paper-checkbox>
//Other markup for item name display
<paper-input disabled floatingLabel id="contextRelevantName" style="width:120 px;"></paper-input>
One of the benefits of putting this into it's own polymer element is that you don't have to worry about unique id's anymore. The control id's are obfuscated by the shadowDOM.
For the scripting, you would do something like this:
publish: {
disabled: {
value: true,
reflect: false
}
}
checkChanged: function() {
this.$.disabled= !this.$.disabled;
this.$.contextRelevantName.disabled = this.$.disabled;
}
I haven't tested this, so there might be some tweaks to syntax and what have you, but this should get you most of the way there.
Edit
Based on the example code provided in your comment below, I've modified your code to get it working. The key is to make 1 element that contains an either row, not multiple elements that contain only parts of the whole. so, the code below has been stripped down a little bit to only include the check box and the input it is supposed to disable. You can easily add more to the element for other parts of your item displayed.
<polymer-element name="aw-leistungsphase" layout vertical attributes="label checked defVal contractedVal">
<template>
<div center horizontal layout>
<div>
<paper-checkbox checked on-tap="{{checkChanged}}" id="checkbox" label="{{label}}"></paper-checkbox>
</div>
<div class="container"><paper-input floatingLabel id="contractedInput" label="Enter Value" value="" style="width: 120px;"></paper-input></div>
</div>
</template>
<script>
Polymer('aw-leistungsphase', {
publish: {
/**
* The label for this input. It normally appears as grey text inside
* the text input and disappears once the user enters text.
*
* #attribute label
* #type string
* #default ''
*/
label: '',
defVal : 0,
contractedVal : 0
},
ready: function() {
// Binding the project to the data-fields
this.prj = au.app.prj;
// i18n mappings
this.i18ndefLPHLabel = au.xlate.xlate("hb.defLPHLabel");
this.i18ncontractedLPHLabel = au.xlate.xlate("hb.contractedLPHLabel");
},
observe : {
'contractedVal' : 'changedLPH'
},
changedLPH: function(oldVal, newVal) {
if (oldVal !== newVal) {
//this.prj.hb.honlbl = newVal;
console.log("GeƤnderter Wert: " + newVal);
}
},
checkChanged: function(e, detail, sender) {
console.log(sender.label + " " + sender.checked);
if (!this.$.checkbox.checked) {
this.$.contractedInput.disabled = 'disabled';
}
else {
this.$.contractedInput.disabled = '';
}
console.log("Input field disabled: " + this.$.contractedInput.disabled);
}
});
</script>
</polymer-element>

how to style a selected button/link with css or javascript?

I would like to style my selected button.
I would like to display a light-blue border around the image of my selected button to show which page the user is on. (or just use the same hover image as the selected button image when the button is pushed.)
I didn't have success with the css link selectors :visited, :focus, or :selected.
Does this require a javascript solution?
thanks for any pointers!
i usually just a extra class name called selected
<div class="button selected">Button 1</div>
<div class="button">Button 2</div>
.selected {
border: 1px solid #0000ff;
}
It depends on how you display your page (using ajax or refresh on every click). If you are using javascript to load the page content than you just put an extra classname using javascript when the button is clicked.
you should use :active pseudo class in css to achieve what you want.
jQuery Solution with your CSS
You would probably want to check first if it is selected, that way this solution works with things like Twitter Bootstrap, where you can make any element act like a button:
$(function () {
$('div.button').click(function(){
if ($(this).hasClass('selected') {
$(this).removeClass('selected');
//Insert logic if you want a type of optional click/off click code
}
else
{
$(this).addClass('selected');
//Insert event handling logic
}
})
});
You will, in fact, need to use javascript. I did this in a project a while back, by iterating through the links in the navbar, and setting a class called "selected" on the one the user is currently visiting.
If you use jQuery, you can accomplish it like this:
$(function() {
$('#navbar li').each(function() {
if ($(this).children('a').attr('href') == window.location.pathname)
{
$(this).addClass('active');
}
});
})
The CSS Pseudo-selector :active won't still be active after a pagereload.

Resources