Two instaces of my directive, but only one output - angularjs

I've coded a custom directive in angularJs, in order to show a chess board. But though I include it two times into the html page, only one is rendered.
Here is my JS Bin attempt
My index.html
<!DOCTYPE html>
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.2/angular.min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body ng-app="static-board">
<chess-board /></br>
<chess-board cells-size="30"/>
</body>
</html>
Here is my script :
(function(){
angular.module('static-board', [])
.directive('chessBoard', [function(){
function getBoardHtml(cellsSize){
// taken from http://stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format
function sprintf() {
var args = arguments,
string = args[0],
i = 1;
return string.replace(/%((%)|s|d)/g, function (m) {
// m is the matched format, e.g. %s, %d
var val = null;
if (m[2]) {
val = m[2];
} else {
val = args[i];
// A switch statement so that the formatter can be extended. Default is %s
switch (m) {
case '%d':
val = parseFloat(val);
if (isNaN(val)) {
val = 0;
}
break;
}
i++;
}
return val;
});
}
function getBackground(size){
return sprintf("<rect x='0' y='0' width='%d' height='%d' fill='#BAA' />", size, size);
}
function getCells(cellsSize){
function getSingleCell(cellX, cellY){
var x = cellX*cellsSize + cellsSize/2;
var y = cellY*cellsSize + cellsSize/2;
var color = (cellX+cellY)%2 === 0 ? "#E9E637" : "#7C4116";
return sprintf("<rect x='%d' y='%d' width='%d', height='%d' fill='%s' />",
x,y, cellsSize, cellsSize, color);
}
var result = "";
for (var line = 0; line < 8; line++){
for (var col = 0; col < 8; col++){
result += getSingleCell(col, line)+'\n';
}
}
return result;
}
var size = 9*cellsSize;
var result = sprintf("<svg width='%d' height='%d'>\n%s\n%s\n</svg>",
size, size, getBackground(size), getCells(cellsSize));
return result;
}
return {
restrict: 'E',
link: {
post : function(scope, element, attrs){
var cellsSize = attrs.cellsSize || 20;
var newElem = angular.element(getBoardHtml(cellsSize));
element.replaceWith(newElem);
}
}
};
}]);
})();
I tried with isolated scope, but it does not change anything.

You need to explicitly close your custom chess-board elements.
So change this:
<chess-board /><br/>
<chess-board cells-size="30" />
to this:
<chess-board></chess-board><br/>
<chess-board cells-size="30"></chess-board>
This is based on a misconception that HTML5 self-closing tags work the same as XML/XHTML (I thought so too - I only found out about this in answering your question!).
Have a look at these two links for more information:
http://tiffanybbrown.com/2011/03/23/html5-does-not-allow-self-closing-tags/
https://stackoverflow.com/a/3558200/81723
To summarise the issue, in HTML5:
<chess-board /> == <chess-board>
<chess-board /> != <chess-board></chess-board>
In your specific case, because the tags weren't closed the second directive received the same element as the first directive so you only saw one chessboard.

Related

Swapping data on ui-grid take a lot of time

