update angularjs model from jquery [duplicate] - angularjs

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.

Related

Passing data via service not updating second controller

I am trying to use service in AngularJS and pass data from one controller to another on click of a button.
I tried and can see that service value is updated but I am unable to retrieve in the second controller, however, I can retrieve in the first Controller.
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="myCtrl">
<p>Search:<input type="text" ng-model="newValue"></p>
<button ng-click="myFunc(newValue)">OK</button>
</div>
<div ng-controller="myCtrl2">
{{receivedVal}}
</div>
<script>
var app= angular.module('myApp', []);
app.controller('myCtrl', function($scope,sharedProperties) {
$scope.stringValue = sharedProperties.getString();
$scope.myFunc = function(newValue) {
sharedProperties.setString(newValue);
$scope.stringValue = sharedProperties.getString();
console.log($scope.stringValue);
//I am getting the value here by calling sharedProperties.getString();
};
});
app.controller('myCtrl2', function($scope,sharedProperties) {
$scope.receivedVal = sharedProperties.getString();
console.log($scope.receivedVal);
//But I am not getting the updated value here by calling sharedProperties.getString();
});
app.service('sharedProperties', function() {
var stringValue = 'firstoccurence';
return {
getString: function() {
return stringValue;
},
setString: function(value) {
stringValue = value;
},
}
});
</script>
</body>
</html>
receivedVal is always coming blank even service is getting updated.
By looking at your HTML code; I can see both the controllers have already been instantiated.
So when you do $scope.receivedVal = sharedProperties.getString(); in controller 2, you are just getting value from service one time only (Note : You are not continuously observing the value from service). And hence in template of controller 2 the default value firstoccurence shall be displayed.
You are actually updating the value on click of OK button, which in turns updates value in service. But there is no way you told angular that now as values has been changed then now controller 2 should get this new value.
To active the scenario that you want , you need to use $broadcast and $on so that you can continuously observe change happening in controller 1.
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="myCtrl">
<p>Search:<input type="text" ng-model="newValue"></p>
<button ng-click="myFunc(newValue)">OK</button>
</div>
<div ng-controller="myCtrl2">
{{receivedVal}}
<button ng-click="thisWillGetValFromService()" >update this scope's value</button>
</div>
<script>
var app= angular.module('myApp', []);
app.controller('myCtrl', function($rootScope,$scope,sharedProperties) {
$scope.stringValue = sharedProperties.getString();
$scope.myFunc = function(newValue) {
sharedProperties.setString(newValue);
$scope.stringValue = sharedProperties.getString();
console.log($scope.stringValue);
//I am getting the value here by calling sharedProperties.getString();
$rootScope.$broadcast('greeting', newValue);
};
});
app.controller('myCtrl2', function($scope,sharedProperties) {
$scope.receivedVal = sharedProperties.getString();
console.log($scope.receivedVal);
//But I am not getting the updated value here by calling sharedProperties.getString();
$scope.$on('greeting', function(ev,val){
$scope.receivedVal = val
})
});
app.service('sharedProperties', function() {
var stringValue = 'firstoccurence';
return {
getString: function() {
return stringValue;
},
setString: function(value) {
stringValue = value;
},
}
});
</script>
</body>
Above snippet shall solve your problem.
Updated :
Consider a scenario where you have routing configuration defined. So by default only controller 1 and its templates loads in HTML. Then you update ur input box and click OK button. This will save data to service.
Then later on consider on click of some link you re redirecting the app to route of your controller 2 so at this point your controller 2 will get instantiated and $scope.receivedVal = sharedProperties.getString(); this will give you updated value.
Its just a matter of when you load your template (controller) In your case you load both the controllers at a time so you need to use broadcast and on. But if your second component going to load sometime later then you can always use service.
Value is updating in the second controller
But it is not reflecting in the :
<div ng-controller="myCtrl2">
{{receivedVal}}
</div>
Because ng-controller creates new scope
you should write one extra method like in this:
https://codepen.io/amar9312/pen/yRJKGj

how to watch the `ng-model` from the form element

