How to bind DOM property in directive - css

Given this example:
var SomeApp = angular.module('SomeApp', [])
.controller('SomeController', function($scope){
$scope.items = [0,1,2,3]
})
.directive('gridResize', function(){
return {
scope: true,
link: function(scope, elem) {
scope.gridResize = {
width: $(elem).width(),
height: $(elem).height()
};
}
}
})
.parent {
width: 80%;
position: relative;
left: 50%;
transform: translateX(-50%);
border: 1px solid red;
}
.parent > * {
background-color: rgba(0,0,0,.14);
margin-bottom: 1px;
padding: 20px;
box-sizing: border-box;
}
.parent > *:last-child {
margin-bottom: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="SomeApp">
<div class="parent" ng-controller="SomeController" grid-resize>
<div ng-style="{'min-height':($parent.gridResize.width/8) + 'px'}"
ng-repeat="item in items"
>
height: {{$parent.gridResize.height}} | width: {{$parent.gridResize.width}}
</div>
</div>
</div>
Can anyone tell me how I could bind the height and width of the grid-resize directive to the DOM element? I want the angular properties to change when the DOM element changes.

In your directive use the window "resize" event to update the sizes on the scope:
var SomeApp = angular.module('SomeApp', [])
.controller('SomeController', function($scope){
$scope.items = [0,1,2,3]
})
.directive('gridResize', function(){
return {
scope: false,
link: function(scope, elem) {
scope.gridResize = {
width: $(elem).width(),
height: $(elem).height()
};
angular.element(window).on('resize', function(e) {
scope.gridResize = {
width: $(elem).width(),
height: $(elem).height()
};
scope.$apply();
});
}
}
})
Also notice that I changed the directive's scope to 'false'. You already have a scope on that element created by the ng-controller directive.

Related

How to change css width 50% to 100% using Vue

How can I change css width from 50% to 100 % when click the button see more detail here >>> Sample sandbox
<template>
<div id="theSpecial">Hello World Special</div>
<button #click="changeWidth">Change width</button>
</template>
<script>
export default {
data() {
return {
testBoolean: false,
};
},
methods: {
changeWidth() {
this.testBoolean = true;
//change width to 100%
},
},
};
</script>
CSS
#theSpecial {
background-color: purple;
color: white;
width: 50%;
}
You have to make some change on your code
First of all add this to your css
.theSpecial{width:50%}
.fullWidth{width:100%}
To toggle the full width modify the method
changeWidth() {
this.testBoolean = !this.testBoolean;
//this will toggle the width on every click
},
and then use this in your component template
<div class="theSpecial" v-bind:class="{fullWidth:testBoolean}">
N.B. change the id into class, beacuse id has more css specifity.
This will toggle the class full width accordly to the value of testBoolean.
This is your Sandbox
Here you can find documentation about class binding
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<div id="theSpecial" :class="{ 'full-width': testBoolean }">
Hello World Special
</div>
<button #click="changeWidth">Change width</button>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
msg: String,
},
data() {
return {
testBoolean: false,
};
},
methods: {
changeWidth() {
this.testBoolean = true;
},
},
};
</script>
#theSpecial {
background-color: purple;
color: white;
width: 50%;
}
#theSpecial.full-width {
width: 100%;
}
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
data() {
return {
testBoolean: false,
};
},
methods: {
changeWidth() {
this.testBoolean = !this.testBoolean;
//change width to 100%
},
},
.theSpecial {
background-color: purple;
color: white;
width: 50%;
}
.fullwidth {
background-color: purple;
color: white;
width: 100%;
}
<div :class="(this.testBoolean === true)? 'fullwidth':'theSpecial'">Hello World Special</div>
<button #click="changeWidth">Change width</button>

How to fix v-align styling for imgs with different sizes in Vue.js transition-group?

