I have an interface in my application that maintains properties that I want to send to my database, as well as properties that I do not.
Specifically I maintain an property called state that can be set to open or null (closed) which then triggers Angular2's Animation state function. I use this in *ngFor lists to open an close a panel of information about the item.
However, I don't want to store the value of state in my database since it always defaults to null. Currently, I pass the whole object to the http call so the state property gets sent too. How can I instead ignore it?
pushItemToDay(item: any, dateStr: Date): void {
let body = JSON.stringify(item);
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
this.http.post(this.baseURL + 'api/addItem/' + dateStr, body, options)
.toPromise()
.catch(this.handleError);
}
Delete can do damage if the object is used after the post. The function stringify has an additional parameter exactly to ignore unwanted entries.
let source = {
'meal': 'burger',
'milkshake': 'chocolat',
'extra':'2 hot dogs',
'free': 'smile'
};
let ignoreList = [ 'meal', 'extra' ];
function replacer(key,value)
{
if (ignoreList.indexOf(key) > -1) return undefined;
else return value;
}
let data = JSON.stringify(source, replacer);
console.log(data);
Related
I'm new to Angular, I'm using Angular 7 as client side and ASP.NET as server side.
I want to pass more than 2 parameters. I've succeeded to send one parameter.
Here's my Angular service method that sends 2 parameters:
validate_user(user : Users, active: boolean): Observable<UsersError> {
console.log(user);
return this.http.post<UsersError>(this.users, user, active);
}
Here's my ASP.NET post method:
public UsersError Post([FromBody] sp_GetUsers_Result user, bool active)
{
UsersError u = new UsersError
{
UserName = "Username Invalid",
Password = "Password Invalid"
};
return u;
}
I know I can wrap the parameters into an object and send it as one, but I want to know is there a way to do it with two or more parameters (objects).
I tried to use HttpParams with various combinations, but nothing worked.
The third parameter in .post is httpOptions :
Example:
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': 'my-auth-token'
})
};
So you'd need to change this line :
return this.http.post<UsersError>(this.users, user, active);
active should be send as param
Regarding asp.net : submitting/posting must match an object in the controller.
Asp.net wil take the body params and will try to match them into the controller argument (fromBody). You can't mix it with another argument(*).
In short , you'd have to add active to a new model in server.
* You can , but it will require some other hacks - like I've built: https://github.com/RoyiNamir/SimplePostVariableParameterBindingExtended
BundleTypeAttributeView(users: string, Active: boolean, ): Observable<any> {
const params = new HttpParams().set('users', users).set("Active", Active);
return this.http.get<any>(this.endPoint + 'bundleTypeAttributes', { params });
}
I'm developing a profile card that has to get different value's. I'm getting all the value's but i also want to load a network image. I'm using a filemaker server and i had noticed that i needed coockies to load this. When i make a request copy paste the image url into my browser it just loads. But whenever i'm loading it into my application i get the 401 statusCode with my image.
I have tried just a valid network image that's working, i have readed something about coockies but i'm not sure if i need to use them and how. I also find it weird that whenever i load the image in my browser it works but not on my application.
Future makeRequest() async {
var url4 =
"https://fms.xxxxxx.nl/fmi/data/v1/databases/Roscom Management Systeem/layouts/medewerker pa api/_find";
var body = json.encode({
"query": [
{"Emailadres(1)": "xxxx#xxx.nl"}
],
});
Map<String, String> headers = {
'Content-type': 'application/json',
'Accept': 'application/json',
"Authorization":
'Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
};
var response = await http.post(url4, body: body, headers: headers);
setState(() {
var responseJson = json.decode(response.body);
data = responseJson['response']['data'][0];
profielfoto = data['fieldData']['iMedewerker Foto'];
print(profielfoto);
});
Value i get in the terminal
I expect that i can load the image in a networkimage with just the var $profielfoto. I don't know what to do with the coockies or maybe there's a much easier way to do it. I hope someone can help me please let me know if i need to provide more information about the server or anything else. ;)
A few things. Please do not put any type of heavy processing in setState
https://docs.flutter.io/flutter/widgets/State/setState.html
Generally it is recommended that the setState method only be used to
wrap the actual changes to the state, not any computation that might
be associated with the change. For example, here a value used by the
build function is incremented, and then the change is written to disk,
but only the increment is wrapped in the setState:
setState tells the widget when it needs to be redrawn
https://flutter.dev/docs/cookbook/images/network-image
String _profilePhoto = "";
//Change await into an async operation
http.post(url4, body: body, headers: headers).then( (response) {
var responseJson = json.decode(response.body);
print(data['fieldData']['iMedewerker Foto']);
setState(() {
_data = responseJson['response']['data'][0];
_profilePhoto = data['fieldData']['iMedewerker Foto'];
});
})
Widget build(BuildContext context){
//Check if string is empty
if ( _profilePhoto == "" ){
return CircularProgressIndicator();
}else {
return Image.network( _profilePhoto );
}
}
https://flutter.dev/docs/cookbook/images/network-image
https://pub.dartlang.org/packages/cached_network_image
You have two choices to grab images from the network. I believe I presented one way.
the authorization token must be the issue. a secure server will return this token when a user logs in. then all ensuing request must have this token in the 'authorization header'. if it is not there or incorrect the server returns a 401
I've got one view displaying some pictures published by users with some data (let's image Instagram).
I already have these pictures as non-reactive data (otherwise you could see many updates) but these images have one button to like the picture. If I have this as non-reactive data I can't see when I click on "Like" the filled heart (I need to refresh).
This is my subscribe function:
this.subscribe('food', () => [{
limit: parseInt(this.getReactively('perPage')),
//skip: parseInt((this.getReactively('page') - 1) * this.perPage),
sort: this.getReactively('sort')
}, this.getReactively('filters'), this.getReactively('searchText'), this.getReactively('user.following')
]);
And this is my helper:
food() {
const food = Food.find({}, {reactive: true}, {
sort: this.sort
}).fetch().map(food => {
const owner = Meteor.users.findOne(food.owner, {fields: {username: 1, avatarS: 1, following: 1}});
food.avatarS = owner && owner.avatarS;
food.username = owner && owner.username;
if (food.likes.indexOf(Meteor.userId()) == -1) {
// user did not like this plate
food.liked = false;
} else {
// user liked this plate
food.liked = true;
}
return food;
});
}
Is possible to have a non-reactive model but with some reactive properties on it?
I'm using Angular 1.X with TS btw
Thanks in advance!
PS: is it normal that this works as non-reactive when I change reactive to true?
Modification to your code:
//console.log(food.likes);
this.subscribe('reactiveFoodData', {ownerId: food.owner, userId: Meteor.userId()}).subscribe(()=>{
console.log(this.user);
});
// THIS IS THE PUBLISH METHOD LOCATED IN THE SERVER SIDE:
Meteor.publish('reactiveFoodData', function(params: {ownerId:string, userId:string) {
const owner = Meteor.users.findOne(params.ownerId);
if (!owner) {
throw new Meteor.Error('404', 'Owner does not exist');
}
let result = {};
result.avatarS = owner.avatarS;
result.username = owner.username;
const food = Food.find({});
result.liked = !(food.likes.indexOf(params.userId) == -1);
return result;
});
You have few problems:
1. The reactive flag is true by default, you do not need to set it.
2. The function find is accepting only two arguments, not 3.
Should be:
const food = Food.find({}, {reactive: true, sort: this.sort})
If you need some, subset of data to be reactive only (from some collection). You could create a specific Method (which udpates only "likes").
https://guide.meteor.com/methods.html
UPDATE:
Here is how you write a method with return parameter (check two examples, with Future and without):
How to invoke a function in Meteor.methods and return the value
UPDATE2:
You have lost reactivity when you used fetch(). Because you moved from reactive cursor to just simple array over which you map values. Do not expect reactivity after fetch(). If you want fetch or do not want to use Cursors, you could wrap the find inside Tracker.autorun(()=>{}) or utilize publish/subscribe.
Note: But be careful, if you somehow manage to get "empty" cursor in find(), your Tracker.autorun will stop react reactively. Autorun works only if it has something to watch over.
The main point with method, is that if you want to have one time non-reactive action for something. You define the method on server:
Meteor.methods({
myMethod: ()=> {
return "hello";
}
});
And you can call it from client with:
Meteor.call('myMethod', (error, result) => {
console.log(result); // "hello"
});
Instead of working with pure collections. You could start using publish/subscribe. On server you publish 'likes' and on client you just listens to this new reactive view. E.g.,
Meteor.publish('likes', (options: {owner: string, likes: Array<any>}) => {
let result: any = {}
const owner = Meteor.users.findOne(options.owner, username: 1, avatarS: 1, following: 1}});
result.avatarS = options.owner && options.owner.avatarS;
result.username = options.owner && options.owner.username;
result.liked = !(options.likes.indexOf(Meteor.userId()) == -1)
return result;
});
On client side: Meteor.subscibe('likes', {food.owner, food.likes}).subscribe(()=>{});
This is just off the top of my head.
Have you tried looking at Tracker ? https://docs.meteor.com/api/tracker.html
But more specifically the method Tracker.nonreactive
https://docs.meteor.com/api/tracker.html#Tracker-nonreactive
Okay, so I am new to working with HTTP and actually getting some data from the server. Been sifting through a lot of tutorials, examples and questions asked here, but I am not finding what I want. All tutorials I've found only shows how to retrieve and add some data.
So based on those examples I've managed to retrieve data:
service:
getCases(){
return this.http.get('someUrl');
}
Case component constructor:
this._service.getCases()
.map((res: Response) => res.json())
.subscribe(cases => this.cases = cases);
Adding cases
service:
public saveCase(case: case) {
let body = JSON.stringify(case);
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.post('someUrl', body, options)
.map(this.extractData)
.catch(this.handleError)
.subscribe(case => this.cases.push(case));
}
Case Component:
saveCase() {
let case = new Case(this.id, this.name, this.date)
this._service.saveCase(case);
this.name = '';
}
Okay, so I have and Array "Cases" which contains Case objects. Getting the data from the server displays the cases like I want them to. When I add a new case it gets sent to the server, but how do I get the Array updated when I add a new Case. Because now the new case appears only after I refresh the browser.
Second question is that the user can click a case and it then routes to a detail list where the user can add steps and feedback. If it matters, case has the attributes id, name, date and an array of steps, at this point the array is empty. The step object is it's own class and the object contains an array of feedback. Feedback is also an own class and the object has two attributes, which are both strings. So it's all nested. When I click the case, it does route to the detail page, but there the case name should be printed and it doesn't. It also shows my button for adding steps, but it does nothing. Obviously I'm missing something in my methods, but I have no clue to as what to do. As a comment I can say that before adding the http in my code it all worked as it should. Here are the methods, that are probably missing something:
Case Component:
gotoDetail(case: Case) {
this._router.navigate(['CaseDetail', {"id": case.name}]);
}
Service:
public getById(id: string): Case {
for (let case of this.cases) {
if (case.id === id) {
return case;
}
}
return null;
}
Then there is the matter of syntax for removing cases, haven't found an example that works for me yet, I've tried a bunch... among others the example links provided by #shershen below. None works. The original methods I have, that should be changed to work with http:
Component:
removeSearchCase(case: Case) {
this._service.removeCase(case);
}
Service:
public removeCase(value: Case): void {
let index = this.cases.indexOf(value);
this.cases.splice(index, 1);
}
So the case removal is with post.
And about the backend I can say as much that I only have the following three posts and gets:
getCases (GET), saveCase (also works as updating the case)(POST) and removeCase (POST).
It's hard to debug without sample demo, however the descriptions quite detailed. I am adding some points that may fix the problem while improving the code structure:
First, you should move the request subscription/observing into the service methods; that will encapsulate the request handling logic in service layer:
//service.ts
#Injectable()
export class service {
getCases(){
if (!this.request) {
this.request = this.http.get('/assets/data.json')
.map((response: Response) => response.json())
.map((data: string[]) => {
this.request = null;
return this.names = data;
});
}
return this.request;
}
}
Second, you need to create an instance of your service in your Component's constructor instead of using it as a static method of the service:
//component.ts
import {MyService} from 'PATH_TO_YOUR_SERVICE';
class CaseComponent {
constructor(private _service : MyService){
//other stuff..
}
getData(){
this._service.getCases()
}
}
Additional references:
Official "Getting and Saving Data with HTTP"
Service example with Observables (with Firebase, but still)
Simple service in Angular2 seed project
I think you should put your cases Array in the CaseComponent:
case.component.ts:
cases: Case[];
constructor(private caseService: CaseService){}
getCases() {
this.caseService.getCases()
.subscribe(cases => this.cases = cases);
}
saveCase() {
let case = new Case(this.id, this.name, this.date);
this.caseService.saveCase(case)
.subscribe(case => this.cases = [...this.cases, case]);
}
case.service.ts:
getCases() {
return this.http.get(this.casesUrl)
.map(this.extractData)
.catch(this.handleError);
}
saveCase (case: Case) {
let body = JSON.stringify({ case });
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.post(this.casesUrl, body, options)
.map(this.extractData)
.catch(this.handleError);
}
Then try to change "name" to "id" in gotoDetail:
gotoDetail(case: Case) {
this._router.navigate(['CaseDetail', {"id": case.id}]);
}
I have decided to not use ember-data as it's not production ready and still changing. My app only needs to make a few ajax requests anyway so it shouldn't make too big of a difference. I am having trouble understanding how to handle an ajax promise response.
When my user loads the app they already have an authenticated session. I am trying to ping the server for that users info and display it in my template. It seems my template is rendered before my ajax request returns results and then does not update with the promise.
// route
App.ApplicationRoute = Ember.Route.extend({
setupController: function(){
this.set("currentUser", App.User.getCurrentUser());
}
});
// model
App.User = Ember.Object.extend({
email_address: '',
name_first: '',
name_last: '',
name_full: function() {
return this.get('name_first') + ' ' + this.get('name_last');
}.property('name_first', 'name_last')
});
App.User.reopenClass({
getCurrentUser: function() {
return $.ajax({
url: "/api/get_current_user",
type: "POST",
data: JSON.stringify({})
}).then(function(response) {
return response;
});
}
});
In my template:
<h1> Hey, {{App.currentUser.name_first}}</h1>
How would I update the template when I receive a response or delay rendering until I have a response?
Actually the answer is quite easy: You do not need to use a promise. Instead just return an empty object. Your code could look like this:
App.User.reopenClass({
getCurrentUser: function() {
var user = App.User.create({}); //create an empty object
$.ajax({
url: "/api/get_current_user",
type: "POST",
data: JSON.stringify({})
}).then(function(response) {
user.setProperties(response); //fill the object with your JSON response
});
return user;
}
});
What is happening here?
You create an empty object.
You make an asynchronous call to your API...
... and in your success callback you fill your empty object.
You return your user object.
Note: What is really happening? The flow mentioned above is not the sequence in which those actions are happening. In reality the points 1,2 and 4 are performed first. Then some time later, when the response returns from your server, 3 is executed. So the real flow of actions is: 1 -> 2 -> 4 -> 3.
So the general rule is to always return an object that enables Ember to do its logic. No values will be displayed first in your case and once your object is filled Ember will start do its magic and auto update your templates. No hard work needs to be done on your side!
Going beyond the initial question: How would one do this with an array?
Following this general rule, you would return an empty array. Here a little example, which assumes, that you might like to get all users from your backend:
App.User.reopenClass({
getAllUsers: function() {
var users = []; //create an empty array
$.ajax({
url: "/api/get_users",
}).then(function(response) {
response.forEach(function(user){
var model = App.User.create(user);
users.addObject(model); //fill your array step by step
});
});
return users;
}
});
I'd use Ember.Deferred instead of returning an empty array as mentioned before.
App.User.reopenClass({
getAllUsers: function() {
var dfd = Ember.Deferred.create();
var users = [];
$.ajax({
url: "/api/get_users",
}).then(function(response) {
response.forEach(function(user){
var model = App.User.create(user);
users.addObject(model);
});
dfd.resolve(users);
});
return dfd;
}
});
In your model hook all you have to do is this
model: function(){
return App.User.getAllUsers();
}
Ember is smart enought and knows how to handle the promise you return, once it's resolved the model will be correctly set, you can also return a jQuery promise but it will give you some weird behavior.
You can as well set the current user as the model for your ApplicationRoute like so:
App.ApplicationRoute = Ember.Route.extend({
model: function() {
return App.User.getCurrentUser();
}
});
Since getCurrentUser() returns a promise, the transition will suspend until the promise either fulfills or rejects.
This is handy because by the time transition is finished your model is initialized and you will see it rendered in the template.
You can read up more about async routing in Ember guides.