Durandel at browser chrome, not arrive ro compositionComplete - google-chrome

I work in durandal project.
I have a problem:
In one of my pages, at browser chrome, it doesn't arrive to compositionComplete.
(I checked it by debug.)
In explorer- it arrives.
What can be the reasons?
What do I have to check?

You can create a viewModel like this to inspect the binging:
/***==========LIFECIRCLE===============***/
var canActivate = function canActivate(view, parent) {
logger.log('Lifecycle : canActivate : Dashboard');
return true;
},
activate = function activate(view, parent) {
logger.log(viewmodel.title + ' View Activated', null, viewmodel.title, true);
return true;
},
binding = function binding(view, parent) {
logger.log('Lifecycle : binding : Dashboard');
return { cacheViews: false }; //cancels view caching for this module, allowing the triggering of the detached callback
},
bindingComplete = function bindingComplete(view, parent) {
logger.log('Lifecycle : bindingComplete : Dashboard');
},
attached = function attached(view, parent) {
logger.log('Lifecycle : attached : Dashboard');
},
compositionComplete = function compositionComplete(view, parent) {
logger.log('Lifecycle : compositionComplete : Dashboard');
},
canDeactivate = function canDeactivate(view, parent) {
logger.log('Lifecycle : canDeactivate : Dashboard');
return true;
},
deactivate = function deactivate(view, parent) {
logger.log('Lifecycle : deactivate : Dashboard');
},
detached = function detached(view, parent) {
logger.log('Lifecycle : detached : Dashboard');
};
/***==========VIEWMODEL===============***/
var viewmodel = {
title: 'Dashboard for inspection',
canActivate: canActivate,
activate: activate,
binding: binding,
bindingComplete: bindingComplete,
attached: attached,
compositionComplete: compositionComplete,
canDeactivate: canDeactivate,
deactivate: deactivate,
detached: detached
}//-->end of ViewModel
return viewmodel;

Related

Hover-Enabled Sub Context Menu in Viewer

