how to access child elements of <ul>? - javascript

while creating custom directive, I am trying to fetch html elements
'<div class="parent">',
'<ul class="item-list">',
'<li ng-repeat="item in ::items" class="item-element">{{item}}</li>',
'</ul>',
'</div>',
'<div class="selector"></div>'
link: function($scope, element, attrs) {
var ta = element[0],
$ta = element;
var itemList = angular.element(ta.querySelector(".item-list"));
console.log(itemList);
var itemEl = itemList[0].children;
console.log(itemEl);
}
itemEl displayed on console
[]
0: li.item-element.ng-binding
1: li.item-element.ng-binding
2: li.item-element.ng-binding
3: li.item-element.ng-binding
length: 4
__proto__: Object
How can I fetch inner html and length of the <ul> in angularjs ?

i don't know more about AJ but you can do it in jquery
$(".parent .item-list li").length;

Related

How to Dynamically add Direcitves angularjs

I have successfully Added an angularjs directive into another directive using this code
var newElement = $compile( "<div my-diretive='n'></div>" )( $scope );
$element.parent().append( newElement );
but how can i pass the my-diretive='n' n value dynamically .
$scope.showDirective = function(item){
var newElement = $compile( "<div my-diretive='n'></div>" )( $scope );
//here i want to replace 'n' with item
$element.parent().append( newElement );
}
is it possible to pass the value or any other way to do it?
If myDirective has isolated scope, this will create two-way data binding:
$scope.showDirective = function(item){
$scope.item = item;
// the interpolation will be against $scope with item available on it
var newElement = $compile( "<div my-diretive='item'></div>" )( $scope );
$element.parent().append( newElement );
}
Let us know, if it works for you.

AngularJS Directive - compile is not working

