I have a view that contains a button, when the button is clicked, a $http.get request is executed and the content is appended on the view.
View:
<button ng-click="includeContent()">Include</button>
<div id="container"></div>
Controller:
$scope.includeContent = function() {
$http.get('url').success(function(data) {
document.getElementById('container').innerHTML = data;
}
}
The content to include:
<h1>Hey, I would like to be {{ object }}</h1>
How can I scope a value to object? Do I need to approach this in a complete different way?
The built-in directive ng-bind-html is the way you are looking for.
Beware, that ng-bind-html requires a sanitized string, which is either done automatically when the correct libary is found or it can be done manually ($sce.trustAsHtml).
Don't forget to inject $sce in your controller.
$scope.includeContent = function() {
$http.get('url').success(function(data) {
$scope.data = $sce.trustAsHtml(data);
}
}
<button ng-click="includeContent()">Include</button>
<div ng-bind-html="data"></div>
As you also want to interpolate your requested HTML, I suggest using $interpolate or, if it can contain whole directives or should have a full fledged two-way-data-binding, use $compile instead.
In your case alter the assignment to
$scope.data = $sce.trustAsHtml($interpolate(data)($scope));
Don't forget to inject $interpolate/$compile aswell.
As I don't know about your $scope structure I assume that "object" is available in this scope. If this isn't the case then change the $scope parameter to whatever object contains your interpolation data.
You should use a controller to do this (I imagine you are since you're using $scope).
ctrl function () {
var ctrl = this;
ctrl.includeContent = function () {
$http.get("url").success(function (data) {
ctrl.object = data;
});
};
}
<div ng-controller="ctrl as ctrl">
<button ng-click="ctrl.includeContent()">Include</button>
<div id="container">
<h1 ng-show="ctrl.object">Hey, I would like to be {{ctrl.object}}</h1>
</div>
</div>
You need not select an element and append the data to it. Angular does it for you. That's what is magic about angular.
In your controller's scope, just update object and angular does the heavy-lifting
$scope.includeContent = function() {
$http.get('url').success(function(data) {
$scope.object = data;
}
}
If that's html code from a server, then you should use the 'ng-bind-html' attribute:
<button ng-click="includeContent()">Include</button>
<div id="container" ng-bind-html="htmlModel.ajaxData"></div>
Controller:
$scope.htmlModel = {ajaxData:''};
$scope.includeContent = function() {
$http.get('url').success(function(data) {
$scope.htmlModel.ajaxDataL = data;
}
}
One way is to use ng-bind-html as suggested.
Another way is with $compile:
app.controller('MainCtrl', function($scope, $http, $compile) {
$scope.error='error!!!';
$scope.includeContent = function() {
$http.get('url').success(function(data) {
var elm = angular.element(document.getElementById('container')).html(data);
$compile(elm)($scope);
}).error(function(){
var elm = angular.element(document.getElementById('container')).html('{{error}}');
$compile(elm)($scope);
})
}
});
Also, typically in angular, when you want to manipulate the DOM you use directives.
DEMO
Related
Im trying to put some angular js template string inside an element, and expect an complied output. But that's not happening.
HTML
<div ng-controller="testController">
<div ng-bind-html-unsafe="fruitsView"></div>
</div>
Controller:
function filterController($scope){
...
$scope.arr = ["APPLE", "BANANA"];
$scope.fruitsView = '<div><p ng-repeat="each in arr">{{each}}</p></div>';
}
The ouput is just {{each}}.
So how do i insert an angular js template string (here $scope.fruitsView) inside an element?
I have made a fiddle for this.
In this case, you don't want to just "insert HTML", but compile it. You can create DOM nodes using the $compile service.
var tpl = $compile( '<div><p ng-repeat="each in arr">{{each}}</p></div>' )( scope );
As you can see, $compile returns a function that takes a scope object as a parameter, against which the code is evaluated. The resultant content can be inserted into the DOM with element.append(), for example.
Important note: But under no circumstances does any DOM-related code belong in your controller. The proper place is always a directive. This code can easily be thrown into a directive, but I wonder why you are programmatically inserting the HTML at all.
Can you shed some light here so I can provide a more specific answer?
Update
Assuming your data comes from a service:
.factory( 'myDataService', function () {
return function () {
// obviously would be $http
return [ "Apple", "Banana", "Orange" ];
};
});
And your template comes from a service
.factory( 'myTplService', function () {
return function () {
// obviously would be $http
return '<div><p ng-repeat="item in items">{{item}}</p></div>';
};
});
Then you create a simple directive that reads in the provided template, compiles it, and adds it to the display:
.directive( 'showData', function ( $compile ) {
return {
scope: true,
link: function ( scope, element, attrs ) {
var el;
attrs.$observe( 'template', function ( tpl ) {
if ( angular.isDefined( tpl ) ) {
// compile the provided template against the current scope
el = $compile( tpl )( scope );
// stupid way of emptying the element
element.html("");
// add the template content
element.append( el );
}
});
}
};
});
Then from your view:
<div ng-controller="MyCtrl">
<button ng-click="showContent()">Show the Content</button>
<div show-data template="{{template}}"></div>
</div>
And in the controller, you simply tie it together:
.controller( 'MyCtrl', function ( $scope, myDataService, myTplService ) {
$scope.showContent = function () {
$scope.items = myDataService(); // <- should be communicated to directive better
$scope.template = myTplService();
};
});
And it should all work together!
PS: this is all assuming your template comes from the server. If it doesn't, then your template should be in the directive, which simplifies things.
I´m learning about Providers. On a common controller I would use
modu.controller("thecontrol", [function()
{
this.something = "Hello";
]});
and on the HTML
<div ng-controller="thecontrol as ctrl">
{{ ctrl.something }}
</div>
But... I´m trying to the the same with this code and, I really could not, even when I tried all the ways I "know".
Here´s the code...
What I want? Use THIS instead of $scope
<div ng-app="myApp" ng-controller="showingName">
{{theNameIs}}
</div>
<script src="angular.js"></script>
<script>
var myApp = angular.module("myApp", []);
myApp.provider("showingName", function() {
this.name = "Luis";
this.$get = function() {
var name = this.name;
return {
showName: function() {
return name;
}
}
};
this.setName = function(name) {
this.name = name;
};
});
myApp.config(function(showingNameProvider){
showingNameProvider.setName("Juan");
});
myApp.controller("showingName", function($scope, showingName)
{
$scope.theNameIs = showingName.showName();
});
</script>
And yes... It works, but I would like to know if it´s possible to do it with THIS.
Thanks!
I think it's because when you don't name the controller, then the {{ }} has to be scope, since this and $scope can be different depending on the context. Say for instance in an ng-repeat, 1 controller yet essentially 2 scopes.
Name the controller like you did on the first, ctrl as showingName. Make the variable this.theNameIs and then use {{ ctrl.theNameIs }}
Also, personally I don't think you should name the controller and provider the same name, appreciate this is probably just an example.
More information on $scope and this:
'this' vs $scope in AngularJS controllers
I have this simple scenario:
Input element which value is changed by jQuery's val() method.
I am trying to update the angular model with the value that jQuery set. I tried to write a simple directive, but it's not doing what I want.
Here's the directive:
var myApp = angular.module('myApp', []);
myApp.directive('testChange', function() {
return function(scope, element, attrs) {
element.bind('change', function() {
console.log('value changed');
})
}
})
this is the jQuery part:
$(function(){
$('button').click(function(){
$('input').val('xxx');
})
})
and html:
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<input test-change ng-model="foo" />
<span>{{foo}}</span>
</div>
</div>
<button>clickme</button>
Here is the fiddle with my try:
http://jsfiddle.net/U3pVM/743/
Can someone please point me in the right direction?
ngModel listens for "input" event, so to "fix" your code you'd need to trigger that event after setting the value:
$('button').click(function(){
var input = $('input');
input.val('xxx');
input.trigger('input'); // Use for Chrome/Firefox/Edge
input.trigger('change'); // Use for Chrome/Firefox/Edge + IE11
});
For the explanation of this particular behaviour check out this answer that I gave a while ago: "How does AngularJS internally catch events like 'onclick', 'onchange'?"
But unfortunately, this is not the only problem you have. As pointed out with other post comments, your jQuery-centric approach is plain wrong. For more info take a look at this post: How do I “think in AngularJS” if I have a jQuery background?).
Hope this is useful for someone.
I was unable to get the jQuery('#myInputElement').trigger('input') event to be picked up my angular app.
I was however, able to get angular.element(jQuery('#myInputElement')).triggerHandler('input') to be picked up.
The accepted answer which was triggering input event with jQuery didn't work for me. Creating an event and dispatching with native JavaScript did the trick.
$("input")[0].dispatchEvent(new Event("input", { bubbles: true }));
I don't think jQuery is required here.
You can use $watch and ng-click instead
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<input test-change ng-model="foo" />
<span>{{foo}}</span>
<button ng-click=" foo= 'xxx' ">click me</button>
<!-- this changes foo value, you can also call a function from your controller -->
</div>
</div>
In your controller :
$scope.$watch('foo', function(newValue, oldValue) {
console.log(newValue);
console.log(oldValue);
});
You have to use the following code in order to update the scope of the specific input model as follows
$('button').on('click', function(){
var newVal = $(this).data('val');
$('select').val(newVal).change();
var scope = angular.element($("select")).scope();
scope.$apply(function(){
scope.selectValue = newVal;
});
});
I made modifications on only controller initialization by adding listener on action button:
$(document).on('click', '#action-button', function () {
$timeout(function () {
angular.element($('#input')).triggerHandler('input');
});
});
Other solutions did not work in my case.
I know it's a bit late to answer here but maybe I may save some once's day.
I have been dealing with the same problem. A model will not populate once you update the value of input from jQuery. I tried using trigger events but no result.
Here is what I did that may save your day.
Declare a variable within your script tag in HTML.
Like:
<script>
var inputValue="";
// update that variable using your jQuery function with appropriate value, you want...
</script>
Once you did that by using below service of angular.
$window
Now below getData function called from the same controller scope will give you the value you want.
var myApp = angular.module('myApp', []);
app.controller('imageManagerCtrl',['$scope','$window',function($scope,$window) {
$scope.getData = function () {
console.log("Window value " + $window.inputValue);
}}]);
I've written this little plugin for jQuery which will make all calls to .val(value) update the angular element if present:
(function($, ng) {
'use strict';
var $val = $.fn.val; // save original jQuery function
// override jQuery function
$.fn.val = function (value) {
// if getter, just return original
if (!arguments.length) {
return $val.call(this);
}
// get result of original function
var result = $val.call(this, value);
// trigger angular input (this[0] is the DOM object)
ng.element(this[0]).triggerHandler('input');
// return the original result
return result;
}
})(window.jQuery, window.angular);
Just pop this script in after jQuery and angular.js and val(value) updates should now play nice.
Minified version:
!function(n,t){"use strict";var r=n.fn.val;n.fn.val=function(n){if(!arguments.length)return r.call(this);var e=r.call(this,n);return t.element(this[0]).triggerHandler("input"),e}}(window.jQuery,window.angular);
Example:
// the function
(function($, ng) {
'use strict';
var $val = $.fn.val;
$.fn.val = function (value) {
if (!arguments.length) {
return $val.call(this);
}
var result = $val.call(this, value);
ng.element(this[0]).triggerHandler('input');
return result;
}
})(window.jQuery, window.angular);
(function(ng){
ng.module('example', [])
.controller('ExampleController', function($scope) {
$scope.output = "output";
$scope.change = function() {
$scope.output = "" + $scope.input;
}
});
})(window.angular);
(function($){
$(function() {
var button = $('#button');
if (button.length)
console.log('hello, button');
button.click(function() {
var input = $('#input');
var value = parseInt(input.val());
value = isNaN(value) ? 0 : value;
input.val(value + 1);
});
});
})(window.jQuery);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div ng-app="example" ng-controller="ExampleController">
<input type="number" id="input" ng-model="input" ng-change="change()" />
<span>{{output}}</span>
<button id="button">+</button>
</div>
If you are using IE, you have to use: input.trigger("change");
add .change() after setting the value.
example:('id').val.('value').change();
also don't forget to add onchange or ng-change tag in html
I did this to be able to update the value of ngModel from the outside with Vanilla/jQuery:
function getScope(fieldElement) {
var $scope = angular.element(fieldElement).scope();
var nameScope;
var name = fieldElement.getAttribute('name');
if($scope) {
if($scope.form) {
nameScope = $scope.form[name];
} else if($scope[name]) {
nameScope = $scope[name];
}
}
return nameScope;
}
function setScopeValue(fieldElement, newValue) {
var $scope = getScope(fieldElement);
if($scope) {
$scope.$setViewValue(newValue);
$scope.$validate();
$scope.$render();
}
}
setScopeValue(document.getElementById("fieldId"), "new value");
Not what OP asked, but for any soul that might be as well writing an userscript that goes through input fields and fills the required details. Nothing (fully) worked for me, but finally managed to get it done this way:
var el = $('#sp_formfield_fw_ip');
el.val("some value");
angular.element(el).triggerHandler('focus');
angular.element(el).triggerHandler('input');
angular.element(el).triggerHandler('change');
angular.element(el).triggerHandler('blur');
Open developer tools, and inspect input field for added events. There I found all of them (in my case): focus, input, change and blur.
I have a DOM element that I want to associate with a controller, the usual way of course is using ng-controller. But in this case I cannot do this (for reasons that will take too long to explain).
Is there a way to do this association manually?
e.g.
<div id="foo">
...
</div>
angular
.module('APP', [])
.controller('FooCtrl', function ($scope) {
...
});
var $ele = $('#foo');
angular.bootstrap($ele, ['APP']);
// somehow associate #foo with FooCtrl without using ng-controller="FooCtrl"
Is this possible?
Thanks
You could try with a directive.
angular.module('APP').directive('fooCtrl', function () {
return {
controller: 'Foo'
};
});
then in your HTML
<div id="foo" foo-ctrl>
I want to have element where i can have 2 views using their own controller but only one at a time.
I can't use a ng-view and use the routeProvider because in the future I need to include more ng-includes that need to change their content depending on the possible actions.
I created a fiddle http://jsfiddle.net/EvHyT/29/.
So I used a ng-include and then I set the src for it from a main controller. At that point I want to use controller 1 or controller 2.
function MainCtrl($rootScope, $scope, navService){
$scope.template = {};
$scope.loadCtrl1=function(param){
navService.loadCtrl1(param);
}
$scope.loadCtrl2=function(param){
navService.loadCtrl2(param);
}
$rootScope.$on('loadCtrl1', function(e, args){
$scope.template = {'url': 'temp1'};
});
$rootScope.$on('loadCtrl2', function(e, args){
$scope.template = {'url': 'temp2'};
});
}
I use a service for communication because i want to move the load controller functions in a child controller.
var myApp = angular.module('myApp',[]);
myApp.factory('navService', function($rootScope) {
return {
loadCtrl1:function(param){
$rootScope.$broadcast('loadCtrl1', {'id':param});
},
loadCtrl2:function(param){
$rootScope.$broadcast('loadCtrl2', {'id':param});
}
};
});
I know this solution is bad because the controllers are not yet created when a different template is inserted so my event listener will not fire. Also can I destroy the previous instances of the controller because switching between the two controllers makes my event fire multiple times.
function Child1Ctrl($scope, $rootScope){
$rootScope.$on('loadCtrl1', function(e, args){
alert(args.id);
});
}
function Child2Ctrl($scope, $rootScope){
$rootScope.$on('loadCtrl2', function(e, args){
alert(args.id);
});
}
You don't need to broadcast (and shouldn't be broadcasting) to make this happen.
In my experience, if you're broadcasting on the rootScope, you're probably working too hard.
A simpler way of loading the template is very similar to what you're doing:
my.NavService = function() {
this.template = 'index.html';
this.param = null;
};
my.NavService.prototype.setTemplate(t, p) {
this.template = t;
this.param = p;
};
my.ctrl = function($scope, nav) {
$scope.nav = nav;
$scope.load = function(t, p) {
nav.setTemplate(t, p);
};
};
my.ctrl1 = function($scope, nav) {
$scope.param = nav.param;
};
my.ctrl2 = function($scope, nav) {
$scope.param = nav.param;
};
module.
service('nav', my.NavService).
controller('mainCtrl', my.ctrl).
controller('ctrl1', my.ctrl1).
controller('ctrl2', my.ctrl2);
<script type="text/ng-template" id="temp1.html">
<div ng-controller="ctrl1">Foo {{param}}.</div>
</script>
<script type="text/ng-template" id="temp2.html">
<div ng-controller="ctrl2">Bar {{param}}.</div>
</script>
<div ng-controller="mainCtrl">
<a ng-click="load('temp1.html', 16)">Load 1</a>
<a ng-click="load('temp2.html', 32)">Load 2</a>
<div ng-include src="nav.template"></div>
</div>
A setup like this would work much better for you.
Alternatively, you should look into selectively showing elements with ng-switch. Unlike ng-show/hide, ng-switch does not simply add "display:none" to the CSS. It removes it from the DOM.
Some notes:
The above example may not be a working example, it's a demonstration of the idea.
A working example can be seen here: http://jsbin.com/ofakes/1 It modifies your original code.
JSFiddle had some issues with loading the include from the on page script
tag.
JSBin was a little better.
I didn't really get it to work as expected until I broke out the templates
into their own files.