Display a widget using Durandal modal dialog - single-page-application

I recently started using the Durandal library for a SPA I am developing... kudos to the author, it is an excellent library.
I like the concept of widgets, vs using Views for stateless screens, but I was not able to display a widget in a modal dialog, without attaching it to a view. Does anyone know how to do this?
To elaborate, there is a widget.create function that allows for the creation of a widget in the JS but requires a DOM element to attach to. What I would prefer to do is create a widget, without attaching it to the DOM, then call something like:
app.showModal(theWidget);
As an alternative, I know I can create a "dialog" view that maps to swappable widgets, then use that view for dialogs, e.g. the view would have:
<div data-bind="widget: {kind:widgetId}">/div>
... and then:
app.showModal('viewmodels/dialog');
where 'viewmodels/dialog.js' is the view-model for the "Dialog" view.
References:
Modals: http://durandaljs.com/documentation/Showing-Message-Boxes-And-Modals/
Widgets: http://durandaljs.com/documentation/Creating-A-Widget/

Widgets are meant to be reusable controls on a web page, so that's why they require a DOM element. I'm not sure if I completely understand what you're trying to do, but you can define a view that returns its constructor function rather than a singleton object.
Here's a view that returns a singleton:
define([], function() {
var singleton = {
title: "I'm Mr. Singleton"
};
return singleton;
});
Here's the same view, but returns its constructor function:
define([], function() {
var notSingleton = function () {
this.title = "I'm NOT Mr. Singleton"
};
return notSingleton;
});
You can then use either of these within another viewmodel or module, as such:
define(['viewmodels/singleton', 'viewmodels/notSingleton'],
function(singleton, NotSingleton) {
...
app.showModal(singelton);
app.showModal(new NotSingleton());
...
});
In the latter case, you could create multiple dialogs of the same viewmodel across multiple other views, but each would be its own instance with its own properties. If you wanted to share data and/or behaviors across all instances of that viewmodel type, you could add them to the viewmodel's prototype.
Hope this helps.

Related

What is the 'angular' way of displaying a tooltip / lightbox?

