Svelte way for obtaining props values -in parent component and passing to a child component - after clicking on button - sapper

I'm learning svelte and sapper. I already have everything working correctly in my test app in one component but it is not optimal to build the whole app in one component. So my question here is about the svelte way of doing things. Keep this in mind while reading my question. It's a typical shopping app, page with products, button to add the product to the shopping cart and display the shopping cart and checkout button to finalize the user's purchase with the payment.
My issue is about props and how to pass them from the parent component to the child component. I have a child component that includes a button and 4 variables to gather product information (title, price, description, and id). Here is the code:
<script>
export let title
export let price
export let description
export let id
</script>
<button on:click>Add To Cart Component</button>
The parent component has a product div, a function in the script to get the product elements and pass it to the child component (with the button) so I can process the purchase, here is the parent component:
<script>
function passprops (e) {
let items = e.target.parentElement.parentElement
let title = items.firstChild.innerHTML
let price = items.children[1].innerHTML
let description = items.children[2].innerHTML
let id = items.children[3].innerHTML
}
</script>
my product html code :
<div>
<span id="product">parent component</span>
<span id="price">1000000</span>
<span id="description"> parent component product</span>
<span id="id" hidden="">10</span>
<p class="center"> <Addbutton title={titlep} price={pricep} description={descriptionp} id={idp} on:click={passprops}/>
</p>
</div>
Feel free to correct any code or any setup, I just would like to learn svelte/sapper way of building app.
My question is: When I click on the button component (child component) I need to gather the clicked product details (title, price, description, and id) and pass them to my child component (with the button) so I could process adding the product to my backend-session.cart-db...etc
The reason I'm separating the child/button component is to be able to just include that component in any part of my app for any future products or component with a product. For example, I have a top.svelte with top products so I just include the child component with the button to be able to purchase any of those products or a shoe.svelte with shoe products and add the button/child component to buy shoes.
Is this the right way to set up the app, separate each part to make it easy to edit/modify in the future but I ran into this problem, how do I gather and pass those product details to the props in the child component? Inside passprops I gathered all the correct parameters but how to pass it to the props in the button/child component?
When I run the parent component, it gives me a warning that the button/child component was created without expected props (title, price, description, and id) which understood because I didn't press on the button yet. When I click on the button, all props are undefined.
I would like the help of any svelte/sapper master out there to make me understand this issue. Do I need to use something else like a store (to save the product detail and then pass it to another component that will process the order) or getContest...or just pass the values of the clicked product to the child/button component? But How do I do that?

Hey Marco it looks like you could bind all the details to that onclick interaction.
I cobbled this together to show you what I mean, so this is a form for illustration you'd hide these inputs in reality:
https://svelte.dev/repl/78a268fb88764918b5cebdc7e485721f
So, you have a button component that you can use elsewhere, and the product component where all the details are collated in a form to then use on submission.
The product details are then passed down through the component from the main App file.
I'm not sure if this works in context of the large scope of what you are doing but that is a way to gather the data you are after I think.
I'm also new to Svelte btw! I hope this helps in some small way.

Related

BigCommerce Stencil how can I nest the {{filter}} Handlebars Helper?

