How to watch for ng-model created with ng-bind-html - angularjs

I need some help with an ng-model created with ng-bind-html. I have a JSON file in the server in which I have some html and some inputs like this:
*.json
{
"test": {
"1": {
"question":"<span>1. something:</span>",
"options":"<input type='radio' name='q1' ng-model='q.1' value='a'>a) op 1<br><input type='radio' name='q1' ng-model='q.1' value='b'>b) op 2<br><input type='radio' name='q1' ng-model='q.1' value='c'>c) op 3<br><input type='radio' name='q1' ng-model='q.1' value='d'>d) op 4<br><input type='radio' name='q1' ng-model='q.1' value='e'>e) op 5<br>",
"answer":"c"
},
"2": {
...
}
}
}
Then in my HTML file I have something like:
<div class="testContent">
<div class="test">
<div class="questions" ng-repeat="qs in questions" ng-show="questions.length">
<div ng-bind-html="qs.question"></div>
<div class="options" ng-bind-html="qs.options">
</div>
</div>
</div>
<br>
<div class="nextBtn">
continue >
</div>
</div>
And in my Angular controller I have the ajax call for the JSON file:
controller:
.controller('testCtrl', ['$scope', '$http', 'myService', '$sce',
function($scope, $http, myService, $sce, ){
$http.get(urls.url_otis).success(function(data, status){
angular.forEach(data.test, function(value, key){
var q = data.test[key];
q[key] = key;
q.question = $sce.trustAsHtml(q.question);
q.options = $sce.trustAsHtml(q.options);
$scope.questions.push(q);
});
}).error(function(data, status){console.log(status)});
}
The html is populated but I cannot use $watch for the model (q) generated with this approach.
How can I $watch for changes in the models created in this way?
Thanks in advance...

You have to compile dynamically created elements to let angular know about them.
Directive which can do that may look like this one:
app.directive('compile',function($compile, $timeout){
return{
restrict:'A',
link: function(scope,elem,attrs){
$timeout(function(){
$compile(elem.contents())(scope);
});
}
};
});
$timeout is used to run compile function, after ng-bind-html do its job.
Now you can just simply put compile as attribute of div with ng-bind-html:
<div class="questions" ng-repeat="item in questions" ng-show="questions.length" >
<div ng-bind-html="item.question"></div>
<div compile class="options" ng-bind-html="item.options"></div>
</div>
Fiddle: http://jsfiddle.net/nZ89y/7/

javascript:
app.controller('demoController', function($rootScope, $scope, $http, $compile){
var arr = [
'<div>I am an <code>HTML</code>string with links! and other <em>stuff</em></div>'
,'<div>name: <input ng-model="user.name" /></div>'
,'<div>age: <input ng-model="user.age" /></div>'];
$scope.user={};
$scope.testChange2 = function(){
var compileFn = $compile( arr[Number.parseInt(Math.random()*10)%3] );
var $dom = compileFn($scope);
$('#test').html($dom);
};
});
html:
<div ng-controller="demoController">
<button type="button" class="btn w-xs btn-info" ng-click="testChange2();" >test2</button>
<hr/>
<div id = "test"></div>
<hr/>
<div>user:{{user}}</div>

Related

Adding ng-model directive to dynamically created input tag using AngularJs

I am trying that on a button click, a div and and input tag are created and the input tag contain ng-model and the div has binding with that input.
Kindly suggest some solution.
You can create the div and input beforehand and and do not show it by using ng-if="myVar". On click make the ng-if="true".
<button ng-click="myVar = true">
In controller : $scope.myVar = false;
$scope.addInputBox = function(){
//#myForm id of your form or container boxenter code here
$('#myForm').append('<div><input type="text" name="myfieldname" value="myvalue" ng-model="model-name" /></div>');
}
Here is another solution, in which there's no need to create a div and an input explicitly. Loop through an array of elements with ng-repeat. The advantage is that you will have all the values of the inputs in that array.
angular.module('app', [])
.controller('AppController', AppController);
AppController.$inject = ['$scope'];
function AppController($scope) {
$scope.values = [];
$scope.add = function() {
$scope.values.push('');
};
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="AppController">
<button ng-click="add()">Click</button>
<div ng-repeat="value in values track by $index">
<input type="text" ng-model="values[$index]"/>
<div>{{values[$index]}}</div>
</div>
<pre>{{values}}</pre>
</div>
UPDATE. And if you want only one input, it's even simpler, using ng-show.
angular.module('app', []);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<button ng-click="show = true">Click</button>
<div ng-show="show">
<input type="text" ng-model="value"/>
<div>{{value}}</div>
</div>
</div>
You should use $compile service to link scope and your template together:
angular.module('myApp', [])
.controller('MyCtrl', ['$scope', '$compile', '$document' , function MyCtrl($scope, $compile, $document) {
var ctrl = this;
var inputTemplate = '<div><span ng-bind="$ctrl.testModel"></span>--<span>{{$ctrl.testModel}}</span><input type="text" name="testModel"/></div>';
ctrl.addControllDynamically = addControllDynamically;
var id = 0;
function addControllDynamically() {
var name = "testModel_" + id;
var cloned = angular.element(inputTemplate.replace(/testModel/g, name)).clone();
cloned.find('input').attr("ng-model", "$ctrl." + name); //add ng-model attribute
$document.find('[ng-app]').append($compile(cloned)($scope)); //compile and append
id++;
}
return ctrl;
}]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//code.angularjs.org/1.6.2/angular.js"></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl as $ctrl">
<input type="button" value="Add control dynamically" ng-click="$ctrl.addControllDynamically()"/>
</div>
</div>
UPDATE: to add a new compiled template each time the button is clicked, we need to make a clone of the element.
UPDATE 2: The example above represents a dirty-way of manipulating the DOM from controller, which should be avoided. A better (angular-)way to solve the problem - is to create a directive with custom template and use it together with ng-repeat like this:
angular.module('myApp', [])
.controller('MyCtrl', ['$scope', function MyCtrl($scope) {
var ctrl = this;
ctrl.controls = [];
ctrl.addControllDynamically = addControllDynamically;
ctrl.removeControl = removeControl;
function addControllDynamically() {
//adding control to controls array
ctrl.controls.push({ type: 'text' });
}
function removeControl(i) {
//removing controls from array
ctrl.controls.splice(i, 1);
}
return ctrl;
}])
.directive('controlTemplate', [function () {
var controlTemplate = {
restrict: 'E',
scope: {
type: '<',
ngModel: '='
},
template: "<div>" +
"<div><span ng-bind='ngModel'></span><input type='type' ng-model='ngModel'/></div>" +
"</div>"
}
return controlTemplate;
}]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//code.angularjs.org/1.6.2/angular.js"></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl as $ctrl">
<input type="button" value="Add control dynamically" ng-click="$ctrl.addControllDynamically()"/>
<div ng-repeat="control in $ctrl.controls">
<control-template type="control.type" ng-model="control.value"></control-template>
</div>
</div>
</div>

How to modify service url paramer in angularjs

I'm trying to modify the city parameter by searching for a city parameter, but I don't think it's possible to modify an angular service that way. So how would I be able to modify the service parameter in the controller? Any help would be amazing!
HTML:
<section ng-controller="MainController">
<form action="" class="form-inline well well-sm clearfix" >
<span class="glyphicon glyphicon-search"></span>
<input type="text" placeholder="Search..." class="form-control" ng-model="city" />
<button class="btn btn-warning pull-right" ng-click="search()"><strong>Search</strong></button>
</form>
<h1>{{fiveDay.city.name}}</h1>
<div ng-repeat="day in fiveDay.list" class="forecast">
<div class="day">
<div class="weekday">
<p>{{ day.dt*1000 | date}}</p>
<!-- <p>{{ parseJsonDate(day.dt)}}</p> -->
</div>
<div class="weather"><img ng-src="http://openweathermap.org/img/w/{{day.weather[0].icon}}.png"/></div>
<div class="temp">{{day.weather[0].description}}</div>
<div class="temp">Max {{ day.main.temp_max }}°</div>
<div class="temp">Min {{ day.main.temp_min }}°</div>
</div>
</div>
</section>
JS:
var app = angular.module('App', []);
app.controller('MainController', ['$scope', 'forecast', function($scope, forecast) {
forecast.city="orlando";
forecast.success(function(data) {
$scope.fiveDay = data;
});
}]);
app.factory('forecast', ['$http', function($http) {
var city = "orlando";
var key="a1f2d85f6babd3bf7afd83350bc5f2a6";
return $http.get('http://api.openweathermap.org/data/2.5/forecast?q='+city+'&APPID='+key+'&units=metric&cnt=5')
.success(function(data) {
return data;
})
.error(function(err) {
return err;
});
}]);
City is a variable part in your forecast factory so need to pass it as an argument in function will be the recommended for you
Try this
var app = angular.module('App', []);
app.controller('MainController', ['$scope', 'forecast', function($scope, forecast) {
var city = "orlando";
forecast.getWeatner(city).success(function(data) {
$scope.fiveDay = data;
});
}]);
app.factory('forecast', ['$http', function($http) {
var key = "a1f2d85f6babd3bf7afd83350bc5f2a6";
return {
getWeatner: function(city) {
return $http.get('http://api.openweathermap.org/data/2.5/forecast?q=' + city + '&APPID=' + key + '&units=metric&cnt=5');
}
}
}]);
增加参数 callback , 回调:JSON_CALLBACK
$http.jsonp("http://api.openweathermap.org/data/2.5/forecast?q='+city+'&APPID='+key+'&units=metric&cnt=5&callback=JSON_CALLBACK").success(function(data){ ... });

ng-model data not getting when saving the data

here save the ng-model is newattendance saving to database. "newattendance._id" is not taken as a ng-model.how to make it "newattendance._id" is ng-model
<select class="form-control" ng-options="item.empcode as item.empcode for item in totemplist" ng-model="item.empcode">
</select>
<input type="text" ng-repeat="newattendance in totemplist" ng-model="newattendance._id" ng-show="item.empcode ==newattendance.empcode" style="width:200px;" ><br>
<input placeholder="Enter Attendacne Date" ng-model="newattendance.doa">
<button class="btn btn-primary" ng-click="checkOut()">checkOut</button>
Controller
EmpMasterController.controller("AttendanceController", ['$scope', 'AttendanceFactory',"EmpAddService", function($scope, AttendanceFactory,EmpAddService){
$scope.newattendance={};
$scope.totemplist=EmpAddService.getAllEmpAddItems();
console.log($scope.totemplist);
$scope.checkIn = function(){
AttendanceFactory.addAttendance($scope.newattendance);
$scope.newattendance = {}
}
$scope.getAllAttendance = function(){
console.log("$$$$$"+$scope.newattendance._id)
$scope.attendancedetails =AttendanceFactory.getAllAttendance($scope.newattendance._id);
}
}])
Factory
EmpFactModule.factory("AttendanceFactory", function($resource, RES_URL){
var attendanceResource = $resource(RES_URL+"attandence/:id/:attid",
{"id": "#id", "attid": "#attid"}, {update: {method: "PUT"}})
var attendanceDetails;
return {
addAttendance: function(newattendance){
console.log("..1.. " + newattendance._id)
attendanceResource.save({"id":newattendance._id}, newattendance, function(data){
console.log("Add Success ")
}, function(data, status){
console.log("Add Failed*****");
})
},
getAllAttendance: function(_id){
console.log("..#.. " + _id)
attendanceDetails = attendanceResource.query({"id": _id});
return attendanceDetails;
},
}
})
please help me how make it as ng-model and how to save this...
I've create a JSFiddle for you which hopefully will help you understand the 2 way binding in angular.
you dont need to pass the newattendance object to the check-out function, it is already saved on the scope.
HTML:
<div ng-app="app">
<div ng-controller="formValidation">
<div>
<div>
<span>User Name</span>
<input type="text" placeholder="John" ng-model="newattendance._id">
<span>
<button ng-click="submit()">
check out
</button>
</span>
</div>
</div>
<pre>{{newattendance._id}}</pre>
</div>
</div>
JS:
var app = angular.module('app', []);
app.controller('formValidation', function($scope) {
$scope.submit=function(){
var formPost = {
"Username":$scope.newattendance._id
};
console.log(formPost);
}
});

Angularjs scope not working

I am using basic angular.js concept to show and hide a div. For some reasons I am unable to make it work.
While Uploading a file I am showing a div 'myFormSpinner' on the basis of registerStart value. I am creating a phone gap app.
Could someone please help me in finding the issue. Apart from showing/hiding div everything works fine. Below is the code snippet:
<div class="row" ng-controller="RegisterCtrl">
<div id="myFormSpinner" ng-show="registerStart">
<img src="img/ajax-loader.gif">
</div>
<div class="col-md-8">
<form class="ng-pristine ng-invalid ng-invalid-required" style="margin-top:5%;">
<div class="col-md-6">
<input class="form-control " type="text" ng-model="registerData.Email" id="Email" name="Email" required placeholder="Email">
</div>
<div class="col-md-offset-1 col-md-10 ">
<input type="submit" ng-click="register()" value="Register" class="btn btn-default btn-primary">
</div>
</form>
</div>
event.controller('RegisterCtrl', ['$scope', '$state', '$q', '$http', 'eventService', 'authService', '$stateParams', '$rootScope', 'tokenService', 'imageService', 'spinnerService', 'appBackgroundService',
function ($scope, $state, $q, $http, eventService, authService, $stateParams, $rootScope, tokenService, imageService, spinnerService, appBackgroundService) {
var userNumber = 0;
$scope.mediaUploadStart = false;
$scope.eventId = $state.params.id;
$scope.register = function () {
$scope.registerStart = true;
console.log('scope.Pic=' + $scope.pic);
if ($scope.registerData.Email) {
spinnerService.show('myFormSpinner');
$scope.mediaUploadStart = true;
var paramOptions = {
eventId: $state.params.id,
email: $scope.registerData.Email,
fb: {},
number: userNumber,
provider: "Form"
};
uploadPicAndData(paramOptions).then(function (result) {
var data = JSON.parse(result);
$scope.registerStart = false;
appBackgroundService.disableBackgroundMode();
},
function (error) {
spinnerService.hide('myFormSpinner');
$scope.registerStart = false;
appBackgroundService.disableBackgroundMode();
});
$scope.mediaUploadStart = false;
spinnerService.hide('myFormSpinner');
}
};
}])
You are setting the $scope element in an async mode.
the digest cycle is not able to determine that a change has been made to the scope element in an async mode.
You should use $apply to activate the digest cycle.
$scope.$apply(function () { $scope.registerStart = false; });
Read more about $apply here: http://jimhoskins.com/2012/12/17/angularjs-and-apply.html

Why won't my view template bind to a scope variable with AngularJS?

My view is:
<div class="container" ng-controller="MyController">
<div class="row">
<div class="col-md-8">
<textarea class="form-control" rows="10" ng-model="myWords" ng-change="parseLanguage()"></textarea>
</div>
<div class="col-md-4" ng-show="sourceLanguage !== null">
Language: {{ sourceLanguage }}
</div>
</div>
</div>
My controller is:
webApp.controller('MyController', [
'$scope', '$rootScope', 'TranslateService', function($scope, $rootScope, CodeService) {
$scope.init = function() {
return $scope.sourceLanguage = null;
};
$scope.parseLanguage = function() {
return TranslateService.detectLanguage($scope.myWords).then(function(response) {
console.log($scope.sourceLanguage);
$scope.sourceLanguage = response.data.sourceLanguage;
return console.log($scope.sourceLanguage);
});
};
return $scope.init();
}
]);
The console logs show the right data. But in the view, sourceLanguage never updates. Why would this be?
In case the promise you are evaluating is not part of the Angular context you need to use $scope.$apply:
$scope.parseLanguage = function() {
TranslateService.detectLanguage($scope.myWords).then(function(response) {
$scope.$apply(function() {
$scope.sourceLanguage = response.data.sourceLanguage;
});
});
};

Resources