Angular Directive only works on one element at a time - javascript

I have the following angular directive. It uses a range slider to scroll a horizontal div of items. It works if applied to only one row. But if applied to multiple...nothing happens. The transclusion works, but function never runs and no errors are given. What can I do to this to make it be more angular universal and work on multiple elements? Here is repro:CodePen
app.directive('bob', function () {
return {
restrict: 'A',
transclude: true,
template:
`<div style="background-color: #333"><input type="range" value="0" id="scroll-rangeb"><div id="photo-containerb" style="display: flex; overflow-x: scroll; flex-direction: row; align-items: center; height: 90%;" ng-transclude></div></div>`,
link: function (scope, element, attrs) {
var scroll = document.getElementById("scroll-rangeb");
scroll.oninput = function () {
var panel = document.getElementById("photo-containerb");
var total = panel.scrollWidth - panel.offsetWidth;
var percentage = total * (this.value / 100);
panel.scrollLeft = percentage;
}
}
};
});

#scroll-rangeb is a unique element (in theory), if you override the oninput on every directive it clearly will not work, it'll remain only the first one found. Anyways, you mustn't use multiple components with the same id at all. Try to find it from the element parameter given on the link function instead, using classes or somthing else.
For example, I could get it solved by using element[0].getElementsByClassName('scroll-rangeb') :
angular.module('app', [])
.directive('bob', function() {
return {
restrict: 'A',
transclude: true,
template: `
<div style="background-color: #333">
<input type="range" value="0" class="scroll-rangeb">
<div
class="photo-containerb"
style="display: flex; overflow-x: scroll; flex-direction: row; align-items: center; height: 90%;"
ng-transclude>
</div>
</div>`,
link: function(scope, $element, attrs) {
var
element = $element[0],
scroll = element.getElementsByClassName("scroll-rangeb")[0],
panel = element.getElementsByClassName("photo-containerb")[0];
scroll.oninput = function() {
var total = panel.scrollWidth - panel.offsetWidth;
var percentage = total * (this.value / 100);
panel.scrollLeft = percentage;
}
}
};
});
img {
border-radius: 50%;
}
.box {
display: block;
width: 50px;
height: 50px;
min-width: 50px;
margin: 10px;
}
<div ng-app="app">
<div bob>
<img class="box" ng-repeat="img in [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==">
</div>
<div bob>
<img class="box" ng-repeat="img in [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==">
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.js"></script>

Related

The javascript onDrop event is not executing

