KnockOutJS: Very odd behaviour with descendantsComplete and components(custom elements) - javascript

Knockout version: 3.5.1
I have a custom element on my main .html file like so:
<my-component></my-component>
Inside I have a form to which I am attaching jQuery validation which is not working because when the line $("#myForm").validate is called, the form is not yet in the DOM.
I created a very basic jsFiddle to illustrate the issue: https://jsfiddle.net/sb1of04g/
Notice how if you remove the timeout, the result is 0. I get the same result if I move the alert below ko.applyBindings(new Test());
I want my jQuery validation code to run after the component is 100% loaded.
In my project I'm using custom elements and requirejs/text so to fix the issue I tried the below approach:
<my-component data-bind="event: { descendantsComplete: $root.OnComponentReady() }"></my-component>
self.OnComponentReady = function()
{
$("#myForm").validate ...
}
But no luck, OnComponentReady is executed correctly but the form is still not in the DOM.
This is when things got funny. Inside my-component I have another component, footer-component which is at the very bottom of the parent component and if I change my code to the below, I get the correct behaviour.
<my-component></my-component>
<footer-component data-bind="event: { descendantsComplete: $root.OnComponentReady() }"></footer-component>
Like this, when OnComponentReady is executed the form is available and I can attach jQuery validation to it. It also works if I move footer-component at the top of my-component.
I was under the impression that descendantsComplete fires after the component finished loading, similarly to jQuery's $(document).ready. Is this not the case? I'm not entirely sure I will have a child component in the future so ideally descendantsComplete is attached to the parent component.
Edit: I just realised that if I move the .validate code from my main ViewModel($root) to the Component ViewModel, I get the correct behaviour without the descendantsComplete event.

Related

Angular DOM elements are not showing

Hi I am using an UI Library (forced to, company issue..) which provides an Angular component, which renders a form.
Now I want to disable all of the input fields an buttons inside this form. But the component of the library doesn't provide me the possibility to pass a parameter to change the status to read only.
Now I have no other option to do dirty DOM hacking. However it doesn't seem to work.
Here is my HTML of my own component, where I render the Library Component:
<component-of-the-library #formComponent></component-of-the-library>
Now inside my own components class I reference it:
#ViewChild('formComponent', {read: ElementRef}) formComponent: ElementRef;
However when I use the nativeElement feature and the querySelectorAll() function I don't see the button elements:
ngAfterViewInit() {
console.log(this.formComponent.nativeElement);
console.log(this.formComponent.nativeElement.querySelectorAll('button'))
}
The first line outputs the DOM of the library component. There I also see the buttons.
However the second line just returns an empty NodeList.
Am I missing something?
Instead of doing all these, come up with a div overlay and with the size of your form and make show or hide it based on your needs.
It will be easier than disabling each form inputs and buttons. Also the overlay is not the component but your div.
I able to read the DOM Nodes present in the child component from the parent Component using ViewChild() https://stackblitz.com/edit/angular-edmyur?file=src%2Fapp%2Fapp.component.ts
EDIT: I see another problem. AfterViewChecked gets called multiple times...
I found the answer myself. The problem is the LifeCycleHook. AfterViewInit works for your own component, but it doesn't wait for the child components to finish rendering.
When I use AfterViewChecked it works!
However I am still puzzled, that logging the nativeElement has always given me the correct DOM, even though it's still not rendered.

jQuery not detecting dynamic html

I have a series of folders that are represented as links using a VueJS for-loop, wit data pulled from API. For each of those links, I am using a library called x-editable to allow for the editing of the names. Essentially, when you click on the link with defined attributes (assuming you instantiate via editable(), it will bring up a pop-over).
I find that if I create a dummy link (outside of the for loop) and include the instantiation in a document.ready block, it works just fine:
EDIT: Note that the ready: function() is a custom document.ready function, defined in a Vuejs router.
ready: function() {
jQuery.fn.editable.defaults.mode = 'popup';
jQuery('.editable').editable();
},
However, none of the dynamically generated links work when they are clicked. My guess is that jQuery is simply not detecting them, as they were not made when the document was loaded.
I tried to call editable after the data was received and assigned to Vue:
//Folders
this.$http.get('/api/folders').then(function(response) {
this.state.folders = response.data;
jQuery('.editable').editable();
});
However, it seems that this doesn't work either. I read that you can bind events using .on() which will work with generated elements. However, .editable() isn't really an event (I think). Is there a way to get around this?

Directive execution order in angular 2

If I have a simple button with a click handler and a custom attribute directive like so:
<button (click)="save()" attributedirective="project saved">Save</button>
And in my attribute directive I'm using the hostlistener decorator to listen to the click event:
#Directive({
selector: `[attributedirective]`
})
export class AuditPusher {
#Input('attributedirective') attributedirective: string = 'Missing message!';
#HostListener('click', ['$event'])
pushAudit() {
console.log('text:'+this.attributedirective.toString());
}
}
Which of my code will fire first? The save() on the click event or the code in my attribute directive? - And: Imagine having two attribute directives. Which of those will fire first? In Angular 1 there was something like directive priorities, how is this done in Angular 2? I find it difficult to find documentation on this.
As far as I know the order of execution is undefined. You shouldn't depend on a specific order.
I think priority concept is yet not there in Angular2. (If it is already i'm not aware of it yet) but One should not depend on a specific order as already said.
But as you have asked specifically. Order would be,
1)when page or a component is loaded, <button (click)="save()" attributedirective="project saved">Save</button> is loaded too and because of directive**(attributedirective) is attached to button, Angular2 initializes directive(attributedirective) and binds it to button.
2) As Save() is a function attached to native click (Angular2's way) event of button if you click it, it will call save() first and then it will look for other binding's events(if any) attached to it (eg.pushAudit)
A quick and dirty way around this when I had two attribute directives and Angular was executing DirectiveB before DirectiveA but I needed to have it the other way around was to delay the stuff I needed to execute last:
export class DirectiveA {
ngOnInit() {
doStuff();
}
}
export class DirectiveB {
ngOnInit() {
setTimeout(() => {
doMoreStuff();
}, 0);
}
}
When you do setTimeout(0) it will defer execution to the next Angular processing cycle, which is just what I needed in my case for everything in DirectiveA to be set up in time in order to handle events coming from DirectiveB.
I might be wrong but for me it worked like this:
I have an svg:g element and added 2 directives like this:
In order to execute directiveB first in the module at the exports section I added directiveB first and after directiveA.
Note that for svg the z-index is defined by the order the element appears in the document. How to use z-index in svg elements?.
I know you have a click there and a custom attribute but if you have 2 custom attributes this can work!

Jquery Toggle on click of image

I am trying to understand how the jquery toggle works. I want to toggle to next anchor with class plr-anchor on click of image with class go_down. The data is populated using maps.
Jquery code:
$('.go_down').on('click',function(e){
#('.plr-anchor').next('.plr-anchor').toggle()});
}
Code snippet:
{data.map(function(categ) {
return <div>
<a className="plr-anchor" id={categ.short_name}></a>
<img src="/static/img/icon_up.png" className="go_down"/>
</div>}.bind(this)}
There seems to be a problem with the syntax on the Jquery call function. I am newbie with Jquery, any help will be great. Thanks in advance.
You have used # instead of $ inside click handler. Also you need to find exact plr-anchor which is before the clicked image. Right now you are toggling all plr-anchor.
For dynamic elements, use $(document).on(event, selector, function(){}); for binding click handlers. See below code
$(function(){
$(document).on('click','.go_down',function(e){
$(this).prev('.plr-anchor').toggle();
});
});
Your jQuery code has a "#" instead of a "$". By the way, React and jQuery don't always go together. The whole purpose of React is to use virtual dom and let React take care of dom updates. While jQuery is mostly about direct dom manipulation. What you are trying here will interfere with React because it won't expect the dom to change without the v-dom changing.
Ideally, you should be using event handlers and state in your component to toggle visibility.

Ember.js: How do I hook Ember.CollectionView after every child view is rendered?

This question demonstrates that overriding an Ember.View instance's didInsertElement allows you to execute some code after the view's element is in the DOM.
http://jsfiddle.net/gvUux/2/
Naturally, overriding didInsertElement on the child view class you add to an Ember.CollectionView will run the hook after each child view is rendered and inserted.
http://jsfiddle.net/BFUvK/1/
Two collection-oriented hooks on Ember.CollectionView, arrayDidChange and contentDidChange, execute after the underlying content has changed, but they execute before any rendering takes place. arrayDidChange is executed for every element added to the array, and contentDidChange wraps the content binding.
I would like to be able to hook around the rendering pipeline, something like willInsertCollection and didInsertCollection, to manipulate the DOM before and after all child elements are rendered - essentially, before and after filters around contentBinding.
Any ideas? I'm stumped.
If you want to want to do something before and/or after a view has been rendered you should use willInsertElement and/or didInsertElement respectively. In this case, since you want "to manipulate the DOM before and after all child elements are rendered" you should call those on your CollectionView.
I'm not quite sure what you mean by "before and after filters around contentBinding", so if this doesn't answer your question if you could clarify I'd be happy to help.
jsFiddle if needed
I wanted to apply a scroll animation to slide a list up after pushing new objects. The list was rendered using an ArrayController and the #each helper. Simply triggering an event on the controller which the view subscribed to after pushing objects was causing the animation to execute before the changes to the content were actually rendered. The following technique worked perfectly for me.
//excerpt from my loadMore method on the ArrayController
var self = this;
self.content.pushObjects(moreItems);
Ember.run.scheduleOnce('afterRender', this, function()
{
self.trigger('loadMoreComplete');
});

Categories

Resources