How to update view when scope does not change - angularjs

there is a textarea to input. And two buttons to click.
`<textarea ng-bind="msg"></textarea>
<div class="cancel-btn" ng-click="is_hide=false;"></div>
<div class="submit-btn" ng-click="submit_card()"></div>`
What is the process ?
input some characters in textarea, then click 'submit-btn', scope will save the 'msg'. But if click 'cancel-btn', scope won't save the msg.
What do I want ?
After I save msg, then input and click 'cancel-btn', I want to the textarea's innerHTML is scope.msg instead of input.
#
Now, I want to exec $scope.$apply() to update dom, but alert error $digest already in progress.
#
Maybe I need ng-model the textarea, and use the other param saved_msgsave the msg, when click 'cancel-btn', make the msg = saved_msg. But, there are some convenient ways ?
-------------------update 6/25-----------------
`<div ng-controller="Ctrl">
<div ng-class="{'selted':cid == 0}" data-cat="0"></div>
<div ng-class="{'selted':cid == 1}" data-cat="1"></div>
<div ng-class="{'selted':cid == 2}" data-cat="2"></div>
<textarea ng-bind="msg"></textarea>
<div class="cancel-btn" ng-click="is_hide=false;"></div>
<div class="submit-btn" ng-click="submit_card()"></div>
</div>
In fact, I use some directive byjQuery` to change the dom class, when click 'cancel-btn', I want to refresh the dom decide to scope.
It may be similar to $render.
--------------------update 6/26--------------
Thank for your help. I find a way to solve the problem.I add a div#main to contain what I want to save/refresh. ps: care for some change!
The follow is my way. Maybe you have better way, welcome to share!
`<div ng-controller="Ctrl">
<div id="main">
<div ng-class="{'selted':cid == 0}" data-cat="0" selt></div>
<div ng-class="{'selted':cid == 1}" data-cat="1" selt></div>
<div ng-class="{'selted':cid == 2}" data-cat="2" selt></div>
<textarea ng-bind="msg" id="msg"></textarea>
</div>
<div class="cancel-btn" ng-click="reset()"></div>
<div class="submit-btn" ng-click="submit_card()"></div>
</div>`
In controller,
`app.controller('Ctrl', ['$scope', '$compile', function($scope, $compile) {
$scope.reset = function(){ //reset depend on scope
$('#main div').removeClass('selted');
//the dom maybe change, need clear 'selted' class first
//compile(dom)(scope)
$('#main').html($compile($('#main').html())($scope));
}
$scope.save = function(){ //save the scope
$scope.msg = $('#msg').val();
$scope.cid = $('.selted').index()+1;
}
}]);
app.directive('selt', function(){
//this is directive, only change class on dom
return function(scope,ele){
ele.click(function(){
ele.addClass('selted').siblings().removeClass('selted');
})
}
});
`

1 - You need to replace ng-bind of your textarea with ng-model, to update the scope while you are typing on it.
2 - To provide a cancel like that, in a simple manner, replace the ng-click content with a function, like cancel(formName). In your controller:
angular.module('exampleApp', [])
.controller('Ctrl', ['$scope', function ($scope) {
$scope.is_hide = false;
$scope.msg = null;
$scope.cancel = function cancel (form) {
$scope.is_hide = true;
$scope.msg = null;
if (form) {
form.$setPristine();
}
};
}]);
The formName argument is the name of the wrapper form element that you're using on your HTML, to access some specific properties, like $setPristine, which will reset the form processing to its initial state. NOTE: This doesn't mean that the fields values will be reseted.

Related

angularjs dom manipulation based on button click

