How to describe angular controller methods in documentation? - angularjs

I can't describe controllers methods. How i can do this?
/**
* #ngdoc controller
* #name works.controller:worksCtrl
* #requires $http
* #requires $element
* #function
*
* #description
* Description for works controller. All methods will be writen later
*/
var worksCtrl = function ($http, $element) {
var ctrl = this;
//how it do there? this not work
/**
* #name initializeGrid
* #function
* #description
* Description for initializeGrid
*/
ctrl.initializeGrid = function (a) {
//...
}
ctrl.getTemplate = function (workIndex) {
//...
}
//...
};
I am using ngdoc for auto generate documentation. But i can't understand what i do wrong.

I have never used ngdoc but looking to angular code itself, it looks that you need to add a #ngdoc method tag to the documentation for internal functions. For example, inside $locationProvider:
/**
* #ngdoc method
* #name $locationProvider#hashPrefix
* #description
* #param {string=} prefix Prefix for hash part (containing path and search)
* #returns {*} current value if used as getter or itself (chaining) if used as setter
*/
this.hashPrefix = function(prefix) {
if (isDefined(prefix)) {
hashPrefix = prefix;
return this;
} else {
return hashPrefix;
}
};
I hope it helps.

/**
* #ngdoc function
* #name initializeGrid
* #methodOf works.controller:worksCtrl
* #description This method initialize auto grid system for works
* #private
*/
ctrl.initializeGrid = function () {
...
}
That is what i need.)

Related

Forge Autodesk load multiple models in Viewer with ReactJS

