Confuse about angular expressions - javascript

Here is what I want achieve: when user input some text, I will find the character a and using <em> tag to emphasize it in another <label>, here is my html markup:
<div ng-controller="demoController">
<input type="text" ng-model="input" />
<label>{{emphasize()}}</label>
</div>
As showing above, I'm using method emphasize in demoController to do emphasize job:
myapp.controller('demoController', function ($scope) {
$scope.input = 'Hello';
$scope.emphasize = function () {
return $scope.input.replace(/a/g, '<em>a</em>');
}
});
But the result is, the angular escape the <em> tag. For instance , if I input apple, then the label would show <em>a</em>pple, not I want: apple.
So why does this happened? Is there a way I can prevent this happen or another way to do it?

To do so a simple ng-bind-hmtl will do the trick :
<span ng-bind-html="emphasize()"></span>
But this is not really safe so it's always better to add this on your controller :
myapp.controller('demoController', function ($scope, $sce) {
$scope.input = 'angularJS';
$scope.emphasize = function () {
var res = $scope.input.replace(/a/g, '<em>a</em>');
return $sce.trustAsHtml(res);
}
});

add a filter to your module:
myapp.filter('unsafe', ['$sce', function ($sce) {
return function (a) { return $sce.trustAsHtml(a) };
}]);
and in your view:
<span ng-bind-html="emphasize() | unsafe"></span

Related

Angular filter: how to get filter complete callback?

I am looking for an callback function after $filter filter completes the filtering the data
HTML
<input type="text" ng-model="searchvalue">
<span ng-click="searchbtn()">Search</span>
JS
$scope.searchbtn = function() {
$scope.loading = true;
$scope.mysearchvalue = $scope.searchvalue;
}
When user enters keyword my data will be filtered and i need a callback function after filtering the data.
i have tried using "DOMSubtreeModified" but returning continues logs
var myElement = angular.element(document.getElementById("mycontent"));
myElement.bind("DOMSubtreeModified", function() {
console.log("keep outputting this message");
});
As said in my comment, why not just use a little delay (debounce) and filter results in controller w/o separate input button. Consider
HTML template
<body ng-controller="MainCtrl">
<pre ng-bind="filteredData | json"></pre>
<input type="text" ng-model="search" ng-model-options="{debounce:250}">
</body>
JavaScript
angular.module('app', [])
.controller('MainCtrl', function($scope, $filter) {
$scope.data = [{text:'aa'},{text:'ab'}];
$scope.$watch('search', function(val) {
$scope.filteredData = $filter('filter')($scope.data, val);
});
});

Directive doesn't fire after changing textarea model

I have a text with newline separator and URLs:
first row\nFind me at http://www.example.com and also\n at http://stackoverflow.com.
I want to update ng-repeat values after pressing on copy button.
I have this HTML:
<div ng-controller="myCntrl">
<textarea ng-model="copy_note_value"></textarea>
<button data-ng-click="copy()">copy</button>
<div>
<p ng-repeat="row in note_value.split('\n') track by $index"
wm-urlify="row"
style="display: inline-block;"
>
</p>
</div>
</div>
Controller:
app.controller('myCntrl', function ($scope) {
$scope.note_value = "first row\nFind me at http://www.example.com and also\n at http://stackoverflow.com";
$scope.copy_note_value = angular.copy($scope.note_value);
$scope.copy = function(){
$scope.note_value = angular.copy($scope.copy_note_value);
}
});
I have directive that should take text and return urlfied text:
app.directive('wmUrlify', ['$parse', function ($parse) {
return {
restrict: 'A',
scope: true,
link: function (scope, element, attrs) {
function urlify(text) {
var urlRegex = /(https?:\/\/[^\s]+)/g;
return text.replace(urlRegex, function (url) {
return '' + url + '';
})
}
var text = $parse(attrs.wmUrlify)(scope);
var html = urlify(text);
element[0].inneHtml(html)
}
};
}]);
Here is a flow: User changes text in textarea and presses on copy button. I expect to show the change in ng-repeat.
It works only if I add a new line and not line content.
What is wrong here? This is my Fiddle
Just remove the track by $index from your ng-repeat. This is because you are telling Angular that the value of note_value.split('\n') will only be changed when there is a change in the $index i.e. size of the array after splitting by new line.
But the default implementation of track by is the identity of each item. So when you changed the default implementation to track it by the $index and when you are not not adding a new line instead just updating the content of any existing line, Angular is not able to detect that there is a change.
Update
Removing the track by $index function will throw an exception when there are same values after split. So you can use a simple function like: (define it in your controller)
$scope.indexFunction = function($index, val) {
// Creating an unique identity based on the index and the value
return $index + val;
};
And then use it in your ng-repeat like:
<p ng-repeat="row in note_value.split('\n') track by indexFunction($index, row)"></p>
https://docs.angularjs.org/api/ng/directive/ngRepeat

