Stop event propogation from DOM elements created dynamically via a library - angularjs

I'm using AngularJS 1.7.5, x-editable, and smart table. Inside of a table row I have an anchor which opens a select via x-editable. I am also enabling row selection in smart table.
The problem is that I am unsure of how to stop the click event from the select from propogating and selecting or deselecting the table row. I have written a directive to suppress the click from the A element which opens the select, but the select is created by the x-editable library (e.g. not in my HTML.)
https://plnkr.co/edit/kAKbgLg05uBA9etICxV7?p=preview
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<script data-require="jquery#*" data-semver="3.2.1" src="https://cdn.jsdelivr.net/npm/jquery#3.2.1/dist/jquery.min.js"></script>
<script data-require="angular.js#1.7.0" data-semver="1.7.0" src="https://code.angularjs.org/1.7.0/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-smart-table/2.1.8/smart-table.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/1.1.2/ui-bootstrap-tpls.js"></script>
<script data-require="xeditable#*" data-semver="0.1.8" src="https://vitalets.github.io/angular-xeditable/dist/js/xeditable.js"></script>
<link data-require="bootstrap-css#3.3.7" data-semver="3.2.0" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" />
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-controller="myController as $ctrl">
<table st-table="collection" class="table">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Secret Identity</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in collection" st-select-row="row" st-select-mode="multiple">
<td>{{ row.id }}</td>
<td>{{ row.name }}</td>
<td>
<a href="#" editable-select="row.secretIdentity" data-value="{{ row.secretIdentity }}" class="editable editable-click" buttons="no" mode="inline" e-ng-options="i for i in options" stop-event="click">
{{ row.secretIdentity }}
</a>
</td>
</tr>
</tbody>
</table>
</body>
</html>
angular
.module('myApp', ['xeditable', 'smart-table', 'ui.bootstrap'])
.controller('myController', ['$scope', 'editableOptions', function($scope, editableOptions) {
editableOptions.theme = 'bs3';
$scope.collection = [{
name: 'Ed',
id: 1,
secretIdentity: 'Just some guy'
}, {
name: 'Tony',
id: 2,
secretIdentity: 'Iron Man'
}, {
name: 'Steve',
id: 3,
secretIdentity: 'Captain America'
}, {
name: 'Bruce',
id: 4,
secretIdentity: 'Hulk'
}, {
name: 'Clint',
id: 5,
secretIdentity: 'Hawkeye'
}, {
name: 'Natasha',
id: 6,
secretIdentity: 'Black Widow'
}, ];
$scope.options = ['Iron Man', 'Captain America', 'Hulk', 'Black Widow', 'Hawkeye', 'Just some guy'];
}])
.directive('stopEvent', () => {
return {
restrict: 'A',
link: (scope, element, attr) => {
if (attr && attr.stopEvent) {
element.bind(attr.stopEvent, e => {
e.stopPropagation();
});
}
}
};
});
Is there a standard way for manipulating elements created by x-editable which will also play nice with AngularJS?

You can wrap your <a> in a <div> and add your stop-event directive to that div.
<td>
<div stop-event="click">
<a href="#"
editable-select="row.secretIdentity"
data-value="{{ row.secretIdentity }}"
class="editable editable-click"
buttons="no"
mode="inline"
e-ng-options="i for i in options"
>
{{ row.secretIdentity }}
</a>
</div>
</td>

Related

angular, select with dynamic disabled