We are using Forge Autodesk Viewer to load Forge Models.
We are using the framework ReactJS for our application and we have the function bellow to load one model at a viewer :
function loadModel(viewer, documentId) {
function onDocumentLoadSuccess(viewerDocument) {
// viewerDocument is an instance of Autodesk.Viewing.Document
const bubbleNode = viewerDocument.getRoot();
let defaultModel;
if (props.phaseName) {
defaultModel = bubbleNode.getMasterView(props.phaseName);
} else if (props.guid) {
defaultModel = bubbleNode.findByGuid(props.guid);
} else if (props.viewableID) {
const results = bubbleNode.search({viewableID: props.viewableID});
if (results && results.length) {
defaultModel = results[0];
}
} else if (props.geomIndex) {
const geoms = bubbleNode.search({type: "geometry"});
if (geoms.length) {
if (props.geomIndex < 0 || props.geomIndex >= geoms.length) {
console.warn("GeometryIndex Error: Invalid geometry index.");
}
const index = Math.min(Math.max(props.geomIndex, 0), geoms.length - 1); // Ensure index is valid.
defaultModel = geoms[index];
}
}
if (!defaultModel) defaultModel = bubbleNode.getDefaultGeometry(true);
const skipHiddenFragments = props.skipHiddenFragments || false;
viewer.loadDocumentNode(viewerDocument, defaultModel, {
keepCurrentModels: true,
skipHiddenFragments: skipHiddenFragments,
});
viewer.prefs.set("ghosting", false);
viewer.loadExtension("Autodesk.Viewing.MarkupsCore")
viewer.loadExtension("Autodesk.Viewing.MarkupsGui")
}
function onDocumentLoadFailure() {
console.error("Failed fetching Forge manifest");
}
if (documentId) {
Autodesk.Viewing.Document.load(
documentId,
onDocumentLoadSuccess,
onDocumentLoadFailure
);
} else {
props.eventBus.dispatchEvent({type: "VIEWER_READY", data: {viewer}});
}
}
We actually want to know how we could load multiple models using ReactJS.
Thank you for your response.
There is no difference in making the code support multiple models between w/ and w/o react.js. You can find lots of examples by searching https://stackoverflow.com/search?q=%5Bautodesk-forge%5D+multiple+models
Anyway...
Here is one for loading multiple models, but you must change the props passed to the Viewer component, and event calls outside the Viewer component accordingly.
//process each promise
//refer to http://jsfiddle.net/jfriend00/h3zaw8u8/
const promisesInSequence = (tasks, callback) => {
const results = [];
return tasks.reduce((p, item) => {
return p.then(() => {
return callback(item).then((data) => {
results.push(data);
return results;
});
});
}, Promise.resolve());
};
const AGGREGATE_GEOMETRY_LOADED_EVENT = 'aggregateGeometryLoaded';
/**
* #component
* Component for rendering LMV
* #param {Object} props
* #param {("AutodeskProduction"|"AutodeskStaging"|"MD20ProdUS"|"MD20ProdEU")} [props.env] Forge API environment
* #param {Function} props.getToken Returns the Forge API token to access LMV
* #param {"derivativeV2"|"derivativeV2_EU"|"modelDerivativeV2"|"fluent"|"D3S"|"D3S_EU"} [props.api] Default = "derivativeV2". Please refer to LMV documentation for more information.
* #param {Object[]} [props.docUrns] Model data to be loaded
* #param {string} [modelURNs[].urn] Document URN of the model to be loaded
* #param {Object} [modelURNs[].options] model options used in loading the specfic model
* #param {string} [modelURNs[].options.phaseName] phaseName of view to load in scene.
* #param {string} [modelURNs[].options.guid] guid of BubbleNode to load in scene.
* #param {string} [modelURNs[].options.viewableID] viewableID of BubbleNode to load in scene.
* #param {number} [modelURNs[].options.geomIndex] Index of geometry to load in scene.
* #param {Boolean} [modelURNs[].options.skipHiddenFragments] Boolean to specify if hidden fragments should be skipped (Default: false,
* Hidden fragments are required for heatmaps in rooms, only applicable to SVF2)
* #param {OnModelLoaded} [props.onModelLoaded] Callback function invoked when the model has loaded
* #param {OnViewerInitialized} [props.onViewerInitialized] Callback function invoked when LMV has been intialized
* #param {string[]} [props.extensions] List of extension ids forwarded to viewer config to load.
* #param {Object.<string, Object>} [props.disabledExtensions] Default extensions to prevent being loaded.
* #param {Object} [props.viewerOptions] Options object to forward to Autodesk.Viewing.Initializer
* #memberof Autodesk.DataVisualization.UI
* #alias Autodesk.DataVisualization.UI.Viewer
*/
export default function Viewer(props) {
const viewerRef = useRef(null);
const viewerDomRef = useRef(null);
function onModelLoaded(event) {
const viewer = viewerRef.current;
// const av = Autodesk.Viewing;
// viewer.removeEventListener(av.GEOMETRY_LOADED_EVENT, onModelLoaded);
viewer.removeEventListener(AGGREGATE_GEOMETRY_LOADED_EVENT, onModelLoaded);
if (props.onModelLoaded) {
props.onModelLoaded(viewer, event);
}
}
/**
* Initializes LMV.
*
* #memberof Autodesk.DataVisualization.UI
* #alias Autodesk.DataVisualization.UI.Viewer#initializeViewer
* #private
*/
function initializeViewer() {
let viewerOptions = props.viewerOptions;
var options = Object.assign({}, viewerOptions, {
env: props.env,
api: props.api || "derivativeV2", // for models uploaded to EMEA change this option to 'derivativeV2_EU'
getAccessToken: async function (onTokenReady) {
let token = await props.getToken();
var timeInSeconds = 3600; // Use value provided by Forge Authentication (OAuth) API
onTokenReady(token, timeInSeconds);
},
});
Autodesk.Viewing.Initializer(options, async function () {
const extensionsToLoad = props.extensions;
const extensionsWithConfig = [];
const extensionsWithoutConfig = [];
for (let key in extensionsToLoad) {
const config = extensionsToLoad[key];
if (Object.keys(config).length === 0) {
extensionsWithoutConfig.push(key);
} else {
extensionsWithConfig.push(key);
}
}
const viewer = new Autodesk.Viewing.GuiViewer3D(viewerDomRef.current, {
extensions: extensionsWithoutConfig,
disabledExtensions: props.disabledExtensions || {},
});
extensionsWithConfig.forEach((ext) => {
viewer.loadExtension(ext, extensionsToLoad[ext]);
});
viewerRef.current = viewer;
const startedCode = viewer.start(undefined, undefined, undefined, undefined, options);
if (startedCode > 0) {
console.error("Failed to create a Viewer: WebGL not supported.");
return;
}
// loadModel(viewer, props.docUrn);
await loadModels(viewer, props.docUrns);
if (props.onViewerInitialized) {
props.onViewerInitialized(viewer);
}
});
}
/**
* Loads the specified models into the viewer.
*
* #param {Object} viewer Initialized LMV object
* #param {string} documentId Document URN of the model to be loaded
* #param {Object} options
* #param {string} [options.phaseName] phaseName of view to load in scene.
* #param {string} [options.guid] guid of BubbleNode to load in scene.
* #param {string} [options.viewableID] viewableID of BubbleNode to load in scene.
* #param {number} [options.geomIndex] Index of geometry to load in scene.
* #param {Boolean} [options.skipHiddenFragments] Boolean to specify if hidden fragments should be skipped (Default: false,
* Hidden fragments are required for heatmaps in rooms, only applicable to SVF2)
* #memberof Autodesk.DataVisualization.UI
* #alias Autodesk.DataVisualization.UI.Viewer#loadModelAsync
* #private
*/
async function loadModelAsync(viewer, documentId, options) {
return new Promise((resolve, reject) => {
async function onDocumentLoadSuccess(viewerDocument) {
// viewerDocument is an instance of Autodesk.Viewing.Document
const bubbleNode = viewerDocument.getRoot();
let defaultModel;
if (options.phaseName) {
defaultModel = bubbleNode.getMasterView(options.phaseName);
} else if (options.guid) {
defaultModel = bubbleNode.findByGuid(options.guid);
} else if (options.viewableID) {
const results = bubbleNode.search({ viewableID: options.viewableID });
if (results && results.length) {
defaultModel = results[0];
}
} else if (options.geomIndex) {
const geoms = bubbleNode.search({ type: "geometry" });
if (geoms.length) {
if (options.geomIndex < 0 || options.geomIndex >= geoms.length) {
console.warn("GeometryIndex Error: Invalid geometry index.");
}
const index = Math.min(Math.max(options.geomIndex, 0), geoms.length - 1); // Ensure index is valid.
defaultModel = geoms[index];
}
}
if (!defaultModel) defaultModel = bubbleNode.getDefaultGeometry(true);
const skipHiddenFragments = options.skipHiddenFragments || false;
let model = await viewer.loadDocumentNode(viewerDocument, defaultModel, {
keepCurrentModels: true,
skipHiddenFragments: skipHiddenFragments,
});
// modify the preference settings, since ghosting is causing heavy z-fighting with the room geometry
// it would be good we turn it off
if (!viewer.model)
viewer.prefs.set("ghosting", false);
await viewer.waitForLoadDone();
resolve(model);
}
function onDocumentLoadFailure() {
console.error("Failed fetching Forge manifest");
}
if (documentId) {
Autodesk.Viewing.Document.load(
documentId,
onDocumentLoadSuccess,
onDocumentLoadFailure
);
} else {
props.eventBus.dispatchEvent({ type: "VIEWER_READY", data: { viewer } });
}
});
}
/**
* Loads the specified models into the viewer.
*
* #param {Object} viewer Initialized LMV object
* #param {Object[]} modelURNs Model data to be loaded
* #param {string} [modelURNs[].urn] Document URN of the model to be loaded
* #param {Object} [modelURNs[].options] model options used in loading the specfic model
* #param {string} [modelURNs[].options.phaseName] phaseName of view to load in scene.
* #param {string} [modelURNs[].options.guid] guid of BubbleNode to load in scene.
* #param {string} [modelURNs[].options.viewableID] viewableID of BubbleNode to load in scene.
* #param {number} [modelURNs[].options.geomIndex] Index of geometry to load in scene.
* #param {Boolean} [modelURNs[].options.skipHiddenFragments] Boolean to specify if hidden fragments should be skipped (Default: false,
* Hidden fragments are required for heatmaps in rooms, only applicable to SVF2)
* #memberof Autodesk.DataVisualization.UI
* #alias Autodesk.DataVisualization.UI.Viewer#loadModelAsync
* #private
*/
async function loadModelsAsync(viewer, modelURNs) {
// const av = Autodesk.Viewing;
// viewer.addEventListener(av.GEOMETRY_LOADED_EVENT, onModelLoaded, { once: true });
viewer.addEventListener(AGGREGATE_GEOMETRY_LOADED_EVENT, onModelLoaded, { once: true });
const results = await promisesInSequence(modelURNs, (d) => loadModelAsync(d.urn, d.options));
viewer.fireEvent({
type: AGGREGATE_GEOMETRY_LOADED_EVENT,
models: results
});
}
useEffect(() => {
initializeViewer();
return function cleanUp() {
if (viewerRef.current) {
viewerRef.current.finish();
}
};
}, []);
return <div id="forgeViewer" ref={viewerDomRef}></div>;
}
Viewer.displayName = "Viewer";

