**Hello Stack Over Flow community, i'm working on a big project using springboot framework and AngularJS, I am new to angularJS, i've just started learning it for not more than 4 days, i'm trying to use a AngularJS template in order to make my work easier, i choosed a template that contains most of functions that i need in my project, but the problem is that the structure and the syntaxes are so diffrent of what i've seen in angularJS and i don't know which version of angular it is, knowing that i don't have documentation of this template, here's an example of how the code of most of files looks like and the structure too, can you guys help me to know which angular version is?? Structure of the template[Structure of the template 2]`define(['layout/module', 'lodash', 'notification'], function (module, _) {
'use strict';
module.registerDirective('demoStates', function ($rootScope) {
return {
restrict: 'E',
replace: true,
templateUrl: 'app/layout/directives/demo/demo-states.tpl.html',
scope: true,
link: function (scope, element, attributes) {
element.parent().css({
position: 'relative'
});
element.on('click', '#demo-setting', function () {
element.toggleClass('activate')
})
},
controller: function ($scope) {
var $root = $('body');
$scope.$watch('fixedHeader', function (fixedHeader) {
localStorage.setItem('sm-fixed-header', fixedHeader);
$root.toggleClass('fixed-header', fixedHeader);
if (fixedHeader == false) {
$scope.fixedRibbon = false;
$scope.fixedNavigation = false;
}
});
$scope.$watch('fixedNavigation', function (fixedNavigation) {
localStorage.setItem('sm-fixed-navigation', fixedNavigation);
$root.toggleClass('fixed-navigation', fixedNavigation);
if (fixedNavigation) {
$scope.insideContainer = false;
$scope.fixedHeader = true;
} else {
$scope.fixedRibbon = false;
}
});
$scope.$watch('fixedRibbon', function (fixedRibbon) {
localStorage.setItem('sm-fixed-ribbon', fixedRibbon);
$root.toggleClass('fixed-ribbon', fixedRibbon);
if (fixedRibbon) {
$scope.fixedHeader = true;
$scope.fixedNavigation = true;
$scope.insideContainer = false;
}
});
$scope.$watch('fixedPageFooter', function (fixedPageFooter) {
localStorage.setItem('sm-fixed-page-footer', fixedPageFooter);
$root.toggleClass('fixed-page-footer', fixedPageFooter);
});
$scope.$watch('insideContainer', function (insideContainer) {
localStorage.setItem('sm-inside-container', insideContainer);
$root.toggleClass('container', insideContainer);
if (insideContainer) {
$scope.fixedRibbon = false;
$scope.fixedNavigation = false;
}
});
$scope.$watch('rtl', function (rtl) {
localStorage.setItem('sm-rtl', rtl);
$root.toggleClass('smart-rtl', rtl);
});
$scope.$watch('menuOnTop', function (menuOnTop) {
$rootScope.$broadcast('$smartLayoutMenuOnTop', menuOnTop);
localStorage.setItem('sm-menu-on-top', menuOnTop);
$root.toggleClass('menu-on-top', menuOnTop);
if(menuOnTop)$root.removeClass('minified');
});
$scope.$watch('colorblindFriendly', function (colorblindFriendly) {
localStorage.setItem('sm-colorblind-friendly', colorblindFriendly);
$root.toggleClass('colorblind-friendly', colorblindFriendly);
});
$scope.fixedHeader = localStorage.getItem('sm-fixed-header') == 'true';
$scope.fixedNavigation = localStorage.getItem('sm-fixed-navigation') == 'true';
$scope.fixedRibbon = localStorage.getItem('sm-fixed-ribbon') == 'true';
$scope.fixedPageFooter = localStorage.getItem('sm-fixed-page-footer') == 'true';
$scope.insideContainer = localStorage.getItem('sm-inside-container') == 'true';
$scope.rtl = localStorage.getItem('sm-rtl') == 'true';
$scope.menuOnTop = localStorage.getItem('sm-menu-on-top') == 'true' || $root.hasClass('menu-on-top');
$scope.colorblindFriendly = localStorage.getItem('sm-colorblind-friendly') == 'true';
$scope.skins = appConfig.skins;
$scope.smartSkin = localStorage.getItem('sm-skin') || appConfig.smartSkin;
$scope.setSkin = function (skin) {
$scope.smartSkin = skin.name;
$root.removeClass(_.pluck($scope.skins, 'name').join(' '));
$root.addClass(skin.name);
localStorage.setItem('sm-skin', skin.name);
$("#logo img").attr('src', skin.logo);
};
if($scope.smartSkin != "smart-style-0"){
$scope.setSkin(_.find($scope.skins, {name: $scope.smartSkin}))
}
$scope.factoryReset = function () {
$.SmartMessageBox({
title: "<i class='fa fa-refresh' style='color:green'></i> Clear Local Storage",
content: "Would you like to RESET all your saved widgets and clear LocalStorage?1",
buttons: '[No][Yes]'
}, function (ButtonPressed) {
if (ButtonPressed == "Yes" && localStorage) {
localStorage.clear();
location.reload()
}
});
}
}
}
});
});
`**
Related
Is it possible to decide whether to use templateUrl parameter in the link function of AngularJS directive?
Suppose I have the following directive:
app.directive('sitesAndImprovements', function() {
return {
restrict: 'E',
replace:true,
templateUrl: '<path-to-file>/site-and-improvments.html',
link: function (scope, elem, attrs) {
scope.testClick = function() {
var myScope = scope;
//debugger;
}
scope.constructionCompleteClick = function () {
if (scope.construction_complete == 'Yes') {
scope.hold_back = '';
scope.percent_complete = 100;
} else
if (scope.construction_complete == 'No') {
scope.hold_back = '1';
if (scope.percent_complete == 100) {
scope.percent_complete = '';
}
}
}
scope.calcTotal = function () {
var total;
total = (scope.main || 0) + (scope.second || 0) + (scope.third || 0) + (scope.fourth || 0);
scope.total = total || null;
}
}
}
})
I want to control whether to use or not to use the templateUrl and also the replace parameters in the link() function.
This is because I already implemented this directive in about 10+ places without using templateUrl and now I want to start using this feature, but I don't want to make changes to existing and working code.
Is that possible and how?
Tarek
I don't think you can do that in the link, but I believe you can turn templateUrl into a function that can return different values for the directive.
Try doing something like this for your templateUrl:
templateUrl: function() {
if (someCondition) {
return '<path-to-file>/site-and-improvments.html';
} else {
return null;
}
},
app.directive('sitesAndImprovements', function() {
return {
restrict: 'E',
replace:function(){
if (aCondition){
return true;
} else {
return false;
}
},
templateUrl: function(){
if (aCondition){
return '<path-to-file>/site-and-improvments.html';
} else {
return undefined;
}
},
link: function (scope, elem, attrs) {
scope.testClick = function() {
var myScope = scope;
//debugger;
}
scope.constructionCompleteClick = function () {
if (scope.construction_complete == 'Yes') {
scope.hold_back = '';
scope.percent_complete = 100;
} else
if (scope.construction_complete == 'No') {
scope.hold_back = '1';
if (scope.percent_complete == 100) {
scope.percent_complete = '';
}
}
}
scope.calcTotal = function () {
var total;
total = (scope.main || 0) + (scope.second || 0) + (scope.third || 0) + (scope.fourth || 0);
scope.total = total || null;
}
}
}
})
Explanation : As stated in the source code, the template will be compiled only if templateUrl is given :
...
if (directive.templateUrl) {
hasTemplate = true;
assertNoDuplicate('template', templateDirective, directive, $compileNode);
templateDirective = directive;
if (directive.replace) {
replaceDirective = directive;
}
// eslint-disable-next-line no-func-assign
nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
...
Please, note that aCondition could be an attribute passed to the directive to enable/disable the templateUrl and the replace. Also, keep in mind that the replace is deprecated.
I have a pretty simple directive and I want to use the bindToController option. So, I created my directive like this:
(function () {
'use strict';
angular.module('sapphire.directives').directive('list', list);
function list() {
return {
restrict: 'A',
template: '<div class="row flex-column" ng-class="{ \'spinner-dark\': controller.loading }" ng-include="controller.templateUrl" ng-if="controller.loading || controller.models.length"></div>',
controller: 'ListDirectiveController',
controllerAs: 'controller',
scope: true,
bindToController: {
method: '&list',
templateName: '#'
}
};
};
})();
And then I created my controller like this:
(function () {
'use strict';
angular.module('sapphire.directives').controller('ListDirectiveController', listDirectiveController);
listDirectiveController.$inject = ['ListDirectiveService', 'Selections'];
function listDirectiveController(broadcast, selections) {
var self = this;
console.log(self);
// Bindings
self.limit = 0;
self.total = 0;
self.loading = true;
self.templateUrl = 'app/directives/lists/list/' + (self.templateName || 'list-default') + '.html';
self.isSelected = selections.isSelected;
self.select = selections.select;
// Method binding
self.list = list;
init();
//////////////////////////////////////////////////
function init() {
list();
};
// Our list method
function list() {
// Set our initial limit
self.limit += 10;
self.loading = true;
// Get our items
return self.method({ limit: self.limit }).then(function (response) {
self.loading = false;
self.models = response;
self.total = response.length;
});
};
///////// ------ Removed for brevity ------ /////////
};
})();
When I use this directive I get an error stating:
self.method is not a function
which is why I am console.logging the controller to see what is bound to it. Surely enough, the method and templateName are missing.
I have tried a few ways to get this to work:
scope: {
method: '&list',
templateName: '#'
},
bindToController: true
or
scope: {},
bindToController: {
method: '&list',
templateName: '#'
}
but nothing seems to work. I can't get my isolated scope to be bound to my controller....
Does anyone know what I am doing wrong?
PS: I am using angular 1.6.4
To use the directive I do this:
<div class="invisible-container" list="controller.listUsers(limit)" template-name="users"></div>
Ok, so I figured this out. The scope is bound, but it isn't available straight away. I had to create an init method and invoke it from the directive. Only then was everything bound.
I did it like this:
(function () {
'use strict';
angular.module('sapphire.directives').directive('list', list);
function list() {
return {
restrict: 'A',
template: '<div class="row flex-column" ng-class="{ \'spinner-dark\': controller.loading }" ng-include="controller.templateUrl" ng-if="controller.loading || controller.models.length"></div>',
controller: 'ListDirectiveController',
controllerAs: 'controller',
scope: {
method: '&list',
templateName: '#'
},
bindToController: true,
link: function (scope, element, attrs, controller) {
controller.init();
}
};
};
})();
and the controller now looks like this:
(function () {
'use strict';
angular.module('sapphire.directives').controller('ListDirectiveController', listDirectiveController);
listDirectiveController.$inject = ['ListDirectiveService', 'Selections'];
function listDirectiveController(broadcast, selections) {
var self = this;
// Bindings
self.limit = 0;
self.total = 0;
self.loading = true;
self.isSelected = selections.isSelected;
self.select = selections.select;
// Method binding
self.init = init;
////////////////////////////////////////////////////
function init() {
list();
getTemplate();
bindEvents();
};
function bindEvents() {
broadcast.onPrepend(onPrepend);
broadcast.onRefresh(onRefresh);
};
function getTemplate() {
self.templateUrl = 'app/directives/lists/list/' + (self.templateName || 'list-default') + '.html';
};
function list() {
// Set our initial limit
self.limit += 10;
self.loading = true;
// Get our items
return self.method({ limit: self.limit }).then(function (response) {
self.loading = false;
self.models = response;
self.total = response.length;
});
};
function onPrepend(event, args) {
if (args && args.target && args.target === self.templateName) {
self.models.unshift(args.model);
}
};
function onRefresh(event, args) {
if (args && args.target && args.target === self.templateName) {
self.limit -= 10;
self.models = [];
list();
}
};
};
})();
I have an AngularJS Directive defined in a Javascript file that looks like this:
(function () {
'use strict';
angular
.module('ooApp.controllers')
.directive('fileUploader', fileUploader);
fileUploader.$inject = ['appInfo', 'fileManager'];
function fileUploader(appInfo, fileManager) {
var directive = {
link: link,
restrict: 'E',
templateUrl: 'views/directive/UploadFile.html',
scope: true
};
return directive;
function link(scope, element, attrs) {
scope.hasFiles = false;
scope.files = [];
scope.upload = fileManager.upload;
scope.appStatus = appInfo.status;
scope.fileManagerStatus = fileManager.status;
}
}
})();
and in the template URL of the directive there is a button that calls a Javascript function which looks like this:
function upload(files) {
var formData = new FormData();
angular.forEach(files, function (file) {
formData.append(file.name, file);
});
return fileManagerClient.save(formData)
.$promise
.then(function (result) {
if (result && result.files) {
result.files.forEach(function (file) {
if (!fileExists(file.name)) {
service.files.push(file);
}
});
}
appInfo.setInfo({ message: "files uploaded successfully" });
return result.$promise;
},
function (result) {
appInfo.setInfo({ message: "something went wrong: " +
result.data.message });
return $q.reject(result);
})
['finally'](
function () {
appInfo.setInfo({ busy: false });
service.status.uploading = false;
});
}
Once I select files for upload and click the upload button I need to reload the directive or somehow get it back to it's initial state so I can upload additional files. I'm relatively new to AngularJS and I'm not quite sure how to do this. Any help is much appreciated.
Thanks,
Pete
You just need to create a reset method. Also, you may want to call the parent controller function.
Using answer from this
ngFileSelect.directive.js
...
.directive("onFileChange",function(){
return {
restrict: 'A',
link: function($scope,el){
var onChangeHandler = scope.$eval(attrs.onFileChange);
el.bind('change', onChangeHandler);
}
}
...
fileUploader.directive.js
(function () {
'use strict';
angular
.module('ooApp.controllers')
.directive('fileUploader', fileUploader);
fileUploader.$inject = ['appInfo', 'fileManager'];
function fileUploader(appInfo, fileManager) {
return {
link: link,
restrict: 'E',
templateUrl: 'views/directive/UploadFile.html',
scope:{
onSubmitCallback: '&',
onFileChange: '&'
}
};
function link(scope, element, attrs) {
scope.reset = reset;
scope.fileChange = fileChange;
reset();
function reset() {
scope.hasFiles = false;
scope.files = [];
scope.upload = fileManager.upload;
scope.appStatus = appInfo.status;
scope.fileManagerStatus = fileManager.status;
if(typeof scope.onSubmitCallback === 'function') {
scope.onSubmitCallback();
}
}
function fileChange(file) {
if(typeof scope.onFileChange === 'function'){
scope.onFileChange(file);
}
}
}
}
})();
UploadFile.html
<form>
<div>
...
</div>
<input type="submit" ng-click="reset()" file-on-change="fileChange($files)" />Upload
</form>
parent.html
<file-uploader on-submit-callback="onUpload" on-file-change="onFileChange" ng-controller="UploadCtrl" />
upload.controller.js
...
$scope.onUpload = function() {
console.log('onUpload clicked %o', arguments);
};
$scope.onFileChange = function(e) {
var imageFile = (e.srcElement || e.target).files[0];
}
...
Hi i use "asutosh" code:
myApp.directive('droppable', ['$parse',
function($parse) {
return {
link: function(scope, element, attr) {
function onDragOver(e) {
if (e.preventDefault) {
e.preventDefault();
}
if (e.stopPropagation) {
e.stopPropagation();
}
e.dataTransfer.dropEffect = 'move';
return false;
}
function onDrop(e) {
if (e.preventDefault) {
e.preventDefault();
}
if (e.stopPropagation) {
e.stopPropagation();
}
var data = e.dataTransfer.getData("Text");
data = angular.fromJson(data);
var dropfn = attr.drop;
var fn = $parse(attr.drop);
scope.$apply(function() {
scope[dropfn](data, e.target);
});
}
element.bind("dragover", onDragOver);
element.bind("drop", onDrop);
}
};
}
]);
myApp.directive('draggable', function() {
return {
link: function(scope, elem, attr) {
elem.attr("draggable", true);
var dragDataVal='';
var draggedGhostImgElemId='';
attr.$observe('dragdata',function(newVal){
dragDataVal=newVal;
});
attr.$observe('dragimage',function(newVal){
draggedGhostImgElemId=newVal;
});
elem.bind("dragstart", function(e) {
var sendData = angular.toJson(dragDataVal);
e.dataTransfer.setData("Text", sendData);
if (attr.dragimage !== 'undefined') {
e.dataTransfer.setDragImage(
document.getElementById(draggedGhostImgElemId), 0, 0
);
}
var dragFn = attr.drag;
if (dragFn !== 'undefined') {
scope.$apply(function() {
scope[dragFn](sendData);
})
}
});
}
};
});
example here http://plnkr.co/edit/KvJglc?p=preview , to drag and drop table columns, but this code have a little issue, when you drop a column over border or blank area in the headers, the column dissapear. Someone could assist me please?
myApp.directive('angTable', ['$compile',function($compile) {
return {
restrict: 'E',
templateUrl: 'tabletemplate.html',
replace: true,
scope: {
conf: "="
},
controller: function($scope) {
$scope.dragHead = '';
$scope.dragImageId = "dragtable";
$scope.handleDrop = function(draggedData,
targetElem) {
var swapArrayElements = function(array_object, index_a, index_b) {
var temp = array_object[index_a];
array_object[index_a] = array_object[index_b];
array_object[index_b] = temp;
};
var srcInd = $scope.conf.heads.indexOf(draggedData);
var destInd = $scope.conf.heads.indexOf(targetElem.textContent);
swapArrayElements($scope.conf.heads, srcInd, destInd);
};
$scope.handleDrag = function(columnName) {
$scope.dragHead = columnName.replace(/["']/g, "");
};
},
compile: function(elem) {
return function(ielem, $scope) {
$compile(ielem)($scope);
};
}
};
}
]);
Add this 2 lines if(destInd == -1)
destInd = targetElem.cellIndex; after var destInd = $scope.conf.heads.indexOf(targetElem.textContent); and it should work
My goal is to autosave a form after is valid and update it with timeout.
I set up like:
(function(window, angular, undefined) {
'use strict';
angular.module('nodblog.api.article', ['restangular'])
.config(function (RestangularProvider) {
RestangularProvider.setBaseUrl('/api');
RestangularProvider.setRestangularFields({
id: "_id"
});
RestangularProvider.setRequestInterceptor(function(elem, operation, what) {
if (operation === 'put') {
elem._id = undefined;
return elem;
}
return elem;
});
})
.provider('Article', function() {
this.$get = function(Restangular) {
function ngArticle() {};
ngArticle.prototype.articles = Restangular.all('articles');
ngArticle.prototype.one = function(id) {
return Restangular.one('articles', id).get();
};
ngArticle.prototype.all = function() {
return this.articles.getList();
};
ngArticle.prototype.store = function(data) {
return this.articles.post(data);
};
ngArticle.prototype.copy = function(original) {
return Restangular.copy(original);
};
return new ngArticle;
}
})
})(window, angular);
angular.module('nodblog',['nodblog.route'])
.directive("autosaveForm", function($timeout,Article) {
return {
restrict: "A",
link: function (scope, element, attrs) {
var id = null;
scope.$watch('form.$valid', function(validity) {
if(validity){
Article.store(scope.article).then(
function(data) {
scope.article = Article.copy(data);
_autosave();
},
function error(reason) {
throw new Error(reason);
}
);
}
})
function _autosave(){
scope.article.put().then(
function() {
$timeout(_autosave, 5000);
},
function error(reason) {
throw new Error(reason);
}
);
}
}
}
})
.controller('CreateCtrl', function ($scope,$location,Article) {
$scope.article = {};
$scope.save = function(){
if(typeof $scope.article.put === 'function'){
$scope.article.put().then(function() {
return $location.path('/blog');
});
}
else{
Article.store($scope.article).then(
function(data) {
return $location.path('/blog');
},
function error(reason) {
throw new Error(reason);
}
);
}
};
})
I'm wondering if there is a best way.
Looking at the code I can see is that the $watch will not be re-fired if current input is valid and the user changes anything that is valid too. This is because watch functions are only executed if the value has changed.
You should also check the dirty state of the form and reset it when the form data has been persisted otherwise you'll get an endless persist loop.
And your not clearing any previous timeouts.
And the current code will save invalid data if a current timeout is in progress.
I've plunked a directive which does this all and has better SOC so it can be reused. Just provide it a callback expression and you're good to go.
See it in action in this plunker.
Demo Controller
myApp.controller('MyController', function($scope) {
$scope.form = {
state: {},
data: {}
};
$scope.saveForm = function() {
console.log('Saving form data ...', $scope.form.data);
};
});
Demo Html
<div ng-controller="MyController">
<form name="form.state" auto-save-form="saveForm()">
<div>
<label>Numbers only</label>
<input name="text"
ng-model="form.data.text"
ng-pattern="/^\d+$/"/>
</div>
<span ng-if="form.state.$dirty && form.state.$valid">Updating ...</span>
</form>
</div>
Directive
myApp.directive('autoSaveForm', function($timeout) {
return {
require: ['^form'],
link: function($scope, $element, $attrs, $ctrls) {
var $formCtrl = $ctrls[0];
var savePromise = null;
var expression = $attrs.autoSaveForm || 'true';
$scope.$watch(function() {
if($formCtrl.$valid && $formCtrl.$dirty) {
if(savePromise) {
$timeout.cancel(savePromise);
}
savePromise = $timeout(function() {
savePromise = null;
// Still valid?
if($formCtrl.$valid) {
if($scope.$eval(expression) !== false) {
console.log('Form data persisted -- setting prestine flag');
$formCtrl.$setPristine();
}
}
}, 500);
}
});
}
};
});
UPDATE:
to stopping timeout
all the logic in the directive
.directive("autosaveForm", function($timeout,$location,Post) {
var promise;
return {
restrict: "A",
controller:function($scope){
$scope.post = {};
$scope.save = function(){
console.log(promise);
$timeout.cancel(promise);
if(typeof $scope.post.put === 'function'){
$scope.post.put().then(function() {
return $location.path('/post');
});
}
else{
Post.store($scope.post).then(
function(data) {
return $location.path('/post');
},
function error(reason) {
throw new Error(reason);
}
);
}
};
},
link: function (scope, element, attrs) {
scope.$watch('form.$valid', function(validity) {
element.find('#status').removeClass('btn-success');
element.find('#status').addClass('btn-danger');
if(validity){
Post.store(scope.post).then(
function(data) {
element.find('#status').removeClass('btn-danger');
element.find('#status').addClass('btn-success');
scope.post = Post.copy(data);
_autosave();
},
function error(reason) {
throw new Error(reason);
}
);
}
})
function _autosave(){
scope.post.put().then(
function() {
promise = $timeout(_autosave, 2000);
},
function error(reason) {
throw new Error(reason);
}
);
}
}
}
})
Here's a variation of Null's directive, created because I started seeing "Infinite $digest Loop" errors. (I suspect something changed in Angular where cancelling/creating a $timeout() now triggers a digest.)
This variation uses a proper $watch expression - watching for the form to be dirty and valid - and then calls $setPristine() earlier so the watch will re-fire if the form transitions to dirty again. We then use an $interval to wait for a pause in those dirty notifications before saving the form.
app.directive('autoSaveForm', function ($log, $interval) {
return {
require: ['^form'],
link: function (scope, element, attrs, controllers) {
var $formCtrl = controllers[0];
var autoSaveExpression = attrs.autoSaveForm;
if (!autoSaveExpression) {
$log.error('autoSaveForm missing parameter');
}
var savePromise = null;
var formModified;
scope.$on('$destroy', function () {
$interval.cancel(savePromise);
});
scope.$watch(function () {
// note: formCtrl.$valid is undefined when this first runs, so we use !$formCtrl.$invalid instead
return !$formCtrl.$invalid && $formCtrl.$dirty;
}, function (newValue, oldVaue, scope) {
if (!newValue) {
// ignore, it's not "valid and dirty"
return;
}
// Mark pristine here - so we get notified again if the form is further changed, which would make it dirty again
$formCtrl.$setPristine();
if (savePromise) {
// yikes, note we've had more activity - which we interpret as ongoing changes to the form.
formModified = true;
return;
}
// initialize - for the new interval timer we're about to create, we haven't yet re-dirtied the form
formModified = false;
savePromise = $interval(function () {
if (formModified) {
// darn - we've got to wait another period for things to quiet down before we can save
formModified = false;
return;
}
$interval.cancel(savePromise);
savePromise = null;
// Still valid?
if ($formCtrl.$valid) {
$formCtrl.$saving = true;
$log.info('Form data persisting');
var autoSavePromise = scope.$eval(autoSaveExpression);
if (!autoSavePromise || !autoSavePromise.finally) {
$log.error('autoSaveForm not returning a promise');
}
autoSavePromise
.finally(function () {
$log.info('Form data persisted');
$formCtrl.$saving = undefined;
});
}
}, 500);
});
}
};
});