I have the following markup in a form mixed with some asp.net razor:
<div class="account-form__field-container" ng-show="postcodeRequired()" ng-cloak>
#Html.LabelFor(x => x.Postcode)
#Html.TextBoxFor(x => x.Postcode, new { #class = "account-form__field", placeholder = "Postcode here...", ng_required = "postcodeRequired()",ng_validpostcode="", ng_model = "postcode", ng_init = "postcode = '" + Model.Postcode + "'" })
#Html.ValidationMessageFor(x => x.Postcode, null, new { #class = "account-form__error-message" })
<span class="account-form__error-message" ng-show="registrationForm.$submitted && registrationForm.Postcode.$error.required" ng-cloak>
Please enter your postcode
</span>
<span class="account-form__error-message" ng-show="registrationForm.$submitted && !validPostCode()" ng-cloak>
Please enter valid postcode
</span>
</div>
I have a dropdown which will show hide the postcode field, so if uk selected the postcode field will show. The field is required but additionally I am doing a check in whether is a valid postcode via a webservice. The angular controller that deals with form submission looks like:
$scope.submitForm = function () {
$scope.registrationForm.$submitted = true;
if ($scope.enableSubmit()) {
registrationForm.submit();
}
};
$scope.postcodeRequired = function () {
return $scope.country === 'United Kingdom';
};
$scope.validPostCode = function () {
if ($scope.postcodeRequired()) {
if ($scope.postcode !== undefined && $scope.postcode.length > 5) {
postcodeService.ValidatePostCode($scope.postcode).success(function (response) {
return response;
});
} else {
return false;
}
}
return true;
};
$scope.enableSubmit = function () {
return $scope.registrationForm.$valid
&& $scope.passwordsMatch()
&& $scope.acceptTerms
&& $scope.validPostCode();
};
The postCodeService is just doing an http get to validate the post code that returns true or false. The issue i have is on submitting it validates the postcode but then goes into a loop and gives the following error:
angular.min.js:34 Uncaught Error: [$rootScope:infdig] http://errors.angularjs.org/1.4.0/$rootScope/infdig?p0=10&p1=%5B%5D
at angular.min.js:34
at m.$digest (angular.min.js:563)
at m.$apply (angular.min.js:571)
at l (angular.min.js:373)
at O (angular.min.js:388)
at XMLHttpRequest.N.onload (angular.min.js:392)
I have seen other people with this issue when doing an ng-repeat but as you can see I am not doing that.
Any ideas?
Without a plunkr to test against and verify its hard to tell exactly what is causing the infinite digest cycle loop. However I believe it might be cause by the amount of calls made towards your $scope.validPostCode function (which wasn't correctly returning its validity). Basically the change proposed is to only call the validate function when the postcode has been changed (trigged by ng-change on the field). The result of that function sets $scope.validPostCode variable to true or false, which is then what is checked for validity;
HTML (add ng-change to the input)
#Html.TextBoxFor(x => x.Postcode, new { <!-- other attributes -->, ng_change = "validatePostCode()" })
JavaScript
$scope.postcodeRequired = function () {
return $scope.country === 'United Kingdom';
};
// by default its not valid
$scope.validPostCode = false;
// our Validition check
$scope.validatePostCode = function () {
if ($scope.postcodeRequired()) {
if ($scope.postcode !== undefined && $scope.postcode.length > 5) {
postcodeService.ValidatePostCode($scope.postcode).success(function (response) {
$scope.validPostCode = response;
});
} else {
$scope.validPostCode = false;
}
} else {
$scope.validPostCode = true;
}
};
// call our function to properly set the initial validity state.
$scope.validatePostCode();
$scope.enableSubmit = function () {
return $scope.registrationForm.$valid
&& $scope.passwordsMatch()
&& $scope.acceptTerms
&& $scope.validPostCode;
};
Using Angularjs here:
I have a form where user fills up some data and clicks save button to save data :
$scope.save = function (isValid) {
if (isValid) {
if (!$scope.checkBoxChecked) {
$scope.getExistingName($scope.uName);
}
var name = $scope.uName != '' ? $scope.uName? : 'testuser';
//Call save api
}
else {
return false;
}
};
In the save method I am checking for uname, which gets it value by calling another api as below:
$scope.getExistingName = function (uName) {
myService.getDataFromApi(uName).then(function (data) {
var existingSetName = '';
if(data.length >0)
{
$scope.uName = data[i].uName;
}
});
}
The issue is $scope.uName in my Save button is always ''. I tried to debug and found out that the result from the $scope.getExistingName method
being is promise is deffered and returned after the orignal call. Because of which my $scope.uName is empty.
What am I missing here?
--Updated---
Save method:
var name = $scope.getExistingName($scope.uName);
Updated getExistingName
$scope.getExistingName = function (uName) {
return myService.getDataFromApi(uName).then(function (data) {
var existingSetName = '';
if(data.length >0)
{
return data[i].uName;
}
});
}
--This works--
if (!$scope.checkBoxChecked) {
$scope.getExistingName($scope.uName).then(function(data)
{
var name = $scope.uName != '' ? $scope.uName? : 'testuser';
//Call save api
});
}
else
{
var name = $scope.uName != '' ? $scope.uName? : 'testuser';
//Call save api
}
I've this code that work in genymotion enviroment. I need to record audio, so this is my code:
controllers.js
.controller('PlaylistsCtrl', function($scope, $cordovaMedia, $ionicPlatform) {
function __log(e, data) {
log.innerHTML += "\n" + e + " " + (data || '');
}
var recorder = null;
$scope.onSuccess = function() {
console.log('Media success');
}
$scope.onError = function(e) {
console.log('Media error', e);
}
$scope.onStatus = function(type) {
console.log('Media status', type);
}
$scope.startRecording = function () {
if (recorder == null) {
console.log('init recorder');
recorder = new Media('record.mp3', $scope.onSuccess, $scope.onError, $scope.onStatus);
}
recorder.startRecord();
recordBtn.disabled = true;
stopBtn.disabled = false;
__log('startRecording');
}
$scope.stopRecording = function() {
recorder.stopRecord();
__log('stopRecording');
}
$scope.playRecording = function() {
recorder.play();
__log('playRecording');
__log( JSON.stringify(recorder) )
console.log( JSON.stringify(recorder) );
}
record.html
<button id='recordBtn' ng-click='startRecording()'>record</button>
<button id='stopBtn' ng-click='stopRecording()'>stop</button>
<button id='playBtn' ng-click='playRecording()'>play</button>
<h2>Log</h2>
<pre id="log"></pre>
My problem is when i run my app in ionic viewer, and i click to #recordBtn, , nothing happens, startRecording() not work. Thank you !
i am using bootstrap-wysiwyg rich text editor in my application and i am not able to open the hyerlink created in a new window.
i am using the bootstrap-wysiwyg.js file which is below. i am not able to figure out how to make the hyperlink created, to open in a new tab.
(function ($) {
'use strict';
/** underscoreThrottle()
* From underscore http://underscorejs.org/docs/underscore.html
*/
var underscoreThrottle = function(func, wait) {
var context, args, timeout, result;
var previous = 0;
var later = function() {
previous = new Date;
timeout = null;
result = func.apply(context, args);
};
return function() {
var now = new Date;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
} else if (!timeout) {
timeout = setTimeout(later, remaining);
}
return result;
};
}
var readFileIntoDataUrl = function (fileInfo) {
var loader = $.Deferred(),
fReader = new FileReader();
fReader.onload = function (e) {
loader.resolve(e.target.result);
};
fReader.onerror = loader.reject;
fReader.onprogress = loader.notify;
fReader.readAsDataURL(fileInfo);
return loader.promise();
};
$.fn.cleanHtml = function (o) {
if ( $(this).data("wysiwyg-html-mode") === true ) {
$(this).html($(this).text());
$(this).attr('contenteditable',true);
$(this).data('wysiwyg-html-mode',false);
}
// Strip the images with src="data:image/.." out;
if ( o === true && $(this).parent().is("form") ) {
var gGal = $(this).html;
if ( $(gGal).has( "img" ).length ) {
var gImages = $( "img", $(gGal));
var gResults = [];
var gEditor = $(this).parent();
$.each(gImages, function(i,v) {
if ( $(v).attr('src').match(/^data:image\/.*$/) ) {
gResults.push(gImages[i]);
$(gEditor).prepend("<input value='"+$(v).attr('src')+"' type='hidden' name='postedimage/"+i+"' />");
$(v).attr('src', 'postedimage/'+i);
}});
}
}
var html = $(this).html();
return html && html.replace(/(<br>|\s|<div><br><\/div>| )*$/, '');
};
$.fn.wysiwyg = function (userOptions) {
var editor = this,
wrapper = $(editor).parent(),
selectedRange,
options,
toolbarBtnSelector,
updateToolbar = function () {
if (options.activeToolbarClass) {
$(options.toolbarSelector,wrapper).find(toolbarBtnSelector).each(underscoreThrottle(function () {
var commandArr = $(this).data(options.commandRole).split(' '),
command = commandArr[0];
// If the command has an argument and its value matches this button. == used for string/number comparison
if (commandArr.length > 1 && document.queryCommandEnabled(command) && document.queryCommandValue(command) == commandArr[1]) {
$(this).addClass(options.activeToolbarClass);
// Else if the command has no arguments and it is active
} else if (commandArr.length === 1 && document.queryCommandEnabled(command) && document.queryCommandState(command)) {
$(this).addClass(options.activeToolbarClass);
// Else the command is not active
} else {
$(this).removeClass(options.activeToolbarClass);
}
}, options.keypressTimeout));
}
},
execCommand = function (commandWithArgs, valueArg) {
var commandArr = commandWithArgs.split(' '),
command = commandArr.shift(),
args = commandArr.join(' ') + (valueArg || '');
var parts = commandWithArgs.split('-');
if ( parts.length == 1 ) {
document.execCommand(command, 0, args);
}
else if ( parts[0] == 'format' && parts.length == 2) {
document.execCommand('formatBlock', false, parts[1] );
}
editor.trigger('change');
updateToolbar();
},
bindHotkeys = function (hotKeys) {
$.each(hotKeys, function (hotkey, command) {
editor.keydown(hotkey, function (e) {
if (editor.attr('contenteditable') && editor.is(':visible')) {
e.preventDefault();
e.stopPropagation();
execCommand(command);
}
}).keyup(hotkey, function (e) {
if (editor.attr('contenteditable') && editor.is(':visible')) {
e.preventDefault();
e.stopPropagation();
}
});
});
editor.keyup(function(){ editor.trigger('change'); });
},
getCurrentRange = function () {
var sel, range;
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
}
} else if (document.selection) {
range = document.selection.createRange();
} return range;
},
saveSelection = function () {
selectedRange = getCurrentRange();
},
restoreSelection = function () {
var selection;
if (window.getSelection || document.createRange) {
selection = window.getSelection();
if (selectedRange) {
try {
selection.removeAllRanges();
} catch (ex) {
document.body.createTextRange().select();
document.selection.empty();
}
selection.addRange(selectedRange);
}
}
else if (document.selection && selectedRange) {
selectedRange.select()
}
},
// Adding Toggle HTML based on the work by #jd0000, but cleaned up a little to work in this context.
toggleHtmlEdit = function(a) {
if ( $(editor).data("wysiwyg-html-mode") !== true ) {
var oContent = $(editor).html();
var editorPre = $( "<pre />" )
$(editorPre).append( document.createTextNode( oContent ) );
$(editorPre).attr('contenteditable',true);
$(editor).html(' ');
$(editor).append($(editorPre));
$(editor).attr('contenteditable', false);
$(editor).data("wysiwyg-html-mode", true);
$(editorPre).focus();
}
else {
$(editor).html($(editor).text());
$(editor).attr('contenteditable',true);
$(editor).data('wysiwyg-html-mode',false);
$(editor).focus();
}
},
insertFiles = function (files) {
editor.focus();
$.each(files, function (idx, fileInfo) {
if (/^image\//.test(fileInfo.type)) {
$.when(readFileIntoDataUrl(fileInfo)).done(function (dataUrl) {
execCommand('insertimage', dataUrl);
editor.trigger('image-inserted');
}).fail(function (e) {
options.fileUploadError("file-reader", e);
});
} else {
options.fileUploadError("unsupported-file-type", fileInfo.type);
}
});
},
markSelection = function (input, color) {
restoreSelection();
if (document.queryCommandSupported('hiliteColor')) {
document.execCommand('hiliteColor', 0, color || 'transparent');
}
saveSelection();
input.data(options.selectionMarker, color);
},
bindToolbar = function (toolbar, options) {
toolbar.find(toolbarBtnSelector, wrapper).click(function () {
restoreSelection();
editor.focus();
if ($(this).data(options.commandRole) === 'html') {
toggleHtmlEdit();
}
else {
execCommand($(this).data(options.commandRole));
}
saveSelection();
});
toolbar.find('[data-toggle=dropdown]').click(restoreSelection);
toolbar.find('input[type=text][data-' + options.commandRole + ']').on('webkitspeechchange change', function () {
var newValue = this.value; /* ugly but prevents fake double-calls due to selection restoration */
this.value = '';
restoreSelection();
if (newValue) {
editor.focus();
execCommand($(this).data(options.commandRole), newValue);
}
saveSelection();
}).on('focus', function () {
var input = $(this);
if (!input.data(options.selectionMarker)) {
markSelection(input, options.selectionColor);
input.focus();
}
}).on('blur', function () {
var input = $(this);
if (input.data(options.selectionMarker)) {
markSelection(input, false);
}
});
toolbar.find('input[type=file][data-' + options.commandRole + ']').change(function () {
restoreSelection();
if (this.type === 'file' && this.files && this.files.length > 0) {
insertFiles(this.files);
}
saveSelection();
this.value = '';
});
},
initFileDrops = function () {
editor.on('dragenter dragover', false)
.on('drop', function (e) {
var dataTransfer = e.originalEvent.dataTransfer;
e.stopPropagation();
e.preventDefault();
if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) {
insertFiles(dataTransfer.files);
}
});
};
options = $.extend(true, {}, $.fn.wysiwyg.defaults, $.fn.wysiwyg.defaults1, userOptions);
toolbarBtnSelector = 'a[data-' + options.commandRole + '],button[data-' + options.commandRole + '],input[type=button][data-' + options.commandRole + ']';
bindHotkeys(options.hotKeys);
// Support placeholder attribute on the DIV
if ($(this).attr('placeholder') != '') {
$(this).addClass('placeholderText');
$(this).html($(this).attr('placeholder'));
$(this).bind('focus',function(e) {
if ( $(this).attr('placeholder') != '' && $(this).text() == $(this).attr('placeholder') ) {
$(this).removeClass('placeholderText');
$(this).html('');
}
});
$(this).bind('blur',function(e) {
if ( $(this).attr('placeholder') != '' && $(this).text() == '' ) {
$(this).addClass('placeholderText');
$(this).html($(this).attr('placeholder'));
}
})
}
if (options.dragAndDropImages) {
initFileDrops();
}
bindToolbar($(options.toolbarSelector), options);
editor.attr('contenteditable', true)
.on('mouseup keyup mouseout', function () {
saveSelection();
updateToolbar();
});
$(window).bind('touchend', function (e) {
var isInside = (editor.is(e.target) || editor.has(e.target).length > 0),
currentRange = getCurrentRange(),
clear = currentRange && (currentRange.startContainer === currentRange.endContainer && currentRange.startOffset === currentRange.endOffset);
if (!clear || isInside) {
saveSelection();
updateToolbar();
}
});
return this;
};
$.fn.wysiwyg.defaults = {
hotKeys: {
'Ctrl+b meta+b': 'bold',
'Ctrl+i meta+i': 'italic',
'Ctrl+u meta+u': 'underline',
'Ctrl+z': 'undo',
'Ctrl+y meta+y meta+shift+z': 'redo',
'Ctrl+l meta+l': 'justifyleft',
'Ctrl+r meta+r': 'justifyright',
'Ctrl+e meta+e': 'justifycenter',
'Ctrl+j meta+j': 'justifyfull',
'Shift+tab': 'outdent',
'tab': 'indent'
},
toolbarSelector: '[data-role=editor-toolbar]',
commandRole: 'edit',
activeToolbarClass: 'btn-info',
selectionMarker: 'edit-focus-marker',
selectionColor: 'darkgrey',
dragAndDropImages: true,
keypressTimeout: 200,
fileUploadError: function (reason, detail) { console.log("File upload error", reason, detail); }
};
$.fn.wysiwyg.defaults1 = {
hotKeys: {
'Ctrl+b meta+b': 'bold',
'Ctrl+i meta+i': 'italic',
'Ctrl+u meta+u': 'underline',
'Ctrl+z': 'undo',
'Ctrl+y meta+y meta+shift+z': 'redo',
'Ctrl+l meta+l': 'justifyleft',
'Ctrl+r meta+r': 'justifyright',
'Ctrl+e meta+e': 'justifycenter',
'Ctrl+j meta+j': 'justifyfull',
'Shift+tab': 'outdent',
'tab': 'indent'
},
toolbarSelector: '[data-role=editor1-toolbar]',
commandRole: 'edit',
activeToolbarClass: 'btn-info',
selectionMarker: 'edit-focus-marker',
selectionColor: 'darkgrey',
dragAndDropImages: true,
keypressTimeout: 200,
fileUploadError: function (reason, detail) { console.log("File upload error", reason, detail); }
};
}(window.jQuery));
#
i am saving the editor contents and then i am retrieving it in a different page as below.
<p class="textAlignLeft" ng-bind-html="editorContent | unsafe"></p>
"editorContent" will have the contents entered in the richtext editor, and the hyper link in it has to open in a new window.
in the browser console i am getting the following output.
<p class="textAlignLeft ng-binding" ng-bind-html="editorContent | unsafe">ajslkjsak sdsad</p>
One way of doing that would be to bind to click event of anchor tag and open URL in new tab using JavaScript.
Say, the ID of your editor is "editor", the following code would work with it
$("a", "#editor").click(function(e) {
window.open($(this).attr('href'), '_blank')
});
this will bind click event on all the a tags inside editor div and when the user clicks any of it, the url will be opened in new window.
Update:
using jQuery, it is very easy. First assign an id to your <p> tag like
<p id="myCustomContent" class="textAlignLeft" ng-bind-html="newsContent | unsafe"></p>
Where myCustomContent is the id
now use the following code
$("a", "#myCustomContent").each(function() {
$(this).attr('target', '_blank');
});
this will loop once on all the anchor tags and make them open in new tab when user clicks on them.
here is my all code i am trying to upload small image and large image separate but angularjs not let me allow to do this, it only taking one file but not taking other one. plz anyone help with this. thanks in advance.
<div ng-app="eventModule" >
<div ng-controller="eventController">
<div>
<span >Thumbnail Image</span>
<input type="file" id="fileToUpload" onchange="angular.element(this).scope().selectThumbnail(this.files)" accept="image/*" />
</div>
<div>
<span >Large Image</span>
<input type="file" onchange="angular.element(this).scope().selectLargeImage(this.files)" class="LargeImageSubCategory" />
</div>
</div>
<span data-ng-click="SaveFile()">Submit</span>
</div>
<script>
var eventModule = angular.module('eventModule', []);
eventModule.controller('eventController', function ($scope,ArticleService, $http, $sce) {
$scope.selectThumbnail = function (file) {
$scope.SelectedThumbnail = file[0];
}
$scope.selectLargeImage = function (file) {
$scope.SelectedLargeImage = file[0];
}
$scope.SaveFile = function () {
$scope.IsFormSubmitted = true;
$scope.Message = "";
ArticleService.UploadFile($scope.SelectedThumbnail, $scope.SelectedLargeImage).then(function (d) {
alert(d.Message);
ClearForm();
}, function (e) {
alert(e);
});
};
});
eventModule.service("ArticleService", function ($http, $q) {
this.UploadFile = function (Thumbnail, LargeImage, TitleHeading, Topic, SmallDesc, LargeDesc) {
var formData = new FormData();
formData.append("Thumbnail", Thumbnail);
formData.append("LargeImage", LargeImage);
// here when i am trying to send two files so controller is not called
//and function is breaking and alert is comming "File Upload Failed"
formData.append("TitleHeading", TitleHeading);
formData.append("Topic", Topic);
var defer = $q.defer();
$http.post("/Articles/SaveFiles", formData,
{
withCredentials: true,
headers: { 'Content-Type': undefined },
transformRequest: angular.identity
}).success(function (d) {
defer.resolve(d);
}).error(function () {
defer.reject("File Upload Failed!");
});
return defer.promise;
}
});
</script>
//And My ArticlesController.cs code is
[HttpPost]
public JsonResult SaveFiles(string TitleHeading, string Topic)
{
string Message, fileName, actualFileName;
Message = fileName = actualFileName = string.Empty;
bool flag = false;
if (Request.Files != null)
{
var file = Request.Files[0];
actualFileName = file.FileName;
fileName = Guid.NewGuid() + Path.GetExtension(file.FileName);
int size = file.ContentLength;
try
{
file.SaveAs(Path.Combine(Server.MapPath("~/UploadedFiles"), fileName));
using (TCDataClassesDataContext dc = new TCDataClassesDataContext())
{
Article insert = new Article();
insert.ArticleId = Guid.NewGuid();
insert.TitleHeading = TitleHeading;
insert.SmallImagePath = fileName;
dc.Articles.InsertOnSubmit(insert);
dc.SubmitChanges();
Message = "File uploaded successfully";
flag = true;
}
}
catch (Exception)
{
Message = "File upload failed! Please try again";
}}
return new JsonResult { Data = new { Message = Message, Status = flag } };
}
You are appending the files to the formdata, thus you need to specify the Thumbnail and LargeImage as parameters of your MVC controller. Please see below:
[HttpPost]
public JsonResult SaveFiles(
HttpPostedFileBase thumbnail
, HttpPostedFileBase largeImage
, string titleHeading
, string topic)
{
string Message, fileName, actualFileName;
Message = fileName = actualFileName = string.Empty;
bool flag = false;
if (thumbnail != null && thumbnail.ContentLength != 0)
{
SaveFile(thumbnail);
}
if (largeImage != null && largeImage.ContentLength != 0)
{
SaveFile(largeImage);
}
return new JsonResult { Data = new { Message = Message, Status = flag } };
}
private void SaveFile(
HttpPostedFileBase httpFile)
{
var actualFileName = httpFile.FileName;
var fileName = Guid.NewGuid() + Path.GetExtension(httpFile.FileName);
int size = httpFile.ContentLength;
try
{
httpFile.SaveAs(Path.Combine(Server.MapPath("~/UploadedFiles"), fileName));
using (TCDataClassesDataContext dc = new TCDataClassesDataContext())
{
Article insert = new Article();
insert.ArticleId = Guid.NewGuid();
insert.TitleHeading = TitleHeading;
insert.SmallImagePath = fileName;
dc.Articles.InsertOnSubmit(insert);
dc.SubmitChanges();
Message = "File uploaded successfully";
flag = true;
}
}
catch (Exception)
{
Message = "File upload failed! Please try again";
}
}