Using directives with ngSanitize in AngularJS - javascript

In my app I have a really simple controller whose job is to display messages:
angular.module("app.components.messages", ['ngSanitize'])
.controller("MessageBoxController", ['$scope', function($scope)
{
$scope.messageType = 'info';
$scope.hidden = true;
$scope.$on('message.error', function(event, message)
{
$scope.message = message;
$scope.messageType = 'error';
$scope.hidden = false;
});
$scope.$on('message.warning', function(event, message)
{
$scope.message = message;
$scope.messageType = 'warning';
$scope.hidden = false;
});
}]);
Then the message box itself is pretty straight forward:
<div id="message-box" ng-controller="MessageBoxController"></div>
I'm using ngSanitize because sometimes I want to put links in the messages. For example in another controller I may do:
$rootScope.$broadcast('message.warning', 'You are about to download ' + file.name + ' which is ' + file.size + '. Your current quota is ' + user.quotaRemaining + ' click here to confirm.');
And this works, and displays the link and everything. However in this particular case, after the user has clicked the link, I would like it to then fire another function to update the user's quota information. I naively tried putting an ng-click on my tag, but it turns out that ngSanitize filters out certain attributes, so that didn't work.
How can I get stuff to happen after the link in the message is clicked? I don't necessarily have to use ngSanitize, I just used it because it seemed convenient.

Here solved similar problem. Just rename directive and pass to the directive you message.
For example:
<div message="message"></div>

Related

Google Tag Manager DataLayer not allways Pushing

I have a Tag in tag manager that feeds me back the data of fields that have been showing as invalid when a user tries to submit a form but its missing or has invalid fields.
This is done using the following code:
// Get user Agent
var Uagent = navigator.userAgent;
// Validation Errors Check
document.getElementById("btnSubmit").onclick = function() {
errorLoop();
}
function errorLoop() {
var runner = document.getElementsByClassName("invalid"),
formEmail = document.getElementById('email').value,
dataL = [];
dataLayer.push({'formEmail' : formEmail});
if (runner) {
for (var i = 0; i < runner.length; i++) {
var errName = runner[i].getAttribute("name"),
errId = runner[i]
.getAttribute("id");
dataL.push("Field: " + errName + " - ID: " + errId);
} //End for
dataL.toString();
var vadout = dataL + " Device: " + Uagent;
console.log(dataL);
dataLayer.push({
'formEmail' : formEmail,
'validationError': vadout,
'event' : 'errorpush'
});
} //End if
} //End errorLoop
So whats basically happening here is on a submit we are checking the form to see if any of the fields have the class invalid & if it does then it add's the name & Id of the fields to an array then prints then prints the array into a data layer.
The tag itself is triggered using a custom event called errorpush.
The issue is that this works only about 80% of the time we still get alot of people get validation errors but the validation errors don't seem to make it to the datalayer and back to google analytics.
I'm considering adding a settimeout delay to the datalayer push which I will go away and try but wanted to see if anyone knows of anything right off the bat which could be causing this problem.
Your runner is declared as empty array if there are no elements with class name "invalid". But your if statement is only checking for declaration thus:
if(runner){
}
will always be true but there are no "invalid" elements. Consequently dataL.push("Field: " + errName + " - ID: " + errId);will never execute in your for loop but dataLayer.push will, and so you will get errorpush event without any errors.
To solve this, I would suggest to rewrite your if statement:
if(runner.length > 0){
}
I hope this solves your problem.

Render JSON errors from two different hashes