I'm a complete amateur when it comes to javascript and templating language, but I know just enough to get by for our small business. We are using a customized bigcommerce stencil theme (cornerstone). I understand conceptually why handlebars helpers work, but I don't understand the details behind HOW they work. I've tried reading the documentation, but I don't understand all the terminology so I'm still lost.
I want to display the lowest price for products that have bulk discounts as well as per unit price if it's a case-packed product. The problem is, bigcommerce doesn't let you call the product bulk pricing within the Category page, so we are trying to use custom fields instead. I thought maybe I could achieve the intended result by "nesting" the {{filter}} helper, but it doesn't work. Here is the code section in the category template:
{{#if price}}
{{#filter custom_fields 'As low as' property='name'}}
<div>As low as {{value}}{{#filter custom_fields 'Price Unit' property='name'}} / {{value}}{{/filter}}</div>
<div>with bulk discounts</div>
{{else}}
<div class="listItem-price">{{> components/products/price price=price}}</div>
{{/filter}}
{{/if}}
How would you approach this?
What exactly are you trying to accomplish with the filter function - as I understand it, it looks like you're trying to access two neighboring custom fields within a single loop?
If that's the case, I would try using a nested {{#each}} loop - you'll notice that when you nest loops like this, sometimes you'll need to try to reach above the nested context by using ../ -- honestly, it's not very consistent, and sometimes you'll need two rounds (as we did here), sometimes you'll need one - that's something I basically use trial and error to determine in different points
{{#each custom_fields}}
{{#if name '==' 'First Custom Field Name'}}
First Value: {{value}}
{{#each ../../custom_fields}}
{{#if name '==' 'Second Custom Field Name'}}
Second Value: {{value}}
{{/if}}
{{/each}}
{{/if}}
{{/each}}
If you'd like a less hacky way to do this, I would suggest using the stencil-utils library's handy "Fetch Product By ID" function:
(https://github.com/bigcommerce/stencil-utils)
What you can do is call utils.api.product.getById(productID, {template}, callback) -- what this does is basically fetch an HTML response from BigCommerce with a Product Template - handy for getting data that's specific to product-pages only, but outside of those pages - usually you would use product-view.html as a base -- if you check quickview.js, this is how BigCommerce populates the "Quick View" modal on product cards.
This is handy for fetching all the data that would be rendered out on a product page anywhere else that you need it - category pages, content pages, blog posts, etc.
Anyways, a few steps to accomplish this:
First, expose the id of your Products on the Category Cards. You would just go to card.html and add a data-id="{{id}}" attribute, so that you can cycle through each one on page load.
Example: <div class="card" data-id="{{id}}"> ... </div>
Next, we'll build out a custom template that just contains pertinent information for the bulk discounts. Basically, you can just use product-view, but to avoid giant payloads, we can create a new template:
templates/components/products/bulk-discount-rate-fetcher.html:
<div data-bulk-rates="{{json product.bulk_discount_rates}}"></div>
Yep, that's it!
Now the meat of the function, we will iterate through all product cards on the category page, and we'll use stencil-utils to populate our custom template with a simple JSON data tag of the bulk discounts:
import utils from '#bigcommerce/stencil-utils';
$('.card').each((index, el) => {
const id = $(el).data('id');
utils.api.product.getById(id, { template: 'products/bulk-discount-rate-fetcher' }, (err, response) => {
if (err) return console.log(err);
const bulkRates = $(response).data('bulk-rates');
console.log(bulkRates);
});
})
Result:
If you’re looking to incorporate the product price into this customization as well, that would require some additional work.

How to update the component of a master page with changes that happen on its detail page immediately?

I have a nativescript angular app.
Suppose you have a page A (call it master) that has a list of items. B is a detail of A, that shows a particular item. Let say a list of cars, with the prices shown in the list page.
Then the user changes the price on the details page, and comes back to the master page.
In my app what happens is the master page is updated accordingly, but in a lazy way.
The user basically sees a list that changes after that comes back. This is most probably to the way angular triggers the component update, only after the users hits "back".
Is there a way to force the master page to be updated before the user clicks "back"?
More specifically, I would like that this kind of update to not be lazy, but eager.
If you're using {N}+Angular, then the common practice is to use an emitter from your child component to signal your parent that some event happened inside the child component.
so on your parent.html component you'd have something like:
<child (listener)="onEmitterReceivedFunction(objReceived)"></child>
with this on the parent.ts:
onEmitterReceivedFunction(objReceived) {
// Do what has to be done
}
then on your child.ts you'd have something like:
import { EventEmitter } from "#angular/core"
#Output('listener') onSomethingChanged = new EventEmitter<any>()
whenSomethingChanged() {
onSomethingChanged.emit(this.thingThatChanged)
}
These are of course snippets that you'll have to put in the right places of your code.
For more details you can look at: https://angular.io/api/core/EventEmitter

Laravel 5 - getting data to a view

I think this is slightly different to the usual controller passing data to the view. I have a Project which has one DocumentOne. Within my app, the user creates a Project. This then redirects them to the show page for this project.
So with the project created, and the user on the show page for that project, I display the project ID. I then provide a select menu where the user can select a Document to display. So say I am in Project with the ID of 1, I then decide to show DocumentOne for this project. This displays a form with inputs for DocumentOne.
When the user fills in the form and submits, the data is saved to the database. The Project ID is the foreign key for DocumentOne. The following route is set up for DocumentOne
Route::resource('projects.documentOne', 'DocumentOneController');
Now I have data for DocumentOne which is linked to the Project with an ID of 1. However, if I now go back to the projects show page and then select Document One from the dropdown again, all I see is an empty form. This is obviously because the controller for this is
public function show(Project $project)
{
return view('projects.show', compact('project'));
}
So I am never passing it data for DocumentOne because theoretically it is not created when the Project is first shown. What I want to do is when the Document is selected in the Projects show page, is to have the form populated with whatever is in the database for that Document. If nothing is in the database, then the form will be empty. I have a DocumentOne Controller, but I dont know if I can link this to the Projects show page. I was thinking about doing something like this in the DocumentOne controller
public function show(DocumentOne $documentOne)
{
return view('projects.show', compact('documentOne'));
}
But not sure this will work. Hope I have not been too confusing and you understand what I am attempting, hoping someone can offer advice on how best to handle this situation.
Thanks
In my previous project, I also deal with such requirement, I thought so. Here my solution to solve such requirement.
Actual code calling from ajax.
Routes
get('setFlashData',function(Request $request){
$final_response = array();
$data_information = $request->except('_token');
$request->session()->flash('cmg_quick_create_data', $data_information);
if($request->session()->has('cmg_quick_create_data')){
$final_response['result']['success'] = true;
}
return response()->json($final_response);
});
But according to you requirement:
$data_information = $request->except('_token');
$request->session()->flash('cmg_quick_create_data', $data_information);
My basic functionality was, to share form data from Quick Create Section which is pop-up form to Full create form section, and whenever user click to "Go To Full Form" button from pop up, ajax call mentioned function which will set the flash data and than on destination side I only check weather its contain the flash data or not. and deal according to data.
#if (Session::has('cmg_quick_create_data')) {
{!! Form::model(Session::get('cmg_quick_create_data'),["class"=>"form-horizontal","data-parsley-validate"=>"data-parsley-validate",'role'=>'form','files'=>true]) !!}
#else
{!! Form::open(["class"=>"form-horizontal","data-parsley-validate"=>"data-parsley-validate",'role'=>'form','files'=>true]) !!}
#endif
I can understand this solution might be different from you requirement but hope full to figure out your solution. Look forward to hearing from you if still unclear from my side.

Complex Pages and Data

I am trying to figure out the best way to handle pages in our application like the dashboard, where there are a number of different panes with various bits of data in it.
The main issue is that the controller action becomes unwieldy when the page needs so much data. The client side can be broken up into partial views to make it more manageable, but all the data still needs to passed into the View to be distributed down to the partials. Or does it?
Obviously some of the panels could be loaded dynamically or something like that, but I was looking for the best approach besides loading individual piece of the page from the browser.
Have you considered using Html.Action in your view? You would pass enough data to the main view to enable you to give the required data to each of the child actions. The main action would render the main view which would call actions for each pane. Each action would be responsible for that pane, rendering its own partial view. Additionally, you could call back to each of the child actions directly from the client to update that pane dynamically via AJAX.
Here's an example with some mocked up actions of what your main view might look like.
<div class="left-pane">
#Html.Action("Summary", new { id = Model.ID } )
</div>
<div class="middle-pane">
#Html.Action("PendingItems", new { id = Model.ID, timestamp = DateTime.Now } )
</div>
<div class="right-pane">
#Html.Action("News")
</div>

creating sortable portfolio with jquery

I have created the layout for my new portfolio webpage here: http://vitaminjdesign.com/work.html
I want to have a legend with tags (ALL, logo design, marketing, web design, print design) and when these tags are clicked, the page filters the results and display them like they are currently displayed. At first, I want ALL projects displayed, but when the user clicks on a tag (say "print design"), the list is filtered and displayed.
If I have this as my legend: logo
and when logo is clicked, I want all of the div's with the class "logos" to stay and all of the the divs with the other classes to fade out.
What is the easiest way in jquery to make this work. Please note: I am not very experienced with jquery, so please be as thorough and idiot-proof as possible.
First add the classes (logodesign, marketing, webdesign, printdesign) that apply to each project to the div your are assigning the numeric class to.
Then create links that are to filter for each tag like:
Logo Design
Then assign a click event that will hide the others and show the selected one.
var $projects = $('#projects');
$('a.logodesign').click(function() {
hideAll();
showTag('logodesign');
});
function showTag(tag){
$projects.find('div.'+tag).stop(true).fadeIn();
}
function hideAll(){
$projects.find('div.logodesign, div.marketing, div.webdeisgn, div.preintdesign').fadeOut();
}
Although it isnt a direct answer to my question, I found a tutorial on how to achieve EXACTLY what I wanted. It is here:
http://www.newmediacampaigns.com/page/a-jquery-plugin-to-create-an-interactive-filterable-portfolio-like-ours#zip

Resources