AngularJS currency filter on input field

I have the following input field
<input type="text" class="form-control pull-right" ng-model="ceremony.CeremonyFee | number:2">
it is showing up correctly but has been disabled. The error I am receiving is "[ngModel:nonassign] Expression 'ceremony.CeremonyFee | number:2' is non-assignable". I understand why it is in error, but do not know how to get this to work on an input field. Thanks.
input with ng-model is for inputting data, number filter is for displaying data. As filter values are not bindable, they are not compatible, as you can see. You have to decide what you want to do with that input.
Do you want it to be an input? User can input his own number and you only needs to validate? Use i.e. pattern attribute:
<input type="text" ng-model="ceremony.CeremonyFee" pattern="[0-9]+(.[0-9]{,2})?">
Do you want it to be an output? User does not need to input his own value? Do not use ng-model, use value instead:
<input type="text" value="{{ceremony.CeremonyFee | number:2}}" readonly>
UPDATE:
really I don't understand what you need, but, if you want just that users can insert only two digits you should use a simple html attributes, have a look on min, max, step...
Follows a pure js solution, but I don't suggest something like that!
angular.module('test', []).controller('TestCtrl', function($scope) {
var vm = $scope;
var testValue = 0;
Object.defineProperty(vm, 'testValue', {
get: function() { return testValue; },
set: function(val) {
val = Number(val);
if(angular.isNumber(val) && (val < 100 && val > 0)) {
console.log(val);
testValue = val;
}
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<section ng-app="test">
<div ng-controller="TestCtrl">
<input style="display:block; width: 100%; padding: 1em .5em;" type="number" ng-model="testValue" />
</div>
</section>
the ng-model directive requires a viewmodel assignable (or bindable) property, so, you cannot add a pipe...
angular.module('test', [])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test" ng-init="testValue = 0">
<label ng-bind="testValue | currency"></label>
<input style="display:block;" ng-model="testValue" type="number"/>
</div>
As an error states you have got an 'non-assignable' expression in your ng-model attribute.
You should use only ceremony.CeremonyFee.
| is used on ng-repeat to indicate what expression should be used as filter.
If you want to have that <input> populated with initial data in your controller/link you should give it an initial value ex.
$scope.ceremony = {
CeremonyFee: 'My first ceremony'
}
And every time your <input> element data will be changed CeremonyFee will be updated as well.
I found and used the solution found on this page.
http://jsfiddle.net/k7Lq0rns/1/
'use strict';
angular.module('induction').$inject = ['$scope'];
angular.module('induction').directive('format',['$filter', function ($filter) {
  return {
require: '?ngModel',
link: function (scope, elem, attrs, ctrl) {
if (!ctrl) return;
ctrl.$formatters.unshift(function (a) {
return $filter(attrs.format)(ctrl.$modelValue)
});
elem.bind('blur', function(event) {
var plainNumber = elem.val().replace(/[^\d|\-+|\.+]/g, '');
elem.val($filter(attrs.format)(plainNumber));
});
}
  };
}]);
relatively easy to apply it.

Ng-model with Cookie

I'm trying to take the first example from the angular.js homepage and adding in cookie support.
This is what I have so far: https://jsfiddle.net/y7dxa6n8/8/
It is:
<div ng-app="myApp">
<div ng-controller="MyController as mc">
<label>Name:</label>
<input type="text" ng-model="mc.user" placeholder="Enter a name here">
<hr>
<h1>Hello {{mc.user}}!</h1>
</div>
</div>
var myApp = angular.module('myApp', ['ngCookies']);
myApp.controller('MyController', [function($cookies) {
this.getCookieValue = function () {
$cookies.put('user', this.user);
return $cookies.get('user');
}
this.user = this.getCookieValue();
}]);
But it's not working, ive been trying to learn angular.
Thanks
I'd suggest you create a service as such in the app module:
app.service('shareDataService', ['$cookieStore', function ($cookieStore) {
var _setAppData = function (key, data) { //userId, userName) {
$cookieStore.put(key, data);
};
var _getAppData = function (key) {
var appData = $cookieStore.get(key);
return appData;
};
return {
setAppData: _setAppData,
getAppData: _getAppData
};
}]);
Inject the shareDataService in the controller to set and get cookie value
as:
//set
var userData = { 'userId': $scope.userId, 'userName': $scope.userName };
shareDataService.setAppData('userData', userData);
//get
var sharedUserData = shareDataService.getAppData('userData');
$scope.userId = sharedUserData.userId;
$scope.userName = sharedUserData.userName;
Working Fiddle: https://jsfiddle.net/y7dxa6n8/10/
I have used the cookie service between two controllers. Fill out the text box to see how it gets utilized.
ok, examined your code once again, and here is your answer
https://jsfiddle.net/wz3kgak3/
problem - wrong syntax: notice definition of controller, not using [] as second parameter
If you are using [] in controller, you must use it this way:
myApp.controller('MyController', ['$cookies', function($cookies) {
....
}]);
this "long" format is javascript uglyfier safe, when param $cookies will become a or b or so, and will be inaccessible as $cookies, so you are telling that controller: "first parameter in my function is cookies
problem: you are using angular 1.3.x, there is no method PUT or GET in $cookies, that methods are avalaible only in angular 1.4+, so you need to use it old way: $cookies.user = 'something'; and getter: var something = $cookies.user;
problem - you are not storing that cookie value, model is updated, but cookie is not automatically binded, so use $watch for watching changes in user and store it:
$watch('user', function(newValue) {
$cookies.user = newValues;
});
or do it via some event (click, submit or i dont know where)
EDIT: full working example with $scope
https://jsfiddle.net/mwcxv820/

AngularJS template with brackets in it

Why AngularJS doesn't accept brackets inside a ng-template content? I need it to create an input that's going to be an array, but I get this error:
"Error: Syntax Error: Token ']' not a primary expression at column 15 of the expression [form.interval[]] starting at []]."
angular.module("main", []).controller("MyCtrl", function($scope) {
}).directive("ngPortlet", function ($compile) {
return {
template: '<input class="form-control" type="text" placeholder="Interval" ng-model="form.interval[]" />',
restrict: 'E',
link: function (scope, elm) {
scope.add = function(){
console.log(elm);
elm.after($compile('<ng-portlet></ng-portlet>')(scope));
}
}
};
});
<div ng-app="main">
<div ng-controller="MyCtrl">
<div id="container">
<button ng-click="add()" >Add</button>
<ng-portlet></ng-portlet>
</div>
</div>
</div>
jsfiddle:
http://jsfiddle.net/7kcrrapm/1/
EDIT:
Now that I better understand what you're trying to accomplish, here is a different approach:
angular.module("main", []).controller("MyCtrl", function($scope) {
}).directive("ngPortlet", function ($compile) {
return {
template: '<input class="form-control" type="text" placeholder="Interval" ng-model="interval" />',
restrict: 'E',
link: function (scope, elm) {
var intervals = [];
scope.add = function(){
intervals.push(parseInt(scope.interval, 10));
console.log(intervals);
}
}
};
});
Now you have access to an array (intervals) that contains a list of all intervals added.
ORIGINAL:
form.interval[] is not valid JavaScript and thus not a valid scope property. If you need the property to be an array you can simply declare it in your controller ("MyCtrl"):
$scope.form.interval = [];
If you don't create the scope property in the controller your self, it will be implicitly created by the ng-model directive. You can find more info in the docs. I might also suggest this great read about Scopes in the official Angular Wiki
From what I understand, what you really want is ng-repeat.
<span ng-repeat="hour in form.interval">
<input class="form-control" type="text" placeholder="Interval" ng-model="hour" />
</span>
Declare the variable inside the controller or directive:
$scope.form.interval = [];
When you do add() to get another input, add a blank entry to the array in the controller or directive:
$scope.form.interval.push('');
Call add() when you create the variable if you want to start with one empty input box.
The reason it's not working, is because [] is invalid JavaScript syntax on a variable reference.
interval = [1, 2, 3]; // Ok.
interval = []; // Also Ok.
var foo = interval[]; // This isn't valid!
Take those square brackets off, or if you're wanting to do a ng-repeat setup you might consider some of the other given answers.

Categories

Resources