live search with angularjs - javascript

In my angularjs app http://1ffa3ba638.url-de-test.ws/zombieReport/popup.html#/lord i try to make a live instant search : i want nothing show at starting and search start when two letters minimum is writed. And after the searching is again performed for 3 or more letters, new query for each new letter.
/* INIT .myForm */
$scope.myForm_lord = {};
$scope.posts = {};
/* AJAX POST QUERY : calling lord watching */
$scope.$watch('myFormZR_lord.$valid', function() {
// ng-show things
$scope.successLordZR = true;
// RETRIEVE DATAS
var dataName = $scope.myForm_lord.search;
// CONSOLE LOG CONTROL
console.log(defineCLC + "LORD search requested by name : " + dataName);
// $resource
var Post = $resource(
urlLordRest,
{name: dataName},
{'query': {method: 'GET', isArray: true, cache: false, headers: {'Accept': 'application/json'}}}
);
$scope.posts = Post.query();
console.log($scope.posts);
});
html:
<form name="myFormZR_lord" id="myFormZR_lord" class="form-horizontal" role="form" novalidate="">
<div class="form-inline form-inline-sm">
<!-- form search -->
<div class="input-group">
<input class="form-control" name="search" type="text" ng-required="true" minlength="2" ng-minlength="2" ng-model="myForm_lord.search" placeholder="{{ 'TRS_CTRL3_FORM1' | translate }}" autocomplete="off" />
</div>
</div>
<span class="myForm_error" ng-show="myFormZR_lord.$invalid">{{ 'TRS_CTRL3_FORM2' | translate }}</span>
</form>
<div ng-show="successLordZR">
<p>{{ 'TRS_CTRL3_TEXT1' | translate }} :</p>
<ul>
<li ng-repeat="post in posts">
<p>{{post.prefixe}} {{post.name}}</p>
</li>
</ul>
</div>
Problem is actually results are showing at starting, and there is only a query for two letters, not if we put 3 or more. And the query is executed two times (see console log), what's wrong ? i use $watch, it's not good ?

You are watching for the change in the validity of the form. You put in minlength as 2 so as soon as you type in two characters the form becomes valid and thus fires your request. As soon as you backspace from 2 to 1 characters there is a change of validity again and thus firing another request. Instead of watching for the form validity, watch for changes in the form.

Related

Strange binding permanence between controllers

I've got a project in which you write a note in a formulary. Then, you submit that note into an information container (now it's just an array for testing purposes, but it's intended to be a DB later).
The formulary has the following controller:
app.controller('controlFormulario', ['$scope', 'SubmitService', function($scope, submitService) {
$scope.formData = {
"titulo":"",
"texto":"",
"fecha": new Date()
};
$scope.submit = function() {
var temp = $scope.formData;
submitService.prepForBroadcast(temp);
}
// more things we don't need now
... which is bound to this part of the DOM, which is added into it, via a directive:
<form ng-controller="controlFormulario as formCtrl">
<div class="element">
<div class="form-group" ng-class="{'has-error': formData.titulo.length > 50 }">
<label for="inputTitulo">Título</label>
<input type="titulo" class="form-control" id="inputTitulo" ng-model="formData.titulo">
<span ng-show="formData.titulo.length > 50" id="helpBlock" class="help-block">El título no puede exceder los 50 caracteres.</span>
</div>
<div class="form-group">
<label for="inputTexto">Texto</label>
<textarea class="form-control" id="inputTexto" ng-model="formData.texto"></textarea>
</div>
<div class="form-group">
<label for="fecha">Fecha</label>
<input type="fecha" class="form-control" id="fecha" ng-model="formData.fecha" disabled>
</div>
<div class="form-group" >
<button class="btn btn-primary" style="height:35px;width:100px;float:right;" id="submit"
ng-disabled="isDisabled()" ng-click="submit()">
Enviar
</button>
</div>
</div>
<div class="note" ng-show="formData.titulo.length > 0">
<div class="title" ng-model="formData.titulo" class="title">{{formData.titulo | limitTo:50}}</div>
<div class="text" ng-model="formData.texto" class="text">{{formData.texto}}</div>
<div class="date" ng-model="formData.fecha" class="date">{{formData.fecha | date}}</div>
</div>
</form>
This is my directive (I don't think it's really needed, but just in case):
app.directive('formulario', [function() {
return {
restrict: 'E', // C: class, E: element, M: comments, A: attributes
templateUrl: 'modules/formulario.html',
};
}]);
I use a service for passing the data between the previous controller, and the note controller (which controls the note objects of the array). This is the service:
app.factory('SubmitService', function($rootScope) {
var data = {};
data.prepForBroadcast = function(recvData) {
data.data = recvData;
this.broadcastItem();
};
data.broadcastItem = function() {
$rootScope.$broadcast('handleBroadcast');
};
return data;
});
... and I receive it in this part of my note controller:
app.controller('noteController', ['$scope', 'SubmitService', function($scope, submitService) {
var nc = this;
$scope.$on('handleBroadcast', function() {
nc.pruebaNota.push(submitService.data);
$scope.formData.titulo = "";
$scope.formData.texto= "";
$scope.formData.fecha = new Date();
});
// more things, the array, etc...
Ok. This should work, and it does, but something strange happens: as you can see, the preview note is binded with ng-model to the form. That works great, ok. But when I submit the form, the new note object keeps bound to the form (so if I delete the form text, the note appears in blank, and if I write something, it gets automatically updated both in the preview note, and the new note), when there isn't any relation between them. The new note, which appears dynamically on the screen, shouldn't be bound to anything.
Am I doing something wrong? Some help would be really nice!
You are forgetting something really important. Memory address. So, the rought idea is something like: imagine that $scope.formData is in the address 123123. You first create a temp var pointing to 123123 then you send it to the service and the service holds the same address 123123 into data.data.
Then in your second controller you say: hey, I want to work with that data.data (AKA your data in 123123) you have SubmitService.
Now when you modify $scope.formData again, you are updating what you have in that 123123 and everything that is "looking" into that address will be updated.
That is the rough idea. To point it simple, you're modifying the same piece of information everywhere.
See it here: http://plnkr.co/edit/zcEDQLHFWxYg4D7FqlmP?p=preview
As a AWolf suggested, to fix this issue, you can use angular.copy like this:
nc.pruebaNota.push(angular.copy(submitService.data));

How to add multiple items to a list

I'm building an app where users can add items to a list and I decided, for the sake of learning, to use Angular (which I'm very new to). So far, I've been able to successfully add a single item to that list without any issues. Unfortunately, whenever I try to add more than one without a page refresh, I get an error - specifically a "Undefined is not a function."
I've spent more time than I care to think about trying to resolve this issue and I'm hoping an expert out there can give me a hand. Here's what I have so far:
Controllers:
angular.module('streakApp')
.controller('StreakController', function($scope) {
// Removed REST code since it isn't relevant
$scope.streaks = Streak.query();
// Get user info and use it for making new streaks
var userInfo = User.query(function() {
var user = userInfo[0];
var userName = user.username;
$scope.newStreak = new Streak({
'user': userName
});
});
})
.controller('FormController', function($scope) {
// Works for single items, not for multiple items
$scope.addStreak = function(activity) {
$scope.streaks.push(activity);
$scope.newStreak = {};
};
});
View:
<div class="streaks" ng-controller="FormController as formCtrl">
<form name="streakForm" novalidate >
<fieldset>
<legend>Add an activity</legend>
<input ng-model="newStreak.activity" placeholder="Activity" required />
<input ng-model="newStreak.start" placeholder="Start" type="date" required />
<input ng-model="newStreak.current_streak" placeholder="Current streak" type="number" min="0" required />
<input ng-model="newStreak.notes" placeholder="Notes" />
<button type="submit" ng-click="addStreak(newStreak)">Add</button>
</fieldset>
</form>
<h4>Current streaks: {{ streaks.length }}</h4>
<div ng-show="newStreak.activity">
<hr>
<h3>{{ newStreak.activity }}</h3>
<h4>Current streak: {{ newStreak.current_streak }}</h4>
<p>Start: {{ newStreak.start | date }}</p>
<p>Notes: {{ newStreak.notes }}</p>
<hr>
</div>
<div ng-repeat="user_streak in streaks">
<!-- Removed most of this for simplicity -->
<h3>{{ user_streak.fields }}</h3>
</div>
</div>
Could you post the html of StreakController too? Your solution works fine in this fiddle:
http://jsfiddle.net/zf9y0yyg/1/
.controller('FormController', function($scope) {
$scope.streaks = [];
// Works for single items, not for multiple items
$scope.addStreak = function(activity) {
$scope.streaks.push(activity);
$scope.newStreak = {};
};
});
The $scope inject in each controller is different, so you have to define the "streaks" in FormController.
Your problems comes from :
.controller('FormController', function($scope) {
// Works for single items, not for multiple items
$scope.addStreak = function(activity) {
$scope.streaks.push(activity);
^^^^^^
// Streaks is initialized in another controller (StreakController)
// therefore, depending of when is instantiated StreakController,
// you can have an error or not
$scope.newStreak = {};
};
});
A better design would be to implement a StreakService, and to inject that service in the controller you need it. Of course, initializing $scope.streaks in FormController will make your code work, but that's not the responsibility of FormController to initialize this data.
I assume FormController is a nested controller of StreakController, so they share the same scope.
if that works for single object, it should work for mulitiple objects, the problems is you can't just use push to push an array of object to the streaks, you can for loop the array and add them individually or use push.apply trick. I thought the reason of Undefined is not a function. is because the Stack.query() return an element instead of an array of elements so, the method push doesn't exists on the $scope.streaks.
http://jsbin.com/jezomutizo/2/edit

Laravel 4 redirecting inputs back to page after validation fail

I have 4 fields that will be dynamically created by users.
<div class="controls" id="exchange-fields">
<p>
<div id='exchange_div'>
<div class="input-append" id='currency_div1'>
{{ Form::select('currency_id[]', $currencies, null, array('name'=>'currency_id', 'id'=>'currency_id', 'value'=>'Input::old("currency_id")[0]' )) }}
</div>
<div class="input-prepend" id='actual_div1'>
<span class="add-on">Actual</span>
{{ Form::text('exchange_rate[]', null, array('class'=>'input-medium rate', 'maxlength'=>10, 'id'=>'exchange_rate', 'value'=>'Input::old("exchange_rate")[0]' )) }}
</div>
<div class="input-append" id='markup_div1'>
{{ Form::text('exchange_rate_markup[]', null, array('class'=>'input-mini yellow rate', 'maxlength'=>4, 'id'=>'exchange_rate_markup', 'value'=>'Input::old("exchange_rate_markup")[0]' )) }}<span class="add-on">%</span>
</div>
<div class="input-prepend" id='rate_div1'>
<span class="add-on">Marked-up</span>
{{ Form::text('after_markup_rate[]', null, array('class'=>'input-medium yellow', 'maxlength'=>10, 'id'=>'after_markup_rate', 'value'=>'Input::old("after_markup_rate")[0]' )) }}
</div>
</div>
</div>
<div class="controls">
<input class="btn btn-primary" type="button" id="addScnt" value="Add">
</div>
I uses javascript to populate these fields dynamically.
var scntDiv = $('#exchange-fields');
var i = $('#exchange-fields p').size() + 1;
$('#addScnt').click(function() {
$(scntDiv).append('<p>');
$("#exchange_div").clone().attr("id",'exchange_div_'+ i).appendTo(scntDiv);
//Append the remove button
$($('#exchange_div_'+ i)).append('<input class="btn btn-primary" name="remove" type="button" id="remScnt" value="Remove">');
i++;
});
This is working perfectly until I POST these values to my controller and if validation fails.
How do I re populate those fields with the old input that are dynamically populated in my view file?
I use return Redirect::back()->withInput()->withErrors($e->getErrors()); to redirect after validation fail to repopulate those fields. But because these 4 fields only accept string values and the input returning back are in array so I am unable to repopulate these 4 fields after validation fails.
Any good ways to fix this?
Thanks in advance.
#a7omiton Instead of returning all inputs. I use Input::except(array) to return the rest of the fields which are not array.
While redirecting back I use an additional with() to return the array of fields like how you render your view when you load the page initially.
// If validation fails, ignore those exchange rate fields
//because they are in array not string
$input = Input::except([
'currency_id',
'exchange_rate',
'exchange_rate_markup',
'after_markup_rate'
]);
// return normally as separate array and it will store in session
return Redirect::back()
->withInput($input)
->withErrors($e->getErrors())
->with('exchange_rate', Input::get('exchange_rate'))
->with('currency_id', Input::get('currency_id'))
->with('exchange_rate_markup', Input::get('exchange_rate_markup'))
->with('after_markup_rate', Input::get('after_markup_rate'));
Don't set the value of the input fields in the HTML. Laravel will do this automatically when validation fails.
The second argument in Form::text is the value. Setting this to null will allow Laravel to automatically populate it.
laravel will autopopulate the form fields.
Just put second parameter with Input::old('column_name').
{{ Form::text('name', Input::old('name'), array('class' => 'span4','placeholder' => 'name')) }}
Though it is an old post, this will help some one land here looking for the solution.
You can get all the input variable in the view after a redirection by adding this code in your laravel view,
print_r(app('request')->old());
you can assign this values to your javascript variable, and populate the fields dynamically.

Updating multi-model form from Angular to Sinatra

I'm currently having an issue with updating a form in Angular and pushing the update through to Sinatra.
It is supposed to:
When clicked, the form to edit the current item is shown (current data for each field is displayed from the item scope).
When submitted, it is attempting to update to a different scope (updateinfo). I am not sure but do I need a way of using multiscope or one scope to allow it to update?
At present the script sends the correct downloadID parameter, but the JSON from the scope submitted is as I believe, incorrect.
Also, I'm not sure whether the Sinatra app.rb syntax is correct, for someone new to these frameworks, it has been hard to find useful documentation online.
If anybody could help it would be very much appreciated.
downloads.html
<div ng-show="showEdit">
<form ng-submit="updateinfo(item.downloadID); showDetails = ! showDetails;">
<div class="input-group"><label name="title">Title</label><input type="text"
ng-model="item.title"
value="{{item.title}}"/></div>
<div class="input-group"><label name="caption">Download caption</label><input type="text"
ng-model="item.caption"
value="{{item.caption}}"/>
</div>
<div class="input-group"><label name="dlLink">Download link</label><input type="url"
ng-model="item.dlLink"
value="{{item.dlLink}}"/>
</div>
<div class="input-group"><label name="imgSrc">Image source</label><input type="url"
ng-model="item.imgSrc"
value="{{item.imgSrc}}"/>
</div>
<!-- download live input types need to be parsed as integers to avoid 500 internal server error -->
<div class="input-group"><label name="imgSrc">
<label name="dlLive">Download live</label><input type="radio" ng-model="download.dl_live"
value="1"/>
<label name="dlLive">Not live</label><input type="radio" ng-model="download.dl_live"
value="0"/></div>
<div class="input-group"><label name="imgSrc"><input type="submit"/></div>
</form>
controllers.js
$scope.loadData = function () {
$http.get('/view1/downloadData').success(function (data) {
$scope.items = data;
});
};
$scope.loadData();
$scope.updateinfo = function(downloadID) {
id = downloadID
var result = $scope.items.filter(function( items ) {
return items.downloadID == id;
});
console.log(result);
updatedata = $scope.items
$http({
method : 'PUT',
url : '/view1/downloadedit/:downloadID',
data : result
});
};
app.rb
#edit download
put '/view1/downloadedit' do
puts 'angular connection working'
ng_params = JSON.parse(request.body.read)
puts ng_params
#download = Download.update(ng_params)
end
The wrong scope was attempting to be used. Once the scope was corrected to items, the correct JSON was being routed:
$scope.updateinfo = function(downloadID) {
id = downloadID
var result = $scope.items.filter(function( items ) {
return items.downloadID == id;
});
console.log(result);
updatedata = $scope.items
$http({
method : 'PUT',
url : '/view1/downloadedit/:downloadID',
data : result
});

jQuery Autocomplete - Multicolumn and Return Data rather than Value

I am currently using the DevBridge jQuery Autocomplete plugin - it works fine as is, however I would like to display the returned information in a multi-column view, but yet when clicked only return the first value.
HTML
<form class="form" action="index.php" onsubmit="alert('Submit Form Event'); return false;">
<div id="selection"></div>
<input type="text" name="q" id="query" class="textbox" />
</form>
Javascript
jQuery(function() {
var options = {
serviceUrl: '/autocompleterequestcall.php',
maxHeight:400,
width:600,
fnFormatResult: fnFormatResult,
deferRequestBy: 0 //miliseconds
};
a1 = $('#query').autocomplete(options);
});
So I expect I would need to use the fnFormatResult to somehow display the multicolumn values, which are separated by |, ie.
REFERENCEID | POSTCODE | ADDRESS_LINE_1 | SURNAME
I would have liked to wrap the whole return up as a <table> but I can't figure out where to put the start <table> and end </table> tags, or do I just replace | with </div><div>.
Then, when an item is selected, instead of returning
REFERENCEID | POSTCODE | ADDRESS_LINE_1 | SURNAME
I would just like to see
REFERENCEID
Return Data:
I totally missed the onSelect option when skimming over the devbridge site, turns out all I had to do was set the option and create my own function:
function onSelect(v,d) {
$('#query').val(d);
}
jQuery(function() {
var options = {
serviceUrl: '/autocompleterequestcall.php',
maxHeight:400,
width:600,
onSelect: onSelect,
fnFormatResult: fnFormatResult,
deferRequestBy: 0 //miliseconds
};
a1 = $('#query').autocomplete(options);
});

Categories

Resources