I'm trying to update the Toggle/Checkbox value back to default/Off when the user selects the cancel option. Currently it sticks to Red/On.
Any suggestions to figuring this out would be great,
Before
Alert
After
Controller
// Default Value
$scope.turnClearAppData = 'Off';
// Ionic OK Alert
$scope.showConfirm = function(val) {
if ( val == 'On') {
var confirmPopup = $ionicPopup.confirm({
title: 'Clear App Data',
template: 'Select OK to Confirm!'
});
confirmPopup.then(function(res) {
if(res) {
// Reset LocalStorage Data
$localStorage.$reset();
// Redirect After Reset
$location.path('/intro/part-one');
} else {
// On Cancel Update Value
$scope.turnClearAppData = 'Off';
}
});
}
};
HTML
<li class="item item-toggle noborder">
Clear App Data {{turnClearAppData}}
<label class="toggle toggle-assertive">
<input type="checkbox" ng-model="turnClearAppData" ng-true-value="'On'" ng-false-value="'Off'" ng-change="showConfirm(turnClearAppData)">
<div class="track">
<div class="handle"></div>
</div>
</label>
</li>
The problem is in the scope.
I answered a question few days ago where I have some links to some tutorials where they tell you exactly why you should avoid using scope as a model.
The best solution is to avoid $scope to attach your view model.
You can have a look at this plunker where your sample works.
I've used a ControllerAs syntax to fix the problem.
It's very easy to implement. In my plunker I've defined the ControllerAs in the state:
.state('home', {
url: '/home',
templateUrl: 'home.html',
controller: 'HomeController as vm'
});
but you can do it in many other ways:
<div ng-controller="HomeController as vm">
</div>
in your controller now you create your viewmodel:
.controller('HomeController', function($scope, $state, $timeout, $ionicPopup) {
var vm = this;
vm.turnClearAppData = 'Off';
vm.showConfirm = function(val) {
if ( val === 'On') {
var confirmPopup = $ionicPopup.confirm({
title: 'Clear App Data',
template: 'Select OK to Confirm!'
});
confirmPopup.then(function(res) {
if(res) {
// Reset LocalStorage Data
// $localStorage.$reset();
// Redirect After Reset
// $location.path('/intro/part-one');
alert('Ok pressed!');
} else {
// On Cancel Update Value
vm.turnClearAppData = 'Off';
return vm.turnClearAppData;
}
});
}
};
});
Notice the var vm = this as first expression.
Now all your objects and methods are mapped on the viewmodel (vm).
Your HTML now can work with the viewmodel
<li class="item item-toggle noborder">
Clear App Data {{vm.turnClearAppData}}
<label class="toggle toggle-assertive">
<input type="checkbox" ng-model="vm.turnClearAppData" ng-true-value="'On'" ng-false-value="'Off'" ng-change="vm.showConfirm(vm.turnClearAppData)">
<div class="track">
<div class="handle"></div>
</div>
</label>
</li>
obtaining the expected behaviour.
Try to use $timeout in such cases where updating the scope variable value do not update the related changes. Try this.
// Default Value
$scope.turnClearAppData = 'Off';
// Ionic OK Alert
$scope.showConfirm = function(val) {
if ( val == 'On') {
var confirmPopup = $ionicPopup.confirm({
title: 'Clear App Data',
template: 'Select OK to Confirm!'
});
confirmPopup.then(function(res) {
if(res) {
// Reset LocalStorage Data
$localStorage.$reset();
// Redirect After Reset
$location.path('/intro/part-one');
} else {
// On Cancel Update Value
$timeout(function () {
$scope.turnClearAppData = 'Off';
}, 0);
}
});
}
};
Related
Can you help me with this? I am trying to access the function on my parent/main controller from the template created on the modal instance but I couldn't access it directly. Can you help me with this?
Here's my code:
Here's the function for accessing and showing the modal:
openQuestionModalForm() {
let ctrl = this;
var modalInstance = ctrl.$modal.open({
templateUrl: 'questionModal.tmpl.html',
scope: this.$scope,
controller: ['$modalInstance', function($modalInstance) {
this.parentController = ctrl;
this.modalOptions = {
mode: 'add',
actionType: 'add'
}
console.log(this.Questionnaire_formController);
//this.modalOptions.data = angular.copy(data);
// this.modalOptions = {
// mode: mode,
// id: id,
// data: angular.copy(data),
// actionType: mode //set to mode temporarily should be ActionType
// }
this.close = $modalInstance.close;
}],
size: 'small',
controllerAs: '$ctrl',
windowClass: 'addeditcourseware-modal'
});
modalInstance.result.then(function(result) {
try {
console.log('*-----*', result);
} catch(e) {
console.log('Error!');
}
})
}
This is triggered by this function and I attached this event on a button
openAddEditCoursewareModal(mode, id, data, actionType) {
let ctrl = this;
if(ctrl.QuestionsStore.questionnaireType == 'Questionnaire') {
this.openQuestionModalForm(); //shows my modal based on the condition
....
<div class="row">
<div class="columns title-box move-right">
<p class="title">Questions</p>
<div class="button_link">
<button class="button-primary button-default" ng-click="$ctrl.openAddEditCoursewareModal('add')">Add Question</button>
</div>
</div>
</div>
Now here's the part where I am calling the function on my parent/main controller.
<label class="stacked">
Question Type:
<select name="question_type" class="form-field" id="questionType" ng-model="$ctrl.questionModal.data.attributes.question_type" ng-change="$ctrl.getSelectedQuestionType()" ng-required="true">
<option value="single">Single</option>
<option value="multiple">Multiple</option>
<option value="true_or_false">True or False</option>
<option value="free_text">Free Text</option>
</select>
</label>
I am calling the function getSelectedQuestionType on the onchange event. But the result on the console is undefined.
Can you help me with this?
You could pass the function reference through a resolve block and then access it in your modal controller, as such:
modalInstance = ctrl.$modal.open({
...
resolve: {
'getSelectedQuestionType': function () {
return ctrl.getSelectedQuestionType;
}
},
controller: ['$modalInstance', 'getSelectedQuestionType', function($modalInstance, getSelectedQuestionType) {
// Bind to controller so that you can call it via $ctrl.getSelectedQuestionType in your view
this.getSelectedQuestionType = getSelectedQuestionType;
}
...
});
Hope this helps :)
I have following controller. Which has a custom html show-chat defined using directive.
<div id='homeDiv' ng-controller="bloggingHomeController">
<div id ='chatAreaDiv' style="float:left;width:25%;">
<h3>Messages</h3>
<show-chat></show-chat>
</div>
</div>
Following is the directive definition of show-chat
appDirective.directive('showChat', function() {
return {
restrict: 'E', // E = Element, A = Attribute, C = Class, M = Comment
// template: '',
templateUrl: 'partials/chat.html',
replace: true
};
});
chat.html is as follows.
<div id='chatHtmlDiv' ng-controller='chatController'>
<ul>
<li ng-repeat="aChat in allChats" style="list-style-type: none;">
<b style="color:red;">{{aChat.message}}</b><br/>
<cite>by {{aChat.createdBy}} on {{aChat.createdDate | date}}</cite>
</li>
</ul>
<textarea id='chatBox' ng-model='chatMessage'></textarea>
<br/>
<button id='postChatButton' ng-click = postChatMessage()>Post Message</button>
</div>
Inside bloggingHomeController i have the following.
app.controller('chatController', function($scope, $route, chatService) {
chatService.getChat()
.then(function(data) {
$scope.allChats = data;
}, function(error) {
});
$scope.postChatMessage = function() {
var c = JSON.parse(JSON.stringify($scope.chatMessage));
chatService.save(c)
.then(function(data) {
$scope.chatMessage = '';
}, function(error) {
})
chatService.getChat()
.then(function(data) {
$scope.allChats = data;
$route.reload();
}, function(error) {
});
};
});
When postChatMessage() is being called from chat.html a request is made to the database to get the chat messages .
The issue is $scope.allChats is getting updated with the new value but
show-chat doesnot show the updated data.
May i know is this not getting updated ?
Thanks in advance.
It can happens because you try to save new message and receive list of messages in the same time.
Try to move receiving of new messages inside to success callback for chat message save request
chatService.save(c)
.then(function(data) {
$scope.chatMessage = '';
// Receive new messages here
}, function(error) {
})
Call this $scope.$apply() after set the your response to models.
I am building a web application for our customer support. One of the needs is to be able to keep multiple tickets opened at the same time.
I was able to do the first part easily using a tabulation system and UI-Router.
However, with my current implementation, each time I change active tab, the previously-current tab is unloaded, and the now-current tab is loaded (because it was unloaded with a previous tab change).
This is not at all the expected behavior. I've already spent a couple of days trying to find a way to achieve this, without any luck.
The closest thing I was able to do is to use the multiple views system from UI-Router, but I need multiple instance of the same view to keep in memory (if multiple tickets are opened, they all are on the same view, with the same controller, but a different scope)
Here's my current implementation:
supportApp.js:
var app = angular.module("supportApp", ["ui.router", "ui.bootstrap"]);
app.config(function($stateProvider, $urlRouterProvider, $httpProvider){
$urlRouterProvider.otherwise("/");
$stateProvider
.decorator('d', function(state, parent){
state.templateUrl = generateTemplateUrl(state.self.templateUrl);
return state;
})
.state("main", {
abtract: true,
templateUrl: "main.html",
controller: "mainController"
})
.state("main.inbox", {
url: "/",
templateUrl: "inbox.html",
controller: "inboxController"
})
.state('main.viewTicket', {
url: '/ticket/{id:int}',
templateUrl: "viewTicket.html",
controller: "ticketController"
})
;
});
mainController.js: (handles other stuff, minimal code here)
app.controller("mainController", function($rootScope, $http, $scope, $state, $interval){
// Tabs system
$scope.tabs = [
{ heading: "Tickets", route:"main.inbox", active:false, params:{} }
];
var addTabDefault = {
heading: '',
route: null,
active: false,
params: null,
closeable: false
};
$rootScope.addTab = function(options){
if(!options.hasOwnProperty('route') || !options.route)
{
throw "Route is required";
}
var tabAlreadyAdded = false;
for(var i in $scope.tabs)
{
var tab = $scope.tabs[i];
if(tab.route == options.route && angular.equals(tab.params, options.params))
{
tabAlreadyAdded = true;
break;
}
}
if(!tabAlreadyAdded)
{
$scope.tabs.push($.extend({}, addTabDefault, options));
}
if(options.hasOwnProperty('active') && options.active === true)
{
$state.go(options.route, options.hasOwnProperty('params')?options.params:null);
}
};
$scope.removeTab = function($event, tab){
$event.preventDefault();
if($scope.active(tab.route, tab.params))
{
$scope.go($scope.tabs[0].route, $scope.tabs[0].params);
}
$scope.tabs.splice($scope.tabs.indexOf(tab), 1);
};
$scope.go = function(route, params){
$state.go(route, params);
};
$scope.active = function(route, params){
return $state.is(route, params);
};
$scope.$on("$stateChangeSuccess", function() {
$scope.tabs.forEach(function(tab) {
tab.active = $scope.active(tab.route, tab.params);
});
});
});
main.html:
<div class="container-fluid" id="sav-container">
<div class="row-fluid">
<div class="col-lg-2">
<form role="form" id="searchForm" action="#">
<div class="form-group has-feedback">
<input class="form-control" type="search" />
<span class="glyphicon glyphicon-search form-control-feedback"></span>
</div>
</form>
</div>
<div class="col-lg-10" id="support_main_menu">
<ul class="nav nav-tabs">
<li ng-repeat="t in tabs" ng-click="go(t.route, t.params)" ng-class="{active: t.active, closeable: t.closeable}" style="max-width: calc((100% - 128px) / {{tabs.length}});">
<a href class="nav-tab-text">
<button ng-show="t.closeable" ng-click="removeTab($event, t)" class="close" type="button">×</button>
<span>{{t.heading}}</span>
</a>
</li>
</ul>
</div>
</div>
<div class="row-fluid">
<div class="tab-content" ui-view></div>
</div>
</div>
It seems to me that what I ask is pretty standard, but I sadly couldn't find any usefull thing on the Internet
The basic idea is to store state (i.e. list of tickets) in a service as opposed to a controller. Services hang around for the life of the application. There are some articles on this. I'm still developing my approach but here is an example:
var RefereeRepository = function(resource)
{
this.resource = resource; // angular-resource
this.items = []; // cache of items i.e. tickets
this.findAll = function(reload)
{
if (!reload) return this.items;
return this.items = this.resource.findAll(); // Kicks off actual json request
};
this.findx = function(id)
{
return this.resource.find({ id: id }); // actual json query
};
this.find = function(id) // Uses local cache
{
var itemx = {};
// Needs refining
this.items.every(function(item) {
if (item.id !== id) return true;
itemx = item;
return false;
});
return itemx;
};
this.update = function(item)
{
return this.resource.update(item);
};
};
refereeComponent.factory('refereeRepository', ['$resource',
function($resource)
{
var resource =
$resource('/app_dev.php/referees/:id', { id: '#id' }, {
update: {method: 'PUT'},
findAll: {
method: 'GET' ,
isArray:true,
transformResponse: function(data)
{
var items = angular.fromJson(data);
var referees = [];
items.forEach(function(item) {
var referee = new Referee(item); // Convert json to my object
referees.push(referee);
});
return referees;
}
},
find: {
method: 'GET',
transformResponse: function(data)
{
var item = angular.fromJson(data);
return new Referee(item);
}
}
});
var refereeRepository = new RefereeRepository(resource);
// Load items when service is created
refereeRepository.findAll(true);
return refereeRepository;
}]);
So basically we made a refereeRepository service that queries the web server for a list of referees and then caches the result. The controller would then use the cache.
refereeComponent.controller('RefereeListController',
['$scope', 'refereeRepository',
function($scope, refereeRepository)
{
$scope.referees = refereeRepository.findAll();
}
]);
I've been struggling with the following problem:
I'm trying to delete a 'Post' item from a Firebase Array with the $remove AngularFire method which I have implemented in a Angular Service (Factory). This Post is a child of 'Event', so in order to delete it I have to pass this Service a argument with the relevant Event of which I want to delete the post.
This is my controller:
app.controller('EventSignupController', function ($scope, $routeParams, EventService, AuthService) {
// Load the selected event with firebase through the eventservice
$scope.selectedEvent = EventService.events.get($routeParams.eventId);
// get user settings
$scope.user = AuthService.user;
$scope.signedIn = AuthService.signedIn;
// Message functionality
$scope.posts = EventService.posts.all($scope.selectedEvent.$id);
$scope.post = {
message: ''
};
$scope.addPost = function (){
$scope.post.creator = $scope.user.profile.username;
$scope.post.creatorUID = $scope.user.uid;
EventService.posts.createPost($scope.selectedEvent.$id, $scope.post);
};
$scope.deletePost = function(post){
EventService.posts.deletePost($scope.selectedEvent.$id, post);
// workaround for eventService bug:
// $scope.posts.$remove(post);
};
});
And this is my Service (Factory):
app.factory('EventService', function ($firebase, FIREBASE_URL) {
var ref = new Firebase(FIREBASE_URL);
var events = $firebase(ref.child('events')).$asArray();
var EventService = {
events: {
all: events,
create: function (event) {
return events.$add(event);
},
get: function (eventId) {
return $firebase(ref.child('events').child(eventId)).$asObject();
},
delete: function (event) {
return events.$remove(event);
}
},
posts: {
all: function(eventId){
var posts = $firebase(ref.child('events').child(eventId).child('posts')).$asArray();
return posts;
},
createPost: function (eventId, post) {
// this does work
var posts = $firebase(ref.child('events').child(eventId).child('posts')).$asArray();
return posts.$add(post);
},
deletePost: function (eventId, post) {
// this does not work
var posts = $firebase(ref.child('events').child(eventId).child('posts')).$asArray();
return posts.$remove(post);
}
}
};
return EventService;
});
When I try to delete the link tag just freezes and no error logging appears in the console. While if I call $remove on my $scope.posts directly in my controller it magically works.. Furthermore my Post is not removed from my Firebase DB.
Another weird thing is that 'CreatePost' works perfectly fine using the same construction.
My view:
<div class="col-xs-8 col-xs-offset-2 well">
<form ng-submit="addPost()" ng-show="signedIn()">
<input type="text" ng-model="post.message" />
<button type="submit" class="btn btn-primary btn-sm">Add Post</button>
</form>
<br>
<div class="post row" ng-repeat="post in posts">
<div>
<div class="info">
{{ post.message }}
</div>
<div>
<span>submitted by {{ post.creator }}</span>
delete
</div>
<br>
</div>
</div>
</div>
P.s. I'm not too sure that my 'Service' is implemented in the best possible way.. I couldn't find another solution for doing multiple firebase calls
var posts = $firebase(ref.child('events').child(eventId).child('posts')).$asArray();
within the Post part of my EventService, because it depends on eventId in each CRUD operation. Any ideas would be very welcome :)
The easiest way for me was to use this:
var ref= new Firebase('https://Yourapp.firebaseio.com/YourObjectName');
ref.child(postId).remove(function(error){
if (error) {
console.log("Error:", error);
} else {
console.log("Removed successfully!");
}
});
The only way I'm able to remove the item is using a loop on the array we get from firebase.
var ref= new Firebase('https://Yourapp.firebaseio.com/YourObjectName');
var arr_ref=$firebaseArray(ref);
for(var i=0;i<arr_ref.length;i++){
if(key==arr_ref[i].$id){
console.log(arr_ref[i]);
arr_ref.$remove(i);
}
}
I'm developing a simple Ionic mobile app although the answer likely lies with Angular. The app is really simple, displays a list of employees with an Add button which displays a modal, lets the user enter some details, click Save and it's persists the data to a back-end Firebase store. It has 1 controller and a simple service. Initially I had the template html for the modal inside script tags inside the index.html and it all worked fine. When I decided to structure things out and put the modal template in a separate html file, suddenly the data object assigned to ng-modal via the input boxes no longer passes any data to the event handler to save the data, instead it's always undefined. Everything else works as it should, the modal displays ok, the event handlers are calling the right functions etc. The only change is moving the input template to a separate file. I know it's likely something really simple but can't for the life of me work out why and can't find any info about it anywhere else.
Template HTML file for the modal :
<ion-list>
<h1>Add Employee</h1>
<div class="list list-inset">
<ion-item>
<label class="item item-input">
<input type="text" placeholder="Employee Name" ng-model="data.employeeName">
</label>
<label class="item item-input">
<input type="text" placeholder="Employee Age" ng-model="data.employeeAge">
</label>
</ion-item>
<button class="button button-outline button-block button-balanced"
ng-click="addEmployee(true, data)">
Save & Add Another
</button>
<button class="button button-outline button-block button-positive"
ng-click="addEmployee(false, data)">
Save
</button>
<button class="button button-outline button-block button-assertive"
ng-click="closeAddModal()">
Cancel
</button>
</ion-list>
</ion-modal-view>
addEmployee event - data parameter is now always undefined. Worked fine with embedded template :
$scope.addEmployee = function(retainModal, data) {
var employee = {employeeName:data.employeeName,
employeeAge:data.employeeAge};
employeeService.saveEmployee(employee);
if (! retainModal) {
$scope.closeAddModal();
};
data.employeeName = "";
data.employeeAge = "";
};
Based on this question and other needs I create a service that can be useful.
See this post: Ionic modal service or see in operation: CodePen
(function () {
'use strict';
var serviceId = 'appModalService';
angular.module('app').factory(serviceId, [
'$ionicModal', '$rootScope', '$q', '$injector', '$controller', appModalService
]);
function appModalService($ionicModal, $rootScope, $q, $injector, $controller) {
return {
show: show
}
function show(templateUrl, controller, parameters) {
// Grab the injector and create a new scope
var deferred = $q.defer(),
ctrlInstance,
modalScope = $rootScope.$new(),
thisScopeId = modalScope.$id;
$ionicModal.fromTemplateUrl(templateUrl, {
scope: modalScope,
animation: 'slide-in-up'
}).then(function (modal) {
modalScope.modal = modal;
modalScope.openModal = function () {
modalScope.modal.show();
};
modalScope.closeModal = function (result) {
deferred.resolve(result);
modalScope.modal.hide();
};
modalScope.$on('modal.hidden', function (thisModal) {
if (thisModal.currentScope) {
var modalScopeId = thisModal.currentScope.$id;
if (thisScopeId === modalScopeId) {
deferred.resolve(null);
_cleanup(thisModal.currentScope);
}
}
});
// Invoke the controller
var locals = { '$scope': modalScope, 'parameters': parameters };
var ctrlEval = _evalController(controller);
ctrlInstance = $controller(controller, locals);
if (ctrlEval.isControllerAs) {
ctrlInstance.openModal = modalScope.openModal;
ctrlInstance.closeModal = modalScope.closeModal;
}
modalScope.modal.show();
}, function (err) {
deferred.reject(err);
});
return deferred.promise;
}
function _cleanup(scope) {
scope.$destroy();
if (scope.modal) {
scope.modal.remove();
}
}
function _evalController(ctrlName) {
var result = {
isControllerAs: false,
controllerName: '',
propName: ''
};
var fragments = (ctrlName || '').trim().split(/\s+/);
result.isControllerAs = fragments.length === 3 && (fragments[1] || '').toLowerCase() === 'as';
if (result.isControllerAs) {
result.controllerName = fragments[0];
result.propName = fragments[2];
} else {
result.controllerName = ctrlName;
}
return result;
}
} // end
})();
Usage:
appModalService
.show('<templateUrl>', '<controllerName> or <controllerName as ..>', <parameters obj>)
.then(function(result) {
// result from modal controller: $scope.closeModal(result) or <as name here>.closeModal(result) [Only on template]
}, function(err) {
// error
});
You can use another service to centralize the configuration of all modals:
angular.module('app')
.factory('myModals', ['appModalService', function (appModalService){
var service = {
showLogin: showLogin,
showEditUser: showEditUser
};
function showLogin(userInfo){
// return promise resolved by '$scope.closeModal(data)'
// Use:
// myModals.showLogin(userParameters) // get this inject 'parameters' on 'loginModalCtrl'
// .then(function (result) {
// // result from closeModal parameter
// });
return appModalService.show('templates/modals/login.html', 'loginModalCtrl as vm', userInfo)
// or not 'as controller'
// return appModalService.show('templates/modals/login.html', 'loginModalCtrl', userInfo)
}
function showEditUser(address){
// return appModalService....
}
}]);
You need to attach your models to the scope:
$scope.data.employeeName = "";
$scope.data.employeeAge = "";
...and similar every time you reference them.