Angular JS - differentiating between a save and update with a common template - angularjs

I am working on a project with a PHP backend and Angular 1.x on the front end. I have a Listings model and I use a common template to create (add) and update (edit) a Listing.
Since eidt and add both use the same front end template I need to be able to detect when a user is creating and when they are editing. It seems there are several ways to do this:
I could pass a paramater in the ng-submit:
ng-submit="saveListing({{isNewListing}}"
Then I could read the value of the paramter in my controller, but I think this is overkill?
When editing a Listing some variables set for the form auto-fill
$scope.listing.id = x;
Therfore I could just check for a value in the above:
$scope.saveListing = function() {
if(listing.id) {
// update action
} else {
// save action
}
};
Is the second option a sound and non-hacky approach. I am not an Angular pro so although it seems the logical approach to me I want to ensure that I am not hot woring this.

I usually do something similar to the second approach. Since editing means you have to "get" the original record in most cases, the record should exist somewhere in the scope. I use ui-router and have a resolve for the record, which means I can check right at the top of the controller:
$scope.isEdit = record != null;
With a scope variable or similar (e.g. controllerAs vm) you can leverage the fact that you're in "edit mode" and change the UI up a bit. Instead of "+ New" on a button you can have "+ Save".
Hope that helps!

We have a large ERP system with angularJs as a front-end framework, and we are using the "check id" approach.
When updating/edit an item there would be existing id for that item.
I think the second approach is good and I don't see any drawbacks.

Related

Cordova - Angular - <select> tag open a new view

I have an Angular/Cordova app and I'm trying to figure out how should I handle the HTML SELECT tag. What I would like to do is to open a new window with all the options in a list, the user picks one and returns with that value.
The problem is when I do that I lose all the data I had in the first screen as I am closing it when I move to the second one.
I am using Angular's UI.ROUTER. One thing, which I am not too convinced to do, is to save all data entered into StateParams, and when I return, place it back.
What would be the best approach?
It really depends on the use case. If you need to be able to "deep link" to the view where a link loads the view with the pop-up active then using ui-router and stateparams makes the most sense. If deep linking isn't a concern and the user must always select something then you can just use a service/factory/value/provider in order to share the data between the controllers during the lifetime of the app.

Angular - How can I clear factory variables when the view changes?

I'm fairly new to angular, so bear with me. :-)
I have a list of contacts in one view. I put together a service with some setter and getter functions to hold the contact ID. When the user clicks a contact on the list view, it sets the ID in the service and moves to the edit form view. This is working well, but when I'm done editing and click to go back to the list view I want to clear the variables in the service. How would I do that?
I found one other answer on this topic, but I think it's using the built in angular router instead of ui-router.
Angular updating and clearing factory variables
Also, wouldn't the method in that answer also clear the variables when the user moved from the list to the edit form in the first place?
Thanks!
If it's a route change you can use $locationChangeStart, see $location
For example in your edit controller, register for the event on route change:
var onloc = $scope.$on('$locationChangeStart', function (event, newUrl, oldUrl) {
myService.cleanUp(whatever);
onloc();
});

How to reload the ionic view?

I have created a sidemenu based app, in that after login I am displaying a number of tasks. If I click on the task it will redirect to the task details page, in that page I can update the tasks.
So after updating a task I need to go back to the previous task list page. I am using $ionicHistory.goBack(); to go back.
My problem is after come back, I need to refresh the task list i.e. updated task should not be there in the task list. How can I refresh/reload the task list?
If you bind your task to a tasks array, which will be used in the task list page, it should be automatically updated.
But the question is about not displaying, newly added tasks (still my previous suggestion should work) if not, performance reasons ionic views are cached, So when you come back to the previous view it doesn't go through the normal loading cycle. But you 2 options
1 - disable the caching by using <ion-view cache-view="false" view-title="My Title!"> in your ion-view, but this is not a very elegant solution. read more
2 - use ionRefresher (my preferred). read more here
https://github.com/angular-ui/ui-router/issues/582
according to #hpawe01 "If you are using the current ionicframework (ionic: v1.0.0-beta.14, angularjs: v1.3.6, angular-ui-router: v0.2.13), the problem with the not-reloading-controller could be caused by the new caching-system of ionic:
Note that because we are caching these views, we aren’t destroying scopes. Instead, scopes are being disconnected from the watch cycle. Because scopes are not being destroyed and recreated,controllers are not loading again on a subsequent viewing.
There are several ways to disable caching. To disable it only for a single state, just add cache: false to the state definition.
This fixed the problem for me (after hours of reading, trying, frustration).
For all others not using ionicframework and still facing this problem: good luck!"
Hope this helps.
You can also listen to ionic events such as $ionicView.enter on $scope and trigger the code that refreshes the list if you haven't bound your list as #sameera207 suggested.
EG:
// List.controller.js
angular.module('app')
.controller('ListController', ['$scope', function($scope) {
// See http://ionicframework.com/docs/api/directive/ionView/ for full events list
$scope.$on('$ionicView.enter', function() {
_someCodeThatFetchesTasks()
.then(function(tasks) {
$scope.tasks = tasks;
});
});
});
Bear in mind that it's not the most proper way (if proper at all) and if you do this you certainly have a design flaw. Instead you should share the same data array via a factory or a service for example.
For your task you can also use ion-nav-view.
It is well documented. And if you are using now Ionic 2 beta you can use some of the view lifecyle hooks like onPageWillLeave() or onPageWillEnter(). I just faced the same problem and defined a refresh() function, but the user had to click on a button to actually update the view. But then I found:
https://webcake.co/page-lifecycle-hooks-in-ionic-2/
You just have to import the Page and NavController module and also define it in the constructor. The you can use for example onPageWillEnter(), which will always invoke when you go again to a view:
onPageWillEnter() {
// Do whatever you want here the following code is just to show you an example. I needed it to refresh the sqlite database
this.storage.query("SELECT * FROM archivedInserates").then((data) = > {
this.archivedInserates =[];
if (data.res.rows.length > 0) {
for (var i = 0; i < data.res.rows.length; i++) {
this.archivedInserates.push({userName:data.res.rows.item(i).userName, email:
data.res.rows.item(i).email});
}
}
},(error) =>{
console.log("ERROR -> " + JSON.stringify(error.err));
});
}
With ionic beta 8 the lifecylcle events changed their names. Check out the official ionic blog for the full list of the lifecycle events.
if you are building data driven app then make sure use $ionicConfigProvider.views.maxCache(0);in your app.config so that each review can refresh for more details read this http://ionicframework.com/docs/api/provider/$ionicConfigProvider/

AngularJS typeahead select on blur

I'm using typeahead through in my AngularJS project and I would like to have it select the entry if I type the full value and click out of the field.
I've put together an example of what I mean
http://plnkr.co/edit/NI4DZSXofZWdQvz0Y0z0?p=preview
<input class='typeahead' type="text" sf-typeahead options="exampleOptions" datasets="numbersDataset" ng-model="selectedNumber">
If I type in 'two' and click on 'two' from the drop down then I get the full object {id: 2, name: 'two'}. This is good, if however I type 'two' and click to the next field without selecting is there a way to accept the top of the list on loss of focus on a text field?
I'm not sure if I'd want to have that sort of functionality in my app. The user hasn't actually selected anything. So selecting something for them would introduce frustrations.
But I do understand that often odd requirements are needed. In this case, I'd attack it using ngBlur. Assign a function to be called on blur. You can grab the contents of ng-model and then loop through your data (assuming static & not being sent via server) to find a match.
You can most likely just look at the source code of your typeahead directive and strip out the part does the comparison and then choose the first item in the array.
Unfortunately the underlying component does not emit any events for this condition. This will make the solution more complex. However when the value is being entered and the Typehead magic has happened you can supplement those events and catch them to update your ngModel.
I have created a plnkr based on your plnkr and although have not cleaned up but it is a working plnkr doing by far what you need.
The gist of this is following code however you can put this code wherever best suited
The explanation below:
//Crux - this gets you the Typeahead object
var typeahead = element.data('ttTypeahead');
//This gets you the first
var datum = typeahead.dropdown.getDatumForTopSuggestion();
if (datum){
//you can do lot of things here however
//..I tried to - fill in the functionality best suited to be provided by Typeahead
//for your use case. In future if Typeahead gets this
//..feature you could remove this code
typeahead.eventBus.trigger("hasselections", datum.raw, datum.datasetName);
}
In the above code you can also save the datum somewhere in the scope for doing whatever you like with it later. This is essentially your object {num: 'Six'} Then you may also use ngBlur to set it somewhere (however the plnkr I created doe snot need these gimmicks.)
Then further down - ngModel's value is set as below
element.bind('typeahead:hasselections', function(object, suggestion, dataset) {
$timeout(function(){
ngModel.$setViewValue(suggestion);
}, 1);
//scope.$emit('typeahead:hasselections', suggestion, dataset);
});
I'm with EnigmaRM in that ngBlur seems to be the way to do what you want. However, I agree with the others that this could be somewhat strange for the end users. My implementation is below (and in plnkr). Note that I trigger on ngBlur, but only apply the model if and only if there is only one match from Bloodhound and the match is exact. I think this is probably the best of both worlds, and hope it should give you enough to go on.
$scope.validateValue = function() {
typedValue = $scope.selectedNumber;
if(typedValue.num !== undefined && typedValue.num !== null)
{
return;
}
numbers.get(typedValue, function(suggestions) {
if(suggestions.length == 1 && suggestions[0].num === typedValue) {
$scope.selectedNumber = suggestions[0];
}
});
};

AngularJS pattern for persisting record selection through multiple asynchronous calls?

We're working on CRUD patterns for a fairly large application. One common UI pattern we're using to define a one-to-many relationship where a record is associated by checkbox. The challenge is to persist selections (checked/unchecked) through asynchronous calls (search/sort) that refresh the record list (and the associated ng-model). I'd like to hear from more advanced AngularJS users (I'm a noob) what's considered a best practice for this? Any feedback is appreciated!
EDIT
Here's a working plunk showing how I'd most likely tackle this with my current understanding of Angular. Please let me know if you have a better approach!
I think u could maintain a separate collection of selected names. So the next time you filter the list, you just need to lookup in the collection in order to keep the item selected. That you can do by binding some variable (arrSelected) in the controller or you can create a separate service also.
Have you considered using a filter instead of refetching. Of course this really depends on how many items you plan on having, but I've had success filtering a nested JSON object with around 5000 items.
Here is the full plunker : http://plnkr.co/edit/l4jYgt0LjRoP2H0YuTIT?p=preview
Highlights:
In index.html for your repeat you add | filter
<tr ng-repeat="user in users | filter:userFilter">
In script.js we add the filter function and a $scope variable to hold the filtered letter:
$scope.filteredLetter
$scope.userFilter = function (elem) {
if (elem.name.lastIndexOf($scope.filteredLetter, 0) === 0 || $scope.filteredLetter == '') {
return true;
} else {
return false
}
}
As a bonus I added in ng-class to show which letter was highlighted.
Pretty simple code but this gives you full persistence now even as people change things. You can even experiment now with adding a <input> tag with an ng-model binding to say $scope.filteredName. Then all you need to do is add the JS to the filter to do a deeper filter for part of the name.

Resources