.datepicker call inside of Durandal activate function not working - javascript

I'm Composing this in a view, then trying to call .datepicker() on the result, but nothing happens.
The compose container
<div>
<!--ko compose: { model:'viewmodels/schedule', view: 'views/schedule.html', activate:true} -->
<!--/ko-->
</div>
schedule.html
<div class="schedule-editor">
</div>
And the schedule module
define([], function () {
var vm = {
activate: activate,
};
return vm;
function activate() {
$('.schedule-editor').datepicker();
console.log("activated schedule module");
return true;
}
});
Console logs "activated schedule module", but the datepicker is not created.
If I go to the chrome console and run the jQuery call,
$('.schedule-editor').datepicker(); it brings up the datepicker just fine.
The Durandal docs claim that the activate function is called after the DOM is full composed, so I don't know what else to try.

Like nemesv mentioned you should use viewAttached instead.
define([], function () {
var vm = {
viewAttached: viewAttached,
};
return vm;
function viewAttached(view) {
$(view).find('.schedule-editor').datepicker();
console.log("activated schedule module");
return true;
}
});
Activate happens in the lifecycle before your model has been data-bound to the new view and before the view has been added to the dom. viewAttached happens after the view has been data-bound to your model and attached to the dom.
EDIT
Durandal 2.0 has renamed viewAttached to attached

There is another approach to this that stays true to the declarative UI philosophy that knockout.js and durandal are striving for.
It will allow you to declare the datepicker within the HTML like this:
<div class="schedule-editor" data-bind="
jqueryui: {
widget: 'datepicker',
options: {
// you can set options here as per the jquery ui datepicker docs
}
}">
</div>
Simply include the jquery ui widget bindings found in this gist: https://github.com/SteveSanderson/knockout/wiki/Bindings---jqueryui-widgets
Make sure you include the above javascript after you have loaded jquery, jquery ui and knockout.

Related

Ember js how can i run a function after model change?

I am using materialize css library and it says
Collapsible elements only need initialization if they are added dynamically
And i add some elements dynamically. So after the view is rendered i should run this function:
$('.collapsible').collapsible({
accordion : false
});
In setupController function i make some requests and after every request finished i setup the controller. When controller gets the model and after the view is rendered with the new model data i should run a function to initialize the ui elments
You have to setup an ember.js component for your accordion and use the didInsertElement hook:
export default Ember.Component.extend({
classNames: ['.collapsible'],
didInsertElement() {
Ember.run.scheduleOnce('afterRender', this, function() {
this.$().collapsible({
accordion: false
});
});
},
});

Does DOJO offers an alternative to "extension points" for custom widgets?

I am designing a custom widget using DIJIT and DOJO 1.10.
Basically my custom widget needs to have some behavior like a button, so when user click on it something can happen. I need to make sure other developers can add custom code when onClick it is fired on that widget.
After reading this guide I understood that my custom widget should implement extension points. I have notice in the source code in DIJIT for Button.js and I see they using a special mixin called dijit._OnDijitClickMixin.
Below code for my widget, so far it works fine, but I would like to know:
Is extension point the right way? Does a better alternative exists?
Reading at the documentation I see the following code.
_onButtonClick: function( /*Event*/ e){
... // Trust me, _onClick calls this._onClick
},
_onClick: function( /*Event*/ e){
...
return this.onClick(e);
},
onClick: { // nothing here: the extension point!
}
My custom widget does not implement any of these functions and seems working fine.
Shall I include these functions? What is the reason for that?
Widget
define([
'dojo/_base/declare',
'dijit/_WidgetBase',
'dijit/_OnDijitClickMixin',
'dijit/_TemplatedMixin',
'dojo/text!./templates/template.html'
], function (
declare,
_WidgetBase,
_OnDijitClickMixin,
_TemplatedMixin,
template
) {
return declare([_WidgetBase, _TemplatedMixin, _OnDijitClickMixin], {
templateString: template
});
});
HTML template
<div data-dojo-attach-event="ondijitclick:onClick"> </div>
Initialize the widget
this._iconPage = new IconPages({
id: 'iconPage',
onClick: function () {
//do smt
}.bind(this)
}).placeAt('content');
What you have is fine as the onClick method is meant to be overwritten to hook into events. What you can also do is hook into your IconPages "click" event using dojo/on by doing something like this:
on(this._iconPage, "click", /*function here*/);

