How to create an element after directive element in Angular 4 - javascript

I have one directive in angularjs which is working fine but it uses jQuery and now I want tot convert it in Angular 4
Old Directive (AngularJS)
app.directive('errName', function () {
return {
link: function (scope, element, attrs) {
element.bind('error', function () {
if (attrs.src != attrs.errName) {
var name = attrs.errName.split(' ');
var attr = '?';
if (name.length == 1)
{
attr = name[0].substring(1, 0);
}
if (name.length > 1)
{
attr = name[0].substring(1, 0) + name[1].substring(1, 0);
}
$(element).hide();
$(element).after('<span class="thumb-alpha"><span class="default-thumb"><span class="default-thumb-placeholder">' + attr.toUpperCase() + '</span></span></span>');
}
});
}
}
});
it simply check there's an error in image on load then it hide that element and create one span after that I changes its name in Angular 4 to show initials I'm getting value hiding element as well but can't able to create span after that
New Directive which I try to build :
export class ShowInitialsDirective {
#Input() initials;
constructor(el: ElementRef, renderer:Renderer) {
var element = el.nativeElement
renderer.listen(el.nativeElement, 'error', (event) => {
var initial_default = '?';
var name = this.initials;
name = name.split(' ');
if (name.length == 1)
{
initial_default = name[0].substring(1, 0);
}
if (name.length > 1)
{
initial_default = name[0].substring(1, 0) + name[1].substring(1, 0);
}
this.initials = initial_default;
renderer.setElementStyle(el.nativeElement,'display','none');
// element.after('<span class="thumb-alpha"><span class="default-thumb"><span class="default-thumb-placeholder">' + this.initials.toUpperCase() + '</span></span></span>');
})
}
}
Is there any way to create span in it and put this html code in it

In Angular 2+, you should create a component when you want to change html. Components are directives with html templates. The directive you are creating here is an attribute directive. Attribute directives do not contain html templates. For more information, please refer angular website.

Sure, but you might think about rewriting it all together, and creating it into a component. If you really really don't want that, you can use the insertBefore on the parent of nativeElement, and with a little trickery:
const div: HTMLDivElement = document.createElement('div');
div.innerHTML = '<span class="thumb-alpha"><span class="default-thumb"><span class="default-thumb-placeholder">' + this.initials.toUpperCase() + '</span></span></span>';
const insert: HTMLElement = div.firstChild as HTMLElement;
const before: HTMLElement = el.nativeElement.nextElementSibling;
const parent: HTMLElement = el.nativeElement.parentElement;
if (before) {
parent.insertBefore(insert, before);
} else {
parent.appendChild(insert);
}
But again, way prettier would be to create a completely new component doing all this awesome work for you

Related

create jquery components using a function and using them in the HTML

I want to create some jquery components using a function and use them in the html, but dont know how to do it. Here's what i've attempted.
function CreateElement (id, type) {
$(document).ready (function () {
if (type == "metricCombobox") {
var element = $('#' + id).combobox();
return element;
}
});
}
And in the HTML page.
<select id="' + CreateElement('metricComboboxSimples', 'metricCombobox') + '" >
You really need to create the element in the DOM and then append things to it?
Or can you create all elements in the jQuery? Creating in jQuery, at least for me, is easier to understand, you can control the entire creation of the element and then append it to another element that is already in the DOM or even to the document.body... please, take a look at the code below and run the snippet to see if my answer helps you.
var selectOptions= [
{val : 1, text: 'One'},
{val : 2, text: 'Two'},
{val : 3, text: 'Three'}
];
function CreateElement (id, type, options) {
$(document).ready (function () {
if (type == "metricCombobox") {
var arr = options;
var element = $('<select>');
element.attr('id', id);
$(arr).each(function() {
var selOption = $("<option>");
selOption.attr('value', this.val);
selOption.text(this.text);
element.append(selOption);
});
}else if (type == 'input'){
var element = document.createElement('input');
element.value = 'input created'
$(element).attr('id',id);
}
$("#MyDiv").append(element);
});
}
CreateElement('metricComboboxSimples', 'metricCombobox', selectOptions);
CreateElement('testeId', 'input');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="MyDiv"></div>

