Creating dialog with knockoutjs components - javascript

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.

Related

How to detect when DOM is ready after *ngIf condition is set to true

I have an Angular 2 (Ionic 2) application where I hide some elements using an *ngIf if the network is offline, and show another button, "retry".
When I click the retry button, if the network is back I then want to reshow the elements hidden by the *ngIf. Also I then need to reference some of them via #ViewChild (e.g. an Ionic 2 slider to get it's current index). My problem is that these elements are not all defined when I try to get a reference to them in the same function call that sets my property bound to the *ngIf back to true.
I have tried calling from within a setTimeout but this does not seem to be very safe - some DOM elements are back but not all.
Is there a way to know then the DOM is ready with all the elements that were hidden by the *ngIf, or do I need to find some other way of doing this (not using the *ngIf, so elements are not actually removed).
Thanks in advance for any suggestions!
You should be able to do what you want by looking at the onStable event emitted via the NgZone:
In the component template:
<button (click)="showFoo = !showFoo">toggle</button>
<div *ngIf="showFoo" #foo >foo</div>
In your component class
#ViewChild('foo') foo: ElementRef;
showFoo = false;
constructor(private zone: NgZone) {
zone.onStable.subscribe(() => {
console.log(this.foo);
// this will log either undefined or an ElementRef as you toggle
});
}
Here's a plunk showing it working: https://plnkr.co/edit/jKSaEI0XUcJQehZnJTxB
I think you are looking to understand this,
I think the ngAfterViewInit() would help you with this,
ngAfterViewInit() Respond after Angular initializes the component's
views and child views.
Called once after the first ngAfterContentChecked().
A component-only hook.

Custom Element issue: ref element is null in viewmodel

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.

jQuery events not firing after "cleaning" Knockout bindings

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.

EmberJS - Handling events for multiple elements inside a view

I have a Handlebars template called 'projects' and a view called 'ProjectsView' which references the template with the templateName property.
This projects template generates a list of divs from a model and each div needs to be expanded on a click.
{{#each project in model}}
<div class="project">
<img src="{{project.logo}}" /> <!-- Execute a different logic for the click event -->
<h4>{{project.title}}</h4> <!-- Execute a different logic for this click
{{/each}}
I initially used the action helper and added a relevant method inside the actions hash of the ProjectsController. However, the action is being executed on all the divs in the list instead of just the clicked one.
Previous versions of Ember used to provide a context argument for the action but it is no longer the case. It turns out the recommended way to handle DOM events is within in the view like below.
App.ProjectsView = Ember.View.extend({
templateName: 'projects',
click: function(event) {
// Do something with the event
}
})
However, it doesn't feel right to write a bunch of if's inside the click handler to find if the click event was triggered on an image or a h4 and act accordingly. Is this is the right way of handling events or am I doing it wrong?
You can instead use App.ProjectsView as a CollectionView with each project as it's child view.
App.ProjectsView = Ember.View.extend({
templateName: 'projects',
itemViewClass: 'App.ProjectView'
})
You can handle actions in your child views directly using the target=view option in action helper.
Since you want to do DOM related tasks (expand/collapse), handling the actions in the views will help as you can get a jQuery object of the view using this.$().

backbone remove view deletes the el

I am creating a single page application, and I am quite new to backbone. I have a problem with creating multiple views which uses the same wrapper-div.
My setup:
I have added a close function to all views:
Backbone.View.prototype.close = function(){
this.remove();
this.off();
if (this.onClose){
this.onClose();
}
}
I have a wrapper-div where I want to render views, remove them and render new ones. So my SetupView looks like this:
app.SetupView = Backbone.View.extend({
el: '#my_view_wrapper',
...
});
From the function where I swap views I close the current (open) view like this:
var v = this.model.get('view');
v.close();
Question
My problem is that I have multiple view's using the same wrapper-div. But when I close a view, this wrapper-div seems to be removed, and the next view I try to create can't find this div.
I guess there is an easy solution? I want to reuse the same wrapper, and only remove the view inside it, not the wrapper itself.
Just as an addition (for later reference) : another option is to overwrite the subviews remove so that it just empties $el instead of removing it. Eg.
remove: function() {
this.$el.empty().off(); /* off to unbind the events */
this.stopListening();
return this;
}
Personally I prefer this, as it removes the need to insert wrapper elements that have no real use.
In your scenario don't use an existing DOM element as your "el" value. Backbone will create the element for you. When you instantiate your view you can do the following to attach it to your existing wrapping element.
$(viewName.render().el).appendTo('#my_view_wrapper');

Categories