basically i want to change the attribute value based on the button i clicked,
these are the two buttons
<button ng-click="fn(a)"></button>
<button ng-click="fn(b)"></button>
and then i have a prebuilt directive who takes value as input,
<div directive-name="" id="abc"></div>
if i click on first button,i want the value of directive based on button clicked.
What i did earlier;
$scope.go = function(data){
if(data==a){
var b = document.querySelector( 'div' );
b.setAttribute("directive-name","value");
}
else{}
}
here the problem is that it is selecting the first div of document and setting attribute value for that.
I also tried to pass it with id like
var b = angular.element(document.querySelector('#abc'));
I also saw some custom directives to do so, but they are not working
AngularJS DOM Manipulation through Directives
If possible provide me a demo in plunkr or fiddle
and also if i want to change css property of div based on button clicked
Thanks in advance
You can do it like this.
Assign the directive-name value to a $scope.variable and then use variable as the value in HTML.
HTML - 1:
<button ng-click="go(a)"></button>
<button ng-click="go(b)"></button>
HTML - 2:
<div directive-name="{{directive}}" id="abc"></div>
JS:
$scope.go = function(data){
if(data==a){
$scope.directive = "directive-1";
}else if(data==b){
$scope.directive = "directive-2";
}
}
To assign class name to div you can define other $scope.classVar and then use that in HTML like below:
<div directive-name="{{directive}}" id="abc" ng-class="classVar"></div>
I hope this will solve your problem.
This should work, (you had some errors in your code):-
var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
$scope.fn = function(data,id) {
if (data == 'a') {
var b = document.querySelector('#'+id);
b.setAttribute("directive-name", "value");
} else {
}
}
});
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<div directive-name="" id="abc"></div>
<button ng-click="fn('a','abc')">A</button>
</div>
"Basically I want to change the attribute value based on the button I clicked."
You can do this by changing the attribute value the angular way, referencing a property of $scope or the controller instance in your template. When clicking a button, set the variable to the value you require to be passed to your directive.
Note: When you pass a value into your ngClick directive, you need to pass it as a string unless a and b are declared as properties of $scope.
Here's a basic example:
// app.js
(function() {
'use strict';
angular.module('app', []);
})();
// main.controller.js
(function() {
'use strict';
angular.module('app').controller('MainController', MainController);
MainController.$inject = ['$scope'];
function MainController($scope) {
$scope.fn = fn;
function fn(data) {
// set the value so it's accessable in the view
// therefore we can pass it into our directive
$scope.myVar = data;
}
}
})();
// directive-name.directive.js
(function() {
'use strict';
angular.module('app').directive('directiveName', directiveNameDirective);
function directiveNameDirective() {
return {
restrict: 'A',
scope: {
directiveName: '='
},
template: '<span>directiveName: {{ directiveName }}</span>'
};
}
})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="MainController as MainCtrl">
<!-- here we pass a and b as strings otherwise they get evaluated as variables -->
<button ng-click="fn('a')">Set a</button>
<button ng-click="fn('b')">Set b</button>
<hr>
<!-- here we pass myVar which is declared as a property of $scope when the fn function is called -->
<div directive-name="myVar" id="abc"></div>
<hr> myVar: {{ myVar }}
</div>
</div>

Not update scope immediately, watchers dont work at modal windows using ngDialog angular js

I am using ngDialog module for angularjs. My problem is that when I make a action at modal windows, I hope that my interface change depending of scope changes, but ng-if or ng-show or simply print variable not update. If I print in console I can see the rules values, but the interface not update.
My Code:
controller:
export class NewEntityController {
constructor($scope, $routeParams, $location, ngDialog) {
"ngInject";
// initialize scope
$scope.ruleForm = false;
$scope.rules = [];
$scope.showRulesSetting = function()
{
ngDialog.open({ template: 'views/modal-windows/rules.html', className: 'ngdialog-theme-default', scope: $scope });
};
$scope.addRule = function()
{
$scope.rules.push('rule-'+new Date().getTime());
console.log($scope.rules);
}
}}
Normal Page View:
<span style="color: blue;" ng-click="showRulesSetting()"><i class="fa fa-cog" aria-hidden="true"></i> Advance rules</span>
Modal Windows View:
<div>
<button ng-click="addRule()">Add</button>
</div>
<div id="ruleslist" ng-if="rules.length > 0">
{{rules}}
</div>
When I click in "Add" button I can see rules value in console, but in interface div with id="ruleslits" never show and the result of {{rules}} is always []. I test with $scope.$apply() but whithout result.

angularJS radio buttons not functioning