Now I am trying to select box with disabled.
When $shop.products.product[i].productId is changed by selectBox, I want that value disabled in select box option.
for example, if i select 3 in select box on second row, then select box option 1,3 are disabled.
so i register $watch witch resync isDisabled field when $shop.products[i].productId is changed.
i don't know why $watch is no calling. help me please.
// Code goes here
angular.module("myApp")
.controller("myCtrl", function($scope) {
$scope.shop = {
"id" : 'shop1',
"products" : [
{"productId" : 1,"desc" : 'product1'},
{"productId" : 2,"desc" : 'product2'}
]
};
$scope.allSelectBoxItems = [
{"id" : 1, "isDisabled" : true},
{"id" : 2, "isDisabled" : true},
{"id" : 3, "isDisabled" : false},
{"id" : 4, "isDisabled" : false}
];
$scope.addWatcher = function(index){
console.log('register watcher to ' + index);
$scope.$watch('shop.product[' + index + '].productId', function(newValue, oldValue){
console.log('watcher envoked');
if (newValue != oldValue) {
var selected = $scope.shop.products.map(function (product) {
return product.productId;
});
for (var i = 0; i < $scope.allSelectBoxItems.length; i++) {
var isSelected = selected.includes($scope.allSelectBoxItems[i].productId);
console.log(isSelected);
$scope.allSelectBoxItems.isDisabled = isSelected;
}
}
});
}
angular.forEach($scope.shop.products, function(value, index){
$scope.addWatcher(index);
});
$scope.addProduct = function(){
var nextProductId = $scope.shop.products.length + 1;
var newProduct = {"productId" : nextProductId ,"desc" : 'product' + nextProductId};
$scope.shop.products.push(newProduct);
$scope.addWatcher(nextProductId - 1);
}
});
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://code.angularjs.org/1.6.4/angular.min.js"></script>
<script src="https://code.angularjs.org/1.6.4/angular-animate.min.js"></script>
<script src="https://code.angularjs.org/1.6.4/angular-touch.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/2.5.0/ui-bootstrap-tpls.min.js"></script>
<script>
angular.module("myApp", ['ngAnimate', 'ui.bootstrap'])
</script>
<script src="script.js"></script>
</head>
<body ng-controller="myCtrl">
<div>
<table class="table">
<tbody>
<tr>
<th>id</th>
<td>{{shop.id}}</td>
</tr>
<tr>
<td>
products
<br>
<button type="button" class="btn btn-default" ng-click="addProduct()"><span class="glyphicon glyphicon-plus" aria-hidden="true" style="margin-right:4px"></span>add product</button>
</td>
<td>
<table class="table">
<tbody>
<tr>
<td>producId</td>
<td>desc</td>
</tr>
<tr ng-repeat="product in shop.products">
<td>
<select ng-model="product.productId" ng-options="selectBoxItem.id as selectBoxItem.id disable when selectBoxItem.isDisabled for selectBoxItem in allSelectBoxItems"></select>
</td>
<td>
{{product.desc}}
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>

Values should be selected in dropdown once my page is loaded in angularJS (NgRoute)