I've been looking around and have not been quite able to get a clear path to the 'angular' way of accomplishing the following. What I'm trying to achieve is displaying a tooltip with information when hovering over a link within an ng-repeat loop. Based on my research, I understood that this is part of the view, and so I should probably handle this in a directive. So, I created an attribute directive called providertooltip. The html declaration is below:
<table>
<tr id="r1" ng-repeat="doc in providers">
<td>
<a providertooltip href="#{{doc.Id}}" ng-mouseover="mouseOverDoc(doc)" ng-mouseleave="mouseLeave()">{{doc.FirstName}} {{doc.LastName}}</a>
</td>
</tr>
</table
<div id="docViewer" style="display:hidden">
<span>{{currentDoc.FirstName}} {{currentDoc.LastName}}</span>
</div>
In the module, I declare my directive, and declare my mouseOver and mouseLeave functions in the directive scope. I also 'emit' an event since this anchor is a child scope of the controller scope for the page. On the controller function (docTable ) which is passed as a controller to a router, I listen for the event. Partial implementation is seen below:
app.directive("providertooltip", function() {
return {
restrict : 'A',
link: function link(scope, element, attrs) {
//hover handler
scope.mouseOverDoc = function(doc){
scope.currentDoc = doc;
scope.$emit('onCurrentDocChange');
element.attr('title',angular.element('#docViewer').html());
element.tooltipster('show');
//docViewer
};
scope.mouseLeave = function() {
element.tooltipster('hide');
}
}
}});
function docTable(docFactory, $scope, $filter, $routeParams) {
$scope.$on('onCurrentDocChange',function(event){
$scope.currentDoc = event.targetScope.currentDoc;
event.stopPropagation();
});
}
Ok, so here is my question. All of the works as expected; Actually, the tooltip doesn't really work so if someone knows a good tooltip library that easily displays div data, please let me know. But, what I'm really confused about is the binding. I have been able to get the tooltip above to work by setting the title ( default tooltip behavior ), but I can see that the binding has not yet occured the first time I hover of a link. I assume that the onCurrentDocChange is not synchronous, so the binding occurs after the tooltip is displayed. If I hover over another link, I see the previous info because as I mentioned the binding occurs in an asynchronous fashion, i.e., calling scope.$emit('onCurrentDocChange') doesn't mean the the parent scope binds by the time the next line is called which shows the tooltip. I have to imagine that this pattern has to occur often out there. One scope does something which should trigger binding on some other part of the page, not necessarily in the same scope. Can someone validate first that the way I'm sending the data from one scope to the other is a valid? Moreover, how do we wait until something is 'bound' before affecting the view. This would be easier if I let the controller mingle with the view, but that is not correct. So, I need the controller to bind data to the scope, then I need the view to 'display a tooltip' for an element with the data. Comments?
To go the angular way correctly start your directive like:
...
directive('showonhover',function() {
return {
link : function(scope, element, attrs) {
element.parent().bind('mouseenter', function() {
element.show();
});
element.parent().bind('mouseleave', function() {
element.hide();
});
}
...
Or start with http://angular-ui.github.io/ link to go the angular-way UI. Look into the bootstrap-ui module - pure angular bootstrap widgets implemented as directives. You can get a clue how the tooltip binding implemented directly from the source of the module - https://github.com/angular-ui/bootstrap/blob/master/src/tooltip/tooltip.js
Also here is another example - (having jQuery and bootstrap scripts included) - use the ui-utils module Jquery passthrough directive ui-jq'. It allows to bind Jquery plugins ( style of $.fn ) directly as angular directive.
Here is their example for binding twitter bootstrap tooltip.
<a title="Easiest. Binding. Ever!" ui-jq="tooltip">
Hover over me for static Tooltip</a>
<a data-original-title="{{tooltip}}" ui-jq="tooltip">Fill the input for a dynamic Tooltip:</a>
<input type="text" ng-model="tooltip" placeholder="Tooltip Content">
<script>
myModule.value('uiJqConfig', {
// The Tooltip namespace
tooltip: {
// Tooltip options. This object will be used as the defaults
placement: 'right'
}
});
</script>
Also look into the official angular documentation for writing directives examples,
and have a happy coding time with Angular!

knockout.js data-bind 'with' conflicts with jQuery change event

For some reason, when I use the data-bind="with: detailedStudent" the jQuery change() binding does not get called. I'm dynamically populating the select options but I'm not sure that should matter. This is some of the code I'm using just to try to give a decent picture of what's going on:
var viewModel;
$(document).ready(function() {
viewModel = new StudentViewModel();
ko.applyBindings(viewModel);
// this change event is not getting called, but if i put the onchange directly into the html as an attribute, it works fine.
$("#accountDialog").find("#mySelect").change(function() {
alert('hi');
}
}
function Student(data) {
var self = this;
ko.mapping.fromJS(data, {}, this);
}
function StudentViewModel() {
var self = this;
this.students = ko.observableArray([]);
this.detailedStudent = ko.observable();
}
<div id="accountDialog" class="modal hide fade" data-bind="with: detailedStudent">
<select id="mySelect" name="mySelect" data-bind="value: GraduationClassId"></select>
</div>
The with binding is a wrapper to the template binding. It copies off the child elements and uses them as the template. So, if your detailedStudent is changing, then KO will be rendering new elements each time that did not have the event handler attached to it.
Some alternatives:
use a binding to attach the event handler (can use event binding)
create a manual subscription against your detailedStudent observable and perform your action in the view model (best option, if your action does not involve DOM manipulation)
try to use a delegated event handler like jQuerys $.on() http://api.jquery.com/on/.
If the action does not involve DOM manipulation, then I agree with RP Niemeyer, the manual subscription is the best option.
However, usually we will have some event with DOM manipulation, for example, to setup the jquery dialog / datepicker plugin to your property. I regard the custom binding would be the best option. The custom binding will work perfectly with the "with" binding clause to setup event handlers to arbitary javascript function.
You could read through this and it is not as hard as it seems to be.
http://knockoutjs.com/documentation/custom-bindings.html

How to access properties of a Tridion component like schema name based on which it is created in aspx page?

I am customizing the ribbon tool bar by adding a button to it in TRIDION 2011 SP1 version.
When I click on the button it will open an aspx page.Inside that aspx page I need to access the name of the schema used to create that component before creating the component itself(I mean to say while creating the component itself).
Please provide me a way to solve this issue. Thanks in advance.
You should pass it to your popup. The URI of the Schema is available on the Component model object within the CME - so your button command can access it and pass it to the popup (in the URL, for example).
var schemaId = $display.getView().getItem().getSchemaId();
If you have the component (as an object), you can get it's schema id as Peter indicated. If you only have the component id, you can load the component and through that get to the schema.
When you need to load any item, you have to be aware that it's not a synchronous call in the UI API, so you should use delegate methods for that. For example something like this:
Example.prototype._loadItemInformation = function Example$_loadItemInformation(itemId, reload) {
var item = $models.getItem(itemId);
if (item) {
var self = this;
function Example$_loadItemInformation$_onItemLoaded() {
$evt.removeEventHandler(item, "load", Example$_loadItemInformation$_onItemLoaded);
// proceed with the actions on the loaded item here
};
if (item.isLoaded(true) && !reload) {
Example$_loadItemInformation$_onItemLoaded();
}
else {
$evt.addEventHandler(item, "load", Example$_loadItemInformation$_onItemLoaded);
//$evt.addEventHandler(item, "loadfailed", Example$_loadItemInformation$_onItemLoadFailed);
item.load(reload, $const.OpenMode.VIEW);
}
}
};
Also be aware the item could fail loading, you should actually also register an event handler for loadfailed (as my example code neglects to do).

Dialogs (Real ones)

Having tried a number of different solutions I keep coming back to this. I need a Window.ShowDialog, using the ViewModelLocator class as a factory via a UnityContainer.
Basically I have a View(and ViewModel) which on a button press on the the view needs to create a dialog (taking a couple of parameters in its constructor) that will process some logic and eventally return a result to the caller (along with the results of all the logic it computed).
Maybe I'm wrong for stilll looking at this from a Windows Forms perspective, but I know exactly what I want to do and I want to ideally do it using WPF and MVVM. I'm trying to make this work for a project, and ultimately don't want to have to go back to vanilla WPF in order to make it work.
I break the rules to implement a dialogwindow but tried to reduce it to a minimum. I have a method OpenDialog in my BaseViewModel:
public void OpenDialog(DialogViewModel model)
{
this.MessengerInstance.Send<DialogViewModel, MainWindow>(model);
}
And in my MainWindow:
Messenger.Default.Register<DialogViewModel>(this, model =>
{
// Instantiate the dialog box
var dlg = new DialogWindow();
// Configure the dialog box
dlg.Owner = this;
dlg.Content = model;
// Open the dialog box modally
dlg.ShowDialog();
});
That way i only have a loose coupling between my viewmodel and my MainView.
You can do the same for closing, my BaseDialogViewModel has a method:
public void CloseDialog()
{
this.MessengerInstance.Send<PopUpAction, DialogWindow>(PopUpAction.Close);
}
(PopupAction is just an enum) and my DialogWindow registers for that:
Messenger.Default.Register<PopUpAction>(this, action =>
{
switch (action)
{
case PopUpAction.Close:
this.Close();
break;
}
});
You could also leave the receiver away when sending, to keep the view class out of the viewmodel but either way i think it's a acceptable solution :)
You can do that. Just create an instance of a page/usercontrol/window and call instance.ShowDialog().
Here's my T4 templates to generate a view/viewmodel with the messaging for closing a window and other tricks.

Adobe Flex3: Keyboard shortcuts when a view is visible?

I have a quite large Flex application with a large set of views and I ceratain views I'd like to add shortcuts.
And i'm looking for something like:
<mx:Vbox>
<foo:Shortcut keys="ctrl+s" action="{bar();}"/>
....
</mx:VBox>
Is there any framwork or component already done that does something like this? I guess it should be too difficult to build? When building this I only want the shortcuts to be active when the view is visible. How do I detect this? What base class is best to inherit from when working with non visual components?
I don't know of any framework component that does that already, but the examples above should get you started if you try to build your own.
There's no need to inherit from any component for a non-visual component like the one you've described here (your "foo" class needs no parents.) There's nothing in the Flex framework you need to inherit from for this.
However you architect it, your foo class is going to have to take in and parse keyboard codes to listen for and accept one or more methods to call. All you have to do is figure out when to add and remove the event listeners that will call the passed-in methods.
To handle turning your keyboard events on and off based on visibility, just have your foo component bind to the "visible" property of it's parent and add/remove event listeners accordingly.
You might also consider having the listeners added when the component that foo is nested in is on the display list rather than just visible. To do this, simply added and remove your event listeners in one of the component lifecycle methods - probably commitProperties is the most appropriate.
I don't think this solution answer your question directly but anyway, to help solve your problem here is an example.
For instance, I've extended the TextArea component like so. This is the best I can do so far, it can definitely be improved upon. Like, I don't know how to make the cursor go to the end after the next shortcut is pressed.
public class TextArea extends mx.controls.TextArea
{
// the keysmap is an example dictionary holding keycodes
private var keysmap:*={
112 = "some text for F1"
,113 = "the text for F2!"
//etc, etc
}
public var handleKeyDown:Boolean =false;
public function TextArea(){
if(handleKeyDown ==true){
this.addEventListener(KeyboardEvent.KEY_DOWN,this.keydownHandler);
}
}
public function keydownHandler(e:KeyboardEvent):void{
if(e.keyCode >= 112 && e.keyCode <= 123){
e.currentTarget["text"] += String(keysmap[e.keyCode]) +" ";
}//focusManager.setFocus(this);
}
}
I can't give you a solution using MXML, however my first thought would involve a singleton static class with a Dictionary that contains a list of objects as its keys and dynamically created dictionaries as the value pairing that contain keys denoting the desired key press with a function reference as the value.
So, say you had a Sprite and you wanted to capture ctrl+s for save when focus is on that object, I would get the instance of that Singleton, and call a function such as registerKeyBinding passing in the Sprite, the keyCode you want, and your pre-defined callback:
private var registeredObjects:Dictionary = new Dictionary(true);
public function registerKeyBinding(targetObject:Object, keyCode:int, callback:Function) {
if (registeredObjects[targetObject]) {
Dictionary(registeredObjects[targetObject])[keyCode] = callback;
}
else {
registeredObjects[targetObject] = new Dictionary();
Dictionary(registeredObjects[targetObject])[keyCode] = callback;
targetObject.addEventListener(KeyboardEvent.KEY_DOWN, keyDownListener);
}
}
private function keyDownListener(e:KeyboardEvent):void {
if (e.ctrlKey == true) {
//calls the function if that key exists.
Dictionary(registeredObjects[e.target])[e.keyCode];
}
}
Can't say I've tested this, but it was just the first thing that popped into my head. You could then setup functions to deregister and delete keys from the dictionaries, check states of the objects in addition to the keyCodes, remove old listeners, and delete entire dictionaries when there is no longer a need for them. Hopefully this is at least a tiny bit helpful.

Resources