I have a Web Service which is providing some user data (this is Java backend) and I have an Angular component:
import { Component,state,style,animate,transition, trigger, keyframes,
OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/pluck';
import { IUser } from './user.interface';
#Component({
moduleId: module.id,
selector: 'user-cmp',
templateUrl: 'user.component.html'
})
export class UserComponent {
user: Observable<IUser>;
errorMessage: string;
constructor(private _userService: ActivatedRoute){
this.user = _userService.data.pluck('user');
}
}
I am using a Resolver:
import { Resolve } from '#angular/router';
import { Observable } from 'rxjs/Observable'
import { Observer } from 'rxjs/Observer';
import { IUser } from './user.interface';
import { UserService } from './user.service';
import {Injectable} from "#angular/core";
#Injectable()
export class UsersResolver implements Resolve<IUser> {
constructor (private _userService: UserService) {}
resolve(): Observable<IUser>{
return this._userService.getUser();
}
}
Resolver is using a service:
import {Injectable} from "#angular/core";
import { Http, Response } from "#angular/http";
import { IUser } from './user.interface';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
#Injectable()
export class UserService {
private _userUrl = 'http://localhost:8000/rest/users';
constructor(private _http: Http){
}
getUser(): Observable<IUser>{
return this._http.get(this._userUrl)
.map((response: Response) => {
return <IUser> response.json();
})
.do(data => console.log('All: ' + JSON.stringify(data)))
.catch(this.handleError);
}
private handleError (error: Response){
console.error(error);
return Observable.throw(error.json().error || 'Server Error');
}
}
And finally the View:
<div class="main-content" >
<div class="container-fluid">
<div class="row">
<div class="col-md-8">
<div class="card" [#carduserprofile]>
<div class="header">
<h4 class="title">Edit User Profile</h4>
</div>
<div *ngIf="user" class="content">
<form>
<div class="row">
<div class="col-md-3">
<div class="form-group">
<label>Username</label>
<input type="text" class="form-control" placeholder="Username" value="{{user.username }}">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Email address</label>
<input type="email" class="form-control" placeholder="Email" value="{{user.password}}">
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label>Phone</label>
<input type="text" class="form-control" placeholder="Phone" value="{{user.telephone}}">
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label>Type</label>
<input type="text" class="form-control" disabled placeholder="User type" value="{{user.type}}">
</div>
</div>
</div>
<button type="submit" class="btn btn-info btn-fill pull-right">Update Profile</button>
<div class="clearfix"></div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
The module looks like this:
import { NgModule } from '#angular/core';
import { RouterModule } from '#angular/router';
import { BrowserModule } from '#angular/platform-browser';
import { MODULE_COMPONENTS, MODULE_ROUTES } from './dashboard.routes';
import { UsersResolver } from './user/user.resolver';
#NgModule({
imports: [
BrowserModule,
RouterModule.forChild(MODULE_ROUTES)
],
declarations: [ MODULE_COMPONENTS ],
providers: [ UsersResolver ]
})
export class DashboardModule{}
And also the part of the route is like this:
{ path: 'user', component: UserComponent, resolve: {user: UsersResolver} },
I didn't paste some useless parts of the code, like animations and etc.
My problem is, it prints the data from the webservide using the .do in the service, but it doesn't show nothing in the View. Fields are blank.
I am using *ngIf in case of blank user data from WS.
I have no idea why and also I don't know how to implement some codes to check it in the View.
Any help will be great!
Thanks in advance.
The problem I see is located here:
constructor(private _userService: ActivatedRoute){
this.user = _userService.data.pluck('user');
}
this.user is an observable so you need to subscribe it to get the value either within the component (with this.user.subscribe(...)) or in the template using the async pipe.
Perhaps you could simply leverage the snapshot for the route:
constructor(private _userService: ActivatedRoute){
this.user = _userService.snapshot.data['user'];
}
This way the user would be a raw JS object and not an observable. So using the following should be better:
<div *ngIf="user" class="content">
Related
I don't know what's wrong in my code.
template/components/item.hbs:
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default" {{action 'buttonClicked' item}} disabled={{unless item.isValid true}}>{{buttonLabel}}</button>
</div>
</div>
components/item.js:
import Component from '#ember/component';
export default Component.extend({
buttonLabel: 'Save',
actions: {
buttonClicked(param) {
this.sendAction('action', param);
}
}
});
Ember/library-app/app/components/item.js
8:13 error Use closure actions, unless you need bubbling ember/closure-actions
Since ember > 2.0 closure actions are the favored way to handle actions (Data Down Actions Up DDAU).
I would recommend reading this http://miguelcamba.com/blog/2016/01/24/ember-closure-actions-in-depth/
Since newer ember versions(2.18 I believe), there is a ESlint rule to point out that people should move to closure actions: https://github.com/ember-cli/eslint-plugin-ember/blob/master/docs/rules/closure-actions.md
You could rewrite your code to:
my-button.hbs
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default" onclick={{action "buttonClicked" item}} disabled={{unless item.isValid true}}>{{buttonLabel}}</button>
</div>
</div>
my-button.js
import Component from '#ember/component';
export default Component.extend({
buttonLabel: 'Save',
actions: {
buttonClicked(param) {
this.get('onButtonClicked')(param);
}
}
});
Or you could wave your action through:
my-button.hbs
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default" onclick={{action onButtonClicked item}} disabled={{unless item.isValid true}}>{{buttonLabel}}</button>
</div>
</div>
my-button.js
import Component from '#ember/component';
export default Component.extend({
buttonLabel: 'Save'
});
actions: {
buttonClicked(param) {
this.sendAction('action', param);
}
}
Instead of the name 'action' try using some other actionName
Like
actions: {
buttonClicked(param) {
this.sendAction('onButtonClick', param);
}
}
Then use it in the parent template as
{{item onButtonClick="someActionHandledInTheParent"}}
I am re writing an application into Angular and have to upload a file. The network shows me status: 200 but there is no response and my back end is not receiving any file.
Why Request Method is OPTION and CORS(in chrome) ?
Chrome:
chrome console: Response to preflight request doesn't pass access control check: Credentials flag is 'true', but the 'Access-Control-Allow-Credentials' header is ''. It must be 'true' to allow credentials. Origin is therefore not allowed access.
enter image description here
Firefox:
firefox not has consol error
enter image description here
My cod:
import { Component, OnInit } from '#angular/core';
import { FileUploader } from 'ng2-file-upload';
const URL = 'http://127.0.0.1:8000/api/v1/upload/';
#Component({
selector: 'app-upload',
templateUrl: './upload.component.html',
styleUrls: ['./upload.component.css']
})
export class UploadComponent implements OnInit {
public uploader: FileUploader;
constructor()
{
this.initUpload()
}
ngOnInit() {
}
initUpload() {
this.uploader = new FileUploader({
url: URL,
method: 'POST',
headers: [
{name: 'Access-Control-Allow-Credentials', value: 'true'},
{name:'Access-Control-Allow-Origin', value: '*'}
]
});
}
}
--------------------------------------------
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { FormsModule } from '#angular/forms';
import { HttpModule } from '#angular/http';
import { FileUploadModule } from 'ng2-file-upload';
import { AppComponent } from './app.component';
import { UploadComponent } from './upload/upload.component';
#NgModule({
imports: [
BrowserModule,
FormsModule,
HttpModule,
FileUploadModule
],
declarations: [
AppComponent,
UploadComponent,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
--------------------------------------------
<input type="file" ng2FileSelect [uploader]="uploader" multiple /><br/>
<table class="table">
<thead>
<tr>
<th width="50%">Name</th>
<th>Size</th>
<th>Progress</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of uploader.queue">
<td><strong>{{ item.file.name }}</strong></td>
<td nowrap>{{ item.file.size/1024/1024 | number:'.2' }} MB</td>
<td>
<div class="progress" style="margin-bottom: 0;">
<div class="progress-bar" role="progressbar" [ngStyle]="{ 'width': item.progress + '%' }"></div>
</div>
</td>
<td class="text-center">
<span *ngIf="item.isSuccess"><i class="glyphicon glyphicon-ok"></i></span>
<span *ngIf="item.isCancel"><i class="glyphicon glyphicon-ban-circle"></i></span>
<span *ngIf="item.isError"><i class="glyphicon glyphicon-remove"></i></span>
</td>
<td nowrap>
<button type="button" class="btn btn-success btn-xs"
(click)="item.upload()" [disabled]="item.isReady || item.isUploading || item.isSuccess">
<span class="glyphicon glyphicon-upload"></span> Upload
</button>
<button type="button" class="btn btn-warning btn-xs"
(click)="item.cancel()" [disabled]="!item.isUploading">
<span class="glyphicon glyphicon-ban-circle"></span> Cancel
</button>
<button type="button" class="btn btn-danger btn-xs"
(click)="item.remove()">
<span class="glyphicon glyphicon-trash"></span> Remove
</button>
</td>
</tr>
</tbody>
</table>
django using
CORS_ORIGIN_WHITELIST = (
'localhost:4200'
)
I think the problem is for calling localhost from google chrome
As simple solution you can install CORS extension to all access -origin
You can install it from here
Also you can add these headers in your API response
'Access-Control-Allow-Origin', 'http://mysite')
'Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
'Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
I have this collection in "results.component.ts": results: Result[]
I would like to iterate over the collection, displaying each result.
I would like to have a seperate component for this called result.component.ts. This is because a result could get complex and large.
What I have so far is not displaying anything. The output html has some commented out template bindings:
<div _ngcontent-fud-12="" class="col-sm-8 offset-sm-2 col-md-6 offset-md-3 col-xl-4
offset-xl-4">
<!--template bindings={
"ng-reflect-ng-for-of": "[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]"
}--><!--template bindings={}--><!--template bindings={}--><!--template bindings={}--><!--template bindings={}--><!--template bindings={}--><!--template bindings={}--><!--template bindings={}--><!--template bindings={}--><!--template bindings={}--><!--template bindings={}-->
</div>
Here is my relevant code:
results.component.html:
<div id="results" class="text-uppercase">
<div id="up-button-row" class="row">
<div class="col-sm-8 offset-sm-2 col-md-6 offset-md-3 col-xl-4
offset-xl-4">
<button class="m-x-auto" md-fab [disableRipple]="true" (click)="scrollUp()"></button>
</div>
</div>
<div class="row" *ngIf="noResults">
<div class="col-sm-8 offset-sm-2 col-md-6 offset-md-3 col-xl-4
offset-xl-4">
<h2 class="m-x-auto">No vegan stuff found :-(</h2>
</div>
</div>
<div class="row" *ngIf="!noResults">
<div class="col-sm-8 offset-sm-2 col-md-6 offset-md-3 col-xl-4
offset-xl-4">
<div *ngFor="let result of results"><result></result></div>
</div>
</div>
</div>
results.component.ts:
import { Component, AfterViewInit } from '#angular/core';
import { ResultComponent } from './result.component';
import { Result } from '../result'
#Component({
selector: 'results-div',
templateUrl: 'app/find-page/results.component.html',
styleUrls: ['app/find-page/results.component.css' ],
directives:[ResultComponent]
})
export class ResultsComponent implements AfterViewInit {
results: Result[];
noResults: boolean;
ngAfterViewInit() {
this.scrollDown();
}
scrollDown() {
ScrollToAnchor.goToTargetBottom("#results");
}
scrollUp() {
ScrollToAnchor.goToTarget("#find-page");
}
}
result.component.html:
<div class="col-sm-6 col-lg-2" style="margin-top:20px; padding: 25px;">
<div class="product-item scale-on-hover" (click)="setCurrentlySelectedProduct()">
<div [ngStyle]="{background: result.imagePath}" id="image"></div>
<div id="info">
<h6 id="brand" class="medium-text">{{brand}}</h6>
<h6 id="name" class="medium-text">{{name}}</h6>
</div>
</div>
</div>
result.component.ts:
import { Component, AfterViewInit } from '#angular/core';
import { Result } from '../result';
#Component({
selector: 'result',
templateUrl: 'app/find-page/result.component.html',
styleUrls: ['app/find-page/result.component.css' ]
})
export class ResultComponent{}
How do I pass the result data for 1 result from results.component to result.component.
How do I get the result.component.html to display once for each result?
#Component({
selector: 'result',
templateUrl: 'app/find-page/result.component.html',
styleUrls: ['app/find-page/result.component.css' ]
})
export class ResultComponent{
#Input() result;
}
<div *ngFor="let result of results"><result [result]="result"></result></div>
<div class="col-sm-6 col-lg-2" style="margin-top:20px; padding: 25px;">
<div class="product-item scale-on-hover" (click)="setCurrentlySelectedProduct()">
<div [ngStyle]="{background: result.imagePath}" id="image"></div>
<div id="info">
<h6 id="brand" class="medium-text">{{result.brand}}</h6>
<h6 id="name" class="medium-text">{{result.name}}</h6>
</div>
</div>
</div>
I have a simple contact form, with validation done using ember-cp-validations https://github.com/offirgolan/ember-cp-validations and I now need to integrate the new Google Recaptcha into that.
For the rendering of the recaptcha, I am using this code - https://gist.github.com/cravindra/5beeb0098dda657433ed - which works perfectly.
However, I don't know how to deal with the verification process to allow the form to be submitted/prevented if the challenge is correct/incorrect or not provided
Here is a truncated version of my contact-form component
import Ember from 'ember';
import Validations from './cp-validations/contact-form';
import config from '../config/environment';
export default Ember.Component.extend(Validations,{
data:{},
nameMessage:null,
init() {
this._super(...arguments);
this.set('data',{});
},
actions:{
submitForm() {
this.validate().then(({model,validations}) => {
if (validations.get('isValid')) {
// submit form
}
else {
if(model.get('validations.attrs.data.name.isInvalid')){
this.set('nameMessage',model.get('validations.attrs.data.name.messages'));
}
}
})
}
}
});
Here is the template for the component, which includes the rendering of the recpatcha using the gist above
<form {{action 'submitForm' on='submit'}}>
<div class="row">
<div class="medium-6 columns">
{{input type="text" value=data.name id="name" placeholder="Enter your name"}}
<div class="error-message">
{{nameMessage}}
</div>
</div>
</div>
<div class="row">
<div class="medium-12 columns">
{{google-recaptcha}}
</div>
</div>
<button class="button primary" type="submit">Submit</button>
</form>
The Validations import looks like this
import { validator, buildValidations } from 'ember-cp-validations';
export default buildValidations({
'data.name': {
validators: [
validator('presence',{
presence:true,
message:'Please enter your name'
})
]
},
});
Many thanks for any help!
Register captchaComplete in your google-recaptcha component and mix the answer with your validations
UPDATE
contact-form.hbs
<form {{action 'submitForm' on='submit'}}>
<div class="row">
<div class="medium-6 columns">
{{input type="text" value=data.name id="name" placeholder="Enter your name"}}
<div class="error-message">
{{nameMessage}}
</div>
</div>
</div>
<div class="row">
<div class="medium-12 columns">
{{google-recaptcha captchaComplete=validateRecatcha}}
</div>
</div>
<button class="button primary" type="submit">Submit</button>
</form>
contact-form.js
import Ember from 'ember';
import Validations from './cp-validations/contact-form';
import config from '../config/environment';
export default Ember.Component.extend(Validations,{
data:{},
nameMessage:null,
captchaValidated: false,
init() {
this._super(...arguments);
this.set('data',{});
},
actions:{
validateRecatcha(data){
//if data denotes captcha is verified set captchaValidated to true else false
},
submitForm() {
this.validate().then(({model,validations}) => {
if (validations.get('isValid') && this.get('captchaValidated')) {
// submit form
}
else {
if(model.get('validations.attrs.data.name.isInvalid')){
this.set('nameMessage',model.get('validations.attrs.data.name.messages'));
}
}
})
}
}
});
I have a main page listing some categories / subcategories. Whenever a subcategory is clicked, the action openSubcategory is triggered:
// routes/application.js
import Ember from 'ember';
export default Ember.Route.extend({
userSelections: Ember.inject.service('user-selections'),
actions: {
openSubcategory: function(categoryId, subcategoryId) {
var userSelections = this.get('userSelections');
userSelections.set('category', categoryId);
userSelections.set('subcategory', subcategoryId);
this.transitionTo('filter-categories');
},
}
});
To pass the selections to the corresponding controller, I am using a service:
// services/user-selections.js
import Ember from 'ember';
export default Ember.Service.extend({
category: null,
subcategory: null,
init() {
this._super(...arguments);
this.set('category', null);
this.set('subcategory', null);
},
});
Which is evaluated in:
// controllers/filter-categories.js
import Ember from 'ember';
export default Ember.Controller.extend({
userSelections: Ember.inject.service('user-selections'),
init() {
this._super(...arguments);
this.get('userSelections'); // We need to get it so that we can observe it?
// We can not declare the observers, because we need to make sure userSelections is first read
this.addObserver('userSelections.category', function() {
Ember.run.once(this, 'refreshProducts');
});
this.addObserver('userSelections.subcategory', function() {
Ember.run.once(this, 'refreshProducts');
});
},
actions: {
changedCategory: function(selectedCategory) {
this.set('selectedCategory', selectedCategory);
this.get('userSelections').set('category', selectedCategory.value);
},
changedSubcategory: function(selectedSubcategory) {
this.set('selectedSubcategory', selectedSubcategory);
this.get('userSelections').set('subcategory', selectedSubcategory.value);
},
},
refreshProducts: function() {
var userSelections = this.get('userSelections'),
category = userSelections.get('category'),
subcategory = userSelections.get('subcategory');
var products = this.store.filter('product', function(product) {
var catId = parseInt(product.get('category').get('id')),
subcatId = parseInt(product.get('subcategory').get('id'));
if (category && catId !== category) {
return false;
}
if (subcategory && subcatId !== subcategory) {
return false;
}
return true;
});
this.set('model', products);
},
});
Observing the userSelections (after some hacking, as seen in the comments) works: the actions are properly triggering the refreshProducts method. But it seems the method is not triggered when coming from the application route, probably because the controllers/filter-categories is not yet initialized.
(*) According to the documentation there are lots "issues" observing services.
Observers and asynchrony
Observers and object initialization
Unconsumed Computed Properties Do Not Trigger Observers
"The injected property is lazy; the service will not be instantiated until the property is explicitly called" (link)
As a result, code needs to be written in a difficult to understand way.
Is there a better pattern to share data between routes / controllers than using a service?
EDIT
These are my templates:
// partials/categories.hbs (used on the application.hbs template)
{{#each model.categories as |category| }}
<div class="categories-list row">
<div class="container">
<h3 class="category-name centered">
<span class="bg-left"></span>
<span class="bg-center uppercase">{{category.name}}</span>
<span class="bg-right"></span></h3>
</div>
<div class="category owl-carousel">
{{#each category.subcategories as |subcategory| }}
<div class="category-item">
<a href="{{subcategory.link}}">
<div class="category-icon">
<img src="{{subcategory.image}}">
</div>
<h4 class="capitalize" {{action "openSubcategory" category.id subcategory.id}}>{{subcategory.name}}</h4>
</a>
</div>
{{/each}}
</div>
</div>
{{/each}}
And:
// filter-categories.hbs
<div class="container">
<div class="row">
<div class="col-md-12">
<h2>Our Vault</h2>
<legend>Filter products by category / subcategory</legend>
</div>
</div>
<div class="row">
<div class="col-md-12">
<form class="form-inline portfolio-form">
<div class="form-group col-md-2">
{{#power-select options=optionsCategory selected=selectedCategory onchange=(action "changedCategory") as |category|}}
{{category.text}}
{{/power-select}}
</div>
<div class="form-group col-md-2">
{{#power-select options=optionsSubcategory selected=selectedSubcategory onchange=(action "changedSubcategory") as |subcategory|}}
{{subcategory.text}}
{{/power-select}}
</div>
<div class="form-group col-md-2">
<button type="button" class="btn btn-default" {{action "clearSelections" id}}><i class="fa fa-remove"></i> Clear Filters</button>
</div>
</form>
</div>
</div>
<div class="row">
{{partial "products"}}
</div>
</div>
Is there a reason you're avoiding the use of dynamic segments? If not you can pass the category and subcategory as dynamic segments when using transitionTo('filter-categories'), an added benefit would be that this route will become linkable :)
So for eg. you should define your filter-categories route like this:
// router.js
...
this.route('filter-categories', { path: 'filter-categories/:category_id/:subcategory_id' });
...
Now in your routes/filter-categories.js router you could do:
// routes/filter-categories
import Ember from 'ember';
export default Ember.Route.extend({
model(params) {
// you can put your `refreshProducts` logic in here
// (I mean in this router not literally in this method)
// and return the products model or whatever you want
// I will just return an object with the ids for simplicity
return {
categoryId: params.category_id,
subcategoryId: params.subcategory_id
};
}
...
And now in your application route:
// routes/application.js
import Ember from 'ember';
export default Ember.Route.extend({
actions: {
openSubcategory: function(categoryId, subcategoryId) {
this.transitionTo('filter-categories', categoryId, subcategoryId);
}
}
});
edit:
If you don't always have an categoryId and subcategoryId you can use query parameters instead of dynamic segments.