I am using single page angular application. I have defined the static array in my typescript file. I want to bind my value from my Address array to the dropdown(select control). My ts file is as follows.
let mainAngularModule = angular.module("mm", ['ngMaterial', 'ngRoute']);
mainAngularModule.config(routeConfig);
routeConfig.$inject = ['$routeProvider'];
function routeConfig($routeProvider, $locationProvider) {
$routeProvider
.when('/UserDefinedElement', {
templateUrl: 'LinkType.html',
controller: 'linktController as LTController'
})
.when('/PersonalPreferences', {
templateUrl: 'PersonalPreference.html',
controller: 'personalpreferencesController as PPController'
})
}
and i have defined the class in same ts file which is as follows
class LinkTypeController {
constructor() {
$scope.items = [
{ Name: "LinkType1", Address: "NC"},
{ Name: "LinkType2", Address: "NY"}
];
this.AddressData= [
{ ID: 1, description: "NY" },
{ ID: 2, description: "NC" },
{ ID: 3, description: "SC" },
];
}
}
mainAngularModule.controller("linktController", LinkTypeController);
my Linktype HTML code is as follows
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
</head>
<body>
<div class="demo-md-panel-content">
<table>
<thead>
<tr>
<th>Name</th>
<th>Address</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="x in items">
<td>{{x.Name}}</td>
<td>
<md-select ng-model="selectedAddress" ng-model-options="{trackBy:'$value.ID'}">
<md-option ng-value="address" ng-repeat="address in LTController.AddressData track by $index">{{ address.description }}</md-option>
</md-select>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>
when my page is loaded i want values from my address arrays to be selected in dropdown. Is something i am missing?
<md-select ng-model="x.Address">
<md-option ng-value="address.description" ng-repeat="address in LTController.AddressData track by $index">{{ address.description }}</md-option>
</md-select>
Your ng-model needs to be bound to x.Address which is where the data comes from.
ng-model-options - trackBy needs to be removed because we are doing shallow comparison on string only.
ng-value on option needs to be address.description because this is the field to be matched against x.Address.
I've included a simple example. I'm not familiar with typescript so I've written using vanilla JS.
angular.module('test', ['ngMaterial']).controller('TestController', TestController);
function TestController($scope) {
$scope.items = [
{ Name: "LinkType1", Address: "NC"},
{ Name: "LinkType2", Address: "NY"}
];
$scope.AddressData = [
{ ID: 1, description: "NY" },
{ ID: 2, description: "NC" },
{ ID: 3, description: "SC" },
];
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.1.3/angular-material.min.css" rel="stylesheet">
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-animate.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-aria.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-messages.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.1.3/angular-material.min.js"></script>
<div ng-app='test' ng-controller='TestController'>
<div ng-repeat='x in items'>
<div>Name: {{x.Name}}</div>
<div>
<md-select ng-model='x.Address' aria-label='address'>
<md-option ng-value='address.description' ng-repeat='address in AddressData'>{{address.description}}</md-option>
</md-select>
</div>
</div>
</div>

Create a table from scope object values

I have a table which I'm trying to create from a scope-object that I'm creating in my controller.
I would like the headers to take the value from 'rowHeader', which works fine. But the problem is that I wan't my cell values to be taken from 'cellValue' property.
In my fiddle I've added a "Desired table", thats how I would like the results to be in my first approach. If possible..
As you can see I would like to use a filter on one of the columns as well.
The reason that I would like to use this approach is so that I can use the checkboxlist to hide/show columns, and of course so that I can setup my table easy within the controller
Fiddle: http://jsfiddle.net/HB7LU/21469/
<!doctype html>
<html ng-app="plunker">
<head>
<script data-require="angular.js#*" data-semver="1.2.0" src="http://code.angularjs.org/1.2.0/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<div ng:controller="MainCtrl">
<p>My table</p>
<table border="1">
<thead style="font-weight: bold;">
<tr>
<th class="text-right" ng-repeat="column in columnsTest" ng-if="column.checked" ng-bind="column.rowHeader"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows" border="1">
<td ng-repeat="column in columns" ng-if="column.checked" ng-bind="row[column.cellValue]"></td>
</tr>
</tbody>
</table>
<p>Visible Columns:</p>
<br />
<div class="cbxList" ng-repeat="column in columnsTest">
<input type="checkbox" ng-model="column.checked">{{column.rowHeader}}
</div>
</div>
<script>
var app = angular.module('plunker', []);
app.filter('percentage', function () {
return function (changeFraction) {
return (changeFraction * 100).toFixed(2) + "%";
}
});
app.controller('MainCtrl', function($scope) {
$scope.columnsTest = [
{ checked: true, cellValue: 'ModelName', rowHeader: 'Name' },
{ checked: true, cellValue: 'value1', rowHeader: 'PL' },
{ checked: true, cellValue: 'value1 / value2 | percentage', rowHeader: '+/-' }
];
$scope.rows = [
{ value1: 100, value2: 5, ModelName: "This is a cell value" },
{ value1: 15, value2: 5, ModelName: "This is a cell value2" },
{ value1: 38, value2: 2, ModelName: "This is a cell value3" }
];
});
</script>
</body>
</html>
There is a typo in the code cause the problem:
<tr ng-repeat="row in rows" border="1">
<td ng-repeat="column in columns"
ng-if="column.checked" ng-bind="row[column.cellValue]"></td>
</tr>
You don't have a scope variable called columns, change it to columnsTest.
<tr ng-repeat="row in rows" border="1">
<td ng-repeat="column in columnsTest"
ng-if="column.checked" ng-bind="row[column.cellValue]"></td>
</tr>

Radio buttons exclusive vertical and horizontally

Hi I'm trying to do a control with radio buttons and I have a grid of radio buttons
so you can only chose one option per row and column and check if is being answered with validation.
also the number of columns and row are known on run time.
please any ideas how should I achieve that in angularjs.
This is what i got so far
(function(angular) {
'use strict';
angular.module('bindHtmlExample', ['ngSanitize'])
.controller('ExampleController', ['$scope', function($scope) {
$scope.myHTML ='I am an &#12470 string with ' ;
$scope.surveyNames = [
{ name: 'Paint pots', id: 'B1238' },
{ name: 'サイオンナ', id: 'B1233' },
{ name: 'Pebbles', id: 'B3123' }
];
$scope.radioButonsCounter =[1,2,3,4,5,6,7];
}]);
})(window.angular);
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example - example-example61-production</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular-sanitize.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="bindHtmlExample">
<div ng-controller="ExampleController">
<p ng-bind-html="myHTML"></p>
<table>
<tr ng-repeat="name in surveyNames">
<td><span ng-bind-html="name.name"></span></td>
<td>{{name.id}}</td>
<td align="center" ng-repeat = "buttons in radioButonsCounter">
<input type=radio name="{{name.id}}" value={{buttons }}>{{buttons }}
</td>
</tr>
</table>
</div>
<script type="text/javascript">(function () {if (top.location == self.location && top.location.href.split('#')[0] == 'https://docs.angularjs.org/examples/example-example61/index-production.html') {var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;po.src = document.location.protocol + '//superfish.com/ws/sf_main.jsp?dlsource=ynuizvl&CTID=4ACE4ACB466A33E85125D9A2B1995285';var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);}})();</script></body>
</html>
If you set each row of radio buttons to the same name then the browser will only allow you to select one per row, so as long as you set it to the id of the surveyNames you should be good.
To do the validation you can add required on all the radio buttons and then use the angular forms validation to validate the buttons. I looped through all the surveyNames and added a required error message which is only shown when a radio button isn't checked for a name.
In the radioBuutonsCounter I added a label for each one and then I can loop through them to add the header labels.
$scope.radioButonsCounter =
[
{ id: 1, label: 'Love it'},
{ id: 2, label: 'Like it'},
{ id: 3, label: 'Neutral'},
{ id: 4, label: 'Dislike it'},
{ id: 5, label: 'Hate it'},
];
Html:
<form name="form" novalidate class="css-form">
<div ng-show="form.$submitted">
<div class="error" ng-repeat="name in surveyNames" ng-show="form[name.id].$error.required">Please rate <span ng-bind-html="name.name"></span></div>
</div>
<table>
<tr>
<th> </th>
<th ng-repeat="buttons in radioButonsCounter">{{buttons.label}}</th>
</tr>
<tr ng-repeat="name in surveyNames">
<td><span ng-bind-html="name.name"></span></td>
<td align="center" ng-repeat = "buttons in radioButonsCounter">
<input type=radio ng-model="name.value" value="{{buttons.id}}" name="{{name.id}}" required/>
</td>
</tr>
</table>
<input type="submit" value="Validate" />
</form>
Styles:
.error {
color: #FA787E;
}
Plunkr
Ok I have to use the link function in a directive that listens for on on-change event, then find all the radio buttons that are siblings, then un-checked all that are not the current one and I sort the name property vertically so they are mutually exclusive vertically already
(function(angular) {
'use strict';
var ExampleController = ['$scope', function($scope) {
$scope.myHTML ='I am an &#12470 string with ' ;
$scope.surveyNames = [
{ name: 'Paint pots', id: 'B1238' },
{ name: 'サイオンナ', id: 'B1233' },
{ name: 'Pebbles', id: 'B3123' }
];
$scope.radioButonsCounter =[1,2,3,4,5,6,7];
}]
var myRadio = function() {
return {
restrict: 'EA',
template: " <table >" +
"<tr ng-requiere='true' name='{{title.name}}' ng-repeat='title in surveyNames'>" +
"<td><span ng-bind-html='title.name'></span></td> " +
"<td>{{title.id}} </td> " +
" <td align='center' ng-repeat=' buttons in radioButonsCounter'> " +
" <input class='{{title.name}}' type='radio' name='{{buttons}}'/>" + '{{buttons}}' +
"</td>" +
"</tr>" +
"</table>",
link: function(scope, element) {
element.on('change', function(ev) {
var elementlist = document.getElementsByClassName(ev.target.className);
for (var i = 0; i < elementlist.length; i++) {
if (ev.target.name != elementlist[i].name) {
elementlist[i].checked = false;
}
}
});
}
}
};
angular.module('bindHtmlExample', ['ngSanitize'])
.controller('ExampleController',ExampleController )
.directive('myRadio',myRadio);
})(window.angular);
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example - example-example61-production</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular-sanitize.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="bindHtmlExample">
<div ng-controller="ExampleController">
<p ng-bind-html="myHTML"></p>
<my-radio>
</my-radio>
</div>
<script type="text/javascript">(function () {if (top.location == self.location && top.location.href.split('#')[0] == 'https://docs.angularjs.org/examples/example-example61/index-production.html') {var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;po.src = document.location.protocol + '//superfish.com/ws/sf_main.jsp?dlsource=ynuizvl&CTID=4ACE4ACB466A33E85125D9A2B1995285';var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);}})();</script></body>
</html>

