I'm using React and Laravel for the creation of a list.
In the add button I have the following
function handleAdd() {
const newList = list.concat({ name, id: uuidv4(), value });
setList(newList);
setName("");
setValue("");
}
const onAddSocial = (e) => {
e.preventDefault();
const test = {
value: value,
name: name,
};
axios.post('http://localhost:8000/api/social', test)
.then((res) => {
toast(<div class="text-primary font-weight-bolder">Social link added successfully!</div>,{ delay: 0 });
console.log(res)
}).catch((error) => {console.log(error)
})
}
And the button to trigger both functions for the creation :
<Button
onClick={(e) => {
props.handleAdd();
props.onAddSocial(e);
}}
>
The element is created, but in the datatabse the id receive an incremented value. How do I pass the uuidv4() of the created element instead ?
The store controller :
public function store(Request $request)
{
$data = new Social();
$data->value = $request->value;
$data->name = $request->name;
$data->save();
}
You have to use following code
In your Social Model you have to use like this
public $incrementing = false;
protected $keyType = 'string';
public static function boot(){
parent::boot();
static::creating(function ($social) {
$social->id = Str::uuid(36);
});
}
Add this line in header section
use Illuminate\Support\Str;
In your database\migration social file add below code also
public function up()
{
Schema::create('socials', function (Blueprint $table) {
$table->uuid('id', 36)->primary();
$table->timestamps();
});
}
Related
I had a component that each time something was added to state was added to local storage as well. It was deleted from local storage on componentWillUnmnout. I was told to prepare an indirect abstract layer for local storage handling in order to follow single responsibility principle.
I am confused how this could be done, can someone give an example of such layer, class?
componentWillUnmount() {
localStorage.removeItem('currentUser');
}
static getDerivedStateFromProps(nextProps) {
const currUser = JSON.parse(
localStorage.getItem('currentUser')
);
if (
currUser && nextProps.users.some(
(user) => user.id === currUser.id
)
) {
return {
user: currUser,
};
}
return null;
}
const onSelect = (
user
) => {
this.setState({
user,
});
localStorage.setItem('currentUser', JSON.stringify(user));
}
private onRemove = () => {
this.setState({
user: null,
});
localStorage.removeItem('currentUser');
}
Applying single responsibility principle here might be over-programming, since Javascripts is not OOP. But if you need, there are some concerns with using localStorage directly that can be separated:
Your component doesn't need to know where you store persistent data. In this case, it doesn't need to know about the usage of localStorage.
Your component doesn't need to know how you store the data. In this case, it doesn't need to handle JSON.stringify to pass to localStorage, and JSON.parse to retrieve.
With those ideas, an interface for localStorage can be implemented like so
const Storage = {
isReady: function() {
return !!window && !!window.localStorage;
},
setCurrentUser: function(user) {
if (!this.isReady()) throw new Error("Cannot find localStorage");
localStorage.setItem('currentUser', JSON.stringify(user));
return true;
},
getCurrentUser: function() {
if (!this.isReady()) throw new Error("Cannot find localStorage");
if (localStorage.hasOwnProperty('currentUser'))
{
return JSON.parse(localStorage.getItem('currentUser'));
}
return null;
},
removeCurrentUser: function() {
if (!this.isReady()) throw new Error("Cannot find localStorage");
localStorage.removeItem('currentUser');
return true;
}
}
By importing Storage object, you can rewrite your component:
componentWillUnmount() {
Storage.removeCurrentUser();
}
static getDerivedStateFromProps(nextProps) {
const currUser = Storage.getCurrentUser();
if (
currUser && nextProps.users.some(
(user) => user.id === currUser.id
)
) {
return {
user: currUser,
};
}
return null;
}
const onSelect = (
user
) => {
this.setState({
user,
});
Storage.setCurrentUser(user);
}
private onRemove = () => {
this.setState({
user: null,
});
Storage.removeCurrentUser();
}
I have the following use case:
Two visual grids are using two methods to load the data to display. These methods are automatically called by the grids and this part cannot be changed, it's by design:
loadDataForGrid1 = (params: any): any => {
return this.httpService.getData().then((response) => {
return response.dataGrid1;
}, (err) => {
});
}
loadDataForGrid2 = (params: any): any => {
return this.httpService.getData().then((response) => {
return response.dataGrid2;
}, (err) => {
});
}
Everything is working fine but my problem is performance. Since the getData method does an http request that is quite huge, calling it twice like Im doing right now is not acceptable. It there a way to solve this problem by doing only one call? Like caching the data so that they are reusable by the second call?
Im using typescript and angularjs
Edit:
Something like this would not work since the result would not be available when the grids load the data:
result: any;
// called at the beginning, for example contructor
loadData = (params: any): any => {
return this.httpService.getData().then(result => {
this.result = result;
});
}
loadDataForGrid1 = (params: any): any => {
return this.result.gridGrid1;
}
loadDataForGrid2 = (params: any): any => {
return this.result.gridGrid2;
}}
Using the answer suggested by #georgeawg generates the following javascript (which does 2 calls)
this.loadDataForGrid1 = function (params) {
_this.promiseCache = _this.promiseCache || _this.httpService.getData();
return _this.promiseCache.then(function (response) {
return response.gridGrid1;
}, function (err) {
});
};
this.loadDataForGrid2 = function (params) {
_this.promiseCache = _this.promiseCache || _this.httpService.getData();
return _this.promiseCache.then(function (response) {
return response.gridGrid2;
}, function (err) {
});
};
You can always store the the data array in a variable on the page for SPA. If you want to use the data over different pages, you can use localStorage to 'cache' the data on the client-side.
localStorage.set("mydata", response.dataGrid1);
localStorage.get("mydata");
FYI, i does not seem you are using typescript, but rather native javascript :-)
--
Why don't you do something like this, or am i missing something?
$scope.gridData = {};
loadDataForGrid1 = (params: any): any => {
return this.httpService.getData.then((response) => {
$scope.gridData = response;
}, (err) => {
}).finally(function(){
console.log($scope.gridData.gridData1);
console.log($scope.gridData.gridData2);
});
}
What you can do is store the returned variable into a service variable and then do a check if it already exists.
dataGrid;
loadDataForGrid1 = (params: any): any => {
if(!this.dataGrid) {
return this.httpService.getData.then((response) => {
this.dataGrid = response;
return this.dataGrid.dataGrid1;
}, (err) => {
});
}
return this.dataGrid.dataGrid1;
}
loadDataForGrid2 = (params: any): any => {
if(!this.dataGrid) {
return this.httpService.getData().then((response) => {
this.dataGrid = response;
return this.dataGrid.dataGrid2;
}, (err) => {
});
}
return this.dataGrid.dataGrid2;
}
Something like this should work. Every time you call loadDataForGrid1 or loadDataForGrid2 you will first check if the data is already there - therefore you make an API call only once.
The solution is to cache the promise and re-use it:
var promiseCache;
this.loadDataForGrid1 = (params) => {
promiseCache = promiseCache || this.httpService.getData();
return promiseCache.then(result => {
return result.gridGrid1;
});
}
this.loadDataForGrid2 = (params) => {
promiseCache = promiseCache || this.httpService.getData();
return promiseCache.then(result => {
return result.gridGrid2;
});
}
Since the service immediately returns a promise, it avoids the race condition where the
second XHR is started before the first XHR returns data from the server.
You mean that would be a javascript solution? But how to do it with typescript then?
JavaScript supports private variables.1
function MyClass() {
var myPrivateVar = 3;
this.doSomething = function() {
return myPrivateVar++;
}
}
In TypeScript this would be expressed like so:
class MyClass {
doSomething: () => number;
constructor() {
var myPrivateVar = 3;
this.doSomething = function () {
return myPrivateVar++;
}
}
}
So, after many hours I came to the following solution. It's a bit a hack but it works.
In the initialization (constructor or so) Im loading the data:
this.httpService.getData().then((response) => {
this.data1 = response.dataGrid1;
this.data2 = response.dataGrid2;
// other properties here...
this.isReady= true;
}, (err) => {
});
then I wrote an ugly wait method
wait(): void {
if (this.isReady) {
return;
} else {
setTimeout(this.wait, 250);
}
}
Finally, my two methods look like this
loadDataForGrid1 = (params: any): any => {
this.wait();
return this.$q.resolve(this.data1);
}
loadDataForGrid2 = (params: any): any => {
this.wait();
return this.$q.resolve(this.data2);
}
My code is as shown below:
/// <reference path="../../../typings/app.d.ts" />
/// <reference path="../../../typings/tsd.d.ts" />
module App.Controller {
import Services = Core.Services;
import Shared = Core.Shared;
export class RestaurentInfoController extends BaseController {
public restaurentName: any = [];
public checkBox: any;
public restaurent: any;
public foodTruckList: any = [];
public foodCategories: any = [];
public drinkCategories: any = [];
public restaurentId : any;
static $inject: Array<string> = ['baseAppService', 'userAuthorizationService', 'storageService', 'eventService',];
constructor(
appService: Services.BaseAppService
, public userAuthorizationService: Services.UserAuthorizationService,
public storageService: Services.StorageService,
public eventService: Services.AppEventBusService) {
super(appService);
this.getRestaurentList();
}
routeTo(view) {
this.appService.routerService.routeToPage(view);
}
getRestaurentList = (): void => {
this.appService.networkService.get<any>(this.appService.appConstant.appUrls.getFoodTruckName).then((response) => {
this.foodTruckList = response.data;
},
(error) => { });
}
changeStatus = (): void => {
if (this.checkBox === '1') {
this.getFoodCategories();
}
else if (this.checkBox === '2') {
this.getDrinkCategories();
}
}
getFoodCategories = (): void => {
console.log("rest " + this.restaurent);
angular.forEach(this.foodTruckList, function (item) {
console.log("here" + item.foodtruck_name);
if(item.foodtruck_name === 'world in a box') {
console.log("match res "+ this.restaurent + " " + item._id);
this.restaurentId = item._id;
console.log("ressss "+ this.restaurentId);
}
});
console.log("restaurentId "+this.restaurentId);
this.appService.networkService.get<any>(`${this.appService.appConstant.appUrls.getFoodCategories}/${this.restaurentId}`).then((response) => {
this.foodCategories = response.data;
console.log('popuar Items Loaded', this.foodCategories);
},
(error) => { });
}
getDrinkCategories = (): void => {
var data = {
_id: this.restaurent._id
}
this.appService.networkService.get<any>(this.appService.appConstant.appUrls.getFoodTruckName, data).then((response) => {
this.foodTruckList = response.data;
console.log('popuar Items Loaded', this.foodTruckList);
},
(error) => { });
}
}
}
Here what happens is this.restaurentId is showing value for console.log with ressss .But somehow , the value becomes undefined when console.log with restaurentId is printed. What should I do to make it work?
When you use function() {} for callbacks, the context (this) inside it changes based on how it is called. To retain the correct context (i.e. RestaurentInfoController instance as this) inside your callbacks, use arrow functions:
angular.forEach(this.foodTruckList, (item) => {
// ...
console.log(this.restaurentId); // `this` will point to current `RestaurentInfoController` instance here
});
With Meteor (1.4.2.3) and React, I have the collection Objects which has an itemId which refers to the collection Items.
Currently I subscribe to the collection on the client side with:
export default createContainer(() => {
let objectsSub = Meteor.subscribe('allObjects');
var objects = Objects.find({}, {
transform: function (doc) {
doc.item = Items.findOne({
_id: doc.itemId
});
return doc;
}
}).fetch();
return {
objects: objects,
}
}, App);
This works perfect, but I think it is more elegant to merge the collections on the server side. However, none of the solutions I found seem to work
Transform at collection definition
const Objects = new Mongo.Collection('objects',
{
transform: function (doc) {
doc.item = Items.findOne({
_id: doc.itemId
})
}
});
The console gives:
Error: transform must return object
Transform at publish
if (Meteor.isServer) {
Meteor.publish('allObjects', function () {
return Objects.find({}, {
sort: { startedAt: -1 },
transform: function (doc) {
doc.item = Items.findOne({
_id: doc.itemId
});
return doc;
}
});
});
};
TypeError: Cannot read property 'name' of undefined
Where name is a property of Items
i usually do it in the publish like this:
Meteor.publish('allObjects', function () {
let cursor = Objects.find({}, {
sort: { startedAt: -1 });
});
let transformData = (fields) => {
fields.item = Items.findOne({
_id: fields.itemId
});
return fields;
};
let handle = cursor.observeChanges({
added: (id, fields) => {
fields = transformData(fields);
this.added('objects', id, fields);
},
changed: (id, fields) => {
fields = transformData(fields);
this.changed('objects', id, fields);
},
removed: (id) => {
this.removed('objects', id);
}
});
this.ready();
this.onStop(() => {
handle.stop();
});
}
It is my first steps create angular2 with typescript and i need help.
The first request works well and I'm show it.
When i'm doing click i want to creat a new reqest.
How can i do that?
export class App {
img: Array<Object>;
constructor(http:Http) { http.request('http://boroviha.dev.ooosis.com/api/client/get_photo_sections.php').toRx().subscribe(res => {
console.log('img',res.json().data);
this.img = res.json().data;
});
}
onSelect(item: img) { this.selectedItem = item; console.log(item);
constructor(http:Http) {
this.http.request('http://localhost:3001/api/random-quote')
.map(res => res.text())
.subscribe(
data => this.randomQuote = data,
err => this.logError(err),
() => console.log('Random Quote Complete')
);
}
}
}
bootstrap(App, [HTTP_BINDINGS, bind(RequestOptions).toClass(MyOptions)])
.catch(err => console.error(err));
You define a constructor within the onSelect method which is a bit strange:
onSelect(item: img) { this.selectedItem = item; console.log(item);
constructor(http:Http) {
(...)
I woulsd refactor this to something like that:
export class App {
img: Array<Object>;
constructor(private http:Http) {
(...)
}
onSelect(item: img) {
this.selectedItem = item; console.log(item);
this.http.get('http://localhost:3001/api/random-quote')
.map(res => res.text())
.subscribe(
data => this.randomQuote = data,
err => this.logError(err),
() => console.log('Random Quote Complete')
);
}
}
}
I added private at the level of constructor parameters to make the http parameter part of the App class. So you can use it with the this keyword.
You can notice that now the HTTP_PROVIDERS should be used instead of HTTP_BINDINGS.
Hope it helps you,
Thierry