How can I write my listener function somewhere else if it uses local variables?

I am a beginner in Javascript development and I have to do the classical to-do app. It has to be object-oriented and my program has two classes: Task and Tag.
A task contains some tags.
When the user clicks on a tag, he can modify its name. First, I did wrote an anonymous callback function which was listening to the modification form submission and it worked well. But, I have to create a named function declared somewhere else instead of my existing listener. However, I need to access to some of the properties of my object (which is edited) and I have absolutely no idea how to do a thing like that.
Here is a small part of my code:
module.Tag = class Tag {
constructor(name = 'untitled', parent = null) {
this.name = name;
this.parentTask = parent;
}
//Method which displays the tag name
display_name() {
return $('<li>').addClass('tag').text(this.name);
}
//Method which displays the tag
display() {
let tag_item = this.display_name();
let field = $('<input>').prop('type', 'text').prop('value', this.name);
let button = $('<button>').addClass('validationButton').prop('type', 'submit').text('✓');
let removeButton = $('<button>').addClass('removeButton').text('X');
let form = $('<form>').append(field).append(button).append(removeButton);
let in_edit = false;
tag_item.click((event) => {
event.stopPropagation();
event.preventDefault();
let target = $(event.target);
if (target.is('li') && !in_edit) {
tag_item.empty();
tag_item.append(form);
in_edit = true;
}
if (target.is('button') && target.prop('type') === 'submit') {
if(field.val() !== '') {
this.name = field.val();
module.StorageManager.storeTasks();
}
tag_item.empty();
tag_item.text(this.name);
field.val(this.name);
in_edit = false;
}
if (target.is('button') && target.hasClass('removeButton')) {
if(confirm('Voulez-vous vraiment supprimer ce tag ?')) {
tag_item.remove();
this.removeTagFromParent();
module.StorageManager.storeTasks();
}
}
});
return tag_item;
}
//Method which removes the tag from the parent task
removeTagFromParent() {
this.parentTask.removeTag(this);
}
};
My listener is in the display method and it uses Tag.name property and some of the variables created in the method body. I can't see how to write this function somewhere else and Google didn't help me.
I hope my problem is clear, English is not my native language.
Some advices?
You can extract your anonymouse function to be another class method. It is an event handler so in order to correctly access the defined object you'll have to bind it correctly.
Here is an example of the modified script:
module.Tag = class Tag {
constructor(name = 'untitled', parent = null) {
this.name = name;
this.parentTask = parent;
}
//Method which displays the tag name
display_name() {
return $('<li>').addClass('tag').text(this.name);
}
//Method which displays the tag
display() {
let tag_item = this.display_name();
let field = $('<input>').prop('type', 'text').prop('value', this.name);
let button = $('<button>').addClass('validationButton').prop('type', 'submit').text('✓');
let removeButton = $('<button>').addClass('removeButton').text('X');
let form = $('<form>').append(field).append(button).append(removeButton);
let in_edit = false;
tag_item.click(this.handleClick.bind(this));
// this is where you invoke the function and
//bind it to the context of the class
return tag_item;
}
//Method which removes the tag from the parent task
removeTagFromParent() {
this.parentTask.removeTag(this);
}
// extracted method defined here:
handleClick(event) {
let tag_item = this.display_name();
let field = $('').prop('type', 'text').prop('value', this.name);
event.stopPropagation();
event.preventDefault();
let target = $(event.target);
if (target.is('li') && !in_edit) {
tag_item.empty();
tag_item.append(form);
in_edit = true;
}
if (target.is('button') && target.prop('type') === 'submit') {
if(field.val() !== '') {
this.name = field.val();
module.StorageManager.storeTasks();
}
tag_item.empty();
tag_item.text(this.name);
field.val(this.name);
in_edit = false;
}
if (target.is('button') && target.hasClass('removeButton')) {
if(confirm('Voulez-vous vraiment supprimer ce tag ?')) {
tag_item.remove();
this.removeTagFromParent();
module.StorageManager.storeTasks();
}
}
}
};

