I have developed a directive, but it is always applying functionality on the last element in the page, here is my code:
HTML:
<div ng-controller="MyCtrl">
<input type="text" email-commy comma-separated-data="model1" class="form-control"/>
<input type="text" email-commy comma-separated-data="model2" class="form-control"/>
</div>
JS:
var app=angular.module('myApp', []);
function MyCtrl($scope) {
$scope.model1 = "abc#gmail.com";
$scope.model2 = "abc2#gmail.com";
}
app.directive("emailCommy", function(){
return {
scope: {
commaSeparatedData: '='
},
restrict: "A",
multiple: true,
link: function (scope, elem, attrs, ngModel) {
function handler(dataString){
function buildEmailList() {
var lis = data.map(function (em, index) {
return "<li>" + em + "<span class='remove-element' data-index='" + index + "'>×</span>" + "</li>";
}).join("");
var input = $("<input/>", {"class": "form-control shadow-element", "placeholder": "Enter..."});
lis += "<li class='input-field'>" + input.wrap("<p/>").parent().html() + "</li>";
return "<ul class='list-inline'>" + lis + "</ul>";
}
function renderer() {
$wrapper.empty().append(buildEmailList());
elem.addClass("hide").before($wrapper);
listener();
}
function listener() {
angular.element(".shadow-element").off("keypress").on("keypress", function (evt) {
var $this = $(this),
charCode = evt.which || evt.keyCode;
//add input in case of (comma, enter, and semi-colon) pressed
if ($this.val() && (charCode === 44 || charCode === 13 || charCode === 59)) {
data.push($this.val().toLowerCase());
renderer();
updateModel();
$this.val("");
}
});
angular.element(".remove-element").off("click").on("click", function () {
var $this = $(this),
index = $this.data("index");
data.splice(index, 1);
updateModel();
renderer();
});
}
function updateModel(){
var updatedVal = data.join(",");
$(elem).val(updatedVal);
}
if(dataString) {
var data = dataString.split(",").map(function (em) {
return em.toLowerCase().trim();
});
var $wrapper = $("<div/>", {"class": "email-container"});
renderer();
}
}
handler(scope.commaSeparatedData);
//scope.$watch(attrs.commaSeparatedData, handler);
}
};
});
Here is fiddle:
http://jsfiddle.net/RmDuw/928/
Directive only working on last element. If i add data in first directive it adds to below one.
Please help, and suggest me how to fix.
Thanks
Just change your code in order to select the input of current directive like this:
function listener() {
$wrapper.find('input').on(...
and for remove element:
$wrapper.find('span')..
See the fiddle
Related
I have a custom AngularJS directive for a textdown control. In it is an ng-repeat that is printing a list of div's for the emulated dropdown, and each item has an ng-click function. That function will not fire when the div is clicked. Can you please help me figure out why?
Plunkr: https://plnkr.co/edit/vOwtjqltq2WfCM9A0dFJ?p=preview
I don't remember where I first heard that concept, but it's very similar to StackOverflow's question Tags input, except you can only select 1 item. If you haven't seen that example, it is a text input that has a dropdown list when you start typing into it with related items that has fields that partially match what you've typed so far. Then the user can click on an item in the dropdown and it fills in the text input.
Here is the main page's HTML:
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>
document.write('<base href="' + document.location + '" />');
</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.7/angular.js" data-semver="1.4.7"></script>
<script src="app.js"></script>
<script src="textdown.js"></script>
</head>
<body ng-controller="MainCtrl">
<p>Hello and welcome to the Textdown example!</p>
<label>City:
<textdown input-placeholder-text="Select a City..." is-editable="true" items="cities" ng-model="selectedCity" title="Name" width="150px"></textdown>
</label>
</body>
</html>
Here is the directive's HTML:
var HYG_TEXTBOX_DROPDOWN_TEMPLATE = '\
<div class="hyg-textdown-container activate-textdown" \
ng-class="{ \'hyg-focused\': isFocused() }"> \
<input type="search" class="activate-textdown" placeholder="{{ inputPlaceholderText }}" style="width: 100%;" \
ng-class="{ \'invalid\': !isValid() }" \
ng-change="onInputChanged()" \
ng-focus="onInputFocus($event)" \
ng-model="input" \
ng-blur="onInputBlur()" \
ng-show="isEditable"> \
</input> \
<div class="hyg-textdown-list activate-textdown" ng-show="selectActive" ng-style="{ top: ytop, left: xleft }" style="z-index:5; width: {{ width }}"> \
<div class="hyg-textdown-listed activate-textdown" \
ng-repeat="item in items | property: title: (ngModel != null ? \'\' : input) | orderBy: title | limitTo:5" \
ng-class="{ \'hyg-textdown-listed-active\': isSelected(item) }" \
ng-click="selectItem(item, $event);"> \
<span class="activate-textdown">{{ item[title] }}</span> \
</div> \
</div> \
</div>';
Here is the module, directive, controller, and associated filter code:
angular.module("hyg.Textdown", [])
.directive("textdown", ["$compile", "$document", "$filter", "$log", "$timeout", function ($compile, $document, $filter, $log, $timeout) {
return {
restrict: "E",
replace: false,
controller: "hygTextdownCtrl",
template: function (element, attrs) {
return HYG_TEXTBOX_DROPDOWN_TEMPLATE;
},
require: "?ngModel",
scope: {
inputPlaceholderText: "#",
isEditable: "=",
items: "=",
ngModel: "=",
title: "#",
width: "#"
},
link: function (scope, element, attrs) {
scope.orderBy = $filter("orderBy");
if (scope.isEditable == null)
scope.isEditable = true;
$document.bind("click", function (e) {
var shouldHideSelectList = !Enumerable.From(e.target.classList).Any(function (x) { return x == "activate-textdown"; });
if (shouldHideSelectList) {
$timeout(function () { scope.selectActive = false; }, 0);
}
});
scope.destroy = function () {
if (scope.handler != null)
scope.handler();
};
scope.isFocused = function () {
return scope.focus;
};
scope.isSelectActive = function () {
return scope.selectActive;
};
scope.isValid = function () {
return scope.input == null || scope.input.length == 0 || scope.ngModel != null;
};
scope.onInputChanged = function () {
var input = scope.input == null ? null : scope.input.toLowerCase();
var item = Enumerable.From(scope.items).Where(function (x) { return x[scope.title].toLowerCase() == input; }).ToArray()[0];
scope.selectItem(item);
};
scope.onInputFocus = function ($event) {
scope.focus = true;
scope.selectActive = true;
};
scope.onInputBlur = function () {
scope.focus = false;
scope.selectActive = false;
};
scope.selectItem = function (item, $event) {
if (scope.isEditable) {
scope.ngModel = item;
if (item != null)
scope.selectActive = false;
}
};
scope.isSelected = function (item) {
return scope.ngModel == item;
};
scope.handler = scope.$watch("ngModel", function () {
if(scope.ngModel != null)
scope.input = scope.ngModel[scope.title];
});
}
}
}])
.controller("hygTextdownCtrl", ["$scope", function ($scope) {
$scope.focus = false;
$scope.handler = null;
$scope.selectActive = false;
}])
.filter("property", ["$filter", function ($filter) {
return function (array, propertyString, target) {
if (target == null)
return array;
var matched = [];
var toMatch = target.toLowerCase();
angular.forEach(array, function (item) {
if (item[propertyString].includes != undefined) {
if (item[propertyString].toLowerCase().includes(toMatch)) {
matched.push(item);
}
}
else
{
if (item[propertyString].toLowerCase().indexOf(toMatch) > -1) {
matched.push(item);
}
}
});
return matched;
}
}]);
Thanks,
Jibba
The reason why the ng-click is not firing is because before the option is clicked, blur event is fired on the input, which hides the options and your option never gets clicked.
You can try selecting option using ng-mousedown instead of ng-click.
Create a diretive for content rating. I was used ng-mouseenter and ng-mouseleave.
There are two types content rating user can click and one is readonly.
when I passed readonly attribute ng-mouseleave is working. but When I didn't pass it is not working
basically I am using ng-mouseenter and ng-mouseleave for providing hovering effect on it and remove hovering effect.
This is my jsfiddle link
https://jsfiddle.net/amitme/3Ldad4v6/1/
var app = angular.module("glyph", []);
app.controller("contentRatingController", ['$scope', function($scope) {
$scope.rating1 = 5;
$scope.rating2 = 2.5;
$scope.isReadonly = true;
}]);
app.directive('contentRating', function contentRating() {
var defaults = JSON.parse('{}');
return {
restrict: 'E',
transclude: false,
require: '',
scope: {},
bindToController: {
ratingValue: "=ngModel",
max: "#",
onRatingSelect: "&?",
readonly: "=?",
name: "#"
},
controller: function() {
var g = this;
g.gRatingTemp = g.ratingValue;
if (g.max == undefined) {
g.max = 5;
}
g.updateStars = function(ratingValue) {
g.stars = [];
for (var i = 0; i < g.max; i++) {
g.stars.push({
filled: (i < ratingValue),
halffilled: (ratingValue)
});
}
console.log(g.stars);
};
g.updateStars(g.ratingValue);
g.toggle = function(index) {
if (g.readonly == undefined || g.readonly == false) {
g.ratingValue = index + 1;
console.log("----------toggle--------" + g.ratingValue);
if (g.gRatingTemp != g.ratingValue) {
g.gRatingTemp = g.ratingValue;
g.updateStars(g.ratingValue);
}
}
}
g.mouseEnter = function(index) {
console.log("----------mouseEnter--------" + index);
if (g.readonly == undefined || g.readonly == false) {
g.updateStars(index + 1);
}
};
g.mouseLeave = function(index) {
alert("----------mouseLeave--------" + index);
if (g.readonly == undefined || g.readonly == false) {
g.updateStars(index + 1);
}
};
},
compile: function() {
return {
pre: function(scope, el, attrs) {
for (var key in defaults) {
if (!attrs[key]) {
attrs[key] = defaults[key];
}
}
},
post: function(scope, el, attrs) {
}
};
},
template: '<ul class="rate" ng-class="{readonly: Ctrl.readonly}"> <li ng-repeat="star in Ctrl.stars" class="star" ng-mouseover="$event.stopPropagation(); Ctrl.mouseEnter($index)" ng-mouseleave="$event.stopPropagation(); Ctrl.mouseLeave($index)"> <input type="radio" id="{{Ctrl.name}}{{$index}}" name="{{Ctrl.name}}" value="{{$index+1}}" ng-model="Ctrl.ratingValue" ng-click="Ctrl.toggle($index)"/><label for="{{Ctrl.name}}{{$index}}" title="{{$index+1}}" ng-class="{filled:( star.filled && star.halffilled >= $index+1)}"></label> <input type="radio" id="{{Ctrl.name}}{{$index+1/2}}" name="{{Ctrl.name}}" value="{{$index+1/2}}" ng-model="Ctrl.ratingValue" ng-click="Ctrl.toggle($index-1/2)"/><label class="half" for="{{Ctrl.name}}{{$index+1/2}}" title="{{$index+1/2}}" ng-class="{filled: star.filled}" ></label> </li></ul><!-- <ul class="star-rating" ng-class="{readonly: Ctrl.readonly}"> <li ng-repeat="star in Ctrl.stars" class="star {{yellowStar}}" ng-class="{filled: star.filled}" ng-click="Ctrl.toggle($index)" ng-mouseenter="Ctrl.mouseEnter($index)" ng-mouseleave="Ctrl.mouseLeave($index)"> <span> <i class="fa fa-star"></i> </span> </li></ul> -->',
controllerAs: 'Ctrl',
};
});
I have a md-date-picker inside md-menu, but when I click on datepicker, md-menu closes. Here is codepen for this. It is related to this bug of ng-material. What can be workaround for this?
HTML:
<div class="md-menu-demo menudemoBasicUsage" ng-controller="BasicDemoCtrl as ctrl" ng-app="MyApp">
<div class="menu-demo-container" layout-align="center center" layout="column">
<h2 class="md-title">Month Select</h2>
<p>Select a month by clicking the input</p>
<md-menu>
<input md-menu-origin="" aria-label="Open phone interactions menu" ng-focus="ctrl.openMenu($mdOpenMenu, $event)" ng-model="ctrl.selectedMonth">
<md-menu-content width="4" ng-click="$event.stopPropagation()">
<md-datepicker ng-model="dateFilter" md-placeholder="Till date" md-min-date="dateFilter.fromDate"></md-datepicker>
</md-menu-content>
</md-menu>
</div>
</div>
JS:
angular
.module('MyApp')
.controller('BasicDemoCtrl', function DemoCtrl($mdDialog) {
var originatorEv;
this.openMenu = function($mdOpenMenu, ev) {
originatorEv = ev;
$mdOpenMenu(ev);
};
this.setMonth = function(val) {
this.month = val;
this.setSelectedMonth();
};
this.notificationsEnabled = true;
this.toggleNotifications = function() {
this.notificationsEnabled = !this.notificationsEnabled;
};
this.nextYear = function() {
this.year++;
this.setSelectedMonth();
};
this.preYear = function() {
this.year = this.year - 1;
this.setSelectedMonth();
};
}).directive('stopEvent', function() {
return {
restrict: 'A',
link: function(scope, element, attr) {
if (attr && attr.stopEvent)
element.bind(attr.stopEvent, function(e) {
e.stopPropagation();
});
}
};
});
I found a solution that works but is not the best answer.
HTML:
<md-datepicker id="myDatePicker"
ng-model="dateFilter"
md-placeholder="Till date"
md-min-date="dateFilter.fromDate">
</md-datepicker>
JS:
function setupDateButton()
{
var dateButtonFix = document.getElementById("myDatePicker").children;
for (var i = 0; i < dateButtonFix.length; i++)
{
if (dateButtonFix[i].tagName == 'BUTTON' || dateButtonFix[i].tagName == 'DIV')
{
if (dateButtonFix[i].tagName == 'DIV')
{
var child2 = dateButtonFix[i].children;
for (var j = 0; j < child2.length; j++)
{
if (child2[j].tagName == 'BUTTON')
{
child2[1].setAttribute("md-prevent-menu-close", "md-prevent-menu-close");
}
}
}
else
dateButtonFix[0].setAttribute("md-prevent-menu-close", "md-prevent-menu-close");
}
}
}
setupDateButton();
I'm sure there is a better way to do this but as of right now, it works.
Today I got to the same problem, and I created a class-restricted directive to solve this problem.
This is my directive's code:
const TRUE = 'true';
const PREVENT_CLOSE = 'md-prevent-menu-close';
class CalendarBtnFixDirective {
constructor() {
this.restrict = 'C';
this.require = '^^mdDatepicker'
}
link(scope, element, attrs, datePickerCtrl) {
const nativeElement = element[0];
const preventMenuClose = datePickerCtrl.$attrs.mdPreventMenuClose;
if ([TRUE, PREVENT_CLOSE].indexOf(preventMenuClose) !== -1) {
nativeElement.setAttribute(PREVENT_CLOSE, PREVENT_CLOSE);
}
}
}
export const MdCalendarFixModule = angular
.module('md.calendar.fix.module', [])
.directive('mdDatepickerTriangleButton', () => new CalendarBtnFixDirective())
.name;
Now in your md-datepicker you can use the md-prevent-menu-close attribute
Add md-prevent-menu-close="true" to the button or to the icon. This will prevent the menu from closing.
https://material.angularjs.org/1.0.3/api/directive/mdMenu
I am doing this by taking the cursor position from the content-editable box. When a new tag is created the cursor comes before the tag but it should be after the tag. Also i am not able to merge/split the tag.
Please give some idea how can i do this.
Visit (https://plnkr.co/edit/DSHKEcOnBXi54KyiMpaT?p=preview) !
What i want here, after pressing the enter key for new tag the cursor should be at the end of tag while it is not and also the merging/spliting functionality like the twitter what's happening box.
Thanks in advance.
Now this code is working fr me
$scope.myIndexValue = "5";
$scope.searchTag = function(term) {
var tagList = [];
angular.forEach($rootScope.tags, function(item) {
if (item.name.toUpperCase().indexOf(term.toUpperCase()) >= 0) {
tagList.push(item);
}
});
$scope.tag = tagList;
return $q.when(tagList);
};
$scope.getTagText = function(item) {
// note item.label is sent when the typedText wasn't found
return '<a>#<i>' + (item.name || item.label) + '</i></a> ';
};
$scope.resetDemo = function() {
// finally enter content that will raise a menu after everything is set up
$timeout(function() {
//var html = "Tell us something about this or add a macro like brb, omw, (smile)";
var htmlContent = $element.find('#htmlContent');
var html = "";
if (htmlContent) {
var ngHtmlContent = angular.element(htmlContent);
ngHtmlContent.html(html);
ngHtmlContent.scope().htmlContent = html;
// select right after the #
mentioUtil.selectElement(null, htmlContent, [0], 8);
ngHtmlContent.scope().$apply();
}
}, 0);
};
HTML :
<div class="share_tags fs-12">
<div class="row margin_row">
<div class="col-md-12 no_padding">
<div class="form-group">
<div contenteditable="true" mentio
mentio-typed-term="typedTerm"
mentio-macros="macros"
mentio-require-leading-space="true"
mentio-select-not-found="true"
class="editor tag" placeholder="Tell Us something about This"
mentio-id="'htmlContent'"
id="htmlContent"
ng-model="htmlContent">
</div>
</div>
<mentio-menu
mentio-for="'htmlContent'"
mentio-trigger-char="'#'"
mentio-items="tag"
mentio-template-url="/people-mentions.tpl"
mentio-search="searchTag(term)"
mentio-select="getTagText(item)"
></mentio-menu>
</div>
</div>
<script type="text/ng-template" id="/people-mentions.tpl">
<ul class="list-group user-search">
<li mentio-menu-item="tag" ng-repeat="tag in items" class="list-group-item">
<span ng-bind-html="tag.name | mentioHighlight:typedTerm:'menu-highlighted' | unsafe"></span>
</li>
</ul>
</script>
</div>
Reference link
http://jeff-collins.github.io/ment.io/?utm_source=angular-js.in&utm_medium=website&utm_campaign=content-curation#/
is working fine for me.
This is not working perfectly but for the time being i am using this code.
In app.js
var app = angular.module('plunker', []);
app.controller('MainCtrl', function ($scope, $filter, $element) {
var tags;
$scope.allTags = ['Tag1', 'PrivateTag', 'Xtag', 'PublicTag1', 'newTag', 'socialTag', 'cricketTag'];
var replacedTag = '';
var replacedIndex;
var data;
$scope.log = function (name) {
$scope.tags = [];
$('ul').html(' ');
console.log("here", $('ul'))
var data = $('textarea').val();
replacedIndex = data.indexOf(replacedTag)
console.log('test', name, replacedTag, replacedIndex, data);
var replacedData = data.substring(0, replacedIndex - 1) + ' #' + name + data.substr(replacedIndex + replacedTag.length);
$('textarea').val(replacedData);
$('textarea').keyup();
}
f = $scope.log;
$('textarea').on('keyup', function (e) {
function getIndexOf(arr, val) {
var l = arr.length,
k = 0;
for (k = 0; k < l; k = k + 1) {
if (arr[k] === val) {
return k;
}
}
return false;
}
$('ul').html('');
$scope.tags = [];
tags = $(this).val().match(/#\S+/g);
console.log("---tags-", tags)
var a = data = $(this).val();
if (tags && tags.length) {
tags.forEach(function (tag,index) {
var index1 = getIndexOf(tags, tag);
console.log("index----",index, index1,tag)
replacedTag = tag;
$scope.tags = tag ? $filter('filter')($scope.allTags, tag.substr(1)) : [];
if ($scope.tags && $scope.tags.length && (e.keyCode && e.keCode != 32)) {
$scope.tags.forEach(function (tag1, index) {
$('ul').append('<li>' + '<a href="javascript:;" onclick=f("' + tag1 + '");>'
+ tag1 + '</a>' + '</li>')
})
}
else {
$('ul').html(' ');
}
if(index == index1) {
var b = a.substring(0, a.indexOf(tag) - 1) + ' <a>' + tag + '</a> ' + a.substr(a.indexOf(tag) + tag.length);
}
else {
var b = a.substring(0, a.lastIndexOf(tag) - 1) + ' <a>' + tag + '</a> ' + a.substr(a.lastIndexOf(tag) + tag.length);
}
a = b;
$('p').html(b)
})
}
})
});
HTML
<br>
<br>
<p></p>
<textarea rows="2" cols="80"></textarea>
<div>
<ul>
</ul>
</div>
For live demo Visit
https://plnkr.co/edit/SD9eouQa5yrViwxQD6yN?p=preview
i am also looking for the better answer.
I assume you're talking about gathering hash tags from a string of sorts, the snippet below demonstrates how you can build an array of #hashed tags without modifying the cursor position.
It uses a simple regular expression to match tags found in the textarea and then pushes them to an array.
var tags;
$('textarea').on('keyup', function(){
tags = $(this).val().match(/#\S+/g)
$('ul').html('');
tags.forEach(function(tag){
$('ul').append('<li>' + tag + '</li>')
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea></textarea>
<ul></ul>
I want after add a input and remove it, arrange the numbers in input name element array by jQuery but don't work for me after remove input. How can fix it?
DEMO: http://jsfiddle.net/mUMdW/
My Code:
Html:
<div class="ch">
Add
</div>
<p class="ffdd"></p>
jQuery:
function removeidx(clss, type){
var remove = $(this).closest(clss);
remove.fadeOut('slow', function () {
$(this).remove(); // or change next line to $('.RowCheck:visible')
$(clss).each(function (idx) {
var checkBoxes = $('input[type="'+type+'"]',this);
checkBoxes.each(function(i) {
var str = $(this).attr('name');
var currentIdx = parseInt(str.match(/\d+/)[0], 10);
$(this).attr('name', str.replace(currentIdx,idx));
})
});
});
}
$(document).on('click change', 'a.adding', function(e) {
e.preventDefault();
var idx = $('.Row').length;
$('.ffdd').append('<div class="Row"> <input name="arr['+idx+'][]" type="text" value=""> Remove</div>');
});
$('.ffdd').on('click','a', function(e){
$(this).closest('.Row').remove();
removeidx('.ffdd', 'text');
})
I guess that you want to re-number the inputs after a remove, so that the array is made of contiguous numbers.
I have rewritten some things, among which the renumbering function, using an index contextual to the parent function.
function removeidx(context, clss, type) {
var remove = $(context).closest(clss);
remove.fadeOut('slow', function () {
$(this).remove();
var idx = 0;
$(clss).each(function () {
var checkBoxes = $('input[type="' + type + '"]', this);
checkBoxes.each(function () {
var name = $(this).attr('name');
name = name.replace(/\d+/, idx);
$(this).attr('name', name);
idx = idx + 1;
});
});
});
}
$(document).on('click change', 'a.adding', function (e) {
e.preventDefault();
var idx = $('.Row').length;
$('.ffdd').append('<div class="Row"> <input name="arr[' + idx + '][]" type="text" value=""> Remove</div>');
});
$('.ffdd').on('click', 'a', function (e) {
removeidx(this, '.Row', 'text');
})
You can see a working version there : http://jsfiddle.net/8sVWp/