Multiple urls Tampermonkey

For the first time in my live i make a Tampermonkey script.
I want add multiple url's to a page: https://voertuig.net/zoek/merk/volkswagen/datum-tenaamstelling/30-05-2018?pagina=3
But i don't know how to get all different licenseplates for the custom urls. He now take the first licenseplate div.
url must be: https://finnik.nl/kenteken/XX-XX-XX/gratis
As you can see, all urls have the same licenseplate
// ==UserScript==
// #name New Userscript
// #namespace http://tampermonkey.net/
// #version 0.1
// #description try to take over the world!
// #author You
// #match https://voertuig.net/zoek/merk/volkswagen/datum-tenaamstelling/30-05-2018?pagina=3
// #icon https://www.google.com/s2/favicons?sz=64&domain=voertuig.net
// #grant none
// ==/UserScript==
(function() {
"use strict"
// Your code here...
var adduserinfo = $("div.voertuig > a");
var Username = $("div.kenteken");
var words = Username.html().split(' ');
adduserinfo.each (function () {
var jThis = $(this);
jThis.parent ().append ('Finnik ' + words + ' ');
} );
})()
Something like this should do it.
Note that you need to modify the //#match line or it will only work for the given page (i.e. page 3 -- just end the line with a * at the point where the match can be wild-carded.
// ==UserScript==
// #name voertuig.net SO EXAMPLE
// #namespace http://tampermonkey.net/
// #match https://voertuig.net/zoek/merk/volkswagen/datum-tenaamstelling/30-05-2018?pagina=3
// #grant none
// ==/UserScript==
(function() {
'use strict';
//Look closely - this is not jQuery
const $ = document.querySelector.bind(document);
const $$ = document.querySelectorAll.bind(document);
$('body').insertAdjacentHTML('beforeend', init_css() );
$$('.voertuig').forEach((div) => {
const lp = div.getAttribute('data-kentekenplaat');
div.insertAdjacentHTML('beforeend', `<a target='_blank' class='finTagA' href='https://finnik.nl/kenteken/${lp}/gratis#historie'>Finnik ${lp}</a>`);
});
})();
function init_css(){
return `
<style id="jdInitCss">
.finTagA{width:100%;padding:5px;display:flex;justify-content:center;background:white;border-top:1px solid #ccccccCC;}
.finTagA:hover{color:blue;}
</style>
`;
}

