Storing array data from angularjs Form using MEAN stack - arrays

I am using MEAN stack. while creating a new article i would like to save array data. below code has the section deal which needs to be an array of data, for which i am unable to store array data. how do i modify my html partial and angularJS Controller so that i can store data like below for deal
{
"_id": "565d8a3a0ede9823aca797e9",
"user": {
"_id": "564ef3eaebf280b0abcc4876",
"displayName": "Table 2"
},
"__v": 0,
"deal": ["Special","Holiday"],
"name": "Idli",
"created": "2015-12-01T11:53:30.759Z"
}
My NodeJS Server Model
var FoodSchema = new Schema({
created: {
type: Date,
default: Date.now
},
name: {
type: String,
default: '',
required: 'Please fill Food name',
trim: true
},
deal: [{
type: String,
default: '',
trim: true
}],
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
my AngularJS controller
$scope.create = function (isValid) {
$scope.error = null;
if (!isValid) {
$scope.$broadcast('show-errors-check-validity', 'foodForm');
return false;
}
// Create new Food object
var food = new Foods({
name: this.name,
deal: this.deal,
// Redirect after save
food.$save(function (response) {
$location.path('foods/' + response._id);
// Clear form fields
$scope.name = '';
$scope.deal = '';
}, function (errorResponse) {
$scope.error = errorResponse.data.message;
});
};
HTML Partial
<section ng-controller="FoodsController">
<div layout="row" layout-wrap class="create-food">
<form name="foodForm" ng-submit="create(foodForm.$valid)" novalidate>
<div class="col-md-12">
<md-input-container flex="">
<label >Food Name</label>
<input type="text" data-ng-model="name" id="name" required>
</md-input-container>
</div>
<div class="col-md-6">
<md-input-container class="">
<label class="" for="deal">Deal</label>
<input type="text" data-ng-model="deal" id="deal" required>
</md-input-container>
</div>
<md-button type="submit">Create Food</md-button>
</form>
</div>
</section>

if you want to store data on your browser you can use the new html5 feature localStorage(key,value) you put some key for getting and the json like value.
This is not just for the sesion !

Well, I see that you already updated your mongoose model schema and made some updates in the frontend but what's the current state of your server API? Do you have any endpoints defined for the requests?
In other words (using the articles example of MEAN.js):
var articlesPolicy = require('../policies/articles.server.policy'),
articles = require('../controllers/articles.server.controller');
module.exports = function (app) {
// Articles collection routes
app.route('/api/articles').all(articlesPolicy.isAllowed)
.get(articles.list)
.post(articles.create);
// Single article routes
app.route('/api/articles/:articleId').all(articlesPolicy.isAllowed)
.get(articles.read)
.put(articles.update)
.delete(articles.delete);
// Finish by binding the article middleware
app.param('articleId', articles.articleByID);
};
Do you already have a similar setup for Food?
Once you have your routes setup you should then verify the server controllers logic and see if the data is being handled correctly and save into mongodb.
Edit to explain the workflow of saving data to mongodb in MEAN.js:
The database is only accessible from the server for security reasons. You cant access mongodb directly from angularjs. The process is: angular passes the data to expressjs via a request (for example: GET, POST, PUT, DELETE). Then in the server you should be able to capture the requests by defining your route endpoints. In those route endpoints you can specify route middlewares (i.e., server controllers) that will then use mongoose to interact with mongodb.

Related

Use VueJs to save a template

My project currently is made with VueJS. Now, we need to process a certain template with the input data, then store the result and use it to send an email (for example).
Can i render a template with the user data and save it? how?
I don't want to use another library for this purpose, unless We can't do with VueJS
I have read about SSR. But i don't want to use a server-side rendering. The idea is only render certain messages. Following a behavior like this:
save: function(){
userNote.user = ...
userNote.message = document.getElementById('message').innerHtml;
saveToServer(userNote);
}
The Message template:
<div id="message"> Dear {{user.name}}, please confirm that {{notes}} before {{date}}</div>
I hope i made me understand.
Thanks in advance.
Assuming your template is stored in a component, you could add an export method :
var templateComponent = Vue.component("template-component", {
template: "<p>Hello {{name}}</p>",
props: ["name"],
methods: {
exportHTML: function() {
return this.$el.outerHTML;
}
}
});
var app = new Vue({
el: '#app',
data: {
name: "David",
html: undefined
},
methods: {
getHTML: function() {
this.html = this.$refs.template.exportHTML();
}
}
});
<script src="https://unpkg.com/vue#2.4.2/dist/vue.min.js"></script>
<div id="app">
<div style="display: none">
<template-component :name="name" ref="template"></template-component>
</div>
<label>Name
<input v-model="name">
</label>
<button #click="getHTML">Get html</button>
<pre>{{ html }}</pre>
</div>
Then you just have to call the exportHTML method on the template component to retrieve the HTML.

How to submit selected checkboxes and group name as an objects within an array

I need to pass selected checkbox object data alone into array on form submit.
service returning json data:
[
{
"groupName" : "A",
"groups": [
"Painting",
"coloring"
]
},
{
"groupName" : "B",
"groups": [
"drawing",
"writing"
]
}
]
service expected format when user selected couple of check boxes and submit form:
{
"groups": [
{
"category": "A",
"subCategory": "coloring"
},
{
"category": "B",
"subCategory": "writing"
}
]
}
My controller:
<div ng-controller="groupCtrl">
<form class="form" name="form" role="form" ng-submit="groupData()" autocomplete="off" novalidate>
<div ng-repeat="groups in groupsList">
<p class="category">{{groups.groupName}}</p>
<p ng-repeat="group in groups.groups" >
<input type="checkbox" id="group" name="{{group}}" class="group" value="{{group}}" ng-model="groups"> {{group}}
</p>
</div>
<button type="submit" class="button">Save</button>
</form>
Controller:
angular.module('myApp')
.controller("groupCtrl", function($rootScope, $scope, $state, $http) {
$http.get('group.json').success(function(groupsList) {
$scope.groupsList = groupsList;
});
$scope.groups = {
}
$scope.groupData = function () {
$http.post('<service endpoint>', $scope.groups, {
})
.success(function(){
console.log("success");
})
.error(function(){
console.log("failed.");
});
}
});
I am new to angular. Looking for help on how to construct array object in controller and update array object on user select/un-select check boxes.
First I would put api logic into a service like this:
angular.module('myApp', [])
//service to handle api communication
.service('apiService', ['$http', function($http) {
return {
getGroups: function() {
return $http.get('http://superapi.com');
}
}
}])
Then you need to call the api service from the controller and build up your view model:
api.getGroups().then(function(res) {
//JSON.parse will not be needed if your API returns a JSON body with application/json set as content-type
$scope.groupsList = JSON.parse(res.data.data);
//Build up the view model.
$scope.groupsList.forEach(function(g) {
$scope.selectedGroups.groups.push({
category: g.groupName,
subCategory: []
});
});
},
function(error) {
//handle API errors gracefully here.
});
Then you can use ng-model on the form components to populate it with data
<form class="form" name="form" role="form" ng-submit="groupData()" autocomplete="off" novalidate>
<div ng-repeat="groups in groupsList track by $index">
<p class="category">{{groups.groupName}}</p>
<p ng-repeat="group in groups.groups track by $index">
<input ng-model="selectedGroups.groups[$parent.$index].subCategory" type="checkbox" id="group" name="{{group}}" class="group" ng-true-value="'{{group}}'" ng-init="checked=''" ng-false-value="''"> {{group}}
</p>
</div>
<button type="submit" class="button">Save</button>
</form>
Since your api response model has a double nested hierarchy, we need two nested ng-repeat loops. We can then leverage the fact that ng-repeat creates a child scope, and the parent scope is accessible by using $parent. That way we can use that information to bind to the correct part of the view model with ng-model.
ng-model="selectedGroups.groups[$parent.$index].subCategory"
$parent.$index is the first ng-repeat groups in groupsList
If you want to be able to select more than one subcategory, you can just bind nd-model to each subcategory's index in an array like this:
ng-model="selectedGroups.groups[$parent.$index].subCategory[$index]"
This will use the index of the subCategory to bind to a specific position in an array.
here is a working example of that :
https://jsfiddle.net/zzq16t8u/4/
Here is a working JSFiddle for you:
https://jsfiddle.net/zzq16t8u/3/
Now when you want to map to the request model specified by the system, you will need to handle this in the function handled by the form submit. You could do something clever with ng-true-value, but then you would still need to do a JSON.parse on your data, as it can only work on strings. And I my opinion handling the mapping to the request model is better done once you prepare to make the request.
$scope.groupData = function() {
// map data to the request model specified
var requestModel = {
groups: []
};
$scope.selectedGroups.forEach(function(group) {
group.subCategory.forEach(function(subCat) {
requestModel.groups.push({
category: group.category,
subCategory: subCat
});
});
});
Here is a working JSFiddle of everything, note that I have simplified the view model here, since we no longer bind directly to the request model.
https://jsfiddle.net/zzq16t8u/15/
Please note that the JSON.parse is only necessary here, as I was unable to figure out how to get the JSFiddle echo service to return json as real json, and not a string, and the api service in the example uses this echo service to mock a real http response, so that will need to be replace by a real http call - as I showed in this answer.
Hope it helps.

angularjs and grails 2.5.0 save success, no errors but not saving to database

I've been looking for similar situations like mine has and I've seen that it is common to have this error but I can not find the right solution with my problem. I would really appreciate any help.
My problem right now is that when I execute this it return and HTTP 200 and also prints a line on my terminal which confirms me that it creates the JSON file. There are no errors but when I checked on my database, there's nothing in it.
What I've tried so far:
newBook.save(flush: true)
edit my datasource from create-drop to update (but my whole database disappears so I returned the data. I only need to save it during a demo so I understand that the data will be lost every time the application restarts)
Postman GET and POST
GET: 200 OK
POST: 200 OK
but when I GET again, nothing is showing
debugging using console and println (but there might be something that I missed since I'm just starting to learn both AngularJS and Grails)
I am hoping that these data can give a detailed information about my problem, I am open for any questions.
Here are my sample codes:
back-end codes: Grails 2.5.0
Domain class
class Book {
String name
int page
boolean new
static belongsTo = [student: student]
static constraints = {
}
}
Controller
import grails.converters.JSON
import org.springframework.security.access.annotation.Secured
#Secured('permitAll')
class BookController {
def index() {
render Book.list() as JSON
}
def save() {
def newBook = new Book(request.JSON)
newBook.save(flush: true)
println request.JSON
render(['success': true] as JSON)
}
def show() {
def Book = Book.get(params.id)
render Book as JSON
}
}
front-end codes: AngularJS
Controller
app.controller('Book', function($scope, store, $state, bookFactory){
$scope.book = {}
$scope.saveBook = function() {
$scope.book.assessment = $scope.getBook();
console.log($scope.book);
bookFactory.save($scope.book,function (result){
console.log(result)
}, function (error) {
console.log(error)
})
}
$scope.getBook = function() {
var result = "";
if ($scope.book.page > 10){
result = "many pages";
}else if ($scope.book.new && $scope.book.page > 20){
result = "new and many pages";
}else {
result = "old book";
}
return result;
};
})
Service
app.factory('bookFactory', ['$resource', function ($resource) {
return $resource('api/book',
{
'update': {method: 'PUT'}
})
}])
app.js
.state("book", {
url: "/book",
templateUrl: "assets/app/partials/book.html",
controller: 'Book',
data: {
requiresLogin: true
}
})
HTML
Book Name:
<input type="text" ng-model="book.name" name="bookName" required="" class="form-control" />
Number of Pages:
<input type="number" ng-model="book.page" name="numberPage" required="" class="form-control" placeholder="Number of Pages"/>
Bought within the last 30 days?
<div class="form-group">
<div class="radio">
<label>
<input type="radio" name="new" value=true ng-model="book.new" >
<font color=green>YES</font>
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="new" value=false ng-model="book.new">
<font color=red>NO</font>
</label>
</div>
</div>
<a ui-sref="book" class="btn btn-primary" name="bookBtn" ng-click="saveBook()"> Next </a>

ngResource GET with filter values

I'm writing a small test application where I can retrieve my customers from a parse.com database.
I have the following form in html
...
<body ng-app="myApp">
<div ng-controller="CustomerController">
<button ng-click="getCustomers()">Get customers</button>
<ul>
<li ng-repeat="customer in customers">{{ customer.name }}</li>
</ul>
</div>
</body>
...
My angular app is the following:
Module
var app = angular
.module('myApp', ['ngResource'])
.constant('myConfig', {
'api_url': 'https://api.parse.com/1/classes/',
'parse_application_id': 'xxxxxxxxxxxxx',
'parse_rest_api_key': 'xxxxxxxxxxxxx'
});
Factory
app.factory('CustomersService', function($resource, myConfig) {
return $resource(myConfig.api_url + 'Customer', {}, {
query: {
method: 'GET',
isArray: false,
headers: {
'X-Parse-Application-Id': myConfig.parse_application_id,
'X-Parse-REST-API-Key': myConfig.parse_rest_api_key
}
},
create: {
method: 'POST',
headers: {
'X-Parse-Application-Id': myConfig.parse_application_id,
'X-Parse-REST-API-Key': myConfig.parse_rest_api_key
}
}
})
});
Controller:
app.controller('CustomerController', function($scope, CustomersService, CustomerService) {
$scope.getCustomers = function() {
CustomersService.query().$promise.then(function(result) {
$scope.customers = result.results;
});
};
});
So when I click my button, everything works like it should.
But I also want to add a filter by name when I want to retrieve customers from the database.
When I execute the following in Postman
https://api.parse.com/1/classes/Customer?where={"name":"aaaaa"}
this works and only gets the customer with the name "aaaaa". So I know that the syntax is OK.
So I will add a textbox where the user can enter a customername and after that I want to click on the search button.
But how can I manage the ?where={"name":"aaaaa"} into the angular stuff when I click the button? I also want to expand the filter with other columns from that customer.
Something like this should work (assuming everything goes in the where object)
Add some search fields that bind to a scoped object's properties. We'll call it search
<label for="search_name">Name</label>
<input type="text" ng-model="search.name" name="name" id="search_name">
<label for="search_city">City</label>
<input type="text" ng-model="search.city" name="city" id="search_city">
Then you can execute the query action with
CustomersService.query({where: $scope.search}).$promise...
That should create a query param like
?where=%7B%22name%22%3A%22aaaaa%22%2C%22city%22%3A%22London%22%7D
which is the URI encoded value
?where={"name":"aaaaa","city":"London"}

Persist data across views AngularJS with vs-google-autocomplete

I am building my first Angular App, which allows users to search a database of vendors based on a number of criteria. One of these criteria is their location and the distance to the vendor.
I have used the vs-google-autocomplete directive, which has been a great way to link into the google maps API. However, I have a problem around the persistency of this location data across views in the app.
To explain how it works at the moment. A user inputs their search criteria, a snapshot of all potential vendors is shown based on the users search criteria (this has been achieved through a custom directive). The user can then click to read more about a vendor, this takes them to the vendors full page (with more details etc.). It is at this stage that the location data is lost.
I have already created a service to store the information from other aspects of the search query, which is persisting just fine (except for the location data).
It appears that the problem is that the location input field automatically resets when a user leaves the page, thereby resetting the location field to null. But, I can't see where it is doing that in the code.
Does anyone have any thoughts about how to tackle this?
Code Below (unfortunately I could not get the custom directive actually working in JSFiddle, but hopefully it gives a good idea of what I am trying to achieve):
App.factory("searchquery", function() {
return {};
});
App.controller('searchController', ['$scope', '$routeParams', '$rootScope', 'searchquery',
function($scope, $routeParams, $rootScope, searchquery) {
//Create a search query object
$scope.searchquery = {};
//Inject the searchquery service to ensure data persistency when in searchController pages
$scope.searchquery = searchquery;
//DEAL WITH GOOGLE AUTOCOMPLETE ADDRESSES
//Restricts addresses to South Africa only
$scope.options = {
componentRestrictions: {
country: 'ZA'
}
};
//Creates an object to store the address information and parses it into its consitutent parts
$scope.searchquery.address = {
name: '',
components: {
streetNumber: '',
street: '',
city: '',
state: '',
countryCode: '',
country: '',
location: {
lat: '',
long: ''
}
}
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<form name="Search">
<!-- LOCATION -->
<!-- ask the user to specify their location -->
<div class="form-group">
<label for="address">Address</label>
<input vs-google-autocomplete="options" ng-model="searchquery.address.name" vs-street-number="searchquery.address.components.streetNumber" vs-street="searchquery.address.components.street" vs-city="searchquery.address.components.city" vs-state="searchquery.address.components.state"
vs-country-short="searchquery.address.components.countryCode" vs-country="searchquery.address.components.country" vs-latitude="searchquery.address.components.location.lat" vs-longitude="searchquery.address.components.location.long" type="text" name="address"
id="address" class="form-control">
</div>
</form>
Thanks!
Jack
I don't know your location data but you can persist data across views with two ways:
Service as you mentioned
Nested views
Please have a look at the demo below or here at jsfiddle.
The service data are not shared in the demo but that's also possible by injecting it in other views.
If the data are asynchronous you should have a look at resolve of ui-router. Then the data are loaded before the controller of the view is called.
angular.module('demoApp', ['ui.router'])
.config(AppConfig)
.factory('demoService', DemoService)
.controller('demoController', DemoController);
function AppConfig($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider.state('home', {
url: '/',
template: 'HELLO from home route <input ng-model="text"/>'+
'<div ui-view=""></div'
})
.state('home.profile', {
url: '/profile',
template: 'Hello from profile <input ng-model="text"/>'
})
.state('otherView', {
url: '/other',
controller: 'demoController',
/*resolve: {
text: function(demoService) {
console.log(demoService);
return demoService.get();
}
},*/
template: 'Hello from other scope <input ng-model="text" ng-change="updateService(text)"/>'
});
}
AppConfig.$inject = ['$stateProvider', '$urlRouterProvider'];
function DemoService() {
return {
_text: 'I am stored in service.',
set: function(text) {
this._text = text;
},
get: function() {
return this._text;
}
}
}
function DemoController($scope, demoService) {
$scope.text = demoService.get();
$scope.updateService = function(newValue) {
console.log(newValue);
demoService.set(newValue);
};
}
DemoController.$inject = ['$scope', 'demoService'];
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js">
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.js"></script>
<div ng-app="demoApp">
<div ui-view=""></div>
<a ui-sref="home">home</a>
<a ui-sref="home.profile">profile</a>
<a ui-sref="otherView">other</a>
</div>

Resources