Restangular addElementTransformer / extendModel for ALL routes - angularjs

How can I addElementTransformer for all models? I want all my models have extra method for delayed save, for example:
Restangular.extendModel('*', function(model){
model.delayed_save = _.debounce(model.save, 1000, {maxWait: 5000})
}
But I need to make it for every route I have.
I know I can create factory for wrapping Restangular service with route parameter like this:
angular.module('app')
.service('restangularService', restangularService)
function restangularService(Restangular) {
var self = this;
self.init = init
function init(route) {
Restangular.extendModel(route, function(model){
model.deferred_save = _.debounce(model.save, 1000, {maxWait: 5 * 1000})
return model
})
return Restangular.service(route)
}
}
and this will work, but I believe there have to be a better way

I needed this to remove the parentResource after saving a nested resource so used setOnElemRestangularized:
RestangularConfigurer.setOnElemRestangularized(function(elem, isCollection, what, Restangular) {
if (!isCollection && elem.fromServer && elem.parentResource) elem.parentResource = null;
return elem;
});

You can use setOnElemRestangularized to achieve this.
Something like this should work (untested)
.config(function(RestangularProvider) {
RestangularProvider.setOnElemRestangularized(function(elem, isCollection, what, Restangular) {
if (isCollection) { return; }
console.log("Path: ", what);
elem.newMethod = function() { console.log("TODO: Add code here..."); };
return elem;
});
})

Related

How to pass a variable to javascript in nested function

I have several protractor / angularjs it blocks that repeat the same bit of code that I would like to put inside a function. I want to just call the function instead of repeating this over and over.
it('should move directly to Draft', function() {
posting_sum_page.locate_action_button.click();
posting_action_page.move_action.filter(function(elem) {
return elem.getText().then(function(text) {
return text === 'Draft';
});
}).click();
});
This part of the block is the repeated part I want to create a function for. I am new to javascript so this is eluding me on how to do this.
return elem.getText().then(function(text) {
return text === 'Draft';
});
}).click();
I need to be able to substitute 'Draft' with different variables. I am using page objects for part of this and I am not sure A) how to create a function like this and pass in my text & B) if it should go on the spec side or the page side? This is probably pretty basic for most folks. But since I am new to javascript I am having trouble wrapping my head around this.
I would extract the whole filter function into a "helpers" module.
helpers.js:
var Helpers = function () {
this.filterByText = function (text) {
return function (elem) {
return elem.getText().then(function(actualText) {
return actualText === text;
});
};
}
}
module.exports = new Helpers();
Usage in the test:
var helpers = require("helpers");
describe("My Test", function () {
it('should move directly to Draft', function() {
posting_sum_page.locate_action_button.click();
posting_action_page.move_action.filter(helpers.filterByText('Draft')).click();
});
});
Maybe something like this?
describe('...something...', function()
{
var clickBtn;
beforeEach(function()
{
clickBtn = function(testText)
{
return posting_action_page.move_action.filter(function(elem)
{
return elem.getText().then(function(currentText)
{
return currentText === testText;
});
}).click();
};
});
it('should move directly to Draft', function()
{
posting_sum_page.locate_action_button.click();
expect(clickBtn('Draft')).toEqual('...something...');
});
});
If you want to reuse the return block only
it('should move directly to' + targetText, function() {
posting_sum_page.locate_action_button.click();
posting_action_page.move_action.filter(function(elem) {
checkSameText(elem, targetText);
}).click();
});
function checkSameText(el, targetText) {
return el.getText().then(function(text) {
return text === targetText;
});
}
I'm unsure what the page object type is for posting_action_page.move_action but what I think you are looking for is using by.buttonText or by.linkText.
// html: <button>Draft</button>
element(by.buttonText('Draft')).click();
// html: <a href="">Draft</button>
element(by.linkText('Draft')).click();
There are other locators that might be helpful like by.partialButtonText and by.partialLinkText.

Calling an Angular Factory recursively gives a circular dependency error

I have an application with multithreaded comments, and I'm trying to use an Angular factory to create objects with all the necessary functions and properties recursively, since of course, a comment can have many replies, each of which can have more replies...
Below is my factory representing a comment:
app.factory("Comment", ["commentHttpService", "Comment", function(commentHttpService, Comment) {
return function(comment) {
var self = comment;
// lots of other properties and methods here
self.children = self.children.map(function(reply) {
return new Comment(reply);
});
return self;
}
}]);
Of course, this doesn't work since I'm injecting Comment into itself.
How can I resolve this and still accomplish my original goal?
app.factory("Comment", ["commentHttpService", function(commentHttpService) {
function Comment(comment) {
var self = comment;
// lots of other properties and methods here
self.children = self.children.map(function(reply) {
return new Comment(reply);
});
}
return Comment;
}]);
Another way of achieving what you want could be creating a factory that lets yu create a Comment instance which has all its apis attached to it:
app.factory("CommentFactory", function() {
function Comment(comment) {
this.message = comment;
this.replies = [];
this.getMessage = function() {
return this.message;
}
this.addReply = function(reply) {
var replyComment = new Comment(reply);
this.replies.push(replyComment);
return replyComment;
}
this.getReplies = function() {
return replies;
}
}
return {
createComment: function(message) {
return new Comment(message);
}
}
});
Here is a small plunk:
http://plnkr.co/edit/BmfCm9?p=preview
You can create comments using factory and add replies to comments which are also instances of Comments.

How to scope methods in AnjularJS