The javascript onDrop event is not executing when an object is dropped into a drop zone. I've tried re-writing this in at least half a dozen ways with no luck and no error message either. I was looking around for reasons that would prevent the ondrop event from executing and found:
HTML5/Canvas onDrop event isn't firing?
and
https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Drag_operations#droptargets
To the best of my knowledge, I've accomplished all three requirements to trigger an ondrop event. I implemented both the ondragenter and ondragover events which both contain event.preventDefault(); and return false;. I also have event.preventDefault(); in the ondrop event.
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.lists = [{name: "A", list: ["A Object 1", "A Object 2", "A Object 3"]},
{name: "B", list: ["B Object 1", "B Object 2", "B Object 3"]}];
$scope.drag_index = null;
$scope.drag_obj = null;
});
var dragging = false;
function toggle_dz() {
$(".drop-it").toggle();
}
function get_gbls(cs) {
while (cs.$parent) {
cs = cs.$parent;
}
return cs;
}
app.directive('zoneIt', function(){
return {
restrict: 'A',
link: function(scope, element, attrs){
$(element).on('dragenter',function(event){
event.preventDefault();
console.log("Drag Enter!");
return false;
});
$(element).on('dragover',function(event){
event.preventDefault();
console.log("Drag Over!");
return false;
});
}
};
});
app.directive('dragIt', function(){
return {
restrict: 'A',
link: function(scope, element, attrs){
$(element).on('drag',function(event){
var scope = angular.element(event.target).scope();
get_gbls(scope).drag_index = scope.$index;
get_gbls(scope).drag_obj = scope.obj;
if (!dragging) {
toggle_dz();
dragging = true;
}
});
$(element).on('drop',function(event){
event.preventDefault();
alert("DROPPED!");
});
$(element).on('dragend',function(event){
toggle_dz();
dragging = false;
var scope = angular.element(event.target).scope();
get_gbls(scope).drag_index = null;
get_gbls(scope).drag_obj = null;
get_gbls(scope).$apply();
});
}
};
});
.zone-it {
height: 150px;
width: 90%;
background-color: #1B6269;
border: 10px dashed #4D3A44;
border-radius: 10px;
margin: 10px 5%;
padding: 15px;
font-size: 100px;
color: #4D3A44;
text-shadow: 0 0 10px black;
}
.zone-it span {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<div class="container-fluid" ng-app="myApp" ng-controller="myCtrl">
<h2>Angular Drag n Drop</h2>
<div class="col-xs-6" ng-repeat="list in lists">
<h3>{{list.name}}</h3>
<hr>
<div class="col-xs-12 drop-it zone-it" zone-it style="display: none;"><span class="text-center glyphicon glyphicon-download"></span></div>
<div class="drop-it container-it">
<div class="col-sm-6 col-md-4 col-lg-3" ng-repeat="obj in list.list">
<div class="thumbnail drag-it" draggable="true" drag-it style="cursor:pointer">
<div class="caption text-center">{{obj}}</div>
</div>
</div>
</div>
</div>
</div>
Here is a fiddle of my code: https://jsfiddle.net/Ashkeelun/2uLwd077/1/
So, I figured it out. The ondrop event belongs with the drop zone not the item being dragged.
https://jsfiddle.net/Ashkeelun/2uLwd077/4/
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.lists = [{name: "A", list: ["A Object 1", "A Object 2", "A Object 3"]},
{name: "B", list: ["B Object 1", "B Object 2", "B Object 3"]}];
$scope.drag_index = null;
$scope.drag_obj = null;
});
var dragging = false;
function toggle_dz() {
$(".drop-it").toggle();
}
function get_gbls(cs) {
while (cs.$parent) {
cs = cs.$parent;
}
return cs;
}
app.directive('zoneIt', function(){
return {
restrict: 'A',
link: function(scope, element, attrs){
$(element).on('dragenter',function(event){
event.preventDefault();
console.log("Drag Enter!");
return false;
});
$(element).on('dragover',function(event){
event.preventDefault();
console.log("Drag Over!");
return false;
});
$(element).on('drop',function(event){
event.preventDefault();
alert("DROPPED!");
});
}
};
});
app.directive('dragIt', function(){
return {
restrict: 'A',
link: function(scope, element, attrs){
$(element).on('drag',function(event){
var scope = angular.element(event.target).scope();
get_gbls(scope).drag_index = scope.$index;
get_gbls(scope).drag_obj = scope.obj;
if (!dragging) {
toggle_dz();
dragging = true;
}
});
$(element).on('dragend',function(event){
toggle_dz();
dragging = false;
var scope = angular.element(event.target).scope();
get_gbls(scope).drag_index = null;
get_gbls(scope).drag_obj = null;
get_gbls(scope).$apply();
});
}
};
});
.zone-it {
height: 150px;
width: 90%;
background-color: #1B6269;
border: 10px dashed #4D3A44;
border-radius: 10px;
margin: 10px 5%;
padding: 15px;
font-size: 100px;
color: #4D3A44;
text-shadow: 0 0 10px black;
}
.zone-it span {
display: block;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<div class="container-fluid" ng-app="myApp" ng-controller="myCtrl">
<h2>Angular Drag n Drop</h2>
<div class="col-xs-6" ng-repeat="list in lists">
<h3>{{list.name}}</h3>
<hr>
<div class="col-xs-12 drop-it zone-it" zone-it style="display: none;"><span class="text-center glyphicon glyphicon-download"></span></div>
<div class="drop-it container-it">
<div class="col-sm-6 col-md-4 col-lg-3" ng-repeat="obj in list.list">
<div class="thumbnail drag-it" draggable="true" drag-it style="cursor:pointer">
<div class="caption text-center">{{obj}}</div>
</div>
</div>
</div>
</div>

Issue with multiple child HTML elements using Directives in AngularJS

I'm using a template to create a popup menu that will show alerts if there is a new one and it's working till now. But i wanted to add manual alert, that's why i thought to add an input text but Oupss, i can't write on the input field and i don't even know why.The input field is sort of Disabled!!!
My directive is like so :
$scope.tb = { x: 0, y: 0 };
module.directive('myDraggable', function ($document, $interval) {
return {
restrict: 'EA',
replace: true,
//scope : true,
scope: { menu: '=drSrc'},
link: function (scope, element, attr) {
var startX = 0, startY = 0, x = scope.menu.x || 0, y = scope.menu.y || 0, positionX = [], positionY = [], time = [], width, height, moveInterval;
element.draggable({
position: 'relative',
cursor: 'pointer',
top: y + 'px',
left: x + 'px'
});
element.on('mousedown', function (event) {
// Prevent default dragging of selected content
event.preventDefault();
startX = event.pageX - x;
startY = event.pageY - y;
$document.on('mousemove', mousemove);
$document.on('mouseup', mouseup);
$interval.cancel(moveInterval);
});
function mousemove(event) {
y = event.pageY - startY;
x = event.pageX - startX;
//calculate the borders of the document
width = $(document).width() - 350;
height = $(document).height() - 150;
positionX.push(x);
positionY.push(y);
time.push(Date.now());
}
}
}
});
I tried to make scope true but i faced 2 problems, :
I can't move my popup anymore (yes my popup menu is Draggable)
And Also the input text does not show my text i'm typing.
Here's my cache template :
$templateCache.put('control.tpl.html', '<div class="container" my-draggable dr-src="tb"><div><div class="col-sm-1 col-md-1 sidebar"><div class="list-group" ><span href="#" class="list-group-item active" >Manage<input type="text" class="pull-right" placeholder="Type..." /></span><div ng-repeat="Alert in Alerts"><a href="#" ng-click="showLocation(Alert.id)" class="list-group-item" >Alert {{Alert.id}}</span><img src="../images/alert_icon_manage.png" class="pull-right"/> </a></div><span href="#" class="list-group-item active"></span></div></div></div></div>');
I'm new with AngularJS and Directive and I don't know how to solve this but I think it's a problem with Scopes!!
Thank you.
UPDATE :
If I delete scope:{menu:"=drSrc"} That work and i can type what i want but the problem is that my element is no more draggable.
I think it's sth related to scopes. can anyone help please?
scope: true indicates that your directive should inherit its parent's scope, but scope: {menu: '=drSrc'} creates an isolated scope, which remove your template's access to Alerts. When you remove scope: {menu: '=drSrc'}, menu no longer exists, so scope.menu.x fails and your element is no longer draggable.
The simplest fix is to use scope: true and reference scope.drSrc.x, etc. instead of scope.menu.x. With scope: true, you get access to the parent's scope, including drSrc and the Alerts data your template is using.
These writeups are useful in understanding directives and scopes:
Angular's directive docs
Understanding Scopes
What is the difference between '#' and '=' in directive scope
I'm currently working on a project that depends heavily upon Modal Dialogs. Each with their own purpose and dynamic content.
Here's the system I have been working with:
index.html
<!doctype html>
<html ng-app="myApp" ng-controller="MainCtrl">
<head>
<title>Dialogs</title>
<link rel="stylesheet" href="app.css">
</head>
<body>
<button ng-click="openDialog()">Open Dialog</button>
<modal-dialog show="showMe" dialog-controller="WelcomeDialogCtrl" context="welcome"></modal-dialog>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.js"></script>
<script src="app.js"></script>
</body>
</html>
app.js
var app = angular.module('myApp', []);
// The main controller, which will handle index.html
app.controller('MainCtrl', ['$scope', function($scope) {
$scope.showMe = false;
$scope.openDialog = function(){
$scope.showMe = true; // Show the 'welcome' dialog
};
}]);
// The modal dialog directive
app.directive('modalDialog', [function() {
return {
controller: '#', // Bind a controller
name: 'dialogController', // Bind the controller to this attribute name
restrict: 'E',
scope: {
show: '='
},
link: function(scope, element, attrs) {
// Close this dialog (actually ng-hides it)
scope.closeDialog = function() {
scope.show = false;
};
},
templateUrl: function(element, attrs){
// I prefer to load my dialog templates from a separate folder to keep my project tidy
return 'dialogs/' + attrs.context + '.html';
}
};
}]);
// The 'welcome' dialog has its own controller with its own scope
app.controller('WelcomeDialogCtrl', ['$scope', function($scope){
// This can be called by the template that resides within the directive
$scope.exampleFunction = function(text){
console.log('Example function says: ' + text);
};
}]);
welcome.html
<div class="dialog" ng-show="show">
<div class="dialog-overlay"></div>
<div class="dialog-box">
Welcome, be sure to check out this blazin' dialog.
<button ng-click="exampleFunction('Hi!')">Say Hi!</button>
<button ng-click="closeDialog()">Close</button>
</div>
</div>
app.css
body{
background: #eee;
margin: 80px;
}
/*
* Just some fancy schmuck
*/
button{
border-radius: 5px;
background: #272;
color: #fff;
padding: 5px 12px;
border: 0;
}
/*
* The grey, transparent curtain.
*/
.dialog-overlay {
width: 100%;
height: 100%;
position: absolute;
background: #111;
opacity: 0.2;
top: 0;
left: 0;
z-index: 100;
}
/*
* The dialog itself. Horribly centered.
*/
.dialog-box{
background: #fff;
border-radius: 5px;
padding: 10px 20px;
position: absolute;
width: 600px;
height: 300px;
top: 50%;
left: 50%;
margin-left: -300px;
z-index: 110;
}
I also made a Plunker with the same code.

Rendering a Star Rating System using angularjs

My app is in this Fiddle
I need to render a star rating system dynamically from a http service, where the current stars and maximum stars can vary with each case.
Is it a good idea to create arrays from $scope.current and
$scope.max - $scope.current and pass them and run ng-repeat over them, or there is a more optimised solution than this.
Iteration ng-repeat only X times in AngularJs
Star Rating can be done either statically (read-only) or dynamically
If you want just simply to display Rating as star then try the below one
Static Star Rating
Working Example
html
<body ng-app="starApp">
<div ng-controller="StarCtrl"> <span ng-repeat="rating in ratings">{{rating.current}} out of
{{rating.max}}
<div star-rating rating-value="rating.current" max="rating.max" ></div>
</span>
</div>
</body>
script
var starApp = angular.module('starApp', []);
starApp.controller('StarCtrl', ['$scope', function ($scope) {
$scope.ratings = [{
current: 5,
max: 10
}, {
current: 3,
max: 5
}];
}]);
starApp.directive('starRating', function () {
return {
restrict: 'A',
template: '<ul class="rating">' +
'<li ng-repeat="star in stars" ng-class="star">' +
'\u2605' +
'</li>' +
'</ul>',
scope: {
ratingValue: '=',
max: '='
},
link: function (scope, elem, attrs) {
scope.stars = [];
for (var i = 0; i < scope.max; i++) {
scope.stars.push({
filled: i < scope.ratingValue
});
}
}
}
});
If you want to do Star Rating dynamically try this out
Dynamic Star Rating
Working Demo
Html
<body ng-app="starApp">
<div ng-controller="StarCtrl"> <span ng-repeat="rating in ratings">{{rating.current}} out of
{{rating.max}}
<div star-rating rating-value="rating.current" max="rating.max" on-rating-selected="getSelectedRating(rating)"></div>
</span>
</div>
</body>
script
var starApp = angular.module('starApp', []);
starApp.controller('StarCtrl', ['$scope', function ($scope) {
$scope.rating = 0;
$scope.ratings = [{
current: 5,
max: 10
}, {
current: 3,
max: 5
}];
$scope.getSelectedRating = function (rating) {
console.log(rating);
}
}]);
starApp.directive('starRating', function () {
return {
restrict: 'A',
template: '<ul class="rating">' +
'<li ng-repeat="star in stars" ng-class="star" ng-click="toggle($index)">' +
'\u2605' +
'</li>' +
'</ul>',
scope: {
ratingValue: '=',
max: '=',
onRatingSelected: '&'
},
link: function (scope, elem, attrs) {
var updateStars = function () {
scope.stars = [];
for (var i = 0; i < scope.max; i++) {
scope.stars.push({
filled: i < scope.ratingValue
});
}
};
scope.toggle = function (index) {
scope.ratingValue = index + 1;
scope.onRatingSelected({
rating: index + 1
});
};
scope.$watch('ratingValue', function (oldVal, newVal) {
if (newVal) {
updateStars();
}
});
}
}
});
There is a wonderful tutorial here for more explanation about Angular Star Rating
You can even try angular-ui. Here is the link.
Just need to add this tag.
<rating ng-model="rate" max="max"
readonly="isReadonly"
on-hover="hoveringOver(value)"
on-leave="overStar = null">
//Controller
var starApp = angular.module('starApp', []);
starApp.controller('StarCtrl', ['$scope', function ($scope) {
$scope.maxRating = 10;
$scope.ratedBy = 0;
$scope.rateBy = function (star) {
$scope.ratedBy = star;
}
}]);
.rating {
color: #a9a9a9;
margin: 0;
padding: 0;
}
ul.rating {
display: inline-block;
}
.rating li {
list-style-type: none;
display: inline-block;
padding: 1px;
text-align: center;
font-weight: bold;
cursor: pointer;
}
.rating .filled {
color: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="starApp">
<div ng-controller="StarCtrl">
<ul class="rating">
<li ng-repeat="n in [].constructor(maxRating) track by $index">
<span ng-click="rateBy($index+1)" ng-show="ratedBy > $index" class="filled">★</span>
<span ng-click="rateBy($index+1)" ng-show="ratedBy <= $index">★</span>
</li>
</ul>
</div>
</body>
You could hold an array of objects like so:
var starApp = angular.module('starApp',[]);
starApp.controller ('StarCtrl', ['$scope', function ($scope) {
$scope.ratings = [];
var rating = {
current : 5,
max : 10
}
$scope.ratings.push(rating); // instead you would push what your http service returns into thearray.
}]);
Then in your view you could use ng-repeat like so:
<body ng-app="starApp">
<div ng-controller="StarCtrl">
<span ng-repeat="rating in ratings">{{rating.current}} out of {{rating.max}}</span>
</div>
</body>
My minimalistic approach:
The view
<head>
<!-- alternatively you may use another CSS library (like FontAwesome) to represent the star glyphs -->
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"></link>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0-rc.2/angular.min.js"></script>
<!-- insert the JavaScript controller and the CSS enhancements here -->
</head>
<body>
<div ng-app="StarRatings">
<div ng-controller="myController as ctrl">
<div ng-repeat="n in ctrl.getStarArray()" ng-class="ctrl.getClass(n)" ng-mouseover="ctrl.setClass($event,n)"> </div>
<p>
You have chosen {{ctrl.selStars}} stars
</p>
</div>
</div>
</body>
Note: if you want to use the onClick event instead onMouseOver event then replace ng-mouseover with ng-click in the HTML above.
The controller
<script>
(function() {
var app = angular.module('StarRatings', []);
app.controller('myController', function() {
this.selStars = 0; // initial stars count
this.maxStars = 5; // maximum number of stars
// a helper function used within view
this.getStarArray = function() {
var result = [];
for (var i = 1; i <= this.maxStars; i++)
result.push(i);
return result;
};
// the class used to paint a star (filled/empty) by its position
this.getClass = function(index) {
return 'glyphicon glyphicon-star' + (this.selStars >= index ? '' : '-empty');
};
// set the DOM element class (filled/empty star)
this.setClass = function(sender, index) {
this.selStars = index;
sender.currentTarget.setAttribute('class', this.getClass(index));
};
});
})();
</script>
Optionally some CSS enhancements
<style>
.glyphicon {
color: gold;
cursor: pointer;
font-size: 1.25em;
}
</style>
Not convinced? Try this JSFiddle: https://jsfiddle.net/h4zo730f/2/
let app = angular.module ('myapp',[])
-
##star Rating Styles
----------------------*/
.stars {
padding-top: 10px;
width: 100%;
display: inline-block;
}
span.glyphicon {
padding: 5px;
}
.glyphicon-star-empty {
color: #9d9d9d;
}
.glyphicon-star-empty,
.glyphicon-star {
font-size: 18px;
}
.glyphicon-star {
color: #FD4;
transition: all .25s;
}
.glyphicon-star:hover {
transform: rotate(-15deg) scale(1.3);
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<div ng-app= "myapp" >
<div class="stars">
<div id="stars" class="star">
<span ng-repeat="x in [0,1,2,3,4,5]" ng-if="($index < 4)" class="glyphicon glyphicon-star"> </span>
<span ng-repeat="x in [0,1,2,3,4,5]" ng-if="($index >= 4 && $index < 5) " class="glyphicon glyphicon-star-empty"></span>
</div>
</div>
</div>
hmc.starRating.js
angular.module('starRatings',[]).directive('starRating', function () {
return {
restrict: 'A',
template: '<ul class="rating">' +
'<li ng-repeat="star in stars" ng-class="star">' +
'\u2605' +
'</li>' +
'</ul>',
scope: {
ratingValue: '=',
max: '='
},
link: function (scope, elem, attrs) {
console.log(scope.ratingValue);
function buildStars(){
scope.stars = [];
for (var i = 0; i < scope.max; i++) {
scope.stars.push({
filled: i < scope.ratingValue
});
}
}
buildStars();
scope.$watch('ratingValue',function(oldVal, newVal){
if(oldVal !== newVal){
buildStars();
}
})
}
}
});
<script src="hmc.starRating.js"></script>
**app.js**
var app = angular.module('app', ['ui.bootstrap', 'starRatings']);
**indix.html**
<div star-rating rating-value="7" max="8" ></div>
.rating {
color: #a9a9a9;
margin: 0 !important;
padding: 0 !important;
}
ul.rating {
display: inline-block !important;
}
.rating li {
list-style-type: none !important;
display: inline-block !important;
padding: 1px !important;
text-align: center !important;
font-weight: bold !important;
cursor: pointer !important;
width: 13px !important;
color: #ccc !important;
font-size: 16px !important;
}
.rating .filled {
color: #ff6131 !important;
width: 13px !important;
}

Creating a directive and Isolating scopes in angular

Please view my JSFiddle
I have a fairly wonkey interaction that on a div mouseenter/mouseleave toggles a input checkbox on/off. If said checkbox is set true, it then sets a focus of an adjacent input text field.
I would like to isolate this interaction into a directive that will allow me to duplicate without conflict.
i've color coated the boxes for reference
<body ng-app="ngApp" ng-controller="MainCtrl">
<div class="row">
<div class="span2 box red" leave-edit="uncheckInputBox(false)" enter-edit="checkInputBox(true)">hover</div>
<span class='span8'>
<p>red</p>
<input type="checkbox" ng-model="isChecked">
<input xng-focus='isChecked' ng-model="editingInput">
{{isChecked}}
{{editingInput}}
</span>
</div>
<div class="row">
<div class="span2 box blue" leave-edit="uncheckInputBox(false)" enter-edit="checkInputBox(true)">hover</div>
<span class='span8'>
<p>blue</p>
<input type="checkbox" ng-model="isChecked">
<input xng-focus='isChecked' ng-model="editingInput">
{{isChecked}}
{{editingInput}}
</span>
</div>
</div>
js
var app = angular.module('ngApp', [])
app.controller('MainCtrl', ['$scope', function ($scope) {
'use strict';
$scope.isChecked = false;
$scope.$watch('isChecked', function(newV){
newV && $('#name').focus();
},true);
$scope.checkInputBox = function(val) {
$scope.isChecked = val;
};
$scope.uncheckInputBox = function(val) {
$scope.isChecked = val;
};
}]);
app.directive('xngFocus', function() {
return {
link: function(scope, element, attrs) {
scope.$watch(attrs.xngFocus,
function (newValue) {
newValue && element.focus();
},true);
}
};
});
app.directive('leaveEdit', function(){
return function(scope, element, attrs) {
element.bind('mouseleave', function() {
scope.$apply(attrs.leaveEdit);
});
};
});
app.directive('enterEdit', function(){
return function(scope, element, attrs) {
element.bind('mouseenter', function() {
scope.$apply(attrs.enterEdit);
});
};
});
css
.box {
height:50px;
cursor:pointer;
color: #fff;
text-align: center;
}
.red {
background: red;
}
.blue {
background: blue;
}
Strange interaction, but okay. You need to not use the same scope for each directive since you want them to be isolated.
I just did this by creating a scope for a directive that has the shared template.
app.directive('why', function() {
return {
scope: {},
link: function($scope, elt, attrs) {
//setup in here
}, ...
A few other things:
Don't include angular through external resources and in the framework section of fiddle. It runs angular over your dom twice and will behave strangely.
Also there are ng-mouseenter and ng-mouseleave directives in angular so you don't need to implement those.
The updated fiddle is here
Hope this helped!

Different item size in a listView in a Metro Style App

I am developping a metro style application for Windows 8 using HTML5/CSS/JS
I'm trying to display items with different size in a grouped List View. (like all metro style apps do...) I found on the internet that I need to put a GridLayout to my list View and implement the groupInfo. It succeeds in modifying the first item (the picture inside the first item is bigger than the other items), but then all the items have the size of the first item. I would like something like this instead :
Here is what I have done :
updateLayout: function (element, viewState, lastViewState) {
//I get my listView winControl defined in HTML
var listView = element.querySelector(".blocksList").winControl;
var globalList = new WinJS.Binding.List(globalArray);
var groupDataSource = globalList.createGrouped(this.groupKeySelector,
this.groupDataSelector, this.groupCompare);
// I set the options of my listView (the source for the items and the groups + the grid Layout )
ui.setOptions(listView, {
itemDataSource: groupDataSource.dataSource,
groupDataSource: groupDataSource.groups.dataSource,
layout: new ui.GridLayout({
groupHeaderPosition: "top",
groupInfo: function () {
return {
multiSize: true,
slotWidth: Math.round(480),
slotHeight: Math.round(680)
};
}
}),
itemTemplate: this.itemRenderer,
});
},
// je définis le template pour mes items
itemRenderer: function (itemPromise) {
return itemPromise.then(function (currentItem, recycled) {
var template = document.querySelector(".itemTemplate").winControl.renderItem(itemPromise, recycled);
template.renderComplete = template.renderComplete.then(function (elem) {
//if it is the first item I put it widder
if (currentItem.data.index == 0) {
// elem.querySelector(".item-container").style.width = (480) + "px";
elem.style.width = (2*480) + "px";
}
});
return template.element;
})
},
the html part is :
<section aria-label="Main content" role="main">
<div class="blocksList" aria-label="List of blocks" data-win-control="WinJS.UI.ListView"
data-win-options="{itemTemplate:select('.itemTemplate'), groupHeaderTemplate:select('.headerTemplate')
, selectionMode:'none', swipeBehavior:'none', tapBehavior:'invoke'}">
</div>
</section>
<!--Templates-->
<div class="headerTemplate" data-win-control="WinJS.Binding.Template">
<div class="header-title" data-win-bind="innerText: categorieName"/>
</div>
<div class="itemTemplate" data-win-control="WinJS.Binding.Template">
<div class="item-container" >
<div class="item-image-container">
<img class="item-image" data-win-bind="src: urlImage" src="#" />
</div>
<div class="item-overlay">
<h4 class="item-title" data-win-bind="textContent: title"></h4>
</div>
</div>
</div>
et le css
.newHomePage p {
margin-left: 120px;
}
.newHomePage .blocksList {
-ms-grid-row: 2;
}
.newHomePage .blocksList .win-horizontal.win-viewport .win-surface {
margin-left: 120px;
margin-bottom: 60px;
}
.newHomePage .blocksList .item-container {
height: 340px;
width: 240px;
color: white;
background-color: red;
-ms-grid-columns: 1fr;
-ms-grid-rows: 1fr;
display: -ms-grid;
}
.newHomePage .blocksList .win-item {
/*-ms-grid-columns: 1fr;
-ms-grid-rows: 1fr 30px;
display: -ms-grid;*/
height: 130px;
width: 240px;
background: white;
outline: rgba(0, 0, 0, 0.8) solid 2px;
}
Thank you for your help.
In Release Preview (and also RTM), the property names in the groupInfo function changed:
multiSize -> enableCellSpanning
slotWidth -> cellWidth
slotHeight -> cellHeight
Try that and see if you get better results. If you don't announce cell spanning in this way, then the GridLayout takes the first item's size as that for all the items.
Chapter 5 of my ebook from Microsoft Press (RTM preview coming soon) will cover all the details of this.
.Kraig

Categories