Question: How do I modify the below code so that (i) upon hovering over a menu item in the context menu, the correpsonding submenu appears, and (ii) the menu(s) disappear after clicking on a menu item?
Context: Currently, when you press the menu item corresponding with the sub-menu, the original context menu stays fixed (i.e. when clicking on empty space in the viewer, the menu remains and appears fully interactive). When you press on the same menu item for a second time, it opens the sub-menu, but similar to the original menu, this sub-menu also remains fixed when we press one of its menu items.
For reference, I've included some screenshots of the current context menu and sub-menu.
The corresponding code is as follows:
...
function ContextMenu(viewer, options) {
Autodesk.Viewing.Extensions.ViewerObjectContextMenu.call(this, viewer, options);
}
ContextMenu.prototype = Object.create(Autodesk.Viewing.Extensions.ViewerObjectContextMenu.prototype);
ContextMenu.prototype.constructor = ContextMenu;
ContextMenu.prototype.buildMenu = function(event, context) {
if (contextMenuState.disabled) {
return null;
}
// Context is a true false flag used internally by autodesk to determine which type of menu to build.
// If false, it has the side effect of selecting the right-clicked element.
var autodeskMenu = Autodesk.Viewing.Extensions.ViewerObjectContextMenu.prototype.buildMenu.call(this, event, context);
const filterOut = ['Hide Selected', 'Clear Selection', 'Show All Objects'];
const menu = autodeskMenu.filter(m => !filterOut.includes(m.title));
menu.push({
title: "Custom 1",
target: function() {
doSomeCustom1Stuff();
}
});
menu.push({
this.custom2ItemGenerator(<parameter1>, <parameter2>, <parameter3>, <parameter4>);
});
return menu;
};
ContextMenu.prototype.custom2ItemGenerator = function(p1, p2, p3, p4) {
return {
title: "< Custom 2",
target: [
{
title: "Sub-custom 1",
target: function() {
...doSomething1(p1);
}
},
{
title: "Sub-custom 2",
target: function() {
...doSomething2(p2);
}
},
{
title: "Sub-custom 3",
target: function() {
...doSomething3(p3);
}
},
{
title: "Sub-custom 4",
target: function() {
...doSomething4(p4);
}
},
{
title: "Sub-custom 5",
target: function() {
...doSomething5(p5);
}
}
]
}
};
/* Not sure the following to overrides ('hide' and 'addCallbackToMenuItem') are correct, or even necessary.
ContextMenu.prototype.hide = function() {
if (this.open) {
this.menus = [];
this.open = false;
this.container.removeEventListener('touchend', this.OnHide);
this.container.removeEventListener('click', this.OnHide);
this.container.removeEventListener(<Custom Name>, this.OnMove); // same Custom Name as 1st parameter function below -- Autodesk.Viewing.theExtensionManager.registerExtension
this.container.parentNode.removeChild(this.container);
this.container = null;
return true;
}
return false;
};
ContextMenu.prototype.addCallbackToMenuItem = function (menuItem, target) {
var that = this;
if (target.constructor == Array) {
menuItem.addEventListener('click', function (event) {
that.hide();
target();
event.preventDefault();
return false;
}, false);
} else {
menuItem.addEventListener('mouseover', function (event) {
that.hide();
target();
event.preventDefault();
return false;
}, false);
}
};
function ContextMenuLoader(viewer, options) {
Autodesk.Viewing.Extension.call(this, viewer, options);
this.load = function() {
viewer.setContextMenu(new ContextMenu(viewer, options));
return true;
};
this.unload = function() {
viewer.setContextMenu(new Autodesk.Viewing.Extensions.ViewerObjectContextMenu(viewer, options));
return true;
};
}
ContextMenuLoader.prototype = Object.create(Autodesk.Viewing.Extension.prototype);
ContextMenuLoader.prototype.constructor = ContextMenuLoader;
Autodesk.Viewing.theExtensionManager.registerExtension(<Custom Name>, ContextMenuLoader);
....
Sorry about this problem, we're investigating this. Our tracking code is LMV-3740

Directive changes color and text on a fly

This is a directive that should change the color and text of the element depending on the incoming data
function colorStatus() {
return {
restrict: 'AE',
scope: {
status: '#'
},
link: function (scope, element) {
let status = +scope.status;
switch (status) {
case 0:
element.text(' ');
element.css('color', '#FFFFFF');
break;
case 1:
element.text('Correct!');
element.css('color', '#4CAF50');
break;
case 2:
element.text('Error!');
element.css('color', '#F44336');
break;
case 3:
element.text('Waiting...');
element.css('color', '#FF9800');
break;
}
}
};
}
Initially, it receives resolved data from the controller.
Here is HTML:
<color-status status="{{vm.status}}"></color-status>
<button ng-click="vm.changeStatus()"><button>
Here is function from controller:
vm.changeStatus = changeStatus;
vm.status = chosenTask.status; // It equals 0 in the received data
function changeStatus() {
vm.status = 1;
}
I expect that the text and color of the directive will change, but this does not happen. Where is my mistake?
Post link is only called once
The problem you're having is that you set your element's text and color in your link function. This means that when your directive instantiates and goes through initialisation, the link function will be executed, but it will get executed exactly once. When the value of status changes, you're not handling those changes to reflect the, on your element. Therefore you should add $onChanges() function to your directive and handle those changes.
function StatusController($element) {
this.$element = $element;
this.status = 0;
}
StatusController.mapper = [
{ text: ' ', color: '#FFFFFF' },
{ text: 'Correct!', color: '#4CAF50' },
{ text: 'Error!', color: '#F44336' },
{ text: 'Waiting...', color: '#FF9800' },
}];
StatusController.prototype.setStatus = function(status) {
var statusObj = StatusController.mapper[+status];
this.$element
.text(statusObj.text)
.css('color', statusObj.color);
}
StatusController.prototype.$onInit = function() {
// this.status is now populated by the supplied attribute value
this.setStatus(this.status);
}
StatusController.prototype.$onChanges = function(changes) {
if (changes.status && !changes.status.isFirstChange()) {
this.setStatus(this.status);
}
}
var StatusDirective = {
restrict: 'AE',
controller: StatusController,
scope: true,
bindToController: {
status: '#'
}
};
angular.module('someModule')
.directive('colorStatus', function() { return StatusDirective; });
But apart from this, I also suggest you set element's text by using ng-bind or {{...}} to put that value in. Directive could populate its public properties instead and use those in HTML along with CSS. It's always wiser to not manipulate DOM elements from within AngularJS code if possible.
function StatusController($element) {
this.$element = $element;
this.status = 0;
this.text = '';
this.name = '';
}
StatusController.mapper = [
{ text: ' ', name: '' },
{ text: 'Correct!', name: 'correct' },
{ text: 'Error!', name: '#error' },
{ text: 'Waiting...', name: 'pending' },
}];
StatusController.prototype.setStatus = function(status) {
var statusObj = StatusController.mapper[+status];
this.text = statusObj.text;
this.name = statusObj.name;
}
StatusController.prototype.$onInit = function() {
// this.status is now populated by the supplied attribute value
this.setStatus(this.status);
}
StatusController.prototype.$onChanges = function(changes) {
if (changes.status && !changes.status.isFirstChange()) {
this.setStatus(this.status);
}
}
var StatusDirective = {
restrict: 'AE',
controller: StatusController,
controllerAs: 'colorStatus',
scope: true,
bindToController: {
status: '#'
}
};
angular.module('someModule')
.directive('colorStatus', function() { return StatusDirective; });
And then in your template write use it this way:
<color-status status="{{vm.status}}" ng-class="colorStatus.name" ng-bind="colorStatus.text"></color-status>
This will give you a lot more flexibility in templates. Instead of setting text in the controller you could get away with just class name and use pseudo classes to add text to the element however you please to do, so each instance of your <color-status> directive could then display differently for the same status value.

sapui5/openui5 TreeTable with custom control column throwing duplicate id error when expanding a node

I have a sap.ui.table.TreeTable with several columns, one of which contains a custom control defined as a template for the column.
The TreeTable renders correctly, and the column with the custom control also renders and functions correctly ... that is until I expand an unexpanded node.
When I expand a node the Renderer throws an error
Error: adding element with duplicate id '__link0-col1-row0',
which is the column associated with the custom control.
I can workaround the problem by adding attachToggleOpenState event handler which destroys this columns template and adds it again. Expensive but it works!
this.oTable = new sap.ui.table.TreeTable("myTable", {
columns : [ new sap.ui.table.Column( {
label : "Query",
template : new sparqlish.control.queryClause({
clausePath : {
path : "viewModel>path"
}
}),
visible : true,
width : "500px"
}), ],
}).attachToggleOpenState(function(oEvent) {
// TODO workaround as expanding Tree causes duplicate id
var queryColumn = oEvent.getSource().getColumns()[0];
queryColumn.destroyTemplate();
queryColumn.setTemplate(new sparqlish.control.queryClause({
clausePath : {
path : "viewModel>path"
}
}))
});
sparqlish.control.queryClause is the custom control.
I am using openui5 version 1.28.15
Any other, less expensive, suggestions?
The custom control takes this form, however this control invokes other controls and so on.
jQuery.sap.require("sparqlish.control.conceptClause");
jQuery.sap.require("sparqlish.control.propertyClause");
jQuery.sap.require("sparqlish.control.conjunctionPropertyClause");
sap.ui.core.Control.extend("sparqlish.control.queryClause", {
metadata : {
properties : {
clausePath : {
type : "string"
}
},
events : {},
aggregations : {
_conceptClause : {
type : "sparqlish.control.conceptClause",
multiple : false
},
_propertyClause : {
type : "sparqlish.control.propertyClause",
multiple : false
},
_conjunctionPropertyClause : {
type : "sparqlish.control.conjunctionPropertyClause",
multiple : false
}
}
},
init : function() {
var self = this;
self.setAggregation("_conceptClause", new sparqlish.control.conceptClause());
self.setAggregation("_propertyClause", new sparqlish.control.propertyClause());
self.setAggregation("_conjunctionPropertyClause", new sparqlish.control.conjunctionPropertyClause());
},
renderer : function(oRm, oControl) {
if (oControl.getClausePath() != undefined) {
// TODO no point if path not yet defined
oRm.write("<div ");
oRm.writeControlData(oControl);
oRm.writeClasses();
oRm.write(">");
var currentModel = oControl.getModel("queryModel");
// Set binding context, rather than just the binding path etc, as this seems essential for satisfactory binding of
// aggregations
oControl.setBindingContext(new sap.ui.model.Context(oQueryModel, oControl.getClausePath()), "queryModel")
var currentCtx = oControl.getBindingContext("queryModel");
var currentContext = oControl.getModel("queryModel").getProperty("", currentCtx);
if (currentContext != undefined) {
var sClass = currentContext._class;
if (sClass == "Query") {
oControl.setAggregation("_conceptClause", new sparqlish.control.conceptClause().setBindingContext(oControl.getBindingContext("queryModel")));
oRm.renderControl(oControl.getAggregation("_conceptClause"));
} else if (sClass == "Clause") {
oControl.setAggregation("_propertyClause", new sparqlish.control.propertyClause().setBindingContext(oControl.getBindingContext("queryModel")));
oRm.renderControl(oControl.getAggregation("_propertyClause"));
} else if (sClass == "ConjunctionClause") {
oControl.setAggregation("_conjunctionPropertyClause", new sparqlish.control.conjunctionPropertyClause().setBindingContext(oControl
.getBindingContext("queryModel")));
oRm.renderControl(oControl.getAggregation("_conjunctionPropertyClause"));
} else {
jQuery.sap.log.fatal("Incorrect class of provided query clause");
}
}
oRm.write("</div>");
} else {
jQuery.sap.log.fatal("clausePath not defined");
}
}
});
An example of one of the dependent controls is below.
jQuery.sap.require("sparqlish.control.conceptMenu");
jQuery.sap.require("sparqlish.control.conceptFilters");
jQuery.sap.require("sparqlish.control.addClause");
sap.ui.core.Control.extend("sparqlish.control.conceptClause", {
metadata : {
properties : {
concept : "object"
},
events : {},
aggregations : {
_concept : {
type : "sparqlish.control.conceptMenu",
multiple : false
},
_conceptFilters : {
type : "sparqlish.control.conceptFilters",
multiple : false
},
_addClause : {
type : "sparqlish.control.addClause",
multiple : false
}
}
},
init : function() {
var self = this;
var conceptSelect =function(oEvent){
var currentModel = self.getModel("queryModel");
var currentContext = self.getBindingContext("queryModel");
var currentModelData = currentModel.getProperty("", currentContext);
currentModelData.conceptFilters = [];
currentModelData.clauses = {};
var sConcept = oEvent.getParameter("concept");
var oMetaModel = self.getModel("metaModel");
var oConcept=oMetaModel.getODataEntitySet(sConcept);
self.setConcept( oConcept);
self.oEntityTypeModel = new sap.ui.model.json.JSONModel();
self.oEntityTypeModel.setData(oMetaModel.getODataEntityType(oConcept.entityType));
self.setModel(self.oEntityTypeModel, "entityTypeModel");
self.getAggregation("_conceptFilters").setModel(self.oEntityTypeModel, "entityTypeModel");
self.getAggregation("_conceptFilters").getAggregation("_extendFilter").setVisible(true);
currentModel.refresh();
self.rerender();
};
self.setAggregation("_concept", new sparqlish.control.conceptMenu({
selected : function(oEvent) {
self.getAggregation("_conceptFilters").getAggregation("_extendFilter").setVisible(true);
},
changed : conceptSelect
}).bindElement("queryModel>"));
self.setAggregation("_conceptFilters", new sparqlish.control.conceptFilters().bindElement("queryModel>"));
self.setAggregation("_addClause", new sparqlish.control.addClause({
pressed : function(oEvent) {
alert("concept");
}
}).bindElement("queryModel>"));
},
renderer : function(oRm, oControl) {
oRm.addClass("conceptClause");
oRm.write("<div ");
oRm.writeControlData(oControl);
oRm.writeClasses();
oRm.write(">");
oRm.write(sap.ui.getCore().getModel("i18nModel").getProperty("conceptClauseFind"));
oRm.renderControl(oControl.getAggregation("_concept"));
oRm.renderControl(oControl.getAggregation("_conceptFilters"));
oRm.write(" ");
oRm.renderControl(oControl.getAggregation("_addClause"));
oRm.write("</div>");
}
});

How do i test my custom angular schema form field

I've just started developing with Angular schema form and I'm struggling to write any tests for my custom field directive.
I've tried compiling the schema form html tag which runs through my directives config testing it's display conditions against the data in the schema. However it never seems to run my controller and I can't get a reference to the directives HTML elements. Can someone give me some guidance on how to get a reference to the directive? Below is what I have so far:
angular.module('schemaForm').config(['schemaFormProvider',
'schemaFormDecoratorsProvider', 'sfPathProvider',
function(schemaFormProvider, schemaFormDecoratorsProvider, sfPathProvider) {
var date = function (name, schema, options) {
if (schema.type === 'string' && schema.format == 'date') {
var f = schemaFormProvider.stdFormObj(name, schema, options);
f.key = options.path;
f.type = 'date';
options.lookup[sfPathProvider.stringify(options.path)] = f;
return f;
}
};
schemaFormProvider.defaults.string.unshift(date);
schemaFormDecoratorsProvider.addMapping('bootstrapDecorator', 'date',
'app/modules/json_schema_form/schema_form_date_picker/schema_form_date_picker.html');
}]);
var dateControllerFunction = function($scope) {
$scope.isCalendarOpen = false;
$scope.showCalendar = function () {
$scope.isCalendarOpen = true;
};
$scope.calendarSave = function (date) {
var leaf_model = $scope.ngModel[$scope.ngModel.length - 1];
var formattedDate = $scope.filter('date')(date, 'yyyy-MM-dd');
leaf_model.$setViewValue(formattedDate);
$scope.isCalendarOpen = false;
};
};
angular.module('schemaForm').directive('schemaFormDatePickerDirective', ['$filter', function($filter) {
return {
require: ['ngModel'],
restrict: 'A',
scope: false,
controller : ['$scope', dateControllerFunction],
link: function(scope, iElement, iAttrs, ngModelCtrl) {
scope.ngModel = ngModelCtrl;
scope.filter = $filter
}
};
}]);
<div ng-class="{'has-error': hasError()}">
<div ng-model="$$value$$" schema-form-date-picker-directive>
<md-input-container>
<!-- showTitle function is implemented by ASF -->
<label ng-show="showTitle()">{{form.title}}</label>
<input name="dateTimePicker" ng-model="$$value$$" ng-focus="showCalendar()" ng-disabled="isCalendarOpen">
</md-input-container>
<time-date-picker ng-model="catalogue.effectiveFrom" ng-if="isCalendarOpen" on-save="calendarSave($value)" display-mode="date"></time-date-picker>
</div>
<!-- hasError() defined by ASF -->
<span class="help-block" sf-message="form.description"></span>
</div>
And the spec:
'use strict'
describe('SchemaFormDatePicker', function() {
var $compile = undefined;
var $rootScope = undefined;
var $scope = undefined
var scope = undefined
var $httpBackend = undefined;
var elem = undefined;
var html = '<form sf-schema="schema" sf-form="form" sf-model="schemaModel"></form>';
var $templateCache = undefined;
var directive = undefined;
beforeEach(function(){
module('app');
});
beforeEach(inject(function(_$compile_, _$rootScope_, _$templateCache_, _$httpBackend_) {
$compile = _$compile_
$rootScope = _$rootScope_
$httpBackend = _$httpBackend_
$templateCache = _$templateCache_
}));
beforeEach(function(){
//Absorb call for locale
$httpBackend.expectGET('assets/locale/en_gb.json').respond(200, {});
$templateCache.put('app/modules/json_schema_form/schema_form_date_picker/schema_form_date_picker.html', '');
$scope = $rootScope.$new()
$scope.schema = {
type: 'object',
properties: {
party: {
title: 'party',
type: 'string',
format: 'date'
}}};
$scope.form = [{key: 'party'}];
$scope.schemaModel = {};
});
describe("showCalendar", function () {
beforeEach(function(){
elem = $compile(html)($scope);
$scope.$digest();
$httpBackend.flush();
scope = elem.isolateScope();
});
it('should set isCalendarOpen to true', function(){
var result = elem.find('time-date-picker');
console.log("RESULT: "+result);
));
});
});
});
If you look at the below example taken from the project itself you can see that when it uses $compile it uses angular.element() first when setting tmpl.
Also, the supplied test module name is 'app' while the code sample has the module name 'schemaForm'. The examples in the 1.0.0 version of Angular Schema Form repo all use sinon and chai, I'm not sure what changes you would need to make if you do not use those.
Note: runSync(scope, tmpl); is a new addition for 1.0.0 given it is now run through async functions to process $ref includes.
/* eslint-disable quotes, no-var */
/* disabling quotes makes it easier to copy tests into the example app */
chai.should();
var runSync = function(scope, tmpl) {
var directiveScope = tmpl.isolateScope();
sinon.stub(directiveScope, 'resolveReferences', function(schema, form) {
directiveScope.render(schema, form);
});
scope.$apply();
};
describe('sf-array.directive.js', function() {
var exampleSchema;
var tmpl;
beforeEach(module('schemaForm'));
beforeEach(
module(function($sceProvider) {
$sceProvider.enabled(false);
exampleSchema = {
"type": "object",
"properties": {
"names": {
"type": "array",
"description": "foobar",
"items": {
"type": "object",
"properties": {
"name": {
"title": "Name",
"type": "string",
"default": 6,
},
},
},
},
},
};
})
);
it('should not throw needless errors on validate [ノಠ益ಠ]ノ彡┻━┻', function(done) {
tmpl = angular.element(
'<form name="testform" sf-schema="schema" sf-form="form" sf-model="model" json="{{model | json}}"></form>'
);
inject(function($compile, $rootScope) {
var scope = $rootScope.$new();
scope.model = {};
scope.schema = exampleSchema;
scope.form = [ "*" ];
$compile(tmpl)(scope);
runSync(scope, tmpl);
tmpl.find('div.help-block').text().should.equal('foobar');
var add = tmpl.find('button').eq(1);
add.click();
$rootScope.$apply();
setTimeout(function() {
var errors = tmpl.find('.help-block');
errors.text().should.equal('foobar');
done();
}, 0);
});
});
});