extjs keyMap dynamic keys

Hi I have this implemented and I want the keys to be dynamic based on function return. Lets say I have function in controller called "returnKeyFront" which returns "F" key. I then want to take it and apply it for first element F. So instead of writing F there would be that function which returns that key. No idea how to implement that. I am using keyMap from extJS with coffescript. THX
keyMap:
F:
handler: "onCamerasHotkeyFront"
B:
handler: "onCamerasHotkeyBack"
You can add a helper function.
Something like this (I did not test it, but should be a pretty good hint.
Ext.define('MyApp.KeyChain', {
singleton: true,
/**
* holds the current keymap appwide
* #private
*/
_keymap: null,
/**
* initialize with a base defintion
*/
initKeyChain() {
const keyChain = this.definition;
this.updateKeyMap();
},
/**
* base definition. You can also set it to null
* if you want to start blank
*
* #private
*/
definition: {
CameraFront: {
key: 'f',
alt: true,
fn: 'onCamerasHotkeyFront',
scope: this // typically you want to either set the fn and/or scope
},
CameraBack: {
key: 'f',
alt: true,
fn: 'onCamerasHotkeyBack',
scope: this // typically you want to either set the fn and/or scope
},
},
/**
* changeKey
* to add or change a definition. You can also init via changeKey
* see this.defintion for examples on how keyDefinition should look like
*
* #arg {string} type e.g. 'CameraFront'
* #arg {object} keyDefinition
* #arg {string} keyDefinition.key
* #arg {boolean} [keyDefinition.alt]
* #arg {function|string} keyDefinition.fn
* #arg {scope} [keyDefinition.scope]
*/
changeKey(type, keyDefinition) {
if(!keyDefinition.scope) keyDefinition.scope = this;
if(typeof keyDefinition.fn === 'string') keyDefinition.fn = scope[definition.fn];
MyApp._definition[type] = keyDefinition;
this.updateKeyMap();
},
/**
* updateKeyMap
*
* #private
*/
updateKeyMap() {
if(this._keymap) this._keymap.destroy();
const newBinding = this.createBindings();
this._keymap = new Ext.util.KeyMap({
target: Ext.getBody(),
binding: newBindings
});
},
/**
* createBinding
*
* #private
*/
createBinding() {
const def = this.definition;
let bindings = [];
Ext.Object.each(def, function(key, value) {
bindings.push({value});
});
return bindings;
}
});

Protractor endurance test memory leak

I made a very small test using protractor to open my application (build on angular) and start clicking at random links in a random order. We use this to let the web application run for several days and check for memory leaks.
After some time the memory of the browser (chrome) is huge and when i create a memory dump i find 33k strings all containing the same piece of code (shown below).
I tried searching where this was coming from and it that led me to Webdriver and the code looks like something injected by webdriver to trigger the mouse click ability but from there i'm lost.
Am i doing something wrong or should i clean the memory somewhere in my test?
(When i run the application normally for multiple hours and opening all pages there is no occurrence at all of this string)
"(function() { // Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* Enum for WebDriver status codes.
* #enum {number}
*/
var StatusCode = { STALE_ELEMENT_REFERENCE: 10, JAVA_SCRIPT_ERROR: 17, };
/**
* Enum for node types.
* #enum {number}
*/
var NodeType = { ELEMENT: 1, DOCUMENT: 9, };
/**
* Dictionary key to use for holding an element ID.
* #const
* #type {string}
*/
var ELEMENT_KEY = 'ELEMENT';
/**
* True if using W3C Element references.
* #const
* #type {boolean}
*/
var w3cEnabled = false;
/**
* True if shadow dom is enabled.
* #const
* #type {boolean}
*/
var SHADOW_DOM_ENABLED = typeof ShadowRoot === 'function';
/**
* Generates a unique ID to identify an element.
* #void
* #return {string} Randomly generated ID.
*/
function generateUUID() {
var array = new Uint8Array(16);
window.crypto.getRandomValues(array);
array[6] = 0x40 | (array[6] & 0x0f);"
.... (continues for a lot more lines)
The test i try to run is:
import {browser, by, element} from 'protractor';
describe('Webclient', () => {
const MAX_SAFE_TIMEOUT = Math.pow(2, 31) - 1;
beforeAll(async function () {
await browser.get('/');
});
it('Endurance test', (done) => {
runTest();
}, MAX_SAFE_TIMEOUT);
});
let counter = 0;
async function runTest() {
try {
const elements = await element.all(by.css('.header a, .sidebar-left a, .footer a, .content a'));
let random = Math.floor(Math.random() * elements.length);
while (!(await elements[random].isDisplayed())) {
random = Math.floor(Math.random() * elements.length);
}
await elements[random].click();
console.log('click counter:', counter++, await browser.getCurrentUrl());
} catch (e) {
// Do nothing, just continue
}
// Break the chain so that we don't get one big recursive function
setTimeout(() => {
runTest();
}, 0);
}

Angular input value not changed. i.e oldValue == newValue

Angular adds 'ng-dirty' class to input field when the input field is modified irrespective of whether newValue==oldValue or not.
i.e. if the input value is 'manohar', and i delete 'r' and add it back. Even though the value is 'manohar'( original value), the 'ng-dirty' class still exists.
Is there a way to check whether the input value is modified or not(newValue != oldValue)?
Thanks in advance.
If you want to handle behavior depending on the actual state of data in a form (edit vs original) you need to keep track of that yourself. Like Cétia sais, dirty/prestine is just to keep track of interaction and validation, not state.
It might be overkill but I usually do something like this:
.controller('MyController', function ($scope, Restangular) {
/**
* #ngdoc property
* #name _dataWatcher
* #private
* #propertyOf MyController
*
* #description
* Holds the watch stop-method, if the "data-has-changed" watcher is active.
*/
var _dataWatcher;
/**
* #ngdoc property
* #name data
* #propertyOf MyController
*
* #description Stores "original" and "edit" copy of the data beeing handled by the form.
*/
$scope.data = {};
/**
* #ngdoc property
* #name data
* #propertyOf MyController
*
* #description Stores state for the view.
*/
$scope.state = {
loading: true,
saving: false,
hasChanged: false,
hasBeenSaved: false
};
/**
* #ngdoc method
* #name _setup
* #private
* #methodOf MyController
*
* #description Run on controller init.
*/
function _setup() {
$scope.reset();
$scope.state.loading = false;
};
/**
* #ngdoc method
* #name _startWatcher
* #private
* #methodOf MyController
*
* #description
* Setup watcher for changes in data.test. Stop method will be saved to
* private property _dataWatcher.
*/
function _startWatcher() {
_dataWatcher = $scope.$watch('data.edit', function (newValue, oldValue) {
if (newValue !== oldValue) {
if (JSON.stringify(newValue) === JSON.stringify($scope.data.original)) {
$scope.state.hasChanged = false;
// If you want the form to act like its untouched if no changes are found:
$scope.nameOfYourForm.$setPristine();
} else {
$scope.state.hasChanged = true;
}
}
}, true);
}
/**
* #ngdoc method
* #name _stopWatcher
* #private
* #methodOf MyController
*
* #description
* Stop watching data.test for changes. This is needed for "reset" and
* syncing data without triggering data-has-changed loop.
*/
function _stopWatcher() {
if (!_dataWatcher) {
return;
}
_dataWatcher();
}
/**
* #ngdoc method
* #name save
* #methodOf MyController
*
* #description
* Save data.edit changes to database, Restangular is used in example but you get the idea.
*/
$scope.save = function () {
var newData;
// Set states
$scope.state.error = false;
$scope.state.saving = true;
// Update with new data
// $scope.entity is a restangularized object from some server
newData = Restangular.copy($scope.entity);
newData.name = $scope.data.edit.name;
newData.status = $scope.data.edit.status;
// Save to db
newData.put().then(function (entity) {
_stopWatcher();
// Db returns saved data, update restangular object, to update
// data in view.
$scope.entity.name = entity.name;
$scope.entity.status = entity.status;
$scope.reset();
$scope.state.hasBeenSaved = true;
}, function (error) {
$scope.state.hasChanged = true;
$scope.state.error = error.data;
}).finally(function () {
$scope.state.saving = false;
});
};
/**
* #ngdoc method
* #name reset
* #methodOf MyController
*
* #description
* Resets the data object to current state of the original source
* object. Any unsaved changes in data.edit will be overwritten.
*/
$scope.reset = function () {
// Stop watcher, if initialized
_stopWatcher();
// Save original data (for comparison)
$scope.data.original = {
status: $scope.feed.status,
name: $scope.feed.name
};
// Create new copy of editable data
$scope.data.edit = angular.copy($scope.data.original);
// Make sure state signals "not changed"
$scope.state.hasChanged = false;
// Force form to be prestine
$scope.nameOfYourForm.$setPristine();
// Start watching changes
_startWatcher();
};
_setup();
});
ngDirty/ngPristine goal is to tell you if the user has modified the form regardless of the values. So in your case the behavior is totally normal.
If you want to modify this behavior, you can use the model.$setPristine() function. More details here : https://code.angularjs.org/1.2.26/docs/api/ng/type/ngModel.NgModelController
You might be interested in this project, called:
angular-forms-enhanced
It modifies $prisitine to be able to do these actions:
Save the original values.
Revert to the original values, and change back to pristine state.
Inform parent forms when child control get back to pristine state

Resources