execute action (javascript function) after ember template is rendered

I have very small web page with emberjs, where I want to show some item list and openlayers map for them, and another map for selected item.
Something like that:
<script type="text/x-handlebars" id="list">
<div class="list">
<div id="list_map"></div>
</div>
{{outlet}}
</script>
<script type="text/x-handlebars" id="list/item" >
<div class="item">
<div id="item_map"></div>
</div>
</script>
<script>
function showListMap() {
listMap = new ol.Map({target:'list_map'});
}
function showItemMap() {
itemMap = new ol.Map({target:'item_map'});
}
</script>
There is no problem to display map for list:
model: function(params) {
var content = [];
var url = 'http://localhost:8080/app/list';
$.ajax({
url: url,
success: function(surveys) {
content.pushObjects(surveys);
showListMap();
}
});
return content;
}
and I have action in item controller that is executed, when opening selected item, but if I try to create item map there (in controllers action) it fails (afaik because there is no such div at that moment in DOM).
So if I could execute action or my function after div is already add to DOM, it should work.
And question would be, how to execute something after template is added to DOM, or that's completely wrong way to do such stuff (than what would be correct ember way)?
I can't say much with seeing full code. But to execute some code after the DOM is rendered you schedule a function on the the run loops afterRender queue.
Ember.run.scheduleOnce('afterRender', this, function() {
//The div should be available now.
});
But if you really need to touch the DOM I recommend you wrap your map code in a component. A component gets a didInsertElement where you can write your maps initialization code.
var component = Em.Component.extend({
setup: function() {
//Do you map init here.
}.on('didInsertElement')
});
There unfortunately isn't a really good route or controller hook that fires off after a page has already rendered. I believe the reason for this is that the developers of Ember think it is an anti-pattern to directly talk to the DOM.
That being said, I think it sometimes is quite handy for complex UI on otherwise static web pages. If you want to do some sort of jquery or use the DOM API after a route has rendered, you can do the following in your route file (as #Dainius correctly points out)
routeName.js
import Route from '#ember/routing/route';
import jQuery from 'jquery';
export default class myRouteFile extends Route {
manipulateDom = function() {
$("#myDiv").css( "color", "red" );
}
init() {
this._super(...arguments)
Ember.run.scheduleOnce('afterRender', this, this.manipulateDom)
}
}

Integrating Skrollr w/ AngularJS Single-page App

I setup a single-page app with AngularJS and used Skrollr on the home page. I have not used Skrollr before, so I wanted to check with others about the proper 'Angular' way to integrate it with AngularJS, before I start to dive into using more features
What I did in Angular was create a service to load the script onto the page and call skrollr.init() and return it as a promise. Then injected the service to a directive which calls refresh as needed. If a page needs skrollr, I can use this directive on the page somewhere and set the data attributes per skrollr documentation.
ie this works:
<div class="main" skrollr-tag>
<div data-0="color:rgb(0,0,255);" data-90="color:rgb(255,0,0);">WOOOT</div>
</div>
It seems elements added to DOM later on, such as by ngRepeat, skrollr doesn't know about, so I need to include this directive on all elements generated dynamically w/ skrollr data attributes for it to work.
<div class="main" skrollr-tag>
<!-- this heading will animate all the time -->
<h1 data-0="opacity: 1" data-50="opacity: 0">WOOT!</h1>
<div data-ng-repeat="item in items" class="had-to-add-skrollr-again" skrollr-tag>
<!-- skrollr animates this only on page refresh, unless skrollr-tag duplicated above -->
<div data-0="color:rgb(0,0,255);" data-90="color:rgb(255,0,0);">{{item.name}}</div>
</div>
</div>
So, to recap, skrollr is 'aware' of these dynamic elements on the 1st load after refresh, but then after navigating to a different route then back again they no longer get animated unless you refresh page again, or add skrollr-tag directive to the dynamic elements themselves.
Is this a bad idea for performance reasons to include this directive on each dynamic element needing skrollr, thus calling refresh() again for each one? Ideally solution would be load skrollr-tag directive once per page, and it's aware of dynamic elements. I am open to any completely different cleaner more simple way to integrate skrollr to angular.
The angular code is here:
service:
.service('skrollrService', ['$document', '$q', '$rootScope', '$window',
function($document, $q, $rootScope, $window){
var defer = $q.defer();
function onScriptLoad() {
// Load client in the browser
$rootScope.$apply(function() {
var s = $window.skrollr.init({
forceHeight: false
});
defer.resolve(s);
});
}
// Create a script tag with skrollr as the source
// and call our onScriptLoad callback when it
// has been loaded
var scriptTag = $document[0].createElement('script');
scriptTag.type = 'text/javascript';
scriptTag.async = true;
scriptTag.src = 'lib/skrollr/dist/skrollr.min.js';
scriptTag.onreadystatechange = function () {
if (this.readyState === 'complete') onScriptLoad();
};
scriptTag.onload = onScriptLoad;
var s = $document[0].getElementsByTagName('body')[0];
s.appendChild(scriptTag);
return {
skrollr: function() { return defer.promise; }
};
}
]);
directive:
.directive('skrollrTag', [ 'skrollrService',
function(skrollrService){
return {
link: function(){
skrollrService.skrollr().then(function(skrollr){
skrollr.refresh();
});
}
};
}
])
I'm currently having the same issue trying to integrate Skrollr into AngularJS.
The problem is basically this directive, it works when the page loads for the first time but then nothing is happening even though its being called when new html elements are created - or when you change views.
.directive('skrollrTag', [ 'skrollrService',
function(skrollrService){
return {
link: function(){
skrollrService.skrollr().then(function(skrollr){
skrollr.refresh();
});
}
};
}
])
I think the reason is the way angularJS injects new html content. By the time skrollr does "refresh" its not yet rendered or some sort of conflict.
Maybe the only solution is to modify skrollr script.
This answer should help: AngularJS watch DOM change. Try updating your directive to watch for child node changes. This way, it'll automatically refresh whenever new nodes are added.
.directive('skrollrTag', [ 'skrollrService',
function(skrollrService){
return {
link: function(scope, element, attrs){
skrollrService.skrollr().then(function(skrollr){
skrollr.refresh();
});
//This will watch for any new elements being added as children to whatever element this directive is placed on. If new elements are added, Skrollr will be refreshed (pulling in the new elements
scope.$watch(
function () { return element[0].childNodes.length; },
function (newValue, oldValue) {
if (newValue !== oldValue) {
skrollrService.skrollr().then(function(skrollr){
skrollr.refresh();
});
}
});
}
};
}
]);
EDIT
Updated to account for the promise you're using (that would already be resolved), and added a comment to further explain the solution.
i made a directive for skrollr
(function () {
'use strict';
angular.module('myApp', [])
.directive('skrollr', function () {
var obj = {
link: function () {
/* jshint ignore:start */
skrollr.init().refresh();
/* jshint ignore:end */
}
};
return obj;
});
})();
and use like this
<div skrollr data-0="background-color:rgb(0,0,255);" data-500="background-color:rgb(255,0,0);">WOOOT</div>

Simple Javascript not working in my spa app

I am using the Hot Towel template by John Papa. I have a html view called nav.html, which contains the header portion of my spa. Within that, i need to display the name of the person that is logged into the system (i have a server side utility class that handles the query).
The following is from the html in the nav.html view for that-
data-bind="text: LoggedInAs"
Here is the viewmodel code (nav.js)-
define(['services/logger'], function (logger) {
var vm = {
activate: activate,
title: 'Nav View'
};
return vm;
//#region Internal Methods
function activate() {
logger.log('Nav View Activated', null, 'Nav', true);
return true;
}
//#endregion
});
My problem is that i am not sure how to do this. i tried adding nav.js to my viewmodels folder, but the javascript does not run. I thought durandal would have picked it up like the other viewmodels. The only difference between the nav.js and the other view models is that the other view models are triggered by clicking on a link (wired through route.mapnav).
What am i missing here? How do i get the javascript to run without a user clicking on a link? When the page loads, I need nav.js to run in order to populate the LoggedInAs data-bind.
Make sure that you are activating your nav view. In the example code you have given in the comment above, it would need to be this:
<header> <!--ko compose: {view: 'nav', activate: true} --><!--/ko--> </header>

Categories

Resources