ng-click is not working. Can someone help me, how to use the $compile?
I have created a plunker http://plnkr.co/edit/AhxGYnOsJ7rPqcQquMfq?p=preview
// main.js
var app = angular.module('myApp', ['ngGrid']);
app.controller('MyCtrl', function($scope) {
})
.directive('myDir', function($compile){
return {
restrict: 'CAE',
link: function(scope, element, attrs){
var dropdown = '';
dropdown += '<ul class="dropdown-menu pull-right" role="menu" aria-labelledby="'+ attrs.id +'">';
dropdown += '<li role="presentation">Actions</li>';
dropdown += '</ul>';
//dropdown = $compile(dropdown)(scope);
element.after(dropdown);
}
}
});
I would pull out the click function into your controller. There is no alert on your controller's scope so it does nothing. Also, I try to avoid lots of nested quotes if possible. A simple and clean refactor.
var app = angular.module('myApp', ['ngGrid']);
app.controller('MyCtrl', function($scope) {
$scope.actionClick = function() {
alert("a");
}
})
.directive('myDir', function($compile){
return {
restrict: 'CAE',
link: function(scope, element, attrs){
var dropdown = '';
dropdown += '<ul class="dropdown-menu pull-right" role="menu" aria-labelledby="'+ attrs.id +'">';
dropdown += '<li role="presentation">Actions</li>';
dropdown += '</ul>';
dropdown = $compile(dropdown)(scope);
element.after(dropdown);
}
}
});
see plunkr
You were very close. Compile template with $compile:
var app = angular.module('myApp', ['ngGrid']);
app.controller('MyCtrl', function($scope) {
$scope.doSomething = function(){
alert('d');
};
})
.directive('myDir', function($compile){
return {
restrict: 'CAE',
link: function(scope, element, attrs){
var dropdown = '';
dropdown += '<ul class="dropdown-menu pull-right" role="menu" aria-labelledby="'+ attrs.id +'">';
dropdown += '<li role="presentation">Actions</li>';
dropdown += '</ul>';
var a_input = angular.element($compile(dropdown)(scope));
element.append(a_input);
}
}
});
Demo Plunker
comment
AngularJS doesn't know about alert. If you want to call it from HTML, override it like #udidu showed.
When you're using ng-click the ng-click directive searche for the expression in the current scope. Since their is no alert function on the directive's scope (or it's parent scope) then nothing happen.
You can add the alert function on your scope like so:
.directive('myDir', function($compile){
return {
restrict: 'CAE',
link: function(scope, element, attrs){
// add the alert function on the scope
scope.alert = function(param) {
alert(param);
}
var dropdown = '';
dropdown += '<ul class="dropdown-menu pull-right" role="menu" aria-labelledby="'+ attrs.id +'">';
dropdown += '<li role="presentation">Actions</li>';
dropdown += '</ul>';
dropdown = $compile(dropdown)(scope);
element.after(dropdown);
}
}
});

AngularJS click to edit fields such as dropdown

I stumbled upon this article on how to build a click to edit feature for a form. The author states:
What about if you wanted input type="date" or even a select? This
is where you could add some extra attribute names to the directive’s
scope, like fieldType, and then change some elements in the template
based on that value. Or for full customisation, you could even turn
off replace: true and add a compile function that wraps the necessary
click to edit markup around any existing content in the page.
While looking through the code I cannot seem to wrap my head around how I could manipulate the template in such a way that I could make it apply to any angular component, let alone how I can make it apply to a drop down list. Code from article below:
app.directive("clickToEdit", function() {
var editorTemplate = '<div class="click-to-edit">' +
'<div ng-hide="view.editorEnabled">' +
'{{value}} ' +
'<a ng-click="enableEditor()">Edit</a>' +
'</div>' +
'<div ng-show="view.editorEnabled">' +
'<input ng-model="view.editableValue">' +
'Save' +
' or ' +
'<a ng-click="disableEditor()">cancel</a>.' +
'</div>' +
'</div>';
return {
restrict: "A",
replace: true,
template: editorTemplate,
scope: {
value: "=clickToEdit",
},
controller: function($scope) {
$scope.view = {
editableValue: $scope.value,
editorEnabled: false
};
$scope.enableEditor = function() {
$scope.view.editorEnabled = true;
$scope.view.editableValue = $scope.value;
};
$scope.disableEditor = function() {
$scope.view.editorEnabled = false;
};
$scope.save = function() {
$scope.value = $scope.view.editableValue;
$scope.disableEditor();
};
}
};
});
My question is, how can we extend the above code to allow for drop down edits? That is being able to change to the values that get selected.
One approach you might consider is using template: function(tElement,tAttrs ).
This would allow you to return appropriate template based on attributes.
app.directive("clickToEdit", function() {
return {
/* pseudo example*/
template: function(tElement,tAttrs ){
switch( tAttrs.type){
case 'text':
return '<input type="text"/>';
break;
}
},....
This is outlined in the $compile docs

Is there any way to just use a child directive as a placeholder for holding content?

I am new to angularjs. Want to see if there is any way to just use a child directive as a placeholder for holding content but for really rendering?
I don't want to do rendering in the child directive because I want to let the parent to do everything. So I can have some other special logic in the parent directive.
angular.module('components', []).
directive('tabs', function() {
return {
restrict: 'E',
transclude: true,
scope: {},
controller: function($scope, $element) {
var panes = $scope.panes = [];
$scope.select = function(pane) {
angular.forEach(panes, function(pane) {
pane.selected = false;
});
pane.selected = true;
}
$scope.createPane = function() {
var pane = panes[panes.length - 1];
var clonedPane = Angular.copy(pane);
panes.push(clonedPane);
}
this.addPane = function(pane) {
if (panes.length == 0) $scope.select(pane);
panes.push(pane);
}
},
template: '<div class="tabbable tabs-left">' +
'<ul class="nav nav-tabs">' +
'<li ng-repeat="pane in panes" ng-class="{active:pane.selected}">' +
'{{pane.title}}' +
'</li>' +
'<li ng-class="addLink"><a ng-click="createPane()"><i class="icon-plus"></i> tab</a></li>' +
'</ul>' +
'<div class="tab-content">' +
'<div ng-repeat="pane in panes" class="tab-pane" ng-class="{active: selected},{{pane.pclass}}" id="{{pane.id}}">' +
'{{ pane.content }}' +
'</div>' +
'</div>' +
'</div>',
replace: true
};
}).
directive('pane', function() {
return {
require: '^tabs',
restrict: 'E',
scope: {
title: 'bind',
pclass: 'bind',
id: 'bind'
},
link: function(scope, element, attrs, tabsCtrl) {
var text = element.text();
tabsCtrl.addPane({
title: scope.title,
pclass: scope.pclass,
id: scope.id,
content: text
});
}
};
})
Corresponding html code:
<tabs>
<pane title="tab 1" id="tab1" pclass="tab">
hello
</pane>
<pane title="tab 2" id="tab2" pclass="tab">
world
</pane>
</tabs>
I tried something like the above but nothing is pushed into panes. Seems like the link function of the child directive (pane) is never called. So as a result, only the link for adding tab is displayed.
Any ideas?
The easiest (though sort of annoying) way to approach this would be to have a template for each pane.
In html:
<tabs>
<pane title="tab 1" id="tab1" pclass="tab" template="pane1-templ">
</pane>
<pane title="tab 2" id="tab2" pclass="tab" template="pane2-templ">
</pane>
</tabs>
<script type="text/ng-template" src="pane1-template">hello</script>
<script type="text/ng-template" src="pane2-template>wolrd</script>
In your directive:
'<div class="tab-content">' +
'<div ng-repeat="pane in panes" class="tab-pane" ng-class="{active: selected},{{pane.pclass}}" id="{{pane.id}}">' +
'<div ng-include src="pane.template"></div>' +
'</div>' +
'</div>'
Or.. have you tried doing this to fix it?
In pane:
var html = elm.html();
//...
pane.html = html;
In tabs:
<div ng-bind-html-unsafe="pane.html"></div>
Perhaps the reason why it is not working is because in your child directive you have no template. Without a template, the directive does not know where and how to render the directive.
What you could do is have something like:
template : '<div style="display:none;" ng-transclude></div>'
in your child template..
and your element.text() should still work!
Without a template the linking function would never get the element so your child directive will fail right there.
With the template above it should work.

Jquery plugin get options from option

How do I get the options from another set of options.
JS Fiddle Example
at the moment this is outputting the name of each option in opt.social. Instead I want it to fetch the actual HTML related to each option name.
Thus the idea is that in the future when a new social media site is built, this can easily be added via the plugin options without the need to edit the plugin.
Example:
$.each(opt.social, function(index, value) {
html += "<li>" + value.name + "</li>";
});
I have tried
opt[value.name];
opt.value.name;
opt(value.name);
Full example:
(function ($) {
$.fn.socialMedia = function (options) {
// default configuration properties
var defaults = {
social: [
{ name: "facebook.like_large"},
{ name: "twitter.large"},
{ name: "googlePlus.large"}
],
facebook: {
like_large: '<div class="fb-like" data-href="{url}" data-send="false" data-layout="box_count" data-width="120" data-show-faces="false"></div>',
like_small: '<div class="fb-like" data-href="{url}" data-send="false" data-layout="button_count" data-width="120" data-show-faces="false" data-colorscheme="dark"></div>',
share: '<a name="fb_share" type="box_count" share_url="{url}" href="http://www.facebook.com/sharer.php?u={url}&t={title}">Share</a>'
},
twitter: {
large: 'Tweet',
small: 'Tweet'
},
googlePlus: {
large: '<div class="g-plusone" data-size="tall" data-href="{url}"></div>',
small: '<div class="g-plusone" data-size="medium" data-annotation="inline" data-width="120" data-href="{url}"></div>'
}
};
var opt = jQuery.extend(defaults, options);
// Generate HTML
$(this).append(generateHtml());
function generateHtml() {
var html = '<ul>';
$.each(opt.social, function(index, value) {
html += "<li>" + value.name + "</li>";
});
html += '</ul>';
return html;
}
}
$("body").socialMedia();
})(jQuery);
In that code, opt.social is an array of objects, each with a "name" property.
Thus,
var firstOptName = opt.social[0].name;
And so on. The opt.social array should be indexed numerically. Now, opt.googlePlus is just an object with (in this case) two properties, so there's no array indexing involved:
var googleLarge = opt.googlePlus.large;
edit — if you want to just alter that loop to show the HTML:
$.each(opt.social, function(index, value) {
var parts = value.name.split('.'), partVal = opts;
for (var i = 0; i < parts.length; ++i)
partVal = partVal[parts[i]];
html += "<li>" + partVal + "</li>";
});
The trick is that those "name" properties are in the form of dotted "paths" through an object graph, and JavaScript does not have a built-in way of interpreting those. The code I wrote above walks through the object part by part (parts are separated by "." characters), starting from the outer "opts" object.
I've updated your fiddle, according to what I understood you wanted.
each social entry can contain the name of the social service and a default widget to use.
so your defaults can be like this:
social: [
{ name: 'facebook', widget : 'like_large'},
{ name: 'twitter', widget : 'large'},
{ name: 'googlePlus', widget : 'large'}
]
and your generateHTML() can be:
$.each(opt.social, function(index, value) {
var default_widget = opt[value.name][value.widget];
html += "<li>" + default_widget + "</li>";
// or maybe:
html += $('<li></li>').html(default_widget);
});

Categories

Resources