I know you're not supposed to put your display logic inside of a controller and I'm struggling with the proper AngularJS way to approach this.
I'm presenting forms inside modals. I'm using Zurb Foundation's reveal for the modal.
Markup:
<div class="button" ng-click="modalAddWidget">Add Widget</div>
<div id="modalAddWidget" class="reveal-modal">
<h6>New Widget</h6>
<form>
<fieldset>
<legend>Widget Name</legend>
<input type="text" ng-model="ui.add_widget_value" />
</fieldset>
<div class="small button right" ng-click="addWidget()">Add Widget</div>
<div class="small button right secondary" ng-click="addWidgetCancel()">Cancel</div>
</form>
</div>
Controller:
...
$scope.modalAddWidget = function() {
$("#modalAddWidget").reveal();
}
$scope.addWidget = function() {
$scope.myobject.widgets.push({"name": $scope.ui.add_widget_value});
$scope.ui.add_widget_value = '';
$('#modalAddWidget').trigger('reveal:close');
}
$scope.addBudgetCancel = function() {
$scope.ui.add_widget_value = '';
$('#modalAddWidget').trigger('reveal:close');
}
...
Note: $scope.ui is an object I am using to store UI values that shouldn't be bound to my object until the user actually clicks "Add Widget"
$scope.myobj is where my data is stored.
Foundation's $("#modalAddWidget").reveal(); function presents the modal overlay.
Since I shouldn't be putting my display code inside the controller, what is the proper way to approach this?
You don't want to manipulate the DOM (or even reference it) from your controllers.
A directive is best here.
app.directive('revealModal', function (){
return function(scope, elem, attrs) {
scope.$watch(attrs.revealModal, function(val) {
if(val) {
elem.trigger('reveal:open');
} else {
elem.trigger('reveal:close');
}
});
elem.reveal();
}
});
then in your controller:
$scope.modalAddWidget = function (){
$scope.ui = { add_widget_value: '' };
$scope.showModal = true;
};
$scope.addWidget = function (){
$scope.myobject.widgets.push({"name": $scope.ui.add_widget_value});
$scope.ui.add_widget_value = '';
$scope.showModal = true;
};
And in your HTML
<div class="button" ng-click="modalAddWidget()">Add Widget</div>
<div id="modalAddWidget" class="reveal-modal" reveal-modal="showModal">
<h6>New Widget</h6>
<form name="addWidgetForm" ng-submit="addWidget()">
<fieldset>
<legend>Widget Name</legend>
<input type="text" name="widgetValue" ng-model="ui.add_widget_value" required />
<span ng-show="addWidgetForm.widgetValue.$error.required">required</span>
</fieldset>
<button type="submit" class="small button right">Add Widget</button>
<div class="small button right secondary" ng-click="showModal = false;">Cancel</div>
</form>
</div>
Basically you'd set a boolean in your scope to show and hide your modal. (I'm not sure of reveal modal's open/close mechanism, so I'm guessing in my code above).
ALSO: I went through the effort of adding some validation in there.
Related
I have a very simple script that contains angular js
<script>
var delightApp = angular.module('delightmeter', []);
delightApp.controller('delightController', function ($scope) {
$scope.delightScore = 0;
$scope.test = function () {
if (isNaN($scope.delightScore)) {
// do not bind if this happens
}
}
});
</script>
The html of the above script is
<div id="angularapp" data-ng-app="delightmeter" data-ng-controller="delightController">
<input id="Text1" type="text" data-ng-model="delightScore" />
{{delightScore}}
<input id="Button1" type="button" value="button" data-ng-click="test()"/>
</div>
As we know in angular two way binding happens whatever may be the value in $scope.delightScore it gets bound to the html page.
Is there any way to stop this binding from happening ?
Rather than binding to the variable directly, Bind to a function that does your check
<div id="angularapp" data-ng-app="delightmeter" data-ng-controller="delightController">
<input id="Text1" type="text" data-ng-model="delightScore" />
{{ValideDelightScore()}}
<input id="Button1" type="button" value="button" data-ng-click="test()"/>
</div>
And in your controller define:
$scope.ValideDelightScore = function () {
if (isNaN($scope.delightScore)) {
return "";
}else{
return $scope.delightScore
}
}
There is no way to do that because you are explicity binding the model in both scopes. And even if you could you should'n mess with the Angular life-cycle, or you are going to have a bad time.
The right way to achieve what you need is, or using solution purposed by #Shivas Jayaram, or use a filter in where you don't want to display the model if isNaN.
angular.module('myApp.filters', [])
.filter('NaNFilter', function($moment, $translate) {
return function(value) {
if(isNaN(val)) {
return '';
}
return value;
};
});
And in your template:
<div id="angularapp" data-ng-app="delightmeter" data-ng-controller="delightController">
<div>Show delightScore if !NaN: {{delightScore | NaNFilter}}</div>
<input id="Text1" type="text" data-ng-model="delightScore" />
{{delightScore}}
<input id="Button1" type="button" value="button" data-ng-click="test()"/>
</div>
I'm very new to angular so I may be going about this all wrong but here goes. I have a form
<form name="search_form" novalidate ng-submit="searchForm(search_form.$valid)" >
<div class="maincontainer">
<div class="formcontainer">What to eat?</div>
<div class="formcontainer"><input type="text" name="food_type" ng-model="food_type" placeholder="Enter a search term" required></div>
<div class="formcontainer">Where to look?</div>
<div class="formcontainer"> <input type="text" name="cityname" ng-model="trader.cityname" value="cityname" googleplace="" placeholder="Enter a location" required>
</div>
<div class="formcontainer">
<button type="submit" class="btn-main2" >Submit</button>
</div>
</form>
that when I submit I want to grab the results based on the location I get from google and display them in a new view
myControllers.controller('SearchCtrl',['$scope','Search','$location', function ($scope,Search,$location) {
$scope.setSearchLocation = function(place){
$scope.lat = place.geometry.location.lat();
$scope.lng = place.geometry.location.lng();
}
$scope.searchForm = function() {
// check to make sure the form is valid
if (!$scope.search_form.$valid) {
alert('Please fill out all fields');
}
else{
$scope.results = Search.do_search($scope.lat,$scope.lng);
$location.path('search-results');
}
};
}])
.directive('googleplace', function() {
return {
require : 'ngModel',
link : function(scope, element, attrs, model) {
var options = {
types : [],
};
scope.gPlace = new google.maps.places.Autocomplete(element[0],options);
google.maps.event.addListener(scope.gPlace, 'place_changed',function() {
var place = scope.gPlace.getPlace();
scope.setSearchLocation(place);
scope.$apply(function() {
model.$setViewValue(element.val());
});
});
},
};
});
everything works as expected except the view does not update in the results view. If I set the $scope.results out side the searchForm() function everything renders properly. I realize this is because it exists before the page renders, just saying that part works.
when I try $scope.$apply() it says already in progress
<div id="results-container" ng-repeat="result in results">
<div id="picbox"><img src="../images/test.jpg" alt="" "/></div>
<div id="addressinfo">
<h4>John's Restaurant </h4>
<p>123 York Street, Toronto ON <br>
<span id="type">#
Burgers, #Poutine</span></p>
</div>
<div id="location">4.2m<br>
<img src="../images/heart.png" width="86" height="76" alt=""/><br>
</div>
</div>
</div>
When you call $location.path(...), $scope object of controller is always initialized.
My suggestion is ...
write the element of div#results-container on the same template where form[name=search_form] exists.
remove $location.path('search-results');
I hope this could help you.
I have an angular ui-grid in which data is displayed and in every row there is an "Edit" and "Delete" button. When the user clicks on the "Edit" then a modal window is displayed and the fields are populated and the user can edit the data. This is the user scenario I have. When the user clicks on the "Edit" button I pass through the id value and when angular creates the modal window I query the data from the server.
As you can see in the html the controller is not bound because it is managed by another controller. If it would be bound here then it would be called twice.
The fields are populated properly and when I change something in it and I want to save it then breeze says that nothing has changed in the entity and the log in the save method gives back the original values of the entity.
I assume something is wrong with the data binding but I don't know what.
According to the pluralsight video and breeze's documentation breeze tracks the changes in the entity.
What I'm doing wrong?
The form:
<form class="form-horizontal" name="editModuleModalForm">
<legend>Edit module</legend>
<div class="control-group">
<label class="control-label">Module name</label>
<div class="controls">
<input type="text"
placeholder="Module name here..."
data-ng-model="vm.sysmodule.name"
data-z-validate />
</div>
</div>
<div class="control-group">
<label class="control-label">Module Sort number</label>
<div class="controls">
<input type="text"
placeholder="Module sort number"
data-ng-model="vm.sysmodule.sortNo"
data-z-validate />
</div>
</div>
<div class="control-group">
<label class="control-label">Route</label>
<div class="controls">
<input type="text"
placeholder="Module route comes here..."
data-ng-model="vm.sysmodule.route"
value="vm.sysmodule.route"
data-z-validate />
</div>
</div>
<div class="control-group">
<button type="submit" class="btn btn-success" data-ng-click="vm.save()">Save</button>
<button type="submit" class="btn btn-danger" data-ng-click="vm.cancel()">Cancel</button>
</div>
</form>
Angular controller for the form
(function () {
'use strict';
var controllerId = 'editModuleController';
angular
.module('dilibApp')
.controller(controllerId, ['$scope', '$modalInstance', 'selectedModuleId', 'common', 'datacontext', editModuleController]);
function editModuleController($scope, $modalInstance, selectedModuleId, common, datacontext) {
/* jshint validthis:true */
var vm = this;
vm.title = 'editModule';
vm.sysmodule = undefined;
vm.cancel = cancel;
vm.save = save;
activate();
function activate() {
onDestroy();
common.activateController([getModulePropertiesToBeEdited()], controllerId);
}
function getModulePropertiesToBeEdited() {
return datacontext.sysmodule.getById(selectedModuleId)
.then(function (result) {
vm.sysmodule = result[0];
});
}
function onDestroy() {
$scope.$on('$destroy', function () {
datacontext.cancel();
});
}
function cancel() {
$modalInstance.dismiss('cancel');
}
function save() {
console.log(vm.sysmodule);
if (datacontext.hasChanges) {
datacontext.saveChanges();
console.log('Changes are saved!');
} else {
console.log('There are no changes to be saved!');
}
$modalInstance.close();
}
}
})();
Nothing is wrong with the binding. The root if the problem is that the datacontext doesn't have hasChanges method due to that the breeze's entityManager is wrapped in it. I had to rework the code a little bit, rethinking the responsibilities and it is working now.
I have a simple text box and a button and whenever user click on button the alert shows the text of textbox but I want to do it this way(I know there are a lot better ways but first I want to understand why this way does not work):
var app = angular.module('app', []);
app.factory('Service', function() {
var service = {
add: add
};
return service;
function add($scope) {
alert($scope.user.username);
}
});
app.controller('table', function(Service,$scope) {
//get the return data from getData funtion in factory
this.add = Service.add($scope);
});
As you can see I send the scope to factory and I define the user.username as follow:
<button class="btn btn-primary" ng-click="t.add(user.userName)">
But when I run this nothing happens can anyone tell me what is wrong with this code?
<body ng-app="app">
<form>
<div class="row commonRow" ng-controller="table as t">
<div class="col-xs-1 text-right">item:</div>
<div class="col-lg-6 text-right">
<input id="txt" type="text" style="width: 100%;"
ng-model="user.userName">
</div>
<div class="col-xs-2">
<button class="btn btn-primary" ng-click="t.add(user.userName)">
click me</button>
</div>
</div>
</form>
also the plnk link is as follow:
plnkr
The problem is in this line
this.add = Service.add($scope);
Here you are assigning the returned (which is undefined) value of the Service.add($scope) invocation to the this.add.
The right approach will be either
this.add = function(data) { Service.add($scope); }
or
this.add = Service.add;
// in the service factory.
function add(usrNm) {
alert(usrNm);
}
The first thing is you can't use $scope in a service.and in controller your not assaining this(object) to $scope.so $scope doesn't contain any value.
I suppose you to write like this
$scope.user = this;
i have two divs i want to show them conditionally with onclick event .
my-angular-app.js
$(document).on('click', '#showless', function(el) {
var appElement = document.querySelector('[ng-app=myapp]');
var $scope = angular.element(appElement).scope();
$scope.$apply(function() {
$scope.value = false;
});
});
$(document).on('click', '#showmore', function(el) {
var appElement = document.querySelector('[ng-app=myapp]');
var $scope = angular.element(appElement).scope();
$scope.$apply(function() {
$scope.value = true;
});
});
and my div of myapp (myapp.html)
<div ng-show="desc" id="description" class="text-muted" style="padding-top:5px;padding-left:10px;padding-right:10px;color:#2E2E2E;font-size:11px;">{{myapp.value|truncate}}<span><a id="showmore" href="">more</a></span>
</div>
<div ng-show="!desc" id="description" class="text-muted" style="padding-top:5px;padding-left:10px;padding-right:10px;color:#2E2E2E;font-size:11px;">{{myapp.value}}<span><a id="showless" href="">less</a></span>
</div>
(truncate is a filter i wrote which works fine .)
I'm not exactly sure what you are trying to do, but Angular provides ng-click, so you should not have to bind to $(document).on('click').
I'd suggest a simpler approach for conditional show:
<div ng-show="desc" id="description" class="text-muted" style="padding-top:5px;padding-left:10px;padding-right:10px;color:#2E2E2E;font-size:11px;">{{myapp.value|truncate}}<span><a id="showmore" ng-click="desc = true" href="#">more</a></span>
</div>
<div ng-show="!desc" id="description" class="text-muted" style="padding-top:5px;padding-left:10px;padding-right:10px;color:#2E2E2E;font-size:11px;">{{myapp.value}}<span><a id="showless" ng-click="desc = false" href="#">less</a></span>
</div>
The above uses ng-click to set the value of desc. Therefore, you don't need any other logic in the controller to toggle the divs.