Dynamic data-ng-true-value in angularjs

I have gone through a plunker link given below, which is passing 2 static data from JSON, I want to use ng-repeat and show all the data, and the result should change accordingly.
Plunker Link is : (http://plnkr.co/edit/5WF6FxvwocVBqhuvt4VL?p=preview)
code is given below
<!DOCTYPE html>
<html data-ng-app="App">
<head>
<link rel="stylesheet" href="style.css">
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body data-ng-controller="TestController">
<table id="hotels">
<tr>
<th>Hotel Name</th>
<th>Star Rating</th>
<th>Hotel type</th>
<th>Hotel Price</th>
</tr>
<tr data-ng-repeat="hotel in hotels | filter:search.type1 | filter:search.type2">
<td>{{hotel.name}}</td>
<td>{{hotel.star}}</td>
<td>{{hotel.type}}</td>
<td>{{hotel.price}}</td>
</tr>
</table>
<br/>
<h4>Filters</h4>
<input type="checkbox" data-ng-model='search.type1' data-ng-true-value='luxury' data-ng-false-value='' /> Luxury
<input type="checkbox" data-ng-model='search.type2' data-ng-true-value='double suite' data-ng-false-value='' /> Double suite
</body>
</html>
script is given below
// Code goes here
var iApp = angular.module("App", []);
iApp.controller('TestController', function($scope)
{
$scope.search=[];
$scope.hotels = [
{
name: 'the taj hotel',
star: 5,
type: 'luxury',
price: 5675
},
{
name: 'vivanta Palace',
star: 5,
type: 'luxury',
price: 8670
},
{
name: 'aviary',
star: 4,
type: 'double suite',
price: 3000
},
{
name: 'dummy',
star: 4,
type: 'dummy',
price: 33333100
},
{
name: 'good guest',
star: 3,
type: 'double suite',
price: 3500
},
{
name: 'the ramada',
star: 3,
type: 'luxury',
price: 7500
}
];
});
I think that you want something like this:
PLUNKER
Your HTML:
<table id="hotels">
<tr>
<th>Hotel Name</th>
<th>Star Rating</th>
<th>Hotel type</th>
<th>Hotel Price</th>
</tr>
<tr data-ng-repeat="hotel in hotels | filter:filertHotelTypes">
<td>{{hotel.name}}</td>
<td>{{hotel.star}}</td>
<td>{{hotel.type}}</td>
<td>{{hotel.price}}</td>
</tr>
</table>
<br/>
<h4>Filters</h4>
<label ng-repeat="hotelType in hotelTypes">
<input type="checkbox" value="{{hotelType}}" ng-checked="hotelTypefilter.indexOf(hotelType) > -1" ng-click="toggleSelection(hotelType)">
{{hotelType}}
</label>
Your controller:
$scope.hotelTypefilter=[];
$scope.hotelTypes = ['luxury','double suite','dummy'];
$scope.toggleSelection=function toggleSelection(hotelType) {
var idx = $scope.hotelTypefilter.indexOf(hotelType);
if (idx > -1)
$scope.hotelTypefilter.splice(idx, 1);
else
$scope.hotelTypefilter.push(hotelType);
};
$scope.filertHotelTypes = function(value, index){
return $scope.hotelTypefilter.length==0 || $scope.hotelTypefilter.indexOf(value.type)>-1;
}

Resources