I have one grid and according to some conditions, I have to change the data which comes from the back-end.The first time I load data from the back the grid works fine. When I start switching data the grid displays new data well but stays frozen for a while. I notice that this time is random and is often about 3 seconds or plus.
I tried to assign data to the grid through different ways but the result is the same:
1)
$scope.gridOptions = {
//some options,
data: $scope.myData
}
And then
Demande.query({lb: condition}, function (result) {
$timeout(function () {$scope.myData = result;},0);// I also try without $timeout
}
2) I try to assign the data directly to the grid
Demande.query({lb: condition}, function (result) {
$timeout(function () {$scope.gridOptions.data = result;},0);
}
3) I combine the above approachs with
a) $scope.gridApi.core.notifyDataChange(uiGridConstants.dataChange.ROW)
b) $scope.gridApi.core.refreshRows()
but the problem persists. I am using angular-ui-grid 4.0.1
Any idea will be appreciated.
Another option:
4) $scope.gridOptions.data = angular.copy($scope.myData);
Should give the grid the little time that it needs to breath!
Update:
angular.module('app', ['ui.grid'])
.controller('MainCtrl', ['$scope', function($scope) {
$scope.gridOptions = {
columnDefs: []
}
for (var i = 0; i < 80; i++) {
$scope.gridOptions.columnDefs.push({
name: 'C' + (i + 1),
enableColumnMenu: false
});
}
var dataLoadCounter = 1;
$scope.switchData = function() {
$scope.gridOptions.data = [];
for (var i = 0; i < 1000; i++) {
$scope.gridOptions.data.push({});
for (var j = 0; j < 80; j++) {
$scope.gridOptions.data[i]['C' + (j + 1)] = 'D' + dataLoadCounter + (j + 1);
}
}
dataLoadCounter++;
}
$scope.switchData();
}]);
div[ui-grid] {
width: 4500px;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/4.0.2/ui-grid.min.js"></script>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/4.0.2/ui-grid.min.css" />
<div ng-app="app" ng-controller="MainCtrl">
<button ng-click="switchData()">Switch Data</button>
<div ui-grid="gridOptions">
</div>
</div>

Filter on string

I'm learning angularjs and got an exercise that wants me to Use angular filter to show a title in the following format :
first letter of each word upper cased and each other letter lower cased also
remove any non-English letters from the title. For example:
A title with the name
“##THIS is a Title!!”
should be changed to
“This Is A Title”
I'm getting each title from an array of objects and present them like so.
<div ng-repeat="obj in objects">
<h3 class="panel-title">{{obj.Title}}</h3>
</div>
i understand that filter receives an array and filters through it . but this requires me to filter the string.
been searching for a while, how can i do this?
please refer below fiddle
http://jsfiddle.net/HB7LU/28315/
<div ng-controller="MyCtrl">
Hello, {{ name | ordinal|capitalize }}
</div>
var myApp = angular.module('myApp',[]);
//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});
function MyCtrl($scope) {
$scope.name = 'Super hero!!12##3';
}
myApp.filter('ordinal', function() {
// Create the return function
// set the required parameter name to **number**
return function(strTitle) {
// Ensure that the passed in data is a number
// If the data is not a number or is less than one (thus not having a cardinal value) return it unmodified.
strTitle=strTitle.replace(/[^a-zA-Z ]/g, "")
return strTitle;
}
});
myApp.filter('capitalize', function() {
return function(input){
if(input.indexOf(' ') !== -1){
var inputPieces,
i;
input = input.toLowerCase();
inputPieces = input.split(' ');
for(i = 0; i < inputPieces.length; i++){
inputPieces[i] = capitalizeString(inputPieces[i]);
}
return inputPieces.toString().replace(/,/g, ' ');
}
else {
input = input.toLowerCase();
return capitalizeString(input);
}
function capitalizeString(inputString){
return inputString.substring(0,1).toUpperCase() + inputString.substring(1);
}
};
});
angular.module('app', []).filter('myFilter', function(){
return function(input){
if(!input)
return;
var out = '';
var english = /^[A-Za-z0-9 ]*$/;
for(var letter of input)
if(english.test(letter))
out += letter;
var result = '';
for(var i = 0; i < out.length; i++)
result += out[i][(i === 0 || out[i-1] == ' ') ? 'toUpperCase' : 'toLowerCase']();
return result;
}
})
<script src="//code.angularjs.org/snapshot/angular.min.js"></script>
<body ng-app="app">
<input ng-init='text="##THIS is a Title!!"' type='text' ng-model='text'>
<p>{{text | myFilter}}</p>
</body>

Change Different type of Attributes for Dynamically added every element

How can I Change (type) Attribute for each added dynamic buttons? In below code, label names were changing perfectly, but when i am trying to change button types it is applying to all added dynamic buttons,
My requirement is have to change every button type with different types (means: first added button type to submit, second added type to reset, third added button to cancel). but in my code if i change second button type to 'Reset' at the same time the first button type also going to Reset type... can u please tell me how can i change button type for every added element ...
Working DEMO
Updated:
var app = angular.module('myapp', ['ngSanitize']);
app.controller('MainCtrl', function($scope, $compile) {
var counter = 0;
$scope.buttonFields = [];
$scope.add_Button = function(index) {
$scope.buttonFields[counter] = {button: 'Submit'};
var buttonhtml = '<div ng-click="selectButton(buttonFields[\'' + counter + '\'])"><button id="button_Type">{{buttonFields[' + counter + '].button}}</button>//click//</div>';
var button = $compile(buttonhtml)($scope);
angular.element(document.getElementById('add')).append(button);
$scope.changeTosubmit = function (val) {
$scope.buttonField = val;
var els = document.body.querySelectorAll('#button_Type');
for (var i = 0, ils = els.length; i < ils; i++) {
var el = els[i];
el.setAttribute("type", "submit");
compile(el);
}
};
$scope.changeToreset = function (val) {
$scope.buttonField = val;
var els = document.body.querySelectorAll('#button_Type');
for (var i = 0, ils = els.length; i < ils; i++) {
var el = els[i];
el.setAttribute("type", "reset");
compile(el);
}
};
$scope.changeTocancel = function (val) {
$scope.buttonField = val;
var els = document.body.querySelectorAll('#button_Type');
for (var i = 0, ils = els.length; i < ils; i++) {
var el = els[i];
el.setAttribute("type", "cancel");
compile(el);
}
};
++counter;
};
$scope.selectButton = function (val) {
$scope.buttonField = val;
$scope.showButton_Types = true;
};
});
function compile(element) {
var el = angular.element(element);
$scope = el.scope();
$injector = el.injector();
$injector.invoke(function ($compile) {
$compile(el)($scope);
});
};
<!DOCTYPE html>
<html ng-app="myapp">
<head>
<script src="https://code.angularjs.org/1.4.8/angular.js"></script>
<script src="https://code.angularjs.org/1.5.0-rc.0/angular-sanitize.min.js"></script>
</head>
<body ng-controller="MainCtrl">
<button ng-click="add_Button($index)">Add Buttons</button>
<hr>
<div id="add"></div>
<form ng-show="showButton_Types">
<div>
<label>Button Name(?)</label><br/>
<input ng-model="buttonField.button">
</div>
<div>
<label>change button types(?)</label><br/>
<input ng-click="changeTosubmit(buttonFields['' + counter + ''])" name="submit" type="radio"> Submit
<input ng-click="changeToreset(buttonFields['' + counter + ''])" name="submit" type="radio"> Reset
<input ng-click="changeTocancel(buttonFields['' + counter + ''])" name="submit" type="radio"> Cancel
</div>
</form>
</body>
</html>

Ionic/Angular custom directive and ionic list thumbnail

I created a custom directive, chess-board, showing a chess position. With Ionic framework, I generate a list of chess-board, and would like to thumbnail chess-board on the left. Though I followed documentation, but using svg instead, as my directive generate svg, I can't get the desired layout.
Here is my JsBin (don't worry for missing pictures).
Here is index.html :
<!DOCTYPE html>
<html>
<head>
<script src="//code.ionicframework.com/1.0.0-beta.13/js/ionic.bundle.min.js"></script>
<link href="//code.ionicframework.com/1.0.0-beta.13/css/ionic.min.css" rel="stylesheet" type="text/css" />
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.2/angular.min.js"> </script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body ng-app="static-board">
<ion-pane>
<ion-header-bar class="bar-dark">
<h1 class="title">Chess Positions Archiver</h1>
</ion-header-bar>
<ion-content>
<div class="list">
<a class="item item-thumbnail-left" href="#">
<chess-board cells-size="30" fen="3rr1k1/pb5p/1qp3pB/1pn1Q3/8/7P/PPP3P1/R3KR2 b - - 0 1" show-coords> </chess-board>
<h3>Exercise 1</h3>
</a>
<a class="item item-thumbnail-left" href="#">
<chess-board cells-size="30" show-coords></chess-board>
<h3>Exercise 2</h3>
</a>
</div>
</ion-content>
</ion-pane>
</body>
</html>
Here is my script
(function(){
var chessPieces = {
'P': 'wp',
'N': 'wn',
'B': 'wb',
'R': 'wr',
'Q': 'wq',
'K': 'wk',
'b': 'bb',
'p': 'bp',
'n': 'bn',
'r': 'br',
'q': 'bq',
'k': 'bk'
};
angular.module('static-board', ['ionic'])
.factory('chessPictures', [function(){
return {
getPicture: function(pieceFen){
return chessPieces[pieceFen];
}
}
}])
.directive('chessBoard', [function(){
function getBoardHtml(cellsSize, positionFen, showCoords){
// taken from http://stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format
function sprintf() {
var args = arguments,
string = args[0],
i = 1;
return string.replace(/%((%)|s|d)/g, function (m) {
// m is the matched format, e.g. %s, %d
var val = null;
if (m[2]) {
val = m[2];
} else {
val = args[i];
// A switch statement so that the formatter can be extended. Default is %s
switch (m) {
case '%d':
val = parseFloat(val);
if (isNaN(val)) {
val = 0;
}
break;
}
i++;
}
return val;
});
}
function fenToPosition(){
function getSingleLine(lineFen){
var result = [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '];
var column = 0;
for (var index in lineFen){
var currElem = lineFen[index];
var isDigit = !isNaN(parseInt(currElem));
if (isDigit){
column += parseInt(currElem);
}
else {
result[column] = currElem;
column++;
}
}
return result;
}
var result = [];
var parts = positionFen.split(" ")[0].split("/");
for (var partIndex in parts){
var currPart = parts[partIndex];
result.push(getSingleLine(currPart));
}
return result;
}
function getBackground(size){
return sprintf("<rect x='0' y='0' width='%d' height='%d' fill='#BAA' />", size, size);
};
function getCells(){
function getSingleCell(cellX, cellY){
var x = cellX*cellsSize + cellsSize/2;
var y = cellY*cellsSize + cellsSize/2;
var color = (cellX+cellY)%2 === 0 ? "#E9E637" : "#7C4116";
return sprintf("<rect x='%d' y='%d' width='%d', height='%d' fill='%s' />",
x,y, cellsSize, cellsSize, color);
}
var result = "";
for (var line = 0; line < 8; line++){
for (var col = 0; col < 8; col++){
result += getSingleCell(col, line)+'\n';
}
}
return result;
}
function getPieces(positionPieces){
function getSinglePiece(cellX, cellY){
var x = cellX*cellsSize + cellsSize/2;
var y = cellY*cellsSize + cellsSize/2;
var pieceFen = positionPieces[cellY][cellX];
var piecePictureRef = chessPieces[pieceFen];
var path = sprintf("../img/chess_pieces/%s.svg", piecePictureRef);
return piecePictureRef ? sprintf("<image x='%d' y='%d' width='%d' height='%d' xlink:href='%s' />",
x, y, cellsSize, cellsSize, path
) : undefined;
}
var result = "";
for (var rank = 0; rank < 8; rank++){
for (var file = 0; file < 8; file++){
var line = getSinglePiece(file, rank);
if (line) {
result += line+'\n';
}
}
}
return result;
}
function getPlayerTurn(){
var turnStr = positionFen.split(" ")[1];
var color = turnStr === "w" ? "#FFF" : "#000";
var location = parseInt(8.5*cellsSize);
var size = cellsSize / 2;
return sprintf("<rect x='%d' y='%d' width='%d' height='%d' fill='%s'/>",
location, location, size, size, color);
}
function getCoordinates(){
result = "";
var files = "ABCDEFGH";
for (var index in files){
var currFile = files[index];
result += sprintf("<text x='%d' y='%d' fill='#000' font-size='%d'>%s</text>",
parseInt(cellsSize*index + cellsSize*.8), parseInt(cellsSize*.45),
parseInt(cellsSize*.4), currFile);
result += sprintf("<text x='%d' y='%d' fill='#000' font-size='%d'>%s</text>",
parseInt(cellsSize*index + cellsSize*.8), parseInt(cellsSize*8.9),
parseInt(cellsSize*.4), currFile);
}
var ranks = "87654321";
for (var index in ranks){
var currRank = ranks[index];
result += sprintf("<text x='%d' y='%d' fill='#000' font-size='%d'>%s</text>",
parseInt(cellsSize*.1), parseInt(cellsSize*1.25+cellsSize*index),
parseInt(cellsSize*.4), currRank);
result += sprintf("<text x='%d' y='%d' fill='#000' font-size='%d'>%s</text>",
parseInt(cellsSize*8.7), parseInt(cellsSize*1.25+cellsSize*index),
parseInt(cellsSize*.4), currRank);
}
return result;
}
var size = 9*cellsSize;
var result = sprintf("<svg width='%d' height='%d'>\n%s\n%s\n%s\n%s\n%s\n</svg>",
size, size, getBackground(size), getCells(), getPieces(fenToPosition()),
getPlayerTurn(), showCoords ? getCoordinates() : "");
return result;
}
return {
restrict: 'E',
link: {
post : function(scope, element, attrs){
var cellsSize = attrs.cellsSize || 20;
var positionFen = attrs.fen || 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w';
var showCoords = attrs.showCoords !== undefined ? true : false;
var newElem = angular.element(getBoardHtml(cellsSize, positionFen, showCoords));
element.replaceWith(newElem);
}
}
}
}])
})();
So what's wrong ?
Is that the fact that I am using an <svg> instead of an <img> ?
Or is it something else ?
you will need to do several things:
wrap svg into a div, and give it class item-image
<div class="item-image">
<chess-board cells-size="30"></chess-board>
</div>
then, when you create the SVG, you will need to set properties like this:
width="100%" height="100%" viewBox="0,0,270,270"
width and hight will tell it how to behave inside it's container, while viewBox property sets the size of canvas, on which the image will be drawn.
this line
var result = sprintf("<svg width='%d' height='%d'>\n%s\n%s\n%s\n%s\n%s\n</svg>", size, size, getBackground(size), getCells(), getPieces(fenToPosition()), getPlayerTurn(), showCoords ? getCoordinates() : "");
should look like this
var result = sprintf("<svg width='100%%' height='100%%' viewBox='0,0,%d,%d'>\n%s\n%s\n%s\n%s\n%s\n</svg>", size, size, getBackground(size), getCells(), getPieces(fenToPosition()), getPlayerTurn(), showCoords ? getCoordinates() : "");
You can see it here http://jsbin.com/basimodufa/1/edit?html,js,output
there is a good resource on SVG sizing/containing here

angularjs: mask the first five digits of SSN

I'm trying to mask the first 5 digits of SSN using a filter which should look something like this
XXX-XX-1234
What I came up with so far:
// <td>{{SocialSecurityNumber | ssn}}
angular.module('ng').filter('ssn', function () {
return function (ssn) {
if (!ssn) {
return '';
}
var value = ssn.toString().trim().replace(/^\+/, '');
if (value.match(/[^0-9]/)) {
return ssn;
}
return (ssn.slice(0, 3).replaceWith('*') + '-' + ssn.slice(4, 5).replaceWith('*') + '-' + ssn.slice(4)).trim();
};
});
Strings have no .replaceWith function in JavaScript. You can use .replace, though.
I'm not sure what value.match(/[0-9]/) is supposed to do either. It seems like you can just remove it. This will return true if the value has even one digit, which it should anyway. Perhaps you mean /[^0-9]/
return "XXX-XX-" + ssn.slice(5);
You can mask the input box without changing your model value then, this code must help. Here is the working code Plunkr / Github
var app = angular.module('myapp', []);
app.controller('MainCtrl', function($scope) {
$scope.modelssn = '';
});
app.directive("ssnInput",function(){
return {
require:'ngModel',
link: function(scop, elem, attr, ngModel){
$(elem).mask("999-99-9999");
var temp;
var regxa = /^(\d{3}-?\d{2}-?\d{4})$/;
$(elem).focusin(function(){
$(elem).val(temp);
});
$(elem).on('blur',function(){
temp = $(elem).val();
if(regxa.test($(elem).val())){
$(elem).val("XXX-XX" + temp.slice(6));
}
});
}
}
});
<!DOCTYPE html>
<html ng-app="myapp">
<head>
<script src="https://code.jquery.com/jquery.js"></script>
<script src="https://cdn.rawgit.com/digitalBush/jquery.maskedinput/master/src/jquery.maskedinput.js"></script>
<script data-require="angular.js#1.5.x" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.11/angular.min.js" data-semver="1.5.11"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
Enter SSN <input type="text" ng-model="modelssn" ssn-input >
<p>Real SSN {{modelssn}} </p>
</body>
</html>
## I faced the similer issue. Below is the solution for that :
angular
.module('app')
.directive('ssnOnly', function () {
return {
require: 'ngModel',
link: function (scope, element, attr, ngModelCtrl) {
var flag = 0;
function fromUser(text) {
if (text) {
var transformedInput = text.replace(/[^0-9 -]/g, '');
if (text.length < 3) {
flag = 0;
} else if (text.length > 3 && text.length < 6) {
flag = 1;
}
if (transformedInput !== text) {
ngModelCtrl.$setViewValue(transformedInput);
ngModelCtrl.$render();
alert("Only Numbers Allowed !!!");
} else {
if (transformedInput.length == 3 && flag == 0) {
transformedInput = transformedInput + '-';
flag = 1;
} else if (transformedInput.length == 6 && flag == 1) {
transformedInput = transformedInput + '-';
flag = 2;
}
if (transformedInput.length == 4 || transformedInput.length == 7)
{
var id = transformedInput;
var last = id.substr(id.length - 1);
if (last != '-') {
transformedInput = transformedInput.substring(0, transformedInput.length - 1);
transformedInput = transformedInput + '-' + last;
}
}
ngModelCtrl.$setViewValue(transformedInput);
ngModelCtrl.$render();
}
return transformedInput;
}
return undefined;
}
ngModelCtrl.$parsers.push(fromUser);
}
};
})
<input type="text" class="form-control" ssn-only ng-model="ssnNumber" placeholder="SSN" maxlength="11">

Resources