I'm having trouble communicating with my angularJS radio buttons. I'm using the material design framework. I'm fairly new to angular.
HTML
<div ng-controller="MainCtrl as ctrl">
<md-radio-group class="user-type">
<div layout="row" layout-sm="column" layout-align="space-between" layout-align-sm="space-around center">
<md-radio-button ng-model="userType" value="prospective" name="user_type" ng-change='newValue(value)'>Prospective Patient</md-radio-button>
<md-radio-button ng-model="userType" value="patient" name="user_type" ng-change='newValue(value)'>Patient</md-radio-button>
<md-radio-button ng-model="userType" value="caregiver" name="user_type" ng-change='newValue(value)'> Caregiver </md-radio-button>
<md-radio-button ng-model="userType" value="doctor" name="user_type" ng-change='newValue(value)'>Doctor</md-radio-button>
</div>
</md-radio-group>
</div>
JS
.controller('MainCtrl', ['$scope', function($scope) {
var self = this;
$scope.newValue = function(value) {
console.log(value);
};
$scope.$watch('userType', function(value){
if(value == "patient"){
console.log(value);
self.showPatientStepTwo = true;
}else{
console.log(value);
self.showPatientStepTwo = false;
}
});
}])
My ng-change isn't firing and my $watch isn't working either.
Can anyone find where I'm going wrong? I can't communicate between my controller and view!
When you use the controller as syntax, you should bind to that instead of the scope. I think the md-radio-button directive was creating a child scope that was messing things up but hard to reproduce without that directive.
Here's a plunker with the model and click bound to ctrl instead of $scope: http://plnkr.co/edit/fSTBDAMZLFKJgRD4br9K?p=preview
Radios changed to input, but referencing the ctrl:
<input type="radio" ng-model="ctrl.userType" value="prospective" name="user_type" class="user-type-rdo md-warn md-hue-2" ng-change='ctrl.newValue(value)'>Prospective Patient
And the controller updated to move the newValue function off $scope:
.controller('MainCtrl', ['$scope', function($scope) {
var self = this;
this.newValue = function(value) {
console.log(value);
};
$scope.$watch(function(){return self.userType;}, function(value){
if(value == "patient"){
console.log(value);
self.showPatientStepTwo = true;
}else{
console.log(value);
self.showPatientStepTwo = false;
}
});
}])
The newValue function logs undefined - not sure what you were trying to do there, but you can use self.userType in the newValue function if you want the value.
First thing: You don't need to declare an ng-model on ea. angular-material radio button when using radio groups, as per the angular-material docs for radio buttons.
The second thing is, the standard $scope events behave a bit differently when you build your controllers using controllerAs syntax See controllerAs Reference.
function MainController($scope, $log) {
var vm = this;
vm.title = 'Some Title';
vm.showPatientStepTwo = false;
// does not work
$scope.$watch('userType', function(newVal, oldVal){
// do work
});
// works
$scope.$watch('vm.userType', function(newValue, oldValue) {
// do work with newValue
});
// also works
$scope.$watch(function() {
return vm.userType;
}, function(newValue, oldValue) {
vm.showPatientStepTwo = newValue === 'patient';
});
}
Working plunkr: http://plnkr.co/edit/Dth67cQJKarwt3NiE9yp
<div class="form-group">
<label>Type of ad <b class="text-danger">*</b></label><br>
<input type="radio" name="typeofAd" value="sell" ng-model="product.typeofAd"> I want to sell
<input type="radio" name="typeofAd" value="buy" ng-model="product.typeofAd"> I want to buy
</div>
radio button this way works fine for me
refer link
https://scotch.io/tutorials/handling-checkboxes-and-radio-buttons-in-angular-forms
you need to add ng-model and ng-change event like below.
<md-radio-group ng-model="selectedVal" ng-change="showSelected()">
and in your controller you can defined function as following.
$scope.showSelected= function(){
console.log($scope.selectedVal);
}

Is it possible to update the model after keypress but before keyup?