I have this controller that has several functions that call each other. On success, I want to return something to be displayed (located in the last function). For some reason, without errors, the return is not working but the console.log is. Can someone please tell me why the return does not work and give me a solution please. Thanks so much!
.controller("dayController", function(){
.controller("weatherController", function(){
this.currentWeatherToDisplay = function(){
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition(gotLocation,initialize);
}
else{
alert("Device does not support geolocation");
initialize();
}
};
var currentLocation;
//get the location coords
function gotLocation(pos){
var crd = pos.coords;
currentLocation = loadWeather(crd.latitude+','+crd.longitude);
initialize();
}
function initialize(){
if(!currentLocation){
loadWeather("Washington, DC");
}
else{
loadWeather(currentLocation);
}
}
function loadWeather(location){
$.simpleWeather({
location: location,
woeid: '',
unit: 'f',
success: function(weather) {
var html = weather.temp+'°'+weather.units.temp;
console.log(html);
return html;
},
error: function(error) {
console.log(error);
return error;
}
});
}
});
Well, mmmm you are use some jQuery plugin to get the weather given a current location, and like almost every jQuery plugins this use callbacks call to works (success, and error) first one i recommend you to rewrite this method to something like this:
function loadWeather(location){
var defered = $q.defer();
$.simpleWeather({
location: location,
woeid: '',
unit: 'f',
success: function(weather) {
var html = weather.temp+'°'+weather.units.temp;
console.log(html);
defered.resolve(html);
},
error: function(error) {
console.log(error);
defered.reject(error);
}
});
return defered.promise;
}
Also you must inject the $q dependency to the controller, like this:
module.controller("weatherController", function($q){...}
or this
module.controller("weatherController", ['$q',function($q){...}
I recommend the last by minyfication improvement in angular, when you return a promise like the function loadWeather, you must understand some basic principles about the the $q (based kriskoval Q library), a promise is a expected value in the future, an have a method then to work with that data (its a very short concept), that means:
function gotLocation(pos){
var crd = pos.coords;
loadWeather(crd.latitude+','+crd.longitude)
.then(function(html){
//html contain the expected html from loadWeather defered.resolve(html)
currentLocation = html;
})
.catch(function(error){
//error contain the expected error by execute defered.reject(error)
// maybe gonna try to initialize here
initialize();
})
}
This must work, remember to change the initialize function to some like this:
function initialize(){
var promise;
if(!currentLocation){
promise = loadWeather("Washington, DC");
}
else{
promise = loadWeather(currentLocation);
}
promise.then(function(html){
// some logic with succesful call
}, function(error) {
// some logic with error call
})
}

Can't get waiting for function to return (with promises?) working in angular controller

I'm trying to get the following findTimelineEntries function inside an Angular controller executing after saveInterview finishes:
$scope.saveInterview = function() {
$scope.interviewForm.$save({employeeId: $scope.employeeId}, function() {
$scope.findTimelineEntries();
});
};
The save action adds or edits data that also is part of the timeline entries and therefore I want the updated timeline entries to be shown.
First I tried changing it to this:
$scope.saveInterview = function() {
var functionReturned = $scope.interviewForm.$save({employeeId: $scope.employeeId});
if (functionReturned) {
$scope.findTimelineEntries();
}
};
Later to this:
$scope.saveInterview = function() {
$scope.interviewForm.$save({employeeId: $scope.employeeId});
};
$scope.saveInterview.done(function(result) {
$scope.findTimelineEntries();
});
And finaly I found some info about promises so I tried this:
$scope.saveInterview = function() {
$scope.interviewForm.$save({employeeId: $scope.employeeId});
};
var promise = $scope.saveInterview();
promise.done(function() {
$scope.findTimelineEntries();
});
But somehow the fact that it does work this way according to http://nurkiewicz.blogspot.nl/2013/03/promises-and-deferred-objects-in-jquery.html, doesn't mean that I can use the same method on those $scope.someFuntcion = function() functions :-S
Here is a sample using promises. First you'll need to include $q to your controller.
$scope.saveInterview = function() {
var d = $q.defer();
// do something that probably has a callback.
$scope.interviewForm.$save({employeeId: $scope.employeeId}).then(function(data) {
d.resolve(data); // assuming data is something you want to return. It could be true or anything you want.
});
return d.promise;
}

Use AngularJS Decorator to customise uibAccordion

I have a page where I can't modify HTML. I can only attach angular modules to it.
Page contains uibAccordion, with "close-others" set to "true". I need to somehow set it to "false".
<uib-accordion close-others="true">
The only way that came to my mind was to use a Decorator, but I don't know how to modify the attribute:
angular.module('guide', ['ui.bootstrap'])
.config(['$provide', Decorate]);
function Decorate($provide) {
$provide.decorator('uibAccordionDirective', ['$delegate', function($delegate) {
$delegate[0].closeOthers = function(openGroup) {
var closeOthers = false;
};
return $delegate;
}]);
}
Can anyone suggest how to accomplish this?
Should be able to modify the function https://github.com/angular-ui/bootstrap/blob/master/src/accordion/accordion.js:
...
$delegate.closeOthers = function(openGroup) {
var closeOthers = false;
// var closeOthers = angular.isDefined($attrs.closeOthers) ?
// $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;
if (closeOthers) { // Obviously you can remove this whole line
angular.forEach(this.groups, function(group) {
if (group !== openGroup) {
group.isOpen = false;
}
});
}
};
return $delegate;
https://docs.angularjs.org/guide/decorators#what-are-decorators-
Patch the $Delegate section - it replaces the someFn like you want to replace the closeOthers.

Resources