I have this function in jQuery but I'm facing problem converting that into angularJs function:
$('p').each(function() {
$(this).html($(this).text().split(/([\.\?!])(?= )/).map(
function(v){return '<span class=sentence>'+v+'</span>'}
));
It would really help if someone could explain to me how one would implement these lines of code in angularJs
Thanks in advance guys
easiest way is just splitting into an array and binding that to your controller.
More in depth way would be to roll your own custom directive, let me know if you want to see a way with a directive
<html ng-app="MyCoolApp">
<head>
</head>
<body ng-controller="MyCoolController">
<p ng-repeat="text in Array">
{{text}}
</P>
</body>
</html>
<script type="text/javascript">
var myApp = angular.module('MyCoolApp',[]);
myApp.controller('MyCoolController', ['$scope', function($scope) {
var dataSource = [];//your data split
$scope.Array = dataSource;
}]);
</script>
edit
Updated to use custom directive. YOu will need to tweak the regEx to split properly plunkr example
split.html
<div>
<textarea ng-model="input"></textarea>
<button ng-click="Process(input)">Process</button>
<p>
<span style="border:1px solid red" ng-repeat="text in Output track by $index">
{{text}}
</span>
</p>
</div>
script.js
(function(angular) {
'use strict';
angular.module('splitExample', [])
.directive('mySplit', function() {
return {
templateUrl: 'split.html',
controller: function($scope){
$scope.Process = function(text){
$scope.Output = text.split(/([\.\?!])(?= )/);
console.log(text)
}
}
};
})
})(window.angular);
html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example - example-directive-tabs-production</title>
<script src="//code.angularjs.org/snapshot/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="splitExample">
<my-split>
</my-split>
</body>
</html>
You need to write a directive for it, DOM Manipulation is only allowed in directives in the link function otherwise it is a very bad practice. Here is the code inside link function of directive.
link: function (scope, element, attributes) {
var el= $(element);
el.find('p').each(function(){
el.html(el.text().split(/([\.\?!])(?= )/).map(
function(value){
return '<span class=sentence>'+value+'</span>'
}
));
});
}
Hope it helps you, I am unable to know actually what you are trying to do.. otherwise i would have helped you with full solution and proper directive approach, for any further assistance let me know. Thanks.
Just giving my suggestion : JQuery approach is incompatible with AngularJS.
Updated directive as per your requirement:
angular.module('yourAppModuleName').directive('contentClick', ['$timeout', function($timeout) {
return {
restrict: 'A',
require: 'ngModel',
scope: {
contentEditable: '=contentEditable'
},
link: function(scope, element, attributes) {
scope.$watch(attributes.contentEditable, function(value) {
if (value === undefined)
return;
scope.contentEditable = attributes.contentEditable;
});
if(scope.contentEditable == 'true') {
scope.$watch(attributes.ngModel, function(value) {
if (value === undefined)
return;
value.split(/([\.\?!])(?= )/).map(
function(v){
return '<span onclick="myFunction(this)" >'+v+'</span>'
}
);
});
}
//without timeout
//function myFunction(x) {
// x.style.background="#000000";
//}
//with timeout
function myFunction(x) {
$timeout(function() {
x.style.background = "green";
}, 10000);
}
}
};
}]);
HTML:
<div contentEditable="isEditable" style="cursor:pointer" ng-model="content" content-click></div>
Controller:
yourAppModuleName.controller('myController', ['$scope', function($scope) {
$scope.isEditable = true;
$scope.content;
}]);
Related
I have a mean-stack website. I want to dynamically construct a variable that contains a valid html string, then render it in an iframe. After some research, I tried the following code: (JSBin)
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<script src="https://cdn.rawgit.com/jugglinmike/srcdoc-polyfill/master/srcdoc-polyfill.min.js"></script>
</head>
<body ng-app="app" ng-controller="Ctrl">
<iframe srcdoc="{{content | toTrusted}}"></iframe>
<script>
var app = angular.module('app', []);
app.controller("Ctrl", ["$scope", function($scope) {
$scope.content = "<b>hello</b>";
}])
app.filter('toTrusted', ['$sce', function($sce) {
return function(text) {
return $sce.trustAsHtml(text);
};
}]);
</script>
</body>
</html>
It works fine in Chrome, whereas it does not work in IE (eg, IE 11), even though I use src-polyfill.
Does anyone have a solution?
app.directive('iframeDoc', [ '$sce', function ($sce) {
return {
restrict:"A",
scope:{
iframeDoc:'='
},
link: function(scope, elem, attrs) {
elem.attr("srcdoc", $sce.trustAsHtml(scope.iframeDoc));
// Check browser to support scrdoc.
var isSupportSrcDoc= !!("srcdoc" in document.createElement("iframe"));
if(!isSupportSrcDoc){
var jsUrl = "javascript: window.frameElement.getAttribute('srcdoc');";
if (elem[0].contentWindow) {
elem[0].contentWindow.location = jsUrl;
}
elem.attr("src", jsUrl);
}
}
};
}])
In html:
<iframe iframe-doc="content"></iframe>
When I use this script:
<script>
function TestClick() {
alert("Test clic");
}
</script>
With this HTML code:
<div>
<input name="BtnAddNew" type="button" value="Load all OPCVM" onclick="TestClick()" />
</div>
It works.
But once I try to put everything inside a controller :
<head>
<meta name="viewport" content="width=device-width" />
<title>PetitsTests</title>
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
<script src="~/Scripts/angular.js"></script>
<script>
var myApp= angular.module("myApp", []);
myApp.controller('OPCVMViewModel', function ($scope, $http) {
function TestClick() {
alert("test clic");
}
});
</script>
</head>
<body ng-app="myApp">
<div ng-controller="OPCVMViewModel">
<input name="BtnAddNew" type="button" value="Load all OPCVM" onclick="TestClick()" />
</div>
</body>
Then I get a "TestClick is not defined" error...
In your controller, try
$scope.TestClick = function(){ alert("test click"); };
And then in the button HTML:
type="button" ng-click="TestClick()"
In short, functions needs to be defined in scope. and use ng-click to call the function.
Example from the official docs:
var myApp = angular.module('myApp',[]);
myApp.controller('DoubleController', ['$scope', function($scope) {
$scope.double = function(value) { return value * 2; };
}]);
In short, you must attach whatever functions or properties you declare inside your controller to $scope in order to make them avaialbe to your template/view:
var app = angular.module("myApp", []);
app.controller('OPCVMViewModel', function ($scope, $http) {
function TestClick() {
alert('Test click');
}
$scope.TestClick = TestClick;
// or:
$scope.TestClick = function() {
alert('Test click');
};
});
Also, as JohnMoe has pointed out in the comments, there are other issues in your code.
First of, you should not use onclick, but ng-click. You can find the full list of ng directives here.
Moreover, the controller as syntax is the recommended way to go as from Angular 1.2, even though it will work without it.
Instead of writing something like:
app.controller('FooController', function ($scope) {
$scope.property = 'Foo';
});
You replace $scope for this, and now you don't need to inject $scope and have a nicer class-like looking controller:
app.controller('FooController', function () {
this.property = 'Foo';
});
Your templete will look like this:
<div ng-controller="FooController as vm">
{{ vm.property }}
</div>
If later on you decide to create a directive, it will look something like this:
app.controller('FooController', function () {
this.property = 'Foo';
});
app.directive('fooDirective', function () {
return {
restrict: 'E',
controller: 'FooController',
controllerAs: 'vm',
template: '{{ vm.property }}'
};
});
Also, you could use proper ES6 classes with a transpiler. Take a look at this if you are interested
As far as I can tell, ng-change is called before ng-model is actually changed in a select element. Here's some code to reproduce the issue:
angular.module('demo', [])
.controller('DemoController', function($scope) {
'use strict';
$scope.value = 'a';
$scope.displayValue = $scope.value;
$scope.onValueChange = function() {
$scope.displayValue = $scope.value;
};
})
.directive("selector", [
function() {
return {
controller: function() {
"use strict";
this.availableValues = ['a', 'b', 'c'];
},
controllerAs: 'ctrl',
scope: {
ngModel: '=',
ngChange: '='
},
template: '<select ng-model="ngModel" ng-change="ngChange()" ng-options="v for v in ctrl.availableValues"> </select>'
};
}
]);
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<div ng-app="demo" ng-controller="DemoController">
<div selector ng-model="value" ng-change="onValueChange">
</div>
<div>
<span>Value when ng-change called:</span>
<span>{{ displayValue }}</span>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
<script src="demo.js"></script>
</body>
</html>
If you run this, you should change the combobox 2 times (e.g. 'b' (must be different than the default), then 'c').
The first time, nothing happens (but the value displayed in the text should have changed to match the selection).
The second time, the value should change to the previous selection (but should have been set to the current selection).
This sounds really similar to a couple previous posts: AngularJS scope updated after ng-change and AngularJS - Why is ng-change called before the model is updated?. Unfortunately, I can't reproduce the first issue, and the second was solved with a different scope binding, which I was already using.
Am I missing something? Is there a workaround?
It's good idea not to use the same model value in the directive. Create another innerModel which should be used inside directive and update the "parent" model when needed with provided NgModelController.
With this solution, you don't hack the ng-change behavior - just use what Angular already provides.
angular.module('demo', [])
.controller('DemoController', function($scope) {
$scope.value = 'a';
$scope.displayValue = $scope.value;
$scope.onValueChange = function() {
$scope.displayValue = $scope.value;
};
})
.directive("selector", [
function() {
return {
controller: function() {
this.availableValues = ['a', 'b', 'c'];
},
require: 'ngModel',
controllerAs: 'ctrl',
scope: {
'ngModel': '='
},
template: '<select ng-model="innerModel" ng-change="updateInnerModel()" ng-options="v for v in ctrl.availableValues"> </select>',
link: function(scope, elem, attrs, ngModelController) {
scope.innerModel = scope.ngModel;
scope.updateInnerModel = function() {
ngModelController.$setViewValue(scope.innerModel);
};
}
};
}
]);
<div ng-app="demo" ng-controller="DemoController">
<div selector ng-model="value" ng-change="onValueChange()">
</div>
<div>
<span>Value when ng-change called:</span>
<span>{{ displayValue }}</span>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
Inspired by this answer.
It's a digest issue.. Just wrap the onValueChange operations with $timeout:
$scope.onValueChange = function() {
$timeout(function(){
$scope.displayValue = $scope.value;
});
};
Don't forget to inject $timeout in your controller.
... or you can check this link on how to implement ng-change for a custom directive
I'm trying to get the height of elements in a simple AngularJS app.
See below. What am I doing wrong? The height should be different as the lines wrap, but I get 20 reported back to me regardless of what I input in the "labels" array.
The following code can be executed here, otherwise see below.
http://js.do/code/49177
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<base href="/">
<title>height of element in angularjs</title>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.8/angular.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.8/angular-route.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript">
'use strict';
var app = angular.module('heightApp', ['ngRoute', 'routing']);
app.controller('heightCtrl', ['$scope', function ($scope) {
$scope.labels = [
'Hi there, I\'m a div.',
'Me too, I\'m also a div.',
'Can you see me, because I certainly can\'t see myself. I don\'t even know my own height. Isn\'t that just crazy?'
];
}]);
angular.module('routing', []).config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'height.html',
controller: 'heightCtrl'
});
}]);
angular.module('heightApp').directive('reportMyHeight', function() {
return function (scope, el, attrs) {
alert('offsetHeight = ' + el[0].offsetHeight);
}
})
</script>
</head>
<body ng-app="heightApp">
<div class="container">
<div ng-view></div>
</div>
</body>
<script type="text/ng-template" id="height.html">
<div class="row">
<div class="col-sm-4" report-my-height ng-repeat="lbl in labels">
{{ ::lbl }}
</div>
</div>
</script>
</html>
You need to wait till the next digest cycle. When you do it right away in the directive the interpolations {{ ::lbl }} inside the ng-repeat would not have expanded yet. You can place it in a $timeout turning off the applyDigest argument.
i.e, example:
angular.module('heightApp').directive('reportMyHeight', function($timeout) {
return function (scope, el, attrs) {
$timeout(init, false);
//Initialization
function init(){
console.log('offsetHeight = ' + el[0].offsetHeight, el.html());
}
}
});
Plnkr
Another way to make sure you get the height of the element is to use watch.
angular.module('heightApp').directive('reportMyHeight', function($timeout) {
return function (scope, el, attrs) {
scope.$watch('lbl', function(newval, oldval){
alert(newval + '\n\n' + 'offsetHeight = ' + el[0].offsetHeight);
});
}
})
It will only be triggered once since you use ::.
I try to initialize a slider using AngularJS, but the cursor show 100 when the value is over 100.
Setting the value 150 in a range [50,150] fails with this code :
<html ng-app="App">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script>
angular.module('App', ['App.controllers']);
angular.module('App.controllers', []).controller('AppController', function($scope) {
$scope.min = 50;
$scope.max = 150;
$scope.value = 150;
});
</script>
</head>
<body ng-controller="AppController" >
{{min}}<input ng-model="value" min="{{min}}" max="{{max}}" type="range" />{{max}}<br/>
value:{{value}}
</body>
</html>
The cursor is badly placed (it show 100 instead of 150).
How to display the cursor to its correct place ?
An explanation of what occurs could be on this forum
Update
This bug is reported as issue #6726
Update
The issue #14982 is closed by the Pull Request 14996 and solve the issue see answer.
After searches and tries , a possible way is to define a custom directive :
<html ng-app="App">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script>
angular.module('App', ['App.controllers']);
angular.module('App.controllers', []).controller('AppController', function($scope) {
$scope.min = 50;
$scope.max = 150;
$scope.value = 150;
}).directive('ngMin', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attr) { elem.attr('min', attr.ngMin); }
};
}).directive('ngMax', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attr) { elem.attr('max', attr.ngMax); }
};
});
</script>
</head>
<body ng-controller="AppController" >
{{min}}<input ng-model="value" ng-min="{{min}}" ng-max="{{max}}" type="range" />{{max}}<br/>
value:{{value}}
</body>
</html>
Even it is working this is a non standard extension in order to manage a very basic use case.
Try this new one.
You can configure angular to make it interpolate these values.
And you can use your initial code after that ...
Isn't it magic ?
Use this code only once in your app. Once angular is configured, it will be working for all the future ranges you will use.
<html ng-app="App">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script>
angular.module('App', ['App.controllers']);
angular.module('App.controllers', [])
/* Modify angular to make it interpolate min and max, for ngModel when type = range */
.config(['$provide', function($provide) {
$provide.decorator('ngModelDirective', ['$delegate', function($delegate) {
var ngModel = $delegate[0], controller = ngModel.controller;
ngModel.controller = ['$scope', '$element', '$attrs', '$injector', function(scope, element, attrs, $injector) {
if ('range' === attrs.type) {
var $interpolate = $injector.get('$interpolate');
attrs.$set('min', $interpolate(attrs.min || '')(scope));
attrs.$set('max', $interpolate(attrs.max || '')(scope));
$injector.invoke(controller, this, {
'$scope': scope,
'$element': element,
'$attrs': attrs
});
}
}];
return $delegate;
}]);
}])
.controller('AppController', function($scope) {
$scope.min = 50;
$scope.max = 150;
$scope.value = 150;
});
</script>
</head>
<body ng-controller="AppController" >
{{min}}<input ng-model="value" min="{{min}}" max="{{max}}" type="range"/>{{max}}<br/>
value:{{value}}
</body>
</html>
My lazy way of addressing this bug was to divide the min/max and step values by 100. So 300 becomes 3.0, etc. and values fall below 100. Then I multiply things back as needed.
Since this commit, the initial code give the expected result.
Using release 1.6.0 allow to original code to show slider correctly :
<html ng-app="App">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.0/angular.min.js"></script>
<script>
angular.module('App', ['App.controllers']);
angular.module('App.controllers', []).controller('AppController', function($scope) {
$scope.min = 50;
$scope.max = 150;
$scope.value = 150;
});
</script>
</head>
<body ng-controller="AppController" >
{{min}}<input ng-model="value" min="{{min}}" max="{{max}}" type="range" />{{max}}<br/>
value:{{value}}
</body>
</html>