I was developing a todo-like application with jQuery but I'd like to switch to Angular.
There is an input field for adding a new item, but as soon as anything is typed into this input, the key stroke -and those following it -are effectively moved to a new item among the group of existing items. This means that the new item input text box always remains empty. Better to show than explain:
http://jsfiddle.net/29Z3U/4/ (Many details removed, but the aspect of the app to which I'm referring is demonstrated).
<h1>Song List</h1>
<form id="songs">
<ul id="sortable_songs"></ul>
</form>
<ul id="new_song">
<script id="song_form_template" type="text/x-handlebars-template">
<li class="song" id="{{song_id}}">
<input type="text" placeholder="enter song" autofocus />
</li>
</script>
</ul>
Some jQuery:
var template = Handlebars.compile($('#song_form_template').html()),
counter = (function(){var i=0; return function(){return ++i};})(),
cloneNewSong = function(){
var count = counter(),
templateVals = {song_id: 'song_' + count};
$('ul#new_song').append(template(templateVals));
},
addSong = function(event){
//exclude certain keys here
cloneNewSong();
container = $(event.target).closest('li.song');
container.appendTo('ul#sortable_songs');
$(event.target)
.removeAttr('placeholder')
.focus(); //what I just typed disappears without this! why?
};
$('ul#new_song').on('keypress', 'input', addSong);
cloneNewSong();
Note that the new item input text box always remains empty and the focus behaves as it should so you can continue typing without interruption.
The application code is getting lengthy and I've not even yet attempted to display an existing list of songs derived from JSON. Of course in Angular, ngRepeat makes this easy. However, my attempt at an Angular version doesn't work: http://plnkr.co/edit/xsGRiHFzfsVE7qRxgY8d?p=preview
<!DOCTYPE html>
<html ng-app="songListApp">
<head>
<script src="//code.angularjs.org/1.2.7/angular.js"></script>
<link href="style.css" rel="stylesheet" />
<script src="script.js"></script>
</head>
<body ng-controller="songListController">
<h1>Song List</h1>
<ul id="songs">
<li ng-repeat="song in songs">
<input type="text" ng-model="song.song_name" />
</li>
</ul>
<form>
<input
ng-keypress="newSong(new_song)"
ng-model="new_song.title"
placeholder="enter song" autofocus
/>
</form>
</body>
</html>
JS:
var myapp = angular.module('songListApp', []);
myapp.controller('songListController', function($scope){
songs = [
{"song_name":"song 1","more_info":""},
{"song_name":"song 2","more_info":""},
{"song_name":"song 3","more_info":""}
];
$scope.songs = songs;
$scope.newSong = function(new_song){
var song = {"song_name": new_song.title, "more_info":""};
songs.push(song);
new_song.title = '';
};
});
Before even tackling the problem of focus management, I notice that the updating of Angular's model always lags behind by one keystroke. I assume this is because the keypress event happens before the character is inserted into the DOM.
I realise that switching from keypress to keyup would change things but the original design was based on the responsiveness of keypress.
I tried a custom directive to bind to the input event but things don't behave as I'd hoped: http://plnkr.co/edit/yjdbG6HcS3ApMo1T290r?p=preview
I notice that the first code example at angularjs.org (basic binding with no controller) doesn't seem to suffer from the issue I have -the example model is updated before a key is released.
Although there is an accepted answer, I present a different approach, which is a lot simpler with less tainted controller.
First, controller. it's just simple.
myapp.controller('songListController', function($scope, $timeout){
$scope.songs = [
{"song_name":"song 1","more_info":""},
{"song_name":"song 2","more_info":""},
{"song_name":"song 3","more_info":""}
];
});
Second, tag part
<input ng-keypress="createAndFocusIn(songs)"
create-and-focus-in="#songs input"
placeholder="enter song"
autofocus
/>
To explain,
when a key is pressed, it create a song and focus in songs list, createAndFocusIn(songs).
create-and-focus-in directive provides scope.createAndFocusIn(), so that controller does not have to have this function unless this directive is used. It accepts the selector, where to create new element, in this case, #songs input
Last but most importantly, directive part:
myapp.directive('createAndFocusIn', function($timeout){
return function(scope, element, attrs){
scope.createAndFocusIn = function(collection) {
collection.push({song_name: String.fromCharCode(event.keyCode)});
$timeout(function() {
element[0].value = '';
var el = document.querySelectorAll(attrs.createAndFocusIn);
el[el.length-1].focus();
});
};
};
});
in directive, it isn't doing much else except that is specified by attributes.
That's it.
This is working demo: http://plnkr.co/edit/mF15utNE9Kosw9FHwnB2?p=preview
---- EDIT ----
#KnewB said it does not work in FF. Chrome/FF working version here.
In FF,
window.event is not set when key pressed. we need to pass as parameter
event.keyCode does not exist, we need to check both keyCode || charCode
value and focus makes cursor to the first position, so needed to set focus first then add value
http://plnkr.co/edit/u2RtHWyhis9koQfhQEdW?p=preview
myapp.directive('createAndFocusIn', function($timeout){
return function(scope, element, attrs){
scope.createAndFocusIn = function(ev, collection) {
collection.push({});
$timeout(function() {
element[0].value = '';
var el = document.querySelectorAll(attrs.createAndFocusIn);
el[el.length-1].focus();
el[el.length-1].value = String.fromCharCode(ev.keyCode||ev.charCode);
});
};
};
});
and $event now passed:
<input ng-keypress="createAndFocusIn($event, songs)"
create-and-focus-in="#songs input"
placeholder="enter song"
autofocus
/>
solved:) btw. neat component! here is the plunkr: http://plnkr.co/edit/wYsFRUcqTZFv5uIE0MWe?p=preview
the html:
<body ng-controller="songListController">
<h1>Song List</h1>
<ul id="songs">
<li ng-repeat="song in songs">
<input type="text" ng-model="song.song_name" focus-me="song === newSong"/>
</li>
</ul>
<form>
<input
ng-keypress="createNewSong()"
ng-model="newSongTitle"
placeholder="enter song" autofocus
/>
</form>
</body>
I have changed the ng-keypress function, so that a new song is completely created in the controller. Also not the new directive focus-me - if the current song is the new created song, the input field gets the focus.
the controller:
myapp.controller('songListController', function($scope, $timeout){
$scope.songs = [
{"song_name":"song 1","more_info":""},
{"song_name":"song 2","more_info":""},
{"song_name":"song 3","more_info":""}
];
$scope.newSongTitle = '';
$scope.newSong = null;
$scope.createNewSong = function(){
$timeout(function(){
$scope.newSong = {"song_name": $scope.newSongTitle, "more_info":""};
$scope.songs.push($scope.newSong);
$scope.newSongTitle = '';
});
};
});
As you can see the creation of the new song is wrapped by a $timeout call. This call delayed the execution until the next digest cycle happens, so no pending events can interrupt us.
finally the directive:
myapp.directive('focusMe', function(){
return function($scope, element, attr){
if($scope.$eval(attr.focusMe)){
element[0].focus();
}
};
});
for sure generic, so that every expression can trigger the focus.