I am getting data from server and binding to form elements. in case of these datas are edited by the user, i would like to know the status. for that I am trying to watch the object. but i am not getting the result.
what is the correct way to watch the change from the ng-model?
here is my try:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.issue = {
name:"arr",
age:15
};
$scope.$watch( $scope.issue, function(nv,ov) {
console.log(nv, ov);
}, true)
});
<body ng-controller="MainCtrl">
<form action="">
<input type="text" ng-model="issue.name">
</form>
</body>
Live Demo
Simple answer you'll be happy to know. $watch already presume $scope as its point of watching. so you supply as the first paramter the string value of the variable on $scope you want to watch. If you change
$scope.$watch( $scope.issue, function(nv,ov) {
to
$scope.$watch( "issue", function(nv,ov) {
everything will work :)
$scope.$watch('issue', function(nv,ov) {
console.log(nv, ov);
}, true)

Scope values to a requested content

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

Perform task after model's DOM is displayed in view

I have a code snippet in my content which is a model fetched from http. I am using syntax highlighter to prettify the code. So I need to call a javascript function as soon as the DOM is updated for that particular model.
Here is a sample code to make it clear. I am using alert to demonstrate it. In my project I would use a third party plugin which will find matching dom elements and remodel them.
Here,
I want the alert to occur after the list is displayed
jsfiddle :
http://jsfiddle.net/7xZde/2/
My controller has something like this.
$scope.items = Model.notes();
alert('test');
alert comes even before the items list is shown, I want it after the list is displayed.
Any hint to help me achieve this.
We need to use $timeout ,
$scope.items = Model.notes();
$timeout(function () {
alert('test');
})
Yeah it was silly , $timeout seemed to be a misnomer to me. I am 2 days old to angularjs . Sorry for wasting your time.
Lucky for you, I wanted to do the exact same thing. Mutation observers are the path forward, but if you need backwards compatibility with older browsers, you'll need a bit more code than this.
Working plunker for Firefox, Chrome, and Safari.
Javascript:
var app = angular.module('plunker', [])
.controller('MainCtrl', function($scope) {
$scope.name = 'World';
})
.directive('watchChanges', function ($parse, $timeout) {
return function (scope, element, attrs) {
var setter = $parse(attrs.watchChanges).assign;
// create an observer instance
var observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$timeout(function () {
var text = angular.element('<div></div>').text(element.html()).text();
setter(scope, text);
});
});
});
// configuration of the observer:
var config = {
attributes: true,
childList: true,
characterData: true,
subtree: true
};
// pass in the target node, as well as the observer options
observer.observe(element[0], config);
};
});
HTML:
<body ng-controller="MainCtrl">
<div watch-changes="text">
<p>Hello {{ name }}</p>
<label>Name</label>
<input type="text" ng-model="name" />
</div>
<pre>{{text}}</pre>
</body>

$watch not firing on data change

I have a watch setup against the model of a ui-select2 dropdown (from ui-bootstrap). The watch fires on load but not on data changes and I can't figure out why.
It isn't the usual problem of not $apply'ing the model change or not using the third parameter for equality comparison (at least from my code).
What do I need to do to get it to fire?
Here is a plunk demonstrating the issue.
Try passing true as a 3rd argument to .$watch()
$rootScope.Scope documentation
$watch(watchExpression, listener, objectEquality)
objectEquality(optional) – {boolean=} – Compare object for equality rather than for reference.
I fixed some stuff.
http://plnkr.co/edit/5Zaln7QT2gETVcGiMdoW?p=preview
The JS
var myMod = angular.module("myApp",[]).controller("MainController", function($scope){
$scope.myModel = {selectedId:null};
}).controller("DetailController",function($scope){
$scope.items = [1,2,3,4];
$scope.watchHitCount = 0;
$scope.$watch('myModel.selectedId', function(newVal, oldVal){
console.log(newVal + " " + oldVal);
$scope.watchHitCount++;
},true);
});
The index.html
<body ng-app="myApp">
<div ng-controller="MainController">
<ng-include src="'detail.html'" ng-controller="DetailController"></ng-include>
</div>
</body>
The detail.html
<pre>watch hit: {{watchHitCount}}</pre>
<pre>selected value: {{myModel.selectedId}}</pre>
<select ng-model="myModel.selectedId" ui-select2="">
<option></option>
<option ng-repeat="item in items" value="{{item}}">{{item}}</option>
</select>
It was complaining about not finding the controller so I set it up the way I normally would with a named ng-app and a module declared that has controllers defined on it.
I also added an object to hold the value in your model. It is bad practice to use the $scope object as your model, instead your scope should refer to an object that is your model.
There are a simple fix for that, to use watch with complex object instead of simple variable
For example (DON"T USE)
$scope.selectedType=1;//default
$scope.$watch(
function () {
return $scope.selectedType;
},
function (newValue, oldValue) {
if (!angular.equals(oldValue, newValue)) {
$scope.DoWork();
}
},
true);
But Use below
$scope.selecteditem={selectedType:1};
$scope.$watch(
function () {
return $scope.selecteditem.selectedType;
},
function (newValue, oldValue) {
if (!angular.equals(oldValue, newValue)) {
$scope.DoWork();
}
},
true);
note that "slectedTypes" in the second example located inside object, not just scope variable. this will work even in old Angular versions.
If you're using the controller-as approach some reading might suggest a syntax like this:
var myController = {
myValue: 1
};
$scope.$watch('$ctrl.myValue', function () {
...
}, true);
Instead just wrap the field in a function like this:
var myController = {
myValue: 1
};
$scope.$watch(function () {
return myController.myValue;
}, function () {
...
}, true);
The only thing that worked for me:
var ctrl = this;
this.$onInit = function () {
console.log("id " + ctrl.event.id);
};
from https://docs.angularjs.org/guide/component#!

Resources