jstree Enable a node and its children

I am using:
jstree("disable_node", "#" + NodeID);
to disable a node in jstree. and using:
jstree("enable_node", "#" + NodeID);
to enable a node.
Is there a simple way to disable/enable a node and it's children?
thank you
You can do it with the code as below. Check demo - Fiddle.
Write a recursive function to iterate multi-level structures
function changeStatus(node_id, changeTo) {
var node = $("#tree").jstree().get_node(node_id);
if (changeTo === 'enable') {
$("#tree").jstree().enable_node(node);
node.children.forEach(function(child_id) {
changeStatus(child_id, changeTo);
})
} else {
$("#tree").jstree().disable_node(node);
node.children.forEach(function(child_id) {
changeStatus(child_id, changeTo);
})
}
}
Call function depending on what you need
changeStatus(NodeID, 'enable');
or
changeStatus(NodeID, 'disable');
I write a simple JS function based on jstree documentation about get_node function and jstree JSON data format that enable/disable the input node and all it's child in any level:
NodeToggleEnable = function (node_id, enable) {
var tree = $("#jstree-locations");
var sub_tree = [node_id.toString()];
var index = 0;
while (index < children.length) {
var child = tree.jstree("get_node", "#" + children[index]).children;
sub_tree = sub_tree.concat(child);
if (enable == false)
tree.jstree("disable_node", "#" + sub_tree[index]);
else
tree.jstree("enable_node", "#" + sub_tree[index]);
index++;
}
}
this function uses children(array of strings or objects) property of node that selected by get_node function.

Javascript loading plugin not constructing and adding

I am currently working on a javascript plugin that will be able to take over a html document loading screen. The plugin is very basic at the moment, but more features are to be added soon.
There's a problem though, I cannot contsruct the element and place it in the body of the html document.
I've run out of options, and really need my plugin to construct and place itself in the body, before I can go on styling the plugin.
Here is my javascript code:
(function() {
// Define the loading constructor
this.loader = function() {
// Create global element references
this.loader = null;
this.overlaycolor = null;
// Define option defaults
var defaults = {
loaderclassName: 'loader',
textclassName: 'loadertext',
Content: "WE'RE LOADING SOME STUFF",
overlayColor: '#ffffff',
Width: 100,
Height: 100,
Text: True
};
// Create options by extending defaults with the passed in arugments
if (arguments[0] && typeof arguments[0] === "object") {
this.options = extendDefaults(defaults, arguments[0]);
}
var myLoader = new loader();
myLoader.prototype.open();
};
// Public Methods
loader.prototype.open = function() {
// Build out our loader
buildOut.call(this);
};
// Private Methods
function buildOut() {
var content, contentHolder, docFrag;
/*
* If content is an HTML string, append the HTML string.
* If content is a domNode, append its content.
*/
if (typeof this.options.content === "string") {
content = this.options.content;
} else {
content = this.options.content.innerHTML;
}
// Create a DocumentFragment to build with
docFrag = document.createDocumentFragment();
// Create loading element
this.loader = document.createElement("div");
this.loader.className = "exentory-loader" + this.options.loaderclassName;
this.loader.style.Width = this.options.Width + "%";
this.loader.style.Height = this.options.Height + "%";
this.loader.css("background-color", this.options.overlayColor);
// If closeButton option is true, add a close button
/*if (this.options.closeButton === true) {
this.closeButton = document.createElement("button");
this.closeButton.className = "scotch-close close-button";
this.closeButton.innerHTML = "×";
this.loader.appendChild(this.closeButton);
}*/
// If text is true, add textbox with content
if (this.options.Text === true) {
this.textbox = document.createElement("p");
this.textbox.className = "exentory-text " + this.options.textclassName;
this.textbox.append(this.options.Content);
docFrag.appendChild(this.loader);
}
// Append loader to DocumentFragment
docFrag.appendChild(this.loader);
// Append DocumentFragment to body
document.body.appendChild(docFrag);
}
// Utility method to extend defaults with user options
function extendDefaults(source, properties) {
var property;
for (property in properties) {
if (properties.hasOwnProperty(property)) {
source[property] = properties[property];
}
}
return source;
}
}());
I have no clue as to what is going wrong here. I've tried placing the myLoader.prototype.open() code averywhere in the file, but it does'nt construct.
I hope someone is able to help me.
Thanks in advance.
Try this:
this.loader = function() {
}
loader.prototype.open = function() {
alert("open")
}
And when you construct it:
var myLoader = new loader();
myLoader.open();
Working demo