I prepared example of image slider what I need.
I encounter with styling issue when using images with various dimensions. When element leaving the array, its location is set as absolute value, what is necessary for smooth transition, tho, but the image is also moved up.
I would like to have nice vertical align into middle even leave or enter the array, but could not get on any way.
Another issue, what I would like to solve is when left the window and then went back after a while. The animation running all cycles at once to reach current state instead just stop animation and run after. Maybe it is my responsibility, but browsers doesn't offer nice event to catch blur window or am I wrong?
According to this discussion
Thank you for any ideas.
let numbers = [{key:1},{key:2},{key:3},{key:4},{key:5},{key:6},{key:7}]
let images = [
{ key:1,
src:"http://lorempixel.com/50/100/sports/"},
{ key:2,
src:"http://lorempixel.com/50/50/sports/"},
{ key:3,
src:"http://lorempixel.com/100/50/sports/"},
{ key:4,
src:"http://lorempixel.com/20/30/sports/"},
{ key:5,
src:"http://lorempixel.com/80/20/sports/"},
{ key:6,
src:"http://lorempixel.com/20/80/sports/"},
{ key:7,
src:"http://lorempixel.com/100/100/sports/"}
]
new Vue({
el: '#rotator',
data: {
items: images,
lastKey: 7,
direction: false
},
mounted () {
setInterval(() => {
if (this.direction) { this.prevr() } else { this.nextr() }
}, 1000)
},
methods: {
nextr () {
let it = this.items.shift()
it.key = ++this.lastKey
this.items.push(it)
},
prevr () {
let it = this.items.pop()
it.key = ++this.lastKey
this.items.unshift(it)
}
}
})
.litem {
transition: all 1s;
display: inline-block;
margin-right: 10px;
border: 1px solid green;
background-color: lightgreen;
padding: 10px 10px 10px 10px;
height: 100px;
}
.innerDiv {
border: 1px solid red;
}
.container {
overflow: hidden;
white-space: nowrap;
height: 250px;
border: 1px solid blue;
background-color: lightblue;
}
.list-enter {
opacity: 0;
transform: translateX(40px);
}
.list-leave-to {
opacity: 0;
transform: translateX(-40px);
}
.list-leave-active {
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.11/vue.js"></script>
<div id="rotator">
<button #click="direction = !direction">
change direction
</button>
<transition-group
name="list"
tag="div"
class="container">
<div
v-for="item in items"
:key="item.key" class="litem">
<!--
<div
class='innerDiv'>
{{ item.key }}
</div>
-->
<div class='innerDiv'>
<img :src='item.src'>
</div>
</div>
</transition-group>
</div>
It tooks a while but on the end I think that I have got better result for sliding animation with changing direction feature.
One annoying think is when I swithing the sliding direction so animation is for a 'microsecond' changed to next state and than return to correct one, after it the animation continue as expected. It is happening only in one direction and I don't know how to fix it. Also last box behave differently too only once. No clue at all.
So just 98% solution :-)
let images = [
{key:1, domKey:1, src:"http://lorempixel.com/50/100/sports/" },
{key:2, domKey:2, src:"http://lorempixel.com/50/50/sports/" },
{key:3, domKey:3, src:"http://lorempixel.com/100/50/sports/" },
{key:4, domKey:4, src:"http://lorempixel.com/20/30/sports/" },
{key:5, domKey:5, src:"http://lorempixel.com/80/20/sports/" },
{key:6, domKey:6, src:"http://lorempixel.com/20/80/sports/" },
{key:7, domKey:7, src:"http://lorempixel.com/100/100/sports/" }
]
let setPositionRelative = el => el.style.position = "relative"
new Vue({
el: '#rotator',
data: {
items: images,
lastKey: 7,
direction: true,
changeDirectionRequest: false
},
mounted () {
Array.from(this.$el.querySelectorAll("div[data-key]")).map(setPositionRelative)
setInterval(() => {
if(this.changeDirectionRequest) {
this.changeDirectionRequest = false
this.direction = !this.direction
if (this.direction)
Array.from(this.$el.querySelectorAll("div[data-key]")).map(setPositionRelative)
else
Array.from(this.$el.querySelectorAll("div[data-key]")).map(el => el.style.position = "")
}
if (this.direction) this.prevr()
else this.nextr()
}, 1000)
},
methods: {
nextr () {
let it = this.items.shift()
it.key = ++this.lastKey
this.items.push(it)
},
prevr () {
let it = this.items.pop()
it.key = ++this.lastKey
this.items.unshift(it)
setPositionRelative(this.$el.querySelector("div[data-domkey='"+it.domKey+"']"))
}
}
})
.container {
overflow: hidden;
white-space: nowrap;
height: 200px;
border: 1px solid blue;
background-color: lightblue;
display: flex;
align-items: center;
}
.innerDiv {
border: 1px solid red;
width: auto;
height: auto;
display:-moz-box;
-moz-box-pack:center;
-moz-box-align:center;
display:-webkit-box;
-webkit-box-pack:center;
-webkit-box-align:center;
display:box;
box-pack:center;
box-align:center;
}
.litem {
transition: all 1s;
margin-right: 10px;
border: 1px solid green;
background-color: lightgreen;
padding: 10px 10px 10px 10px;
}
.list2-enter, .list-enter {
opacity: 0;
transform: translateX(40px);
}
.list2-leave-to, .list-leave-to {
opacity: 0;
transform: translateX(-40px);
}
.list-leave-active {
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.11/vue.js"></script>
<div id="rotator">
<button #click="changeDirectionRequest = true">change direction</button>
<transition-group name="list" tag="div" class="container">
<div v-for="item in items"
:key="item.key"
:data-domkey="item.domKey"
class="litem">
<div class='innerDiv'>
<img :src='item.src'>
</div>
</div>
</transition-group>
</div>

Double scrollbar with shrinking toolbar

I am working on a photo gallery in AngularJS using Angular Material (run the snippet in fullscreen to see my problem).
var app = angular.module('app', ['ngMaterial']);
app.controller('TitleController', function($scope) {
$scope.title = 'Gallery';
});
app.controller('GalleryCtrl', function($scope, $http, $q, $mdMedia, $mdDialog) {
//https://material.angularjs.org/latest/demo/virtualRepeat
$scope.Images = [],
//add more images
$scope.LoadMore = function() {
for (i = 0; i < 25; i++) {
var randomWidth = Math.round(Math.random() * (800 - 400) + 400);
var randomHeight = Math.round(Math.random() * (800 - 400) + 400);
$scope.Images.push({
src: "http://placehold.it/" + randomWidth + "x" + randomHeight + "/",
id: Math.round(Math.random() * 10000)
});
};
}
$scope.ShowDetails = function(ev, number) {
var useFullScreen = ($mdMedia('sm') || $mdMedia('xs')) && $scope.customFullscreen;
$mdDialog.show({
controller: DialogController,
templateUrl: 'Home/GetInfo?id=' + number,
parent: angular.element(document.body),
targetEvent: ev,
clickOutsideToClose: true,
fullscreen: useFullScreen
})
$scope.$watch(function() {
return $mdMedia('xs') || $mdMedia('sm');
}, function(wantsFullScreen) {
$scope.customFullscreen = (wantsFullScreen === true);
});
};
function DialogController($scope, $mdDialog) {
$scope.hide = function() {
$mdDialog.hide();
};
$scope.cancel = function() {
$mdDialog.cancel();
};
$scope.answer = function(answer) {
$mdDialog.hide(answer);
};
}
//initial loading
$scope.LoadMore();
});
body {
background: #eeeeee;
}
html {
background: #eeeeee;
}
.gridListdemoBasicUsage md-grid-list {
margin: 8px;
}
.gridListdemoBasicUsage .green {
background: #b9f6ca;
}
.gridListdemoBasicUsage md-grid-tile {
transition: all 400ms ease-out 50ms;
}
.responsiveImage {
max-width: 100%;
max-height: 100%;
}
md-content {
background: #eeeeee;
position: relative;
}
.fit {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.toolbardemoScrollShrink .face {
width: 48px;
margin: 16px;
border-radius: 48px;
border: 1px solid #ddd;
}
.md-toolbar-tools {
background-color: #259b24;
}
.dialogdemoBasicUsage #popupContainer {
position: relative;
}
.dialogdemoBasicUsage .footer {
width: 100%;
text-align: center;
margin-left: 20px;
}
.dialogdemoBasicUsage .footer,
.dialogdemoBasicUsage .footer > code {
font-size: 0.8em;
margin-top: 50px;
}
.dialogdemoBasicUsage button {
width: 200px;
}
.dialogdemoBasicUsage div#status {
color: #c60008;
}
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.0.7/angular-material.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular-animate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular-aria.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.0.7/angular-material.min.js"></script>
<body ng-app="app" ng-controller="GalleryCtrl as gc" ng-cloak="" id="popupContainer" class="gridListdemoBasicUsage dialogdemoBasicUsage">
<md-toolbar md-scroll-shrink="" ng-if="true" ng-controller="TitleController">
<div class="md-toolbar-tools">
<h3><span>{{title}}</span></h3>
</div>
</md-toolbar>
<md-content style="height:100vh" />
<md-grid-list md-cols-xs="1" md-cols-sm="2" md-cols-md="4" md-cols-gt-md="6" md-row-height-gt-md="1:1" md-row-height="2:2" md-gutter="12px" md-gutter-gt-sm="8px">
<md-grid-tile ng-click="ShowDetails($event, n.id)" ng-repeat="n in Images" class="green">
<img class="responsiveImage" src="{{n.src}}">
<md-grid-tile-footer>
<h3>Photo number {{n.id}}</h3>
</md-grid-tile-footer>
</md-grid-tile>
</md-grid-list>
<section layout="row" layout-sm="column" layout-align="center center" layout-wrap="">
<md-button class="md-raised md-primary" ng-click="LoadMore()">Primary</md-button>
</section>
</body>
Go fullscreen, scroll to the bottom of the page, and press a button to load more images. The problem I'm having: I am trying to get the toolbar at the top of the screen to disappear when scrolling down, and appear again when scrolling up. However, 2 scrollbars appear, and only the right one affects the toolbar.The left scrollbar actually scrolls all the way down on the page.
My desired situation: only 1 visible scrollbar to scroll down the entire gallery, that also makes the toolbar appear and disappear. How do I do this?
try adding overflow-y: hidden; to the body css rule.

Custom ReactJS component that respects 'valueLink'

I'm building a custom ReactJS component (A 'Select2' dropdown library wrapper) and want to implement support for the standard two-way binding helper with the 'valueLink' parameter.
However it appears the mixin to handle the 'valueLink' parameter only applies to standard components, not custom components.
Is there a way to have my component implement the standard valueLink behaviour automatically, or will I need to explicitly parse and implement this support myself (Potentially introducing bugs or odd behaviours that aren't present in the base library)
The object returned from this.linkState when using the LinkedStateMixin has two relevant properties: value and requestChange(). Simply use those two properties as your value and change handler, respectively, just as if they had been passed to your custom component via value and onChange.
Here's an example of a composite component that wraps a jQuery color picker; it works both with valueLink and with standard value and onChange properties. (To run the example, expand the "Show code snippet" then click "Run code snippet" at the bottom.)
var ColorPicker = React.createClass({
render: function() {
return <div />;
},
getValueLink: function(props) {
// Create an object that works just like the one
// returned from `this.linkState` if we weren't passed
// one; that way, we can always behave as if we're using
// `valueLink`, even if we're using plain `value` and `onChange`.
return props.valueLink || {
value: props.value,
requestChange: props.onChange
};
},
componentDidMount: function() {
var valueLink = this.getValueLink(this.props);
jQuery(this.getDOMNode()).colorPicker({
pickerDefault: valueLink.value,
onColorChange: this.onColorChange
});
},
componentWillReceiveProps: function(nextProps) {
var valueLink = this.getValueLink(nextProps);
var node = jQuery(this.getDOMNode());
node.val(valueLink.value);
node.change();
},
onColorChange: function(id, color) {
this.getValueLink(this.props).requestChange(color);
}
});
div.colorPicker-picker {
height: 16px;
width: 16px;
padding: 0 !important;
border: 1px solid #ccc;
background: url(https://raw.github.com/laktek/really-simple-color-picker/master/arrow.gif) no-repeat top right;
cursor: pointer;
line-height: 16px;
}
div.colorPicker-palette {
width: 110px;
position: absolute;
border: 1px solid #598FEF;
background-color: #EFEFEF;
padding: 2px;
z-index: 9999;
}
div.colorPicker_hexWrap {width: 100%; float:left }
div.colorPicker_hexWrap label {font-size: 95%; color: #2F2F2F; margin: 5px 2px; width: 25%}
div.colorPicker_hexWrap input {margin: 5px 2px; padding: 0; font-size: 95%; border: 1px solid #000; width: 65%; }
div.colorPicker-swatch {
height: 12px;
width: 12px;
border: 1px solid #000;
margin: 2px;
float: left;
cursor: pointer;
line-height: 12px;
}
<script src="http://fb.me/react-with-addons-0.11.2.js"></script>
<script src="http://fb.me/JSXTransformer-0.11.2.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="https://dl.dropboxusercontent.com/u/113308/dnd/jsfiddle/jquery.colorPicker.min.js"></script>
<p><strong>With valueLink</strong></p>
<div id="app1"></div>
<hr>
<p><strong>With value and onChange</strong></p>
<div id="app2"></div>
<script type="text/jsx">
/** #jsx React.DOM */
var ApplicationWithValueLink = React.createClass({
mixins: [React.addons.LinkedStateMixin],
getInitialState: function() {
return { color: "#FF0000" }
},
render: function() {
return (
<div>
<div>
<span style={{color: this.state.color}}>My Color Picker</span>
<button onClick={this.changeColor.bind(null, "#FF0000")}>Red</button>
<button onClick={this.changeColor.bind(null, "#00FF00")}>Green</button>
<button onClick={this.changeColor.bind(null, "#0000FF")}>Blue</button>
<input type="text" valueLink={this.linkState("color")} />
</div>
<div>
<ColorPicker valueLink={this.linkState("color")} />
</div>
</div>
);
},
changeColor: function(color) {
this.setState({color: color});
}
});
var ApplicationWithoutValueLink = React.createClass({
getInitialState: function() {
return { color: "#FF0000" }
},
render: function() {
return (
<div>
<div>
<span style={{color: this.state.color}}>My Color Picker</span>
<button onClick={this.changeColor.bind(null, "#FF0000")}>Red</button>
<button onClick={this.changeColor.bind(null, "#00FF00")}>Green</button>
<button onClick={this.changeColor.bind(null, "#0000FF")}>Blue</button>
<input type="text" value={this.state.color} onChange={this.changeColorText} />
</div>
<div>
<ColorPicker value={this.state.color} onChange={this.changeColor} />
</div>
</div>
);
},
changeColor: function(color) {
this.setState({color: color});
},
changeColorText: function(evt) {
this.changeColor(evt.target.value);
}
});
var ColorPicker = React.createClass({
render: function() {
return (
<div />
);
},
getValueLink: function(props) {
return props.valueLink || {
value: props.value,
requestChange: props.onChange
};
},
componentDidMount: function() {
var valueLink = this.getValueLink(this.props);
jQuery(this.getDOMNode()).colorPicker({
pickerDefault: valueLink.value,
onColorChange: this.onColorChange
});
},
componentWillReceiveProps: function(nextProps) {
var valueLink = this.getValueLink(nextProps);
var node = jQuery(this.getDOMNode());
node.val(valueLink.value);
node.change();
},
onColorChange: function(id, color) {
this.getValueLink(this.props).requestChange(color);
}
});
React.renderComponent(<ApplicationWithValueLink />, document.getElementById("app1"));
React.renderComponent(<ApplicationWithoutValueLink />, document.getElementById("app2"));
</script>

jQuery UI Droppable block by absolute div

How can a jquery ui droppable be blocked by an overlaid div?
See this example: http://jsfiddle.net/JSFU4/3/. A red div is overlaying the droppable area; however, when dragging on top of this area, the droppable is still activated. How can this be avoided?
Html
<div>
<div class="drop">drop here</div>
</div>
<div>
<div class="drag">drag me</div>
</div>
<div class="overlay">i want to block the droppable</div>
CSS
.drag {
display: inline-block;
width: 100px;
height: 100px;
border: 1px solid black;
background-color: lightblue;
z-index: 1;
}
.drop {
display: inline-block;
border: 1px dotted black;
width: 200px;
height: 200px;
}
.drop-hover {
background-color: grey;
}
.overlay {
position: absolute;
width: 300px;
height: 100px;
top: 50px;
background-color: red;
border: 1px solid black;
}
JS
$(function () {
$('.drag').draggable();
$('.drop').droppable({
tolerance: 'pointer',
hoverClass: 'drop-hover'
});
});
I don't think there is anything out of box to achieve this. You need to do some kind of collision detection and then do your logic accordingly. I have done a POC for you.
DEMO
var drag = $('.drag');
var overlay = $('.overlay');
$(function () {
$('.drag').draggable();
$('.drop').droppable({
tolerance: 'pointer',
hoverClass: 'drop-hover'
});
});
var int = self.setInterval(function () {
if (overlaps(drag, overlay)) {
$('.drop').css('visibility', 'hidden');
} else {
$('.drop').css('visibility', 'visible');
}
}, 100);
var overlaps = (function () {
function getPositions(elem) {
var pos, width, height;
pos = $(elem).position();
width = $(elem).width() / 2;
height = $(elem).height();
return [[pos.left, pos.left + width], [pos.top, pos.top + height]];
}
function comparePositions(p1, p2) {
var r1, r2;
r1 = p1[0] < p2[0] ? p1 : p2;
r2 = p1[0] < p2[0] ? p2 : p1;
return r1[1] > r2[0] || r1[0] === r2[0];
}
return function (a, b) {
var pos1 = getPositions(a),
pos2 = getPositions(b);
return comparePositions(pos1[0], pos2[0]) && comparePositions(pos1[1], pos2[1]);
};
})();
The code above detects the collision between two DIVs and returns true or false.

Resources