I am new to angular and I have been trying to make a pretty advanced directive.
Most of the directive works, but there are two issues presenting themselves currently and I think they are both related to the scope.
Here is my directive:
angular.module('test')
.directive('testKitDesigner', function () {
panels = [];
function bindEvents() {
console.log("bindingEvents");
var styledElements = ["piping", "panel-1", "panel-2", "panel-3", "panel-4", "panel-5", "panel-6", "panel-7", "panel-8", "panel-9", "panel-10"];
for (var i = 0; i < styledElements.length; i++) {
var target = document.getElementById(styledElements[i]);
console.log(target);
if (target) {
bindEvent(target);
}
}
};
function bindEvent(target) {
console.log(target);
target.bindEvent("ngClick", selectPanel);
};
function selectPanel(event) {
var path = angular.element(event.target);
panels = []; // Reset
if (attrs.testKitDesigner && attrs.testKitDesigner === 'advanced') {
panels.push(path);
} else {
var parent = path.parent();
var paths = parent.children();
for (var i = 0; i < paths.length; i++) {
var current = angular.element(paths[i]);
var tag = current[0].nodeName;
if (tag === 'path' || tag === 'polyline') {
panels.push(current);
}
}
console.log(panels.length);
}
};
return {
restrict: 'A',
templateUrl: 'Views/Templates/designer.html',
link: function (scope, element, attrs) {
scope.step = 0;
scope.sport = 'General';
scope.garment = 'Dress';
scope.design = 'Angelus';
scope.nextStep = function () {
scope.step++;
};
scope.setSport = function (sport) {
scope.sport = sport;
scope.setSvg();
scope.nextStep();
};
scope.setGarment = function (garment) {
scope.garment = garment;
scope.setSvg();
scope.nextStep();
};
scope.setDesign = function (design) {
scope.design = design;
scope.setSvg();
scope.nextStep();
};
scope.setSvg = function () {
var children = element.children();
var template = scope.sport + '/' + scope.garment + '/' + scope.design;
for (var i = 0; i < children.length; i++) {
var child = angular.element(children[0]);
if (child.hasClass('base')) {
child.attr('test-svg', template);
bindEvents();
return;
}
}
}
scope.setColor = function (color) {
for (var i = 0; i < panels.length; i++) {
var panel = angular.element(panels[i]);
var parent = panel.parent();
if (parent.attr('id') === 'piping') {
panel.css({
'stroke': color
});
} else {
panel.css({
'fill': color
});
}
}
};
scope.init = function () {
bindEvents();
};
scope.init(); // Set our defaults;
}
}
})
.directive('testSvg', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.contentUrl = 'Views/Templates/' + attrs.testSvg + '.svg';
attrs.$observe('testSvg', function () {
console.log(attrs.testSvg);
scope.contentUrl = 'Views/Templates/' + attrs.testSvg + '.svg';
});
},
template: '<div ng-include="contentUrl"></div>'
};
});
And the designer template looks like this:
<div class="base" test-svg="/General/Polo/Angelus">
</div>
<div class="options">
<h1>Simple kit designer</h1>
<div ng-hide="step != 0">
<p>Choose your sport.</p>
<ul class="list-unstyled">
<li><a href ng-click="setSport('Netball');">Netball</a></li>
<li><a href ng-click="setSport('General');">General</a></li>
</ul>
</div>
<div ng-hide="step != 1">
<p>Choose your garment.</p>
<ul class="list-unstyled">
<li><a href ng-click="setGarment('Dress');">Dress</a></li>
<li><a href ng-click="setGarment('Polo');">Polo</a></li>
</ul>
</div>
<div ng-hide="step != 2">
<p>Choose your design.</p>
<ul class="list-unstyled">
<li><a href ng-click="setDesign('Angelus');">Angelus</a></li>
</ul>
</div>
<div class="colors" ng-hide="step != 3">
<p>Click an area to change the colour.</p>
<ul id="colour-picker" ng-hide="!picking" class="colours">
<li><a class="colour-red" href ng-click="setColor('red');"></a></li>
<li><a class="colour-orange" href ng-click="setColor('orange');"></a></li>
<li><a class="colour-yellow" href ng-click="setColor('yellow');"></a></li>
<li><a class="colour-green" href ng-click="setColor('green');"></a></li>
<li><a class="colour-blue" href ng-click="setColor('blue');"></a></li>
<li><a class="colour-indigo" href ng-click="setColor('indigo');"></a></li>
<li><a class="colour-violet" href ng-click="setColor('violet');"></a></li>
</ul>
</div>
</div>
Now, what should happen, is that when the user selects a sport or garment or design, the test-svg attribute should change to the new values and then the relevant svg will be loaded.
The attribute does change, but the observe function never gets called. I am certain that this is something to do with the scope but I can't figure it out.
You are adding test-svg attribute during link phase of test-kit-designer. The test-svg attribute are not compiled as directive so the $observe is not triggered, read up on $compile to solve your problem.
However, I would recommend restructuring your code. Consider using test-svg in the template, exposing template from setSvg in test-kit-designer and two-way binding it to another variable in test-svg.
Related
I am trying to implement a simple pagination with directives in angularjs
Controller
//load the result server
$scope.getData = function(page) {
$http.post('/search?page='+page,searchParams).then(function (response) {
if(response.data.status){
$scope.arts = response.data.result;
$scope.currentPage = response.data.currentPage;
$scope.pageCount = response.data.pageCount;
}else{
$scope.arts = [];
}
},function (err) {
$scope.arts = [];
console.log(err);
});
}
When the HTTP call is finished i am using a directive is to print the pagination links.
HTML
<ul class="pagination pagination-circle pg-blue mb-0 paginate">
<pagination pagecount="pageCount" currentpage="currentPage"></pagination>
</ul>
Directive
This directive just build pagination links and returns to
app.directive('pagination',function () {
//custom directive for build pagination
return {
restrict: 'E',
scope: {
pagecount: '=',
currentpage: '='
},
link:function (scope,elem,attr) {
var element = angular.element(document.getElementsByClassName('paginate'));
var str ='';
var i = 1;
if (scope.currentpage > 5) {
i = +scope.currentpage - 4;
}
for (i; i<=scope.pagecount; i++) {
if (scope.currentpage == i) {
str+='<li class="page-item active"><a href="#" class="page-link" >'+i+'</a></li>'
}else{
str+=' <li class="page-item"><a class="page-link" href="" ng-click="getData('+i+')">'+i+'</a></li>'
}
if (i == (+scope.currentpage + 4)) {
break;
}
}
element.prepend(str);
}
};
})
But the problem is ng-click="getData()" in the anchor is not worked why its not getting worked.
the difference is getData() is defined in controller
I think href="" might be redirecting or refreshing your page, which is why ng-click is not getting triggered.
Try it as below.
href="#"
or
href="javascript:void(0)"
You need to pass that function to directive for access that function from directive.
Html
<ul class="pagination pagination-circle pg-blue mb-0 paginate">
<pagination pagecount="pageCount" get-data="getData()" currentpage="currentPage"></pagination>
</ul>
Directive
This directive just build pagination links and returns to
app.directive('pagination',function () {
//custom directive for build pagination
return {
restrict: 'E',
scope: {
pagecount: '=',
currentpage: '=',
getData: '&' /**This is how you can access function of controller**/
},
link:function (scope,elem,attr) {
var element = angular.element(document.getElementsByClassName('paginate'));
var str ='';
var i = 1;
if (scope.currentpage > 5) {
i = +scope.currentpage - 4;
}
for (i; i<=scope.pagecount; i++) {
if (scope.currentpage == i) {
str+='<li class="page-item active"><a href="#" class="page-link" >'+i+'</a></li>'
}else{
str+=' <li class="page-item"><a class="page-link" href="" ng-click="getData('+i+')">'+i+'</a></li>'
}
if (i == (+scope.currentpage + 4)) {
break;
}
}
element.prepend(str);
}
};
})
I'm having two problems with this snipet:
1) for some reason the body is not updating with the innerHTML of #test
2) The client's email is coming up on the SHARE link but I can't get it to open on submit
function Mailto_url(){
var encode_mailto_component = function(str){
try{ return encodeURIComponent(str); }
catch(e){ return escape(str); }
}
var AddressList = function(){
var list = [];
this.length = 0;
this.add = function(address){
if(address) {
list.push(address);
this.length = list.length;
}
};
this.get = function(){
return list.join(';');
};
};
var subject = '',
body = '',
mainList = new AddressList(),
ccList = new AddressList(),
bccList = new AddressList();
this.setSubject = function(str){ subject = encode_mailto_component(str); }
this.setBody = function(str){ body = encode_mailto_component(str); }
this.addMain = function(x) { mainList.add(x); }
this.addCC = function(x) { ccList.add(x); }
this.addBCC = function(x) { bccList.add(x); }
this.getURL = function(allow_empty_mainList){
var out = ['mailto:'];
var extras = [];
if(mainList.length === 0 && !allow_empty_mainList){
throw('Mailto_url: no main addressees');
}
else{
out.push(mainList.get());
}
if(subject) { extras.push('subject=' + subject); }
if(ccList.length) { extras.push('cc=' + ccList.get()); }
if(bccList.length) { extras.push('bcc=' + bccList.get()); }
if(body) { extras.push('body=' + body); }
if(extras.length) { out.push('?' + extras.join('&')); }
return out.join('');
}
}
function getContent() {
var mailTo = new Mailto_url();
var test = document.getElementById('test');
mailTo.addMain('rssxyze#gmail.com');
mailTo.addMain('mrsairshow#ddd.net');
mailTo.addCC('linda#xyz.com');
mailTo.addCC('mandy#abc.com');
mailTo.addBCC('susanne#mno.com');
mailTo.addBCC('chris#mno.com');
mailTo.setSubject("test");
mailTo.setBody(test.innerHTML);
window.location=mailTo.getURL(true);
}
<html>
<body>
<div id="wrapper">
<form>
<ul>
<li>
<a class="home" href="#"></a>
</li>
<li><a id="share" class="share" href="#" onclick="getContent()">Share Quote</a></li>
<li>
<a class="info" href="#"></a>
</li>
<li><input id='test' type='text'></input>
</li>
<li><input type='submit' onclick="getContent()"></input>
</li>
</ul>
</form>
</div>
</body>
</html>
Part 2
<form onsubmit="return false">
change this lines
mailTo.setBody(test.value);
It should not be innerHTML it should be value.
Also, there are no function calls in the code you posted.
I'm sorry if this is a duplicate questions, but I've spent over 8hrs trying to figure out how to implement this, but it seems I haven't succeded so far.
I've got a photo gallery, each foto contains multiple tags. If multiple tags are active, it needs to show only items that include all the selected tags.
<ul class="tags-list container">
<li class="tag toate">Toate</li>
<li class="tag">Tag 3</li>
<li class="tag">Tag 2</li>
<li class="tag">Tag 4</li>
</ul>
<div class="grid">
<div class="grid-sizer"></div>
<div id="grid-item-0" class="grid-item " data-tag=" tag-1 tag-3 tag-7"></div>
<div id="grid-item-1" class="grid-item " data-tag=" tag-2 tag-1"></div>
<div id="grid-item-2" class="grid-item big" data-tag=" tag-3"></div>
<div id="grid-item-3" class="grid-item " data-tag=" tag-4 tag-2 tag-1"></div>
</div>
and JS:
function Gallery() {
this.tag = $('.tags-list .tag');
this.gridItem = $('.grid .grid-item');
this.activeTags = ['toate'];
this.itemsList = [];
this.activeItems = [];
this.init();
}
Gallery.prototype.init = function () {
this.masonry();
this.itemsListGen();
};
Gallery.prototype.itemsListGen = function () {
var _this = this;
$(this.gridItem).each(function () {
var tags = $(this).attr('data-tag');
tags = tags.trim();
// creates an array of objects with every item's id and tags
_this.itemsList.push({
'id': $(this).attr('id'),
'tags': tags.split(' ')
});
});
};
Gallery.prototype.masonry = function () {
$('.grid').masonry({
// options
columnWidth: '.grid-sizer',
itemSelector: '.grid-item',
percentPosition: true
});
};
Gallery.prototype.tagClick = function (e) {
e.preventDefault();
// currentTag has the clicked tag
var currentTag = $(e.currentTarget).attr('data-tag-slug');
$(e.currentTarget).toggleClass('active');
if ($(e.currentTarget).attr('data-tag-slug') === 'toate') {
this.toateClick();
} else {
// refresh current filters
this.refreshTags(currentTag);
// return validated items based on filters
this.returnValidItems();
// refresh the layout with the current items
this.refreshMasonry();
}
};
Gallery.prototype.refreshTags = function (e) {
// checks if the current tag is already active
// if it's active, it removes it and checks if there is any other active tag or should trigger 'toate'
// if it's not active, it adds it to the activeTags and removes 'toate'
if (this.activeTags.includes(e)) {
this.activeTags.splice(this.activeTags.indexOf(e), 1);
if (this.activeTags.length < 1) {
this.showAll();
}
} else {
this.activeTags.push(e);
if (this.activeTags.includes('toate')) {
this.removeToate();
}
}
};
Gallery.prototype.returnValidItems = function () {
for (var i = 0; i < this.itemsList.length; i++) {
var itemTags = this.itemsList[i].tags;
for (var j = 0; j < this.activeTags.length; j++) {
if (itemTags.includes(this.activeTags[j])) {
this.activeItems.push(this.itemsList[i].id);
} else {
this.activeItems.splice(this.activeItems.indexOf(this.itemsList[i].id), 1);
break;
}
}
}
};
Gallery.prototype.refreshMasonry = function () {
$('.grid-item').hide();
for (var i = 0; i < this.activeItems.length; i++) {
$('#' + this.activeItems[i]).show();
}
$('.grid').masonry();
};
Gallery.prototype.showAll = function () {
console.log('show all');
this.activeTags = ['toate'];
this.activeItems = [];
$('.grid-item').show();
$('.tags-list').find('.toate a').addClass('active');
$('.grid').masonry();
};
Gallery.prototype.toateClick = function (e) {
console.log('toate click');
$('.tags-list').find('.active').removeClass('active');
$('.tags-list .toate a').addClass('active');
this.showAll();
};
Gallery.prototype.removeToate = function () {
$('.tags-list li.toate > a').removeClass('active');
this.activeTags.splice(this.activeTags.indexOf('toate'), 1);
};
I am not capable of doing it. Please help? :)
Besides of helping me with the problem, any feedback regarding my code is welcomed.
I'm trying to learn local storage. I have five links each with a data-date attribute and I want them to be sorted using that attribute. I've tried numerous ways but nothing seems to work. From what I understand, I should parse before sorting but it didn't work for me. I must have done it wrong because I don't see how else to do it.
Here is my HTML:
<div id="div1">
<input id='clearHistory' type='button' value='Remove All History' />
<input id='showHistory' type='button' value='Show History' />
<ul id='history'>
<li>
<a class='date' href="#aa" data-date="November 12, 2001 03:24:00">Link 1</a>
</li>
<li>
<a class='date' href="#bb" data-date="August 4, 1993 03:24:00">Link 2</a>
</li>
<li>
<a class='date' href="#cc" data-date="October 17, 1995 03:24:00">Link 3</a>
</li>
<li>
<a class='date' href="#dd" data-date="December 1, 2010 03:24:00">Link 4</a>
</li>
<li>
<a class='date' href="#ee" data-date="August 17, 2004 03:24:00">Link 5</a>
</li>
</ul>
</div>
<p>Click on 'Show History' to see the 'user history'.</p>
<ul id='storedHistory'></ul>
And my JavaScript:
$(document).ready(function() {
var storedHistory = document.getElementById('storedHistory');
Storage.prototype.setObject = function(key, value) {
this.setItem(key, JSON.stringify(value));
};
Storage.prototype.getObject = function(key) {
var value = this.getItem(key);
return value && JSON.parse(value);
};
//function sortDatesAscending(a, b) { return a.valueOf() - b.valueOf(); } function sortDatesDescending(a, b) { return b.valueOf() - a.valueOf(); }
function sortLinkDatesAscending(obj1, obj2) {
return obj1.date.valueOf() - obj2.date.valueOf();
}
function sortLinkDatesDescending(obj1, obj2) {
return obj2.date.valueOf() - obj1.date.valueOf();
}
var history = {
items: []
};
// Get each anchor and retrieve its date and link. Add to an object and add that object to the history's array Sort by ascending. Add to local storage.
$('ul > li > a').click(function(e) {
var date = $(this).attr('data-date');
var listData = {
link: $(this).attr("href"),
date: date
};
history.items.push(listData);
window.localStorage.setObject("history", history);
});
/* Remove items from local storage */
$('#clearHistory').click(function() {
window.localStorage.clear();
});
/* Retrieve items from local storage and add to stored history unordered list */
$('#showHistory').click(function() {
console.log(window.localStorage);
var listHistory = localStorage.getObject('history');
var counter = 1;
for (var i = 0; i < listHistory.items.length; i++) {
$("#storedHistory").append('<li>' + counter + '. Link: ' + listHistory.items[i].link + '<br>' + 'Date: ' + listHistory.items[i].date + '</li>');
counter++;
}
});
});
And here is the jsfiddle: https://jsfiddle.net/fLLsfd5j/2/
Try this for sorting! (http://trentrichardson.com/2013/12/16/sort-dom-elements-jquery/)
var $history = $('#history li'),
$historyA = $history.children();
$historyA.sort(function (a, b) {
var an = Date.parse(a.getAttribute('data-date')).valueOf(),
bn = Date.parse(b.getAttribute('data-date')).valueOf();
if (an > bn) {
return 1;
}
if (an < bn) {
return -1;
}
return 0;
});
$('#history').empty();
$.each($historyA, function () {
$('#history').append($('<li>').html(this));
});
I guess this should do your job
function getHistory(){
var as = document.querySelectorAll(".date"); // get elements with date class
Array.prototype.map.call(as, e => e.cloneNode(true)) //clone them into an array and sort
.sort((p,c) => Date.parse(p.dataset.date)<=Date.parse(c.dataset.date) ? -1 : 1)
.forEach((e,i) => as[i].parentNode.replaceChild(e, as[i]));
}
showHistory.onclick = getHistory; //add "click" eL to the DOM element with showHistory id
http://jsbin.com/yoraqusora/2/edit?js,console,output
I'm migrating my jQuery app to AngularJS.
What I need to do, is change the Data Array when a scroll occurred, how can i do this?
I have this code with jQuery at plunk:
http://plnkr.co/edit/jdwxH5pmyecuWTsrutrO?p=preview
When you scroll the div, a list with the visible elements index is show.
What I want to do, is to set a directive or a filter (ng-check-visibility) at the ng-repeat element, like:
<div ng-repeat="item in data" ng-check-visibility>
{{item.name}}
</div>
And this directive change the item setting the value item.visible=true when the element is visible, otherwise, set it to false.
Can I do this with Angular? Any ideas?
Here's a way to do it as a directive:
var app = angular.module('myapp', []);
app.controller('MainCtrl', function($scope) {
arr = [];
for(var i=0; i<500; i++){
arr.push({id: i, name: 'name'+i});
}
$scope.data = {
items: arr,
visible: []
};
});
app.directive('checkVisibility', function() {
return {
scope: {
data: '=checkVisibility'
},
link: function(scope, el, attrs) {
el.scroll( function() {
var reference_top = el.offset().top;
var reference_height = el.height();
var $elements = el.find('.check');
scope.data.visible = [];
for(var i=0; i<$elements.length; i++){
var $element = $($elements[i]);
var element_top = $element.offset().top;
var element_height = $element.height();
if (reference_top < element_top + element_height &&
reference_top + reference_height > element_top) {
scope.data.visible.push( i );
}
}
scope.$apply();
});
}
};
});
--
<body ng-controller="MainCtrl">
<div class="outer-panel" check-visibility="data">
<div class="inner-panel">
<div ng-repeat="item in data.items" class="check">
{{item.name}}
</div>
</div>
</div>
<div id="visibles">
{{data.visible}}
</div>
</body>
plunkr