Match url folders with a tag href to make an active state

I made an active state for my menu on a certain urls. I have urls like this:
/products/other-clothing/sporting/adults-anzac-australia-polo
/products/other-clothing/sporting/adults-nz-tee
/products/bags/backpacks
My code gets the folder from after the / so other-clothing, sporting, etc.
It is working fine, I just assume there is a more efficient way to write the code.
Here is my code:
jQuery(".product-nav li a").each(function() {
// URL url
var cat = location.pathname.split("/")[2];
var subcat = location.pathname.split("/")[3];
var c = "/products/" + cat + "/" + subcat;
// A tag url
var acat = this.href.split("/")[4];
var asubcat = this.href.split("/")[5];
var e = "/products/" + acat + "/" + asubcat;
if(e == c) {
jQuery(this).parent().addClass("active");
jQuery(this).parent().parent().parent().addClass("active");
}
});
If anyone can provide a cleaner way of writing the code that'd be great. I probably dont need "/products/" +.
Notice the output of the following expressions:
$('')[0].href;
/*
* http://stackoverflow.com/questions/7564539/match-url-folders-with-a-tag-href-to-make-a-active-state
*/
$('').eq(0).attr('href');
/*
* /questions/7564539/match-url-folders-with-a-tag-href-to-make-a-active-state
*/
So, if your <a> tags contain URLs that start with / then you can compare the .attr('href') with location.pathname. For testing, try running this in console from this page:
$('a').each(function () {
if ($(this).attr('href') == location.pathname) {
$(this).css({
'font-size': '40px',
'background-color': 'lime'
});
}
});
Here's a brief whack at it:
jQuery(".product-nav li a").each(function() {
// URL url
var c = location.pathname.split('/').slice(2, 4)
// A tag url
, e = this.href.split('/').slice(4, 6)
;
if(e[0] == c[0] && e[1] == c[1]) {
jQuery(this).parentsUntil(
'div:not(.subnav)', // go up the tree until the 1st div that isn't .subnav
'.product-nav li, .subnav' // and only match these parents
).addClass('active');
}
});
.parent().parent().parent()... has a pretty bad code smell to it but can't be improved without a look at your markup. You should probably be using .closest() instead.
Interesting question. Here is my attempt to clean it up:
jQuery(function ($) {
function Category(outer, inner) {
this.outer = outer
this.inner = inner
}
Category.fromURL = function (url) {
var parts = url.replace(/^(https?:\/\/.*?)?\//, "").split("/")
return new Category(parts[1], parts[2])
}
Category.prototype.equals = function (other) {
return this.outer === other.outer
&& this.inner === other.inner
}
var category = Subcategory.fromURL(location.href)
$(".product-nav a").each(function () {
if (Category.fromURL(this.href).equals(category)) {
$(this).closest("li.inner").addClass("active")
$(this).closest("li.outer").addClass("active")
}
})
})

Categories

Resources