Initially I had 1 hash that would just return the error for a form. However now I have two hashes that can contain errors. Now I'd like to look through both of them and display it to the user. What I had before when I only had 1, that was working was this piece of code:
<script>
$("#payment-form").on("ajax:error", function(e, data, status, xhr) {
App.OrderRequests.throwFormError();
$('#payment-form').render_form_errors(data.responseJSON)
});
$.fn.render_form_errors = function(errors) {
var form;
form = this;
this.clear_form_errors();
return $.each(errors, function(field, messages) {
return form.append('<span class="help-block">' + $.map(messages, function(m) {
return m.charAt(0).toUpperCase() + m.slice(1);
}).join('<br />') + '</span>');
});
};
$.fn.clear_form_errors = function() {
this.find('.form-group').removeClass('has-error');
return this.find('span.help-block').remove();
};
</script>
This is already pushing my expertise with javascript. Now the typical JSON response I'd return would be like this:
"{\"base\":[\"Your card's security code is incorrect.\"]}"
However now I want to return the JSON like this (because I'm saving to two different models on the same form):
"{\"address\":{},\"card\":{\"base\":[\"Your card's security code is incorrect.\"]}}"
So I'd like to essentially print what errors might be hiding inside both address or card. So I need to adjust the code to check those, however I'm not sure how to achieve that.

Creating names for elements created by an Angular directive

For a project I'm working on, I created a simplified version of the UI Bootstrap Calendar widget.
Plunker of my Simplified Calendar, and how I'm using it, is here.
One interesting aspect of the UI Bootstrap calendar, is that even though it goes onto a input[text], it still produces a date validation in the $error dictionary for a form controller, just as if I had specified an input[date] element in my DOM.
However, there's a catch with numerous sub-catches. One thing you'll notice right away in my plunker's DOM is that I've specified error spans for times when the given date fields are not actually dates (try entering something ridiculous like 'cat' for a value!) If you enter something that isn't a date, those should appear, but they don't.
I've tried a few things to expose the markup being created to the name field of the parent:
$transclude set to false, such that the <calendar></calendar> tags get replaced with the contents of the calendar directive's template, with a name attribute specified. This "works", except that said input is wrapped in a span that has a class necessary to look correct using the Bootstrap styling framework.
Directly creating a name attribute in the calendar directive's input field with a binding, like so*:
app.directive('mustPrecedeDate', [
function () {
return {
restrict: 'E',
template: '<input type="text" name="{{ someName }}" />',
scope: {},
controller: 'calendarCtrl',
link: function () {}
};
}
};
Writing link code to explicitly find the input that is a child of the calendar generated markup, and assign it a name attribute. Both 2 and 3 failed, because apparently that's not really something that can be done (I can't find the SO question that was the source of that discovery.)
This leads to my Question: in what way can I get a name down to the input element, such that validation results can be reported to the $error dictionary, so that I can give my users helpful validation messages?
*: Apparently, code blocks with the 'four spaces from the left' formatting don't behave well with numbered lists, so I had to use back-quote code notation to get the text to format halfway correctly. Please feel free to correct my formatting, if I haven't found a bug in the markdown setup SO uses.
The #3 thing needed to be tried a bit harder!
I was able to get a name on the input by adding the following code into my link function:
var inputElement = elem.find('input');
inputElement.attr('name', inputName);
...Where inputName is scraped from the attributes list. I was able to get the inputName down to the generated input[text] field by using a compile function as below.
app.directive('calendar', [
function() {
return {
restrict: 'E',
transclude: false,
scope: {},
template:
'<span class="input-group">'
+ '<input class="form-control" required '
+ 'type="text" placeholder="MM/dd/yyyy" '
+ 'data-ng-model="dt" data-ng-click="toggle($event)" '
+ 'data-ng-change="updateParentProperty()" '
+ 'datepicker-popup="MM/dd/yyyy" is-open="isOpen" />'
+ '<span class="input-group-btn">'
+ '<button type="button" class="btn btn-default" data-ng-click="toggle($event)">'
+ '<i class="fa fa-calendar"></i>'
+ '</button>'
+ '</span>'
+ '</span>',
controller: 'calendarCtrl',
compile: function(elem, attrs) {
var inputName = attrs.inputName;
var inputElement = elem.find('input');
inputElement.attr('name', inputName);
// Compile returns a Link function!
return function(scope, elem, attrs, ctrl) {
var modelName = attrs.ngModel;
scope.parentProperty = modelName;
scope.dt = scope.$parent[modelName];
};
}
};
}
]);

Angularjs - log in, stay on page, load div after authentication

I am not sure how to approach this for my AngularJS site...
I need to load a login form to any page on the site (without leaving the page) then asynchronously pass user login info and get success or fail back and then load a div html template to the same page if login is successful...
Seeing a full example would be amazing but I do not expect that of course. However I can't find a single simple example of this on the web (Nothing that would help a newbie).
Can someone point at good solutions or at least understandable examples? .. or something similar that is not cluttered with unrelated code..
I use a directive for my login, kind of like this. You can get a general idea of how it works.
.directive('login', function($http, $rootScope) {
return {
restrict: 'E',
template: " <form> " +
"<label>Username</label>" +
"<input type='text' ng-model='username'>" +
"<label>Password</label>" +
"<input type='password' ng-model='password'>" +
"<br>" +
"<input type='submit'>" +
"</form>",
link: function(scope, elem, attrs) {
elem.bind('submit', function() {
var user_data = {
"username": scope.username,
"password": scope.password,
};
$http.post("http://localhost:8001/api-token-auth/", user_data)
.success(function(response) {
$http.defaults.headers.common['Authorization'] = 'Token ' + response.token;
$rootScope.$broadcast('event:login-confirmed');
elem.slideUp();
});
scope.$on('event:auth-loginRequired', function () {
var main = document.getElementById("main");
main.hide();
elem.slideDown();
});
});
}
}
});
I found a solution that does exactly what I asked about and can be adjusted to my needs fairly easily.
https://gist.github.com/clouddueling/6191173
It is created by Michael Calkins who I find to be one of the most clear and easy to understand sources for AngularJs tutoring. I found his videos on youtube http://www.youtube.com/watch?v=ZHMVP5aLAPM

ngInit not working asynchronously(with $q promise)

Edit:
Plunker is working, actual code isn't:
http://plnkr.co/edit/5oVWGCVeuTwTARhZDVMl?p=preview
The service is contains typical getter\setter stuff, beside that, it functions fine, so I didn't post it's code to avoid TLDR.
TLDR version: trying to ng-init a value fetched with AJAX into the ngModel of the text-area, the request resolves with the correct value, but the textarea remain empty.
parent controller function(talks to the service):
$scope.model.getRandomStatus = function(){
var deffered = $q.defer();
var cid = authService.getCompanyId();
var suggestions = companyService.getStatusSuggestions(cid);
if(suggestions && suggestions.length > 0){
deffered.resolve(suggestions[Math.floor(Math.random(suggestions.length) + 1)].message);
return deffered.promise;//we already have a status text, great!
}
//no status, we'll have to load the status choices from the API
companyService.loadStatusSuggestions(cid).then(function(data){
companyService.setStatusSuggestions(cid, data.data);
var result = data.data[Math.floor(Math.random(data.data.length) + 1)];
deffered.resolve(result.message);
},
function(data){
_root.inProgress = false;
deffered.resolve('');
//failed to fetch suggestions, will try again the next time the compnay data is reuqired
});
return deffered.promise;
}
child controller:
.controller('shareCtrl', function($scope){
$scope.layout.toggleStatusSuggestion = function(){
$scope.model.getRandomStatus().then(function(data){
console.log(data);//logs out the correct text
//$scope.model.formData.shareStatus = data;//also tried this, no luck
return data.message;
});
$scope.model.formData.shareStatus = $scope.layout.toggleStatusSuggestion();//Newly edited
}
});
HTML:
<div class="shareContainer" data-ng-controller="shareCtrl">
<textarea class="textAreaExtend" name="shareStatus" data-ng-model="model.formData.shareStatus" data-ng-init="model.formData.shareStatus = layout.toggleStatusSuggestion()" cols="4"></textarea>
</div>
I believe what you are wanting is :
$scope.model.getRandomStatus().then(function(data){
$scope.model.formData.shareStatus = data.message;
});
Returning something from within then does not return anything from the function wrapping it and therefore does nothing
Turns out that I had a custom validation directive that was watching the changes in the model via $formatters, and limting it to 80 chars(twitter), it was failing silently as I didn't expect to progmatically insert invalid values into my forms, very stupid, but could happen to anyone.
Had to make some changes to it, so it's worth to remember in case it happens to anyone else.

Categories

Resources