Dynamic update to custom property - polymer-1.0

I am using polymer v1.9.1 and testing on Chrome.
I have a custom element containing a <paper-input>, and I want its text color to depend on some other property. This color is determined by the custom properties --paper-input-container-input-color or --primary-text-color, so I set a class-dependent value for those:
#input { --primary-text-color: red; }
#input.green { --primary-text-color: green; }
<paper-input id='input' class$='[[_getClasses(checked)]]'></paper-input>
_getClasses: function(checked) { return checked ? '':'green'; }
The text is always red, I guess because of this limitation in the shim (which I guess my browser must be using). So I add a call to updateStyles:
_getClasses: function(checked) {
this.async(function() {
this.$.input.updateStyles();
});
return checked ? '':'green'; }
}
Now it works correctly after checked first changes, but the initial state is incorrect (ie if checked is initially false, it is initially red but should be green). I tried adding another async(updateStyles()) to ready but no luck (yet if I call input.updateStyles() from the javascript console it corrects itself). How can I work around this?
Complete example: http://embed.plnkr.co/VC1ZMw9iyUO3K2SQq5Oy/

I've updated the plunk with the fix.
I've updated styles in attached callback instead of ready.
_getClasses: function(checked) {
return checked ? '' : 'green';
},
attached: function() {
this.updateStyles();
}

Related

Setting Attributes within Angular Directives: Radio Button > set > checked = true

EDIT PLUNKER EXAMPLE: http://plnkr.co/edit/WuiCAmMwbQnC0n197LSJ?p=preview
In the examples "sa" shoulbe checked and remain as checked. It is checked for a short time and then it looses its check status. I dont now why?
I am using a classical old fashion radio-button-based-navigation-tab-menu with Angular-UI-Router, it works well. Each click on a tab gets its URL.
If a user puts the URL manually into the adress bar of a browser and presses enter, the proper URL's content will be shown, it is also OK.
But my tab menu doesn't react on the manually changes at the adress bar. The correponding radio button should be checked. Therefore I've written a directive:
.directive ('checkIfMe', function (){
return {
link: function (scope, e, a) {
////////////////
if (currentUrl == currentNaviElement) {
console.log("Yes it is");
a.$set("checked", true);
}
}
}
I can detect the correct radio button, I see "yes it is" and I want to set its checked attribute to true. I've tried:
e.g. The ID of the current radio button is "navRadio_sa"
a.$set("checked", true);
a.checked = true;
$('#'+a.id+'').prop("checked",true);
All of them didn't work. But it I try it in the firebug console
$('#navRadio_sa').prop("checked",true);
it works. Where is my mistake?
Last but not least, that is a:
Try this
.directive('checkIfMe', function ($timeout) {
return {
link: function (scope, e, a) {
scope.my.info.e.push(e);
scope.my.info.a.push(a);
console
$timeout(function(){
if(a.id == "navRadio_sa") {
e.prop("checked", true);
var me = (a.id).replace("navRadio_","");
}
}, 10);
}
}
});
The e variable is a jQuery object of the element itself. You can work with it the same way that you'd normally work with jQuery objects.
I wrapped it in a timeout of 10 ms to give the ng-repeat time to complete before manipulating the dom. IT looks like ti was not working before because of a race condition. By setting the timeout, it should alleviate the issue.

Bind data-opacity attribute to scope variable

There's a jquery.minicolors pluggin, and there is a angularified version of this plugin that I wish use.
The usage is like so:
On page open data for color picker (hex color and opacity) is retrieved from server.
Then that values is used to "build" colorpicker (so that its color and opacity are equal to values received from server).
Then user may click on picker and change color and opacity. Results must be reflected in corresponding $scope variables.
I have binded color hex value via ng-model and it works like a charm, but I have troubles with opacity. I have tried to use ng-attr- for data-opacity attribute, but when I change manually opacity, results are not "sent" back to scope variable..
<input minicolors="options" ng-model="thecolor" ng-attr-data-opacity="{{opacity}}">
To demonstrate this I have created a plunker demo with description.
The problem is that the input field only binds to color part. If you add a ng-change to your input you will see that this is only fired if you change the color but not when opacity is changed.
You should move your code into a directive. Here you can use the "change" eventHandler from minicolors as describedin another question Minicolors AngularJS and the docs
UPDATE
If you take a look at the code
http://kaihenzler.github.io/angular-minicolors/angular-minicolors.js
It seems like the change event is there but only binds the hex value.
var settings = getSettings();
settings.change = function (hex) {
scope.$apply(function () {
ngModel.$setViewValue(hex);
});
};
I am guessing it should be.
var settings = getSettings();
settings.change = function (hex,opacity) {
scope.$apply(function () {
ngModel.$setViewValue(hex);
});
};
But the opacity is not passed into any value. Seems like a bug in the directive. If you use a console.log(opacity) you can see the value change.
Update 2.
Simple fix for you
settings.change = function (hex,opacity) {
scope.opacity = opacity
scope.$apply(function () {
ngModel.$setViewValue(hex);
});
};
After many attempts I have managed to modify source code to be able to two-way-bind opacity value to variable I desire. Here's a modified plunker.
What I've added:
First, we modify the tag by passing in additional watch-opacity="varname" attribute to define what variable should hold the initial opacity value. This variable will also be changed when we change the opacity slider and viceversa - the opacity slider will change when new data is written into this variable (ex. data received from server).
<input minicolors="options" ng-model="thecolor" data-opacity="" watch-opacity="settings.somewhat.opacity">
Then we get the opacity value from our settings and ask minicolors to use it as opacity for now, or use 1.0 if the variable have no value yet.
var opacity = scope.$eval(attrs.watchOpacity) || "1.0";
element.minicolors('opacity', opacity);
And then we modify our onchange function so that our settings variable for opacity is being updated also when opacity is changed due to opacity slider change:
var settings = getSettings();
settings.change = function (hex, opacity) { // <- fix!
scope.$apply(function () {
ngModel.$setViewValue(hex);
$parse(attrs.watchOpacity).assign(scope, opacity); // <- fix!
});
};
That's it! This way we can define variable for each colorpicker that will hold opacity value and bind any opacity changes via slider to it.
You can use an attribute watch:
(edited)
// Template
<input minicolors="options" ng-model="thecolor">
// Controller:
$("input[minicolors]").each(function() {
var $self = $(this);
$scope.$watch(
function() {
return $self.attr("data-opacity");
},
function(opacity) {
$scope.opacity[ $self.index() ] = opacity;
});
});

Fieldset and disabling all child inputs - Work around for IE

I have a fieldset that has a ui-view under it.
Each view had lots of fields(a field is a directive that wraps an input) under it.
It looks something like this:
<fieldset ng-disabled='myCondition'>
<div ui-view></div> // this changes with lot's of fields that look like <div field='text-box'></div>
</fieldset>
Now, this worked great, the fields get disabled on all browsers except IE.
I've done some google and seen that ie doesn't support fieldset + disabled and I'm looking for a quick workaround.
I've tried some things that were close but not perfect and I assume I'm not the first one that needs a solution(even though I didn't find anything on google).
It has 1 line solution now.
.
Though status is fixed in Microsoft documentation Issue still not resolved!
But, Now we can also use pointer-events: none;. It will disable all input elements
fieldset[disabled] {
pointer-events: none;
}
Seems like related to IE issues, see this and related (sorry, can't post more than 2 links yet).
The first one will be fixed in next major IE release (Edge?).
The second one is still opened.
As I suspect, the problem is that user still can click into inputs inside disabled fieldset an edit them.
If so, there is "css only" workaround for IE 8+ that creates transparent overlay above disabled fieldset that prevents fieldset from being clicked.
The workaround is described in Microsoft Connect issues.
There is fiddle, that demonstrates workaround in action.
fieldset {
/* to set absolute position for :after content */
position: relative;
}
/* this will 'screen' all fieldset content from clicks */
fieldset[disabled]:after {
content: ' ';
position: absolute;
z-index: 1;
top: 0; right: 0; bottom: 0; left: 0;
/* i don't know... it was necessary to set background */
background: url( data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==);
}
The workaround has some limitations, see code for details.
There are some options with JavaScript.
Seems like for IE9+ you can catch mousedown events on fieldset and call e.preventDefault() if fieldset is disabled.
fieldset.onmousedown = function(e) {
if (!e) e = window.event;
if (fieldset.disabled) {
// for IE9+
if (e.preventDefault) {
e.preventDefault();
}
// for IE8-
else {
// actualy does not work
//e.returnValue = false;
}
return false;
}
}
For IE8 and below it is imposible to catch bubbling mousedown events on disabled fieldset, event handlers does not even gets called. But it is possible to catch them on fieldset ancestors, on documetn.body for exampe. But again, for IE8- you can't prevent element from being focused by preventing default action of mousedown event. See jQuery ticket #10345 for details (sorry, can't post more than 2 links). You can try to use UNSELECTABLE attribute to temporary forbid element to get focus. Something like this:
document.body.onmousedown = function(e) {
if (!e) e = window.event;
var target = e.target || e.srcElement;
if (fieldset.contains(target) && fieldset.disabled) {
// no need to do this on body!!! do it on fieldset itself
/*if (e.preventDefault) {
e.preventDefault();
}
else {*/
// this is useless
//e.returnValue = false;
// but this works
fieldset.setAttribute("UNSELECTABLE", "on");
window.setTimeout(function() { target.setAttribute("UNSELECTABLE", ""); },4);
/*}*/
return false;
}
}
I had the exact same problem, and i came up with this directive:
angular.module('module').directive('fieldset', function () {
return {
restrict: 'E',
link: function (scope, element, attrs) {
if (angular.isUndefined(element.prop('disabled'))) { //only watch if the browser doesn't support disabled on fieldsets
scope.$watch(function () { return element.attr('disabled'); }, function (disabled) {
element.find('input, select, textarea').prop('disabled', disabled)
});
}
}
}
});
The feature detect is flawed though. On IEs it appears that the fieldset element (all elements it seems actually) have a 'disabled' property that is just set to false.
Edit: i just realised that it is inside an 'ng-view'. You may have to mess around with $timeouts to get it to apply the changes after the view has loaded. Or, even easier, place the fieldset inside the view.
This is a fix to disable fieldsets in IE11:
https://connect.microsoft.com/IE/feedbackdetail/view/962368/can-still-edit-input-type-text-within-fieldset-disabled
Detect IE:
Detecting IE11 using CSS Capability/Feature Detection
_:-ms-lang(x), fieldset[disabled].ie10up
{
pointer-events: none;
opacity: .65;
}
As other browser shows (disabled(/)) symbol on hover for disabled field so this change we should apply to only IE using #media
#media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
fieldset[disabled] {
pointer-events: none;
}
}

AngularJS animate dynamic margin

I have an element that appears when the user clicks a button elsewhere on the screen. The element appears to come out of the top of the screen. The element by default needs to be tucked out of view above the screen, so I will have a margin-top style that is based on the height of the element (and will be a negative value). This cannot be hardcoded in css because the element height may vary. When I click the button, I want the element margin-top to change to 0 and I want a transition animation.
The sample shown on angularJS documentation is for adding a removing a class. This would work fine if I knew the values to be set and could code them in CSS, however I cannot. What is the correct way to solve this?
The code below works for displaying and hiding my element using a margin but there is no animation. How do I trigger an animation here when the margin changes?
https://docs.angularjs.org/guide/animations
Quote Total: {{salesPriceTotal + taxesTotal - tradeInsTotal | currency}}
<div class="totals" ng-style="setTopMargin()">
// totals stuff here.
</div>
$scope.setTopMargin = function() {
return {
marginTop: $scope.marginTop
}
};
$scope.$watch('showTotals', function() {
var margin = $scope.showTotals ? 10 : -160 + $scope.modelTotals.length * -200;
$scope.marginTop = margin.toString() + 'px';
});
I added the following code per a suggested solution, but this code is never hit.
myApp.animation('.totals', function () {
return {
move: function (element, done) {
element.css('opacity', 0);
jQuery(element).animate({
opacity: 1
}, done);
// optional onDone or onCancel callback
// function to handle any post-animation
// cleanup operations
return function (isCancelled) {
if (isCancelled) {
jQuery(element).stop();
}
}
},
}
});
As the documentation explains: "The same approach to animation can be used using JavaScript code (jQuery is used within to perform animations)".
So you basically needs to use animate() from jQuery to do what you want.

Add class to DIV if checkbox is checked onload

I need help with a script to add an "active" class to a div when a hidden checkbox is checked. This all happening within a somewhat complex form that can be saved and later edited. Here's the process:
I have a series of hidden checkboxes that are checked when a visible DIV is clicked. Thanks to a few people, especially Dimitar Christoff from previous posts here, I have a few simple scripts that handle everything:
A person clicks on a div:
<div class="thumb left prodata" data-id="7"> yadda yadda </div>
An active class is added to the div:
$$('.thumb').addEvent('click', function(){
this.toggleClass('tactive');
});
The corresponding checkbox is checked:
document.getElements("a.add_app").addEvents({
click: function(e) {
if (e.target.get("tag") != 'input') {
var checkbox = document.id("field_select_p" + this.get("data-id"));
checkbox.set("checked", !checkbox.get("checked"));
}
}
});
Now, I need a fourth ( and final ) function to complete the project (using mootools or just plain javascript, no jQuery). When the form is loaded after being saved, I need a way to add the active class back to the corresponding div. Basically reverse the process. I AM trying to figure it out myself, and would love to post an idea but anything I've tried is, well, bad. I thought I'd at least get this question posted while I work on it. Thanks in advance!
window.addEvents({
load: function(){
if (checkbox.checked){
document.getElements('.thumb').fireEvent('click');
}
}
});
Example: http://jsfiddle.net/vCH9n/
Okay, in case anyone is interested, here is the final solution. What this does is: Create a click event for a DIV class to toggle an active class onclick, and also correlates each DIV to a checkbox using a data-id="X" that = the checkbox ID. Finally, if the form is reloaded ( in this case the form can be saved and edited later ) the final piece of javascript then sees what checkboxes are checked on page load and triggers the active class for the DIV.
To see it all in action, check it out here: https://www.worklabs.ca/2/add-new/add-new?itemetype=website ( script is currently working on the third tab, CHOOSE STYLE ). You won't be able to save/edit it unless you're a member however, but it works:) You can unhide the checkboxes using firebug and toggle the checkboxes yourself to see.
window.addEvent('domready', function() {
// apply the psuedo event to some elements
$$('.thumb').addEvent('click', function() {
this.toggleClass('tactive');
});
$$('.cbox').addEvent('click', function() {
var checkboxes= $$('.cbox');
for(i=1; i<=checkboxes.length; i++){
if(checkboxes[i-1].checked){
if($('c_'+checkboxes[i-1].id))
$('c_'+checkboxes[i-1].id).set("class", "thumb tactive");
}
else{
if($('c_'+checkboxes[i-1].id))
$('c_'+checkboxes[i-1].id).set("class", "thumb");
}
}
});
// Add the active class to the corresponding div when a checkbox is checked onLoad... basic idea:
var checkboxes= $$('.cbox');
for(i=1; i<=checkboxes.length; i++){
if(checkboxes[i-1].checked){
$('c_field_tmp_'+i).set("class", "thumb tactive");
}
}
document.getElements("div.thumb").addEvents({
click: function(e) {
if (e.target.get("tag") != 'input') {
var checkbox = document.id("field_tmp_" + this.get("data-id"));
checkbox.set("checked", !checkbox.get("checked"));
}
}
});
});

Resources