How to set repeated element id in AngularJS?

I'd like to do something like:
<div class='row' ng-repeat='row in _.range(0,12)'>
<div id='{{row}}'></div>
</div>
but when in the controller I try:
function SetterForCatanCtrl($scope) {
$scope._ = _;
try {
var tile = document.getElementById('5');
tile.style.backgroundImage = "url('aoeu.png')";
} catch (e) {
alert(e)
}
}
getElementById returns null so how can an element's id be set using AngularJS variables?
The function SetterForCatanCtrl is run only once, when angular encounters a ngController directive while it bootstraps your app. When this happens the element you want to access from the DOM doesn't exist yet.
Doing DOM manipulation from a controller is not a good practice, directives are can solve the kind of problem you are facing. Your use case can be solved with CSS and just switching classes but I guess you want to do more than just setting a background image.
DOM manipulation from a controller
You are not asking for custom directives, so a quick solution could done using the ngClick directive and call a method that can switch images
Example HTML
<div ng-controller='ctrl'>
<div class='row' ng-repeat='row in _.range(0,12)'>
<div id='{{row}}' ng-click="click($index)">
<button>{{row}}</button>
</div>
</div>
</div>
And JS
var App = angular.module('app', []);
App.run(function($rootScope) {
$rootScope._ = _;
});
App.controller('ctrl', function($scope){
$scope.click = function(idx){
var elem = document.getElementById(idx);
console.log('clicked row', idx, elem);
};
​}); ​
So when a button is clicked you will get an id and use it to get an element from the DOM. But let me repeat, a for this use case a directive is a better choice.
JSFiddle: http://jsfiddle.net/jaimem/3Cm2Y/
pd: if you load jQuery you can use angular.element(<selector>) to select elements from the DOM.
edit: adding directive example
DOM manipulation from a directive
Using a directive is simpler, since you can just bind an event to the element the directive is applied to
HTML
<h1>Directive</h1>
<div class='row' ng-repeat='row in _.range(0,12)'>
<div id='{{row}}' my-directive>
<button>{{row}}</button>
</div>
</div>
JS
App.directive('myDirective', function(){
return function(scope, element, attr){
element.bind('click', function(){
console.log('clicked element: ', element, element.html());
});
};
});
http://jsfiddle.net/jaimem/3Cm2Y/1/

Resources