I've looked at quite a few links for people trying to add content / change the template for a Dojo Dialog. This was the most promising.
However, whenever I do something like this:
Dialog declared in HTML:
<div class="djDialog" id="dgViewer" data-dojo-type="TemplatedDialog" data-dojo-props="title: 'My Dialog', draggable:false"></div>
Dialog Template:
<div class="dijitDialog" role="dialog" aria-labelledby="${id}_title">
<div data-dojo-attach-point="titleBar" class="dijitDialogTitleBar">
<span data-dojo-attach-point="titleNode" class="dijitDialogTitle" id="${id}_title"></span>
<span data-dojo-attach-point="closeButtonNode" class="dijitDialogCloseIcon" data-dojo-attach-event="ondijitclick: onCancel" title="${buttonCancel}" role="button" tabIndex="-1">
<span data-dojo-attach-point="closeText" class="closeText" title="${buttonCancel}">x</span>
</span>
</div>
<!-- containerNode from original Dialog template -->
<div data-dojo-attach-point="containerNode" class="dijitDialogPaneContent">
<!-- All "custom" content -->
<div data-dojo-type="dijit.layout.BorderContainer" data-dojo-props="design:'headline'" style="height:300px">
<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'center'">
</div>
<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'top'">
<button data-dojo-type="dijit.form.Button">Edit</button>
<button data-dojo-type="dijit.form.Button">Status</button>
</div>
</div>
<!-- End "custom" content -->
</div>
</div>
Custom Dialog class:
define([
'dojo/_base/declare',
'dijit/Dialog',
'dijit/_TemplatedMixin',
'dojo/text!./dialog_templates/View.html'],
function(
declare,
Dialog,
_Mixin,
_template){
return declare('TemplatedDialog', [Dialog, _Mixin], {
templateString : _template,
constructor : function(){
}
})
})
In my console (using Chrome) I just see:
<div data-dojo-attach-point="containerNode" class="dijitDialogPaneContent" style="width: auto; height: auto; "></div>
... and empty node where content should be.
So far I haven't found anyone who seems to have successfully extended dijit.Dialog in terms of templates. Is this possible?
Edit
After trying some variations on this template:
<div class="dijitDialog" role="dialog" aria-labelledby="${id}_title">
<div data-dojo-attach-point="titleBar" class="dijitDialogTitleBar">
<span data-dojo-attach-point="titleNode" class="dijitDialogTitle" id="${id}_title"></span>
<span data-dojo-attach-point="closeButtonNode" class="dijitDialogCloseIcon" data-dojo-attach-event="ondijitclick: onCancel" title="${buttonCancel}" role="button" tabIndex="-1">
<span data-dojo-attach-point="closeText" class="closeText" title="${buttonCancel}">x</span>
</span>
</div>
<!-- containerNode from original Dialog template -->
<div class="dijitDialogPaneContent">
<div data-dojo-type="dijit.layout.BorderContainer" data-dojo-props="design:'headline'" style="height:100%;width:100%">
<div data-dojo-attach-point="containerNode" data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'center'"></div>
</div>
<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'bottom'">
<button data-dojo-type="dijit.form.Button">Edit</button>
</div>
</div>
</div>
The error Uncaught TypeError: Cannot read property '0' of undefined is being thrown. This is the stack, if it helps. I'm using the uncompressed version from the Google CDN to help in debugging.
_childElements dojo.js.uncompressed.js:8341
getStepQueryFunc dojo.js.uncompressed.js:8597
query dojo.js.uncompressed.js:9005
query dojo.js.uncompressed.js:5248
_2._checkIfSingleChild _ContentPaneResizeMixin.js:2
_4._size Dialog.js:2
_4.show Dialog.js:2
Removing the data-dojo-type and -props from the containerNode resolves this, but it's still not getting things closer to having a working custom templated Dialog.
Reason for troubles with doing templating on a contentpane is, that whatever template-contents you put into the domNode referenced with the attach-point 'containerNode', you will loose on startup.
If there is no 'href' nor 'content' attributes set, they will simply be set to an empty string, thus leaving the Dialog.containerNode.innerHTML == ""
You have no need for deriving from _TemplatedMixin as the Dialog itself is a templated widget. Instead change this to _WidgetsInTemplateMixin to compensate for the BorderContainer layout widgets and your dijit.form contents. Also, your markup within the custom template should be pre-required, so you could go with something like this here:
Change the template from old attachpoint for container to this
<div data-dojo-attach-point="containerNode"
data-dojo-type="dijit.layout.ContentPane"
data-dojo-props="region:'center'">
Then add requirements to your markup widgets in the template plus the _WidgetsInTemplateMixin:
define(["dijit/Dialog",
"dijit/_WidgetsInTemplateMixin",
"dojo/text!./dialog_templates/View.html",
// rest are for rendering example
"dijit/layout/BorderContainer",
"dijit/layout/ContentPane",
"dijit/form/Button"
}. ... );
Result should ressemble this, keeping your template View.html change in mind:
define([
'dojo/_base/declare',
'dijit/Dialog',
"dijit/_WidgetsInTemplateMixin",
"dojo/text!./dialog_templates/View.html",
// rest are for rendering example
"dijit/layout/BorderContainer",
"dijit/layout/ContentPane",
"dijit/form/Button"],
function(
declare,
Dialog,
_Mixin,
_template){
return declare('TemplatedDialog', [Dialog, _Mixin /*careful, widgetsintemplate is tricky*/ ], {
templateString : _template
})
})
You can fiddle here
EDIT:
As there is troubles with dialogs containing borderlayouts (its not unheard of anyways) here's a workaround:
_checkIfSingleChild: function() {
delete this._singleChild;
domClass.toggle(this.containerNode, this.baseClass + "SingleChild", !!this._singleChild);
},
templateString: '....'
Im not certain of the consequences, im thinking the borderlayout of yours might start to misbehave if you try to programmatically change its contents and dimensions.. But it will render - at least it does here: updated fiddle
Related
I'm trying to select the next element to add the class image-radio-selected with JQuery.
My html is like
<div id="photocall">
#foreach ($photocalls as $photocall)
<div class="d-inline-block mx-1">
<div style="background-image: url('')" class="photocallThumb image-radio-selected"></div>
</div>
#endforeach
<input>
</div>
Im trying to:
$( "#mybutton" ).on("click", function() {
var selected = $('.photocallThumb.image-radio-selected'); // working
selected.next('.photocallThumb').addClass('image-radio-selected'); // not working
});
After 2 hours, trying to solve, reading doc,
I'm more confused than when I started...
what am I doing wrong?
One method is you will need to get out of the parent div, then do a next for the parent.
$( "#mybutton" ).on("click", function() {
var selected = $('.photocallThumb.image-radio-selected');
selected.parent(".d-inline-block").next(".d-inline-block").find('.photocallThumb').addClass('image-radio-selected'); // not working
});
.image-radio-selected{border:1px solid #ff00aa;}
.mx-1{width:100px;height:100px;border:1px solid #000000;}
.d-inline-block{display:inline-block;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="photocall">
<div class="d-inline-block mx-1">
<div style="background-image: url('')" class="photocallThumb image-radio-selected"></div>
</div>
<div class="d-inline-block mx-1">
<div style="background-image: url('')" class="photocallThumb"></div>
</div>
<div class="d-inline-block mx-1">
<div style="background-image: url('')" class="photocallThumb"></div>
</div>
<div class="d-inline-block mx-1">
<div style="background-image: url('')" class="photocallThumb"></div>
</div>
<button type="button" id="mybutton">next</button>
</div>
JQuery's next method selects the next sibling of the selected element. However, since your photocallThumb div is inside a d-inline-block div, it has no siblings. You'd have to go back up a level, then find the next photocallThumb, maybe something like selected.parent().find('.photocallThumb').eq(0).
However, an even better pattern that will help you avoid bugs like these is called templating. Basically, on the client side, you have an html template string, and you pass it data that represent your current state. In your case, you'd pass it an array of javascript objects, each one with an image url and an isSelected boolean. Then, when your state changes, instead of using jquery to try to fix what's changed, you just rerender your template and replace your html element's content with the newly rendered template, and it's now magically in the correct state. This is the pattern favored by large frameworks like React and Angular.
Here's an example from lodash that renders a list of usernames:
// Use the "evaluate" delimiter to execute JavaScript and generate HTML.
var compiled = _.template(
`<% _.forEach(users, function(user) { %>
<li><%- user %></li>
<% }); %>`);
compiled({ 'users': ['fred', 'barney'] });
// => '<li>fred</li><li>barney</li>'
Web project: backend - ASP.NET, frontend - AngularJS.
I have the "main page" with mainApp module.
Also i have the another page with the registration, which is the responsibility of LoginLayerApp.
What i want: when you click on "main window" i need pop-up window with the registration content (html, css, angularController and so on).
What was tried:
Loaded using JQuery, implement the DOM and call angular.bootstrap by
hands.
Loaded via $http and using ngDialog library to open a pop-up
window.
Use the iframe.
The problem is that the loaded naked HTML, but I do not know how to inject LoginLayerApp, to then rebuild the naked HTML. I can do it only once, but not as usual, when angular always rebuild view by changes in controller (for example, different ng-class depending on the width of the screen).
How to do the most right? Perhaps I should do differently to organize something?
RequireJS, WebPack will not work, too much legacy-project implementation for such a trifle.
Example of registration page:
#using Inventum.ViewModels.Account
#using Microsoft.Owin.Security
#{
Layout = null;
}
<link href="~/Content/css/layers/Login/loginRegistrationLayer.min.css" rel="stylesheet" />
<div ng-app="loginLayerApp"
ng-controller="loginLayerController as loginCtrl"
ng-init="init(); returnUrl = '#ViewBag.ReturnUrl'; type = ('#ViewBag.Type' == '') ? 'login' : '#ViewBag.Type'; form={}; user={};"
esc-key="closeLayer()"
class="login-app-container"
tabindex="0">
<div class="container" ng-class="{'mobile': !desktop, 'desktop': desktop }" ng-switch on="type">
<!-- =============== LOGIN =============== -->
<div class="registration-container" ng-switch-when="login">
{{loginCtrl.desktop}}
{{desktop}}
<h1>#Html.ClickZone("NewRegLogin.Login.Title")</h1>
<div class="main-label">
<span>#Html.ClickZone("NewRegLogin.Login.HelpLabel")</span>
<br ng-show="phoneBreakpoint">
</div>
<!-- ======== Errors block ======== -->
<!-- ======== Errors block ======== -->
<div class="options">
<ng-form name="form.userForm" ng-class="{'showMe': nosocial && !desktop}" novalidate enter-key="trySubmit = true; submitForm(form.userForm.$valid, user)">
<!-- <form name="userForm" ng-submit="submitForm(userForm.$valid)" novalidate> -->
<div class="emailFocusHandler" ng-hide="desktop || nosocial" ng-click="nosocial = true;"></div>
<div class="group">
</div>
<div ng-show="desktop || nosocial">
<div class="group">
</div>
<div class="help-buttons">
</div>
<div class="error-block" ng-if="loginFailed && answerCode == ANSWERS.LOGIN.UserNoutFound">
</div>
<button type="submit" ng-click="trySubmit = true; submitForm(form.userForm.$valid, user)">#Html.ClickZone("NewRegLogin.SignIn") </button>
</div>
</ng-form>
<div class="delimeter" ng-show="desktop || nosocial"></div>
</div>
</div>
<!-- =============== LOGIN =============== -->
</div>
#section scripts {
<!-- ANGULARJS -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular-route.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular-messages.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular-animate.min.js"></script>
#Scripts.Render("~/bundles/js/layer/newLogin")
<script src="https://www.google.com/recaptcha/api.js?onload=vcRecaptchaApiLoaded&render=explicit" async defer></script>
}
What I would do is create the modal window (pop-up window) as a angular-ui modal. (their modal docs). That way you don't have to write your own modal code.
When you implement it, the modal will get its own controller and its own html file and the implementation will look like this:
Current Controller-
appName.controller('loginLayerController', ["$scope","uibModal", function ($scope, $uibModal){
$scope.openComponentModal = function () {
var modalInstance = $uibModal.open({
templateUrl: 'myModalContent.html',
controller: 'myModalController',
controllerAs: 'myModalController'
}
});
}])
And from there you can put your form into myModalContent.html, and hide and show buttons there accordingly
I'm not sure, but you can try to create component, that will show your errors. By default set in invisible. In controllers you can manage how many and when you should show your error messages.
I am using bootstrap v3.3.5 in my application and wanted to include collapse function such that, when user clicks on a link, div below it toggles. Referring to answer in this Can you specify a "data-target" for Bootstrap which refers to a sibling DOM element without using an ID? I was able to achieve similar result as my requirement, in the below fiddle link
https://jsfiddle.net/szp1cg0k/, which is using bootstrap v2.1.1.min.js and v2.1.1.min.css
But in the same fiddle when I include bootstrap v3.3.5.min.js and v3.3.5.min.css reference the toggle/collapse functionality doesn't work here, neither throws any error
updated JS https://jsfiddle.net/ohoLxap6/2/
html code:
<fieldset class="fsStyle">
<legend class="legendStyle">
<a data-toggle="collapse-next" data-target=".demo" href="#">Activity Log Filter Criteria4</a>
</legend>
<div class="demo" >
<div class="col-md-2">
<label for="activity_from_date" class="labelStyle">Activity From Date:</label>
</div>
<div class="col-md-4">
<input name="fromDate" maxlength="10" size="11" tabindex="59" value="" onblur="javascript:DateFormat(this,event,true);" class="textInput" id="activity_from_date" type="text">
</div>
</div>
</fieldset>
script:
$('body').on('click.collapse-next.data-api', '[data-toggle=collapse-next]', function() {
console.log($(this).parent());
var $target = $(this).parent().next()
console.log($target);
$target.data('collapse') ? $target.collapse('toggle') : $target.collapse()
});
Can anyone give me some hint where I am going wrong ?
UPDATE:
I am aware of the collapse function of bootstrap, I have multiple divs in my form and I want to include collapse function on most of the divs. One way to achieve this is
<div data-toggle="collapse" data-target="#demo1">
<div id="demo1">
<p>demo 1 ......</p>
</div>
<div data-toggle="collapse" data-target="#demo2">
<div id="demo2">
<p>demo 2 ......</p>
</div>
But I dont want to use ids, instead I want to specify class. The reason being, I have jqtree at backend and I have to include clone function as well, so using ids would mean after cloning I need to take care of ids of cloned child node div. Hence want to use class instead , something like below
<div data-toggle="collapse" data-target=".demo">
<div class="demo">
<p>demo 1 ......</p>
</div>
<div data-toggle="collapse" data-target=".demo">
<div class="demo">
<p>demo 2 ......</p>
</div>
Got this working. I have updated the fiddle accordingly. https://jsfiddle.net/ohoLxap6/3/
$('body').on('click.collapse-next.data-api', '[data-toggle=collapse-next]',
function() {
//console.log($(this).parent());
var $target = $(this).parent().next()
console.log($target);
$target.data('bs.collapse') ? $target.collapse('toggle') :
$target.collapse()
});
Why would you write your own code for a existing Bootstrap function? Following code is an example of the latest Bootstrap version collapse method:
<button class="btn" data-toggle="collapse" data-target="#demo">Collapsible</button>
<div id="demo" class="collapse">
Some text..
</div>
Docs: http://getbootstrap.com/javascript/#collapse
UPDATE:
According to your new question, collapsing using classes, I have created a custom script:
$('[data-toggle="collapse"]').click(function() {
var $target = $(this).data('target');
var $target = $($target);
$target.data('bs.collapse') ? $target.collapse('toggle') :
$target.collapse()
});
Now all classes inside the data-target attribute will get toggled. https://jsfiddle.net/ohoLxap6/4/
I have a Custom dojo widget with dojo layout widgets
template as below
<div>
<div data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="gutters:false" id="mainPanel" style="padding: 0px">
<div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'top', splitter:false" style="padding: 0px">
Saartha Labs Pvt Ltd
</div>
<div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'top', gutters: false, splitter:false" style="padding: 0px" >
<div id="toolBar"></div>
</div>
<div id="map-div" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'center', splitter:false"></div>
<div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'bottom', splitter:false" style="display: none" ></div>
</div>
</div>
and the Custom Widget as below "Canvas.js"
define([
"dojo/_base/declare",
"dijit/_WidgetBase",
"dijit/_WidgetsInTemplateMixin",
"dijit/_OnDijitClickMixin",
"dijit/_TemplatedMixin",
"dojo/text!./canvas.html",
"dijit/layout/BorderContainer",
"dijit/layout/ContentPane"
], function (declare, _WidgetBase,_WidgetsInTemplateMixin, _OnDijitClickMixin,_TemplatedMixin, template) {
return declare([_WidgetBase,_OnDijitClickMixin, _TemplatedMixin,_WidgetsInTemplateMixin], {
templateString: template
// your custom code goes here
});
});
When use try to use the Canvas with new
it throws a error as below.
require([
"bhuvi/canvas/Canvas",
"dojo/domReady!"],
function(Canvas){
var canvas = new Canvas();
canvas.placeAt(window.document.body);
});
Error as
"Tried to register widget with id==mainPanel but that id is already registered"
Never use IDs in your widget templates. IDs must be unique, so unless your ID is dynamically generated (which isn't in this case), your ID won't be unique if you create multiple instances of your widget.
In stead of that, use the data-dojo-attach-point mechanism, for example:
<div>
<div data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="gutters:false"
data-dojo-attach-point="mainPanel" style="padding: 0px">
<!-- Rest of the code -->
</div>
</div>
Now you can use this.mainPanel if you need access to that widget.
Even if you don't create multiple instances of your widget, you're still better off by using the attach points, you never know what happens behind the screens.
A small side note: Layout widgets are not officially supported by the dijit/_WidgetsInTemplateMixin mixin, so be careful when using them. However, this is not the cause of your problem.
var dojoConfig = {
async: true,
parseOnLoad: false
}
parseOnLoad should be false
i'm migrating one of my older jquery plugins from DOM jungle to this fancy mvvm framework knockout.
Which technique would i use to properly display a popup container? I ahve to populate it 'by call' since i get a json feed every time.
I tried an approach using the with binding, but it still attempts to populate the partial at its first runtime.
<!-- ko with: daySubmitFormViewModel -->
<div class="ec-consulation-lightbox">
<form id="cForm" class="form-container">
// Some bindings here.
</form>
</div>
<!-- /ko with: -->
It can be done without custom binding as well. Example is below
<div class="modalWindowBackground" data-bind="visible: popupDialog" >
<div class="modalWindow" data-bind="with:popupDialog">
<div class="content">
<h2 data-bind="text: title"></h2>
<p>
<span data-bind="text: message"></span>
</p>
<div class="buttonSpace">
<input type="button" class="closeButton" data-bind="value: closeButtonText, click: $root.hidePopupDialog" />
</div>
</div>
</div>
</div>
Viewmodel code:
self.showAlert = function (title, message, closeButtonText) {
self.popupDialog({ title: title, message: message, closeButtonText: closeButtonText });
};
self.hidePopupDialog = function () {
self.popupDialog(null);
};
//Code which opens a popup
self.remove = function () {
.... some code ...
if (someCondition) {
self.showAlert('SomeTitle', 'Message', 'OK');
return;
}
.... some code ...
};
Create a custom binding, have its open / close function trigger on a observable.
I've done a custom binding for jQuery Dialog that uses this approuch in combination with KO
templates.
<div id="dialog" data-bind="dialog: { autoOpen: false, modal: true, title: dialogTitle }, template: { name: 'dialog-template', data: dialogItem, 'if': dialogItem }, openDialog: dialogItem"></div>
You can find my binding here along with some others
https://github.com/AndersMalmgren/Knockout.Bindings
Live demo http://jsfiddle.net/H8xWY/102/
https://github.com/One-com/knockout-popupTemplate
That pretty much does what you ask for. It's deeply configurable, and under steady development (we use it in our web applications ourselves).
Disclaimer: I'm a One.com developer. I am also the person who originated the above mentioned lib.