I have a custom element in Aurelia that uses a jQuery plugin under the hood (KendoUI). This custom element is being used within a view that uses the if.bind attribute.
The custom element uses an inline template, like so:
#inlineView('<template><input type="text" ref="proxy" class.bind="class"></template>')
Due to the nature of the jQuery plugin, I have to pass an element to initialize. I use the ref that I've defined in my template, like so:
$(this.proxy).doJQueryStuff(...)
After the element is detached, and then reattached, the ref element (this.proxy in my viewmodel) is null.
I initially thought that the problem was that since jQuery mutates the DOM, it was also mutating the view template. I thought that after the element is detached (and destroyed with a jQuery call to remove all KendoUI metadata), the input ref was no longer available resulting in an error upon reattachment because Aurelia was caching the view. However, this is not the case. It's been confirmed that Aurelia doesn't cache views unless you explicitly tell it to do so, and in this simplified plunk, the behavior is as expected.
Why would the reference to my ref element, in my viewmodel, be null after attaching and detaching the element?
Of note, the custom element is part of a page view, inside this construct:
<div id="application" class="au-animate" if.bind="isLoggedIn">
<nav-bar router.bind="router"></nav-bar>
<div class="page-host">
<router-view></router-view>
</div>
</div>
I don't know if anyone has run into any problems with a router-view inside of an if binding.
This issue was fixed this evening by this pull request. Run jspm update and confirm you have github:aurelia/templating-resources#0.17.3 installed.
The issue was the if template controller was not rebinding the view when re-attaching it to the DOM after it had been hidden.
Related
I've found this answer here:
https://stackoverflow.com/a/50431015/11735826
and i wonder why .$el was used here, and also why does it not work without the el element?
when you use ref attribute on the html tag, the DOM-element is returned by this.$refs.modal.
when you use ref attribute on the template tag, the component instance is returned, so this.$refs.modal.$el returns directly the DOM element. See https://v2.vuejs.org/v2/api/#vm-el
$el returns the HTML element to which a given Vue instance (be it a main instance or a component) is bound. By using this.$refs.modal.$el the answer gets the underlying HTML element for the this.$refs.modal, and then encapsulates it in a jQuery object to call the modal method.
We are developing our own library of AngularJs (1.6.x) components/directives and we would like to have all HTML attributes that are specified on our components automatically and dynamically passed on to the underlying templates, without having to specify every single attribute as a binding.
For example, if I have a component like <my-component> with a template like <div class="my-component"></div> and I write in my HTML:
<my-component ng-required="ctrl.isRequired" ng-change="ctrl.onChange()" ng-attr-disabled="ctrl.isReadOnly"></my-component>
I want my template to render:
<div class="my-component" ng-required="ctrl.isRequired" ng-change="ctrl.onChange()" ng-attr-disabled="ctrl.isReadOnly"></div>
As I said above, we cannot know in advance all attributes, so they can't all be defined in the component bindings. The problem gets more complicated in Angular 1.5+, because replace has been deprecated, so the above template would render something like this:
<my-component ng-required="ctrl.isRequired" ng-change="ctrl.onChange()" ng-attr-disabled="ctrl.isReadOnly">
<div class="my-component" ng-required="ctrl.isRequired" ng-change="ctrl.onChange()" ng-attr-disabled="ctrl.isReadOnly"></div>
</my-component>
And also, when we have nested, deep hierarchies of components, we want the values to be passed down from the parent controllers to the (grand)children, i.e. the ctrl.onChange() method will be defined in the scope of the page controller where <my-component> is defined, but as each component in the hierarchy gets its own scope, as you go deeper, we want the method to still be callable.
I am relatively new to Angular, I hope the above make sense.
In my project, I have an <div> where I specifically apply my Knockout.js bindings. I have to instantiate different viewmodels in that area depending on what the user clicks.
To prevent getting a cannot call bindings twice on the same element error, I first have to "Clean" the bindings to make the area available again. I call the initial applyBindings() function:
ko.applyBindings(new ViewModel("Planet", "Earth"), document.getElementById("bindings-area"));
Eventually, I will clean the <div> and call the new bindings:
var element = $("#bindings-area")[0];
ko.cleanNode(element);
ko.applyBindings(new ViewModel("NEW", "Bindings"), document.getElementById("bindings-area"));
Problem: When I include an HTML button in the #bindings-area div, it will no longer work after I clean the bindings and instantiate the new model. I'm sure it has to do with the ko.cleanNode() function somehow removing the button bindings as well. How can I re-initiate them or prevent cleanNode() from operating on the button in the first place?
Fiddle: http://jsfiddle.net/jL6L01xs/2/
This issue is nicely described in Knockout documentation. This quote describes what the issue is and what needs to be done:
When removing an element, Knockout runs logic to clean up any data
associated with the element. As part of this logic, Knockout calls
jQuery’s cleanData method if jQuery is loaded in your page. In
advanced scenarios, you may want to prevent or customize how this data
is removed in your application. Knockout exposes a function,
ko.utils.domNodeDisposal.cleanExternalData(node), that can be
overridden to support custom logic. For example, to prevent cleanData
from being called, an empty function could be used to replace the
standard cleanExternalData implementation:
ko.utils.domNodeDisposal.cleanExternalData = function () {
// Do nothing. Now any jQuery data associated with elements will
// not be cleaned up when the elements are removed from the DOM.
};
Here is the updated jsFiddle.
I already use custom binding with knockout for displaying jqueryui dialog, but I would like to use knockout component feature.
So, I would like to write something like:
<window params="isVisible: isVisible">
//there will be some html
</window>
And later somewhere in code:
self.isVisible(true); //That would open window
//or
self.isVisible(false); //That would close window
Problem is that I don't know how to apply $(element).dialog. When I register knockout component, I can only get container element of this component, but not injected element.
ko.components.register('window', {
viewModel: {
createViewModel: function(params, componentInfo) {
// - 'params' is an object whose key/value pairs are the parameters
// passed from the component binding or custom element
// - 'componentInfo.element' is the element the component is being
// injected into. When createViewModel is called, the template has
// already been injected into this element, but isn't yet bound.
// - 'componentInfo.templateNodes' is an array containing any DOM
// nodes that have been supplied to the component. See below.
// Return the desired view model instance, e.g.:
return new MyViewModel(params);
}
},
template: ...
});
So, componentInfo.element is parent node, and if I apply dialog with $(componentInfo.element) I will set as dialog parent node, then my window tag would be:
<div><!-- Dialog will be applyed there-->
<window params="isVisible: isVisible">
//there will be some html
</window>
</div>
I think it will work, but that extra div look unneeded here... Or this is the only way to do job done?
What is knockout.components way to do this? Thanks.
I had a similar requirement when I wanted to build a Knockout component for hosting a dialog window using Bootstrap v2 'modal' functionality. I used an observable boolean value in my page's viewModel, set to false initially. There isn't a simple way to communicate with components after they have been initialized, except by passing in observables via params.
This observable was passed as a param to a <dialog-window> component in the params, e.g.
<dialog-window params="visible: showDialog">[content]</dialog-window>
This then used a special binding handler for the 'modal' dialog, but in your case you could bind it direcly using a data-bind='visible: YourParam' attribute.
The main page could then simply call showDialog(true) to make the dialog visible.
Hope this helps.
Using jQuery and Meteor I am trying to bind the TokenInput plugin to the DOM like so:
$(function(){
console.log("binding tokeninput");
$(".nameInput").tokenInput(friendsList.data)
});
The issue is that the particular DOM element is being redrawn (removed from the DOM and then re-added in a quick flash) occasionally. I need to ensure that the plugin is ALWAYS in effect on that input.
A few things come to mind:
Could I use a callback from Meteor to re-apply it whenever it updates? I haven't found a callback from Meteor for when a template object is refreshed.
Can I use some sort of reactive bind (like .on, though .on is only for events)?
Am I doing this completely wrong?
If you're DOM element being removed by something reactive. If mytiem changes its going to fire the 'rendered' template callback
e.g
<template name="MeteorIsAwesome">
{{#each myitem}}
<div class="dom element meteor">
</div>
{{/each}}
{{!comment - you can put it here or above}}
<input class="nameInput" type="text">
</template>
Js (kind of the callback you might be looking for)
Template.MeteorIsAwesome.rendered = function () {
$(".nameInput").tokenInput(friendsList.data)
}
One thing only worries me is if it ignores the state of the tokenbox when redrawing so it might become double tokenized