Bind data-opacity attribute to scope variable - angularjs

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;
});
});

Related

How to get the value of selected row directly in HTML using ag-grid

i try to get the the value of number row selected, and print it in HTML using Angularjs, but no issue,
i have the count only when i clic in the grid column header.
The value of " selectedRowsCounter " is 0 in html, when i dosn't clic in the grid header
my code is like
var activeButtons = function() {
var countRowsSelected = $scope.gridOptions.api.getSelectedRows().length;
$scope.selectedRowsCounter = countRowsSelected;
console.log($scope.selectedRowsCounter);
$rootScope.count.selectedRows = countRowsSelected;
};
$scope.gridOptions = {
rowData: null,
angularCompileRows: true,
onSelectionChanged: activeButtons,
}
there is a screenshot
i have open the same subject here
https://github.com/ceolter/ag-grid/issues/1023
i have added this line to activeButtons function and it work fine
$scope.gridOptions.api.refreshView();
i dont knew if there is a good solution, but that work for now
The problem seems to be with Angular being unaware of the $scope property change because ag-grid does not tell Angular that it has modified something in the $scope. Although it is difficult to tell if you don't show your view.
You can use onSelectionChanged the way you are using it to know how many rows have been selected, but you need to tell Angular that something has changed in its $scope by applying it.
Something like this:
var activeButtons = function() {
var countRowsSelected = $scope.gridOptions.api.getSelectedRows().length;
$scope.selectedRowsCounter = countRowsSelected;
console.log($scope.selectedRowsCounter);
$rootScope.count.selectedRows = countRowsSelected;
window.setTimeout(function() {
this.$scope.$apply();
});
};
That way you can apply the $scope and the html view will reflect the changes.

ng img crop - cropping existing images

ng-img-crop is an awesome directive however I am having trouble adapting it to my scenario. My issue is that when a user has an image I would like to give them the option to resize the image if they would like to.
So here is the code I am attempting to use:
js:
vm.userImageOriginal = vm.editUser.image_pkey ? 'api/file/' + vm.editUser.image_pkey : null;
html:
<img-crop image="profileVM.userImageOriginal" result-image="profileVM.userImageNew"
area-type="square" result-image-size="300" on-change="profileVM.imageCropped = true;"></img-crop>
So I two issues:
1) I only want to upload the new image if the user has indeed changed the cropping. I tried setting a flag in on-change but it looks like on-change gets executed on initialization as well. Is there any way to know if the user has actually cropped?
2) Is there any way to set the position of the square/circle. In my scenario, if there is an existing user image, I would like to set the cropping square to the dimensions of the current image (i.e. the border of the image).
Thanks in advance.
Solved like this:
Add the following attribute to ng-img-crop directive in html:
on-load-done="profileVM.addCroppingWatcher()"
Here is the function:
function addCroppingWatcher(){
if (croppingWatcher)
return;
$window.setTimeout(function(){
croppingWatcher = $scope.$watch(
function(){ return vm.userImageNew; },
function(newVal, oldVal){
if (oldVal && oldVal != newVal) {
vm.imageCropped = true;
croppingWatcher();
}
}
);
}, 0);
}

FabricJS with Angular: Change Font Family, Size, Alignment, etc

I am using FabricJS with an AngularJS application. I am able to add text to a canvas and, using the kitchensink example located here, I can perform functions such as bold, italic, underline, etc.
However, the issue I have is how to change the font family, text align, font size, etc. since when I make a selection from a dropdown for font family, no changes occur... but it works in the Kitchensink example.
I am using the Kitchensink example as I need to not only add text, but edit it once it shows on the Canvas, and this appears to have what I need.
A button (which is working) has an HTML element such as:
<button class="btn btn-object-action" type="button" ng-class="{'btn-inverse': isBold()}" ng-click="toggleBold()">
Bold</button>
Which is backed up by the following in the Controller:
$scope.toggleBold = function () {
setActiveStyle('fontWeight',
getActiveStyle('fontWeight') === 'bold' ? '' : 'bold');
};
As I stated, this works as intended. Where I am having challenges is changing something like the Font Family or Font Size in that the change is achieved without a button click. Here is sample HTML for the Font Family select from the Kitchensink example:
<label style="display: inline-block;" for="font-family">Font family:</label><select class="btn-object-action" id="font-family" bind-value-to="fontFamily">
<option value="arial">Arial</option>
<option value="helvetica" selected="">Helvetica</option>
<option value="myriad pro">Myriad Pro</option>
</select>
This is backed up by this in the controller:
function getActiveProp(name) {
var object = canvas.getActiveObject();
if (!object) return '';
return object[name] || '';
}
function setActiveProp(name, value) {
var object = canvas.getActiveObject();
if (!object) return;
object.set(name, value).setCoords();
canvas.renderAll();
}
$scope.getFontFamily = function () {
return getActiveProp('fontFamily').toLowerCase();
};
$scope.setFontFamily = function (value) {
setActiveProp('fontFamily', value.toLowerCase());
};
function watchCanvas($scope) {
function updateScope() {
$scope.$$phase || $scope.$digest();
canvas.renderAll();
}
canvas
.on('object:selected', updateScope)
.on('group:selected', updateScope)
.on('path:created', updateScope)
.on('selection:cleared', updateScope);
}
$scope.getSelected = function () {
return canvas.getActiveObject();
};
$scope.canvas = canvas;
$scope.getActiveStyle = getActiveStyle;
addAccessors($scope);
watchCanvas($scope);
I am more used to using ng-model than bind-value-to, in fact, I have never seen or used bind-value-to before in Angular apps/not sure how and if I should be using it.
My main question is how do I get the dropdowns working where if I select a value it updates per the Kitchensink example for text here? What I am missing/is there a better way given that my need is to add and edit stylized text.
You need to bind a change event to your select element so that when you change the select value, you execute the function that will change the font family.
bind-value-to is a custom directive in the kitchensink app so don't worry about it.

scrollLeft setter is not working (Angular, jqLitle)

I have this code
var input = element.find('input').eq(0);
scope.focus = function() {
if (!input.length) return;
input[0].focus();
input[0].select();
var divWrapper = input.parent('.tags')[0];
divWrapper.scrollLeft = divWrapper.scrollWidth;
console.log(divWrapper.scrollWidth);
console.log(divWrapper.scrollLeft);
};
This is inside a directive fnLink and focus is called when ui is changed this because use overflow: hidden and input disapear when many items on left... so I need to scroll to far right and keep input visible.
The
divWrapper.scrollLeft = divWrapper.scrollWidth;
works fine on chrome dev tools but when focus is called don't update the scrollLeft attribute.

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.

Resources