BackboneJS - fetching collections from model

I have a JSON file which basically looks like this:
[
{
"First" : [...]
},
{
"Second" : [...]
},
{
"Third" : [...]
},
]
In my router i have:
this.totalCollection = new TotalCollection();
this.totalView = new TotalView({el:'#subContent', collection:this.totalCollection});
this.totalCollection.fetch({success: function(collection) {
self.totalView.collection=collection;
self.totalView.render();
}});
Now i have my Backbone Model:
define([
"jquery",
"backbone"
],
function($, Backbone) {
var TotalModel = Backbone.Model.extend({
url: "/TotalCollection.json",
initialize: function( opts ){
this.first = new First();
this.second = new Second();
this.third = new Third();
this.on( "change", this.fetchCollections, this );
},
fetchCollections: function(){
this.first.reset( this.get( "First" ) );
this.second.reset( this.get( "Second" ) );
this.third.reset( this.get( "Third" ) );
}
});
return TotalModel;
});
and my in my Backbone View i try to render the collection(s):
render: function() {
$(this.el).html(this.template(this.collection.toJSON()));
return this;
}
But I get the Error "First is not defined" - whats the issue here?
Have you actually defined a variable 'First', 'Second' and 'Third'? Based on what you're showing here, there is nothing with that name. One would expect you to have a couple lines like..
var First = Backbone.Collection.extend({});
var Second = Backbone.Collection.extend({});
var Third = Backbone.Collection.extend({});
However you haven't provided anything like that, so my first assumption is that you just haven't defined it.
Per comments, this may be more what you need:
render: function() {
$(this.el).html(this.template({collection: this.collection.toJSON())});
return this;
}
Then..
{{#each collection}}
{{#each First}}
/*---*/
{{/each}}
{{/each}}