socialgekon.com
  • Tärkein
  • Varastointi
  • Ihmiset Ja Joukkueet
  • Ongelmien karttoittaminen
  • Sijoittajat Ja Rahoitus
Web-Käyttöliittymä

Vaiheittainen opetusohjelma ensimmäiselle AngularJS-sovelluksellesi

AngularJS on kehittynyt ja tullut vieläkin paremmaksi. Nyt nimeltään Angular, se kirjoitettiin kokonaan uudelle kehitystyönkululle. Tarkista uusi Angular 5 -opetusohjelma , ja vielä uudempi Angular 6: n koko pino-opetusohjelma, jossa on materiaali ja Firebase .

Mikä on AngularJS?

KulmaJS on Googlen kehittämä JavaScript MVC -kehys, jonka avulla voit rakentaa hyvin jäsenneltyjä, helposti testattavia ja ylläpidettäviä käyttöliittymäsovelluksia.

Ja miksi minun pitäisi käyttää sitä?

Jos et ole vielä kokeillut AngularJS: ää, menetät. Kehys koostuu tiiviisti integroidusta työkalupaketista, joka auttaa sinua rakentamaan hyvin jäsenneltyjä, rikkaita asiakaspuolen sovelluksia modulaarisesti - vähemmän koodia ja enemmän joustavuutta.

AngularJS laajentaa HTML-koodia tarjoamalla direktiivejä jotka lisäävät toiminnot merkinnöihisi ja antavat sinun luoda tehokkaita dynaamisia malleja. Voit myös luoda omia direktiivejä, laatia uudelleenkäytettävät komponentit, jotka täyttävät tarpeesi, ja tiivistää kaikki DOM-manipulointilogiikat.



Se toteuttaa myös kaksisuuntaisen tiedonsiirron, yhdistämällä HTML-koodisi (näkymät) JavaScript-objekteihisi (mallit) saumattomasti. Yksinkertaisesti sanottuna tämä tarkoittaa, että kaikki mallisi päivitykset näkyvät välittömästi näkymässäsi ilman DOM-manipulointia tai tapahtumien käsittelyä (esim. JQueryn kanssa).

Angular tarjoaa XHR: n lisäksi palveluja, jotka yksinkertaistavat dramaattisesti koodiasi ja mahdollistavat abstraktin API-puhelut uudelleenkäytettäviksi palveluiksi. Tämän avulla voit siirtää mallisi ja liiketoimintalogiikkasi käyttöliittymään ja rakentaa taustan agnostisia verkkosovelluksia.

Lopuksi, rakastan Angularia sen joustavuuden vuoksi palvelinviestinnässä. Kuten useimmat JavaScript MVC -kehykset, sen avulla voit työskennellä minkä tahansa palvelinpuolen tekniikan kanssa, kunhan se voi palvella sovellustasi RESTful-verkkosovellusliittymän kautta. Mutta Angular tarjoaa myös palveluja päällä XHR: n, joka yksinkertaistaa koodiasi dramaattisesti ja antaa sinun abstraktoida API-puhelut uudelleenkäytettäviksi palveluiksi. Tämän seurauksena voit siirtää mallisi ja liiketoimintalogiikkasi käyttöliittymään ja rakentaa taustan agnostisia verkkosovelluksia. Tässä viestissä teemme juuri sen, askel kerrallaan.

Joten, mistä aloitan?

Ensinnäkin, päätetään rakennettavan sovelluksen luonne. Tässä oppaassa emme halua viettää liikaa aikaa taustalla, joten kirjoitamme jotain tietojen perusteella, jotka ovat helposti saavutettavissa Internetissä, kuten urheilusyötösovellus!

Koska satun olemaan valtava moottoriurheilun ja Formula 1: n fani, käytän autosport-sovellusliittymän palvelua toimiakseen taustamme. Onneksi kaverit Ergast ovat ystävällisiä tarjoamaan ilmaisen moottoriurheilun sovellusliittymän, joka sopii meille täydellisesti.

Katso piilotettu huippu sille, mitä aiomme rakentaa, katsomalla live-esittely . Esittelemällä esittelyn ja esittelemällä joitain Angular-malleja käytin Bootstrap-teemaa WrapBootstrap , mutta koska tämä artikkeli ei koske CSS: ää, vain abstraktin sen pois esimerkkeistä ja jätän sen pois.

Aloitusopas

Käynnistetään esimerkkisovelluksemme jollain kattilalla. Suosittelen kulmasiemen projektin, koska se ei vain tarjoa sinulle loistavaa luurankoa bootstrappingille, vaan myös asettaa pohjan yksikön testaukselle Karma ja Jasmiini (emme tee mitään testejä tässä demossa, joten jätämme vain tavaran toistaiseksi sivuun; katso Osa 2 tästä opetusohjelmasta saadaksesi lisätietoja projektisi määrittämisestä yksikköä ja päähän-testausta varten).

EDIT (toukokuu 2014): Koska kirjoitin tämän opetusohjelman, kulmasiemen projekti on käynyt läpi joitain suuria muutoksia (mukaan lukien Bower paketinhallintana). Jos sinulla on epäilyksiä projektin käyttöönotosta, tutustu nopeasti sen ensimmäiseen osaan viiteopas . Sisään Osa 2 ths opetusohjelma, Bower , muiden työkalujen ohella, käsitellään tarkemmin.

OK, nyt kun olemme kloonanneet arkiston ja asentaneet riippuvuudet, sovelluksemme luuranko näyttää tältä:

angularjs-opetusohjelma - aloita luurangosta

Nyt voimme aloittaa koodauksen. Kun yritämme rakentaa urheilutapahtumia kilpa-mestaruuskilpailuja varten, aloitetaan asiaankuuluvimmasta näkökulmasta: mestaruuspöytä .

mestaruuspöytä

Ottaen huomioon, että ohjainluettelo on jo määritelty toiminta-alueellamme (ripusta kanssani - tulemme sinne) ja sivuuttamatta mitään CSS: ää (luettavuuden vuoksi), HTML-koodi saattaa näyttää tältä:

Drivers Championship Standings
{{$index + 1}} {{driver.Driver.givenName}} {{driver.Driver.familyName}} {{driver.Constructors[0].name}} {{driver.points}}

Ensimmäinen asia, jonka huomaat tässä mallissa, on lausekkeiden ('{{' ja '}}') käyttö muuttujien arvojen palauttamiseksi. AngularJS-kehityksessä , lausekkeiden avulla voit suorittaa jonkin verran laskutoimitusta halutun arvon palauttamiseksi. Jotkut kelvolliset lausekkeet ovat:

  • {{ 1 + 1 }}
  • { 946757880 }
  • {{ user.name }}

Lausekkeet ovat käytännössä JavaScript-tyyppisiä katkelmia. Huolimatta siitä, että olet erittäin tehokas, sinun ei pitäisi käyttää lausekkeita korkeamman tason logiikan toteuttamiseen. Tätä varten käytämme direktiivejä.

Perusdirektiivien ymmärtäminen

Toinen asia, jonka huomaat, on ng-attributes, jota et näe tyypillisessä merkinnässä. Nämä ovat direktiivejä.

Korkealla tasolla direktiivit ovat markkereita (kuten määritteet, tunnisteet ja luokkien nimet), jotka käskevät AngularJS: ää liittämään tietyn käyttäytymisen DOM-elementtiin (tai muuntamaan sen, korvaamaan sen jne.). Katsotaanpa niitä, jotka olemme jo nähneet:

  • ng-app -direktiivi on vastuussa sovelluksen käynnistämisestä uudelleen määrittelemällä sen soveltamisala. AngularJS: ssä sinulla voi olla useita sovelluksia samalla sivulla, joten tämä direktiivi määrittää, missä kukin erillinen sovellus alkaa ja päättyy.

  • ng-controller direktiivi määrittää, mikä rekisterinpitäjä vastaa näkemyksestäsi. Tässä tapauksessa merkitsemme driversController, joka antaa luettelon ohjaimista (driversList).

  • ng-repeat -direktiivi on yksi yleisimmin käytetyistä ja palvelee mallipohjan määrittelemistä, kun etsit kokoelmia. Yllä olevassa esimerkissä se kopioi taulukon rivin jokaiselle kuljettajalle kohdassa driversList

Ohjainten lisääminen

Näkemyksestämme ei tietenkään ole hyötyä ilman ohjainta. Lisätään driversController ohjaimillemme.js:

angular.module('F1FeederApp.controllers', []). controller('driversController', function($scope) { $scope.driversList = [ { Driver: { givenName: 'Sebastian', familyName: 'Vettel' }, points: 322, nationality: 'German', Constructors: [ {name: 'Red Bull'} ] }, { Driver: { givenName: 'Fernando', familyName: 'Alonso' }, points: 207, nationality: 'Spanish', Constructors: [ {name: 'Ferrari'} ] } ]; });

Olet ehkä huomannut $scope muuttuja, jonka välitämme parametrina ohjaimelle. $scope muuttujan on tarkoitus linkittää ohjaimesi ja näkymät. Erityisesti siinä on kaikki tiedot, joita mallissasi käytetään. Kaikki siihen lisäämäsi (kuten driversList yllä olevassa esimerkissä), ovat suoraan käytettävissä näkymissäsi. Työskentelemme nyt vain nuken (staattisen) tietojoukon kanssa, joka korvataan myöhemmin API-palvelullamme.

Lisää nyt tämä app.js: een:

angular.module('F1FeederApp', [ 'F1FeederApp.controllers' ]);

Tällä koodirivillä alustamme sovelluksemme ja rekisteröimme moduulit, joista se riippuu. Palataan kyseiseen tiedostoon (app.js) myöhemmin.

Laitetaan nyt kaikki yhteen index.html:

F-1 Feeder
Drivers Championship Standings
{{$index + 1}} {{driver.Driver.givenName}} {{driver.Driver.familyName}} {{driver.Constructors[0].name}} {{driver.points}}

Pieniä virheitä, voit nyt käynnistää sovelluksesi ja tarkistaa (staattisen) ohjainluettelosi.

Huomaa: Jos tarvitset apua sovelluksen virheenkorjauksessa ja mallien ja laajuuden visualisoinnissa selaimessa, suosittelen katsomaan mahtavaa Batarang Chrome-laajennus.

Ladataan tietoja palvelimelta

Koska tiedämme jo, kuinka ohjaimen tiedot näytetään näkymässämme, on aika todella hakea reaaliaikaiset tiedot RESTful-palvelimelta.

Viestinnän helpottamiseksi HTTP-palvelimien kanssa AngularJS tarjoaa $http ja $resource palvelut. Edellinen on vain kerros sen päällä XMLHttpRequest tai JSONP , kun taas jälkimmäinen tarjoaa korkeamman abstraktiotason. Käytämme $http.

Jos haluat erottaa palvelimen sovellusliittymäkutsut ohjaimesta, luodaan oma mukautettu palvelumme, joka noutaa tietomme ja toimii kääreenä $http lisäämällä tämä services.js:

angular.module('F1FeederApp.services', []). factory('ergastAPIservice', function($http) { var ergastAPI = {}; ergastAPI.getDrivers = function() { return $http({ method: 'JSONP', url: 'http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK' }); } return ergastAPI; });

Kahden ensimmäisen rivin avulla luomme uuden moduulin (F1FeederApp.services) ja rekisteröimme palvelun kyseiseen moduuliin (ergastAPIservice). Huomaa, että ohitamme $http kyseisen palvelun parametrina. Tämä kertoo Angular'sille riippuvuusinjektio moottori, jota uusi palvelumme vaatii (tai riippuu ) $http palvelu.

Samalla tavalla meidän on kerrottava Angularille sisällyttämään uusi moduulimme sovellukseemme. Rekisteröidään se app.js: lla ja korvataan nykyinen koodi seuraavalla:

angular.module('F1FeederApp', [ 'F1FeederApp.controllers', 'F1FeederApp.services' ]);

Nyt meidän tarvitsee vain säätää controller.js vähän, sisältää ergastAPIservice riippuvuutena, ja meillä on hyvä mennä:

angular.module('F1FeederApp.controllers', []). controller('driversController', function($scope, ergastAPIservice) { $scope.nameFilter = null; $scope.driversList = []; ergastAPIservice.getDrivers().success(function (response) { //Dig into the responde to get the relevant data $scope.driversList = response.MRData.StandingsTable.StandingsLists[0].DriverStandings; }); });

Lataa sovellus uudelleen ja tarkista tulos. Huomaa, että emme tehneet muutoksia malliin, mutta lisäsimme nameFilter vaihtelee soveltamisalamme mukaan. Otetaan tämä muuttuja käyttöön.

Suodattimet

Loistava! Meillä on toiminnallinen ohjain. Mutta se näyttää vain luettelon ohjaimista. Lisätään joitain toimintoja toteuttamalla yksinkertainen tekstihaku, joka suodattaa luettelomme. Lisätään seuraava rivi index.html -sarjaan, joka on suoraan tagin alapuolella:

ng-model

Käytämme nyt $scope.nameFilter direktiivi. Tämä direktiivi sitoo tekstikenttämme ng-repeat muuttuja ja varmistaa, että sen arvo on aina ajan tasalla syötetyn arvon kanssa. Käy nyt osoitteessa index.html vielä kerran ja tee pieni säätö riville, joka sisältää direktiivi:

ng-repeat

Tämä rivi kertoo driversList että ennen tietojen luovuttamista nameFilter taulukko on suodatettava arvoon, joka on tallennettu $scope.nameFilter

Tässä vaiheessa kaksisuuntainen tiedonsiirto käynnistyy: joka kerta kun arvo syötetään hakukenttään, Angular varmistaa välittömästi, että nameFilter siihen liittyvä päivitetään uudella arvolla. Koska sidonta toimii molemmin puolin, tällä hetkellä ng-repeat arvo päivitetään, myös siihen liitetty toinen direktiivi (eli Driver.givenName) saa uuden arvon ja näkymä päivitetään välittömästi.

Lataa sovellus uudelleen ja tarkista hakupalkki.

sovelluksen hakupalkki

Huomaa, että tämä suodatin etsii avainsanaa kaikista mallin määritteistä, myös niistä, joita emme käytä. Oletetaan, että haluamme suodattaa vain Driver.familyName ja driversController: Ensinnäkin lisätään $scope.driversList = [];, aivan $scope.searchFilter = function (driver) ; : n alapuolelle linja:

index.html

Palataan nyt kohtaan ng-repeat ja päivitämme rivin, joka sisältää direktiivi:

$routeProvider

Lataa sovellus uudelleen vielä kerran, ja nyt meillä on haku nimen mukaan.

Reitit

Seuraava tavoitteemme on luoda kuljettajan tietosivu, jonka avulla voimme napsauttaa kutakin kuljettajaa ja nähdä hänen / hänen uransa yksityiskohdat.

Ensinnäkin sisällytetään app.js palvelu (in app.js), joka auttaa meitä käsittelemään näitä erilaisia levitysreitit . Sitten lisätään kaksi tällaista reittiä: yksi mestaruuspöydälle ja toinen kuljettajan yksityiskohdille. Tässä on uusi angular.module('F1FeederApp', [ 'F1FeederApp.services', 'F1FeederApp.controllers', 'ngRoute' ]). config(['$routeProvider', function($routeProvider) { $routeProvider. when('/drivers', {templateUrl: 'partials/drivers.html', controller: 'driversController'}). when('/drivers/:id', {templateUrl: 'partials/driver.html', controller: 'driverController'}). otherwise({redirectTo: '/drivers'}); }]);

http://domain/#/drivers

Muutoksen myötä siirtyminen kohtaan driversController lataa partials/drivers.html ja etsi ositettava näkymä ng-view -sivulta. Mutta odota! Meillä ei ole vielä osittaisia ​​näkemyksiä, eikö? Meidän on myös luotava ne.

Osittaiset näkymät

AngularJS: n avulla voit sitoa reitit tiettyihin ohjaimiin ja näkymiin.

Mutta ensin meidän on kerrottava Angularille, missä nämä osittaiset näkemykset tehdään. Tätä varten käytämme index.html direktiivin muuttamalla F-1 Feeder peilata seuraavat:

partials/drivers.html

Nyt kun navigoimme sovellusreitteissämme, Angular lataa siihen liittyvän näkymän ja hahmottaa sen tagin sijaan. Meidän on vain luotava tiedosto nimeltä

Drivers Championship Standings
{{$index + 1}} {{driver.Driver.givenName}} {{driver.Driver.familyName}} {{driver.Constructors[0].name}} {{driver.points}}
ja laita mestaruuspöydän HTML-koodi sinne. Käytämme myös tätä mahdollisuutta linkittää kuljettajan nimi kuljettajamme reittiin:

services.js

Lopuksi päätetään, mitä haluamme näyttää tietosivulla. Entä yhteenveto kaikista kuljettajaa koskevista tiedoista (esim. Syntymä, kansalaisuus) sekä taulukko, joka sisältää kuljettajan viimeisimmät tulokset? Voit tehdä sen lisäämällä angular.module('F1FeederApp.services', []) .factory('ergastAPIservice', function($http) { var ergastAPI = {}; ergastAPI.getDrivers = function() { return $http({ method: 'JSONP', url: 'http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK' }); } ergastAPI.getDriverDetails = function(id) { return $http({ method: 'JSONP', url: 'http://ergast.com/api/f1/2013/drivers/'+ id +'/driverStandings.json?callback=JSON_CALLBACK' }); } ergastAPI.getDriverRaces = function(id) { return $http({ method: 'JSONP', url: 'http://ergast.com/api/f1/2013/drivers/'+ id +'/results.json?callback=JSON_CALLBACK' }); } return ergastAPI; }); :

controllers.js

Tällä kertaa toimitamme kuljettajan tunnuksen palvelulle niin, että haemme vain tietylle kuljettajalle olennaiset tiedot. Nyt muokkaamme angular.module('F1FeederApp.controllers', []). /* Drivers controller */ controller('driversController', function($scope, ergastAPIservice) { $scope.nameFilter = null; $scope.driversList = []; $scope.searchFilter = function (driver) re.test(driver.Driver.familyName); ; ergastAPIservice.getDrivers().success(function (response) { //Digging into the response to get the relevant data $scope.driversList = response.MRData.StandingsTable.StandingsLists[0].DriverStandings; }); }). /* Driver controller */ controller('driverController', function($scope, $routeParams, ergastAPIservice) { $scope.id = $routeParams.id; $scope.races = []; $scope.driver = null; ergastAPIservice.getDriverDetails($scope.id).success(function (response) { $scope.driver = response.MRData.StandingsTable.StandingsLists[0].DriverStandings[0]; }); ergastAPIservice.getDriverRaces($scope.id).success(function (response) { $scope.races = response.MRData.RaceTable.Races; }); }); :

$routeParams

Tärkeää huomata tässä on, että pistimme juuri :id huolto kuljettajan ohjaimeen. Tämä palvelu antaa meille pääsyn URL-parametreihimme (tässä tapauksessa $routeParams.id: lle) käyttämällä partials/driver.html.

Nyt kun tietomme ovat laajuudessa, tarvitsemme vain jäljellä olevan osittaisen näkymän. Luodaan tiedosto nimeltä <- Back to drivers list
{{driver.Driver.givenName}} {{driver.Driver.familyName}} Country: {{driver.Driver.nationality}}
Team: {{driver.Constructors[0].name}}
Birth: {{driver.Driver.dateOfBirth}}
Biography

Formula 1 2013 Results
Round Grand Prix Team Grid Race
{{race.round}} {{race.raceName}} {{race.Results[0].Constructor.name}} {{race.Results[0].grid}} {{race.Results[0].position}}
ja lisää:

ng-show

Huomaa, että laitamme nyt true hyvään käyttöön. Tämä direktiivi näyttää HTML-elementin vain, jos annettu lauseke on false (ts. ei null eikä index.html). Tässä tapauksessa avatar näkyy vain, kun ohjain on ladannut ohjainobjektin piiriin.

Viimeistelyt

Lisää joukko CSS: ää ja renderöi sivu. Sinun pitäisi päätyä jotain tällaista:

CSS: llä renderöity sivu

Olet nyt valmis käynnistämään sovelluksesi ja varmistamaan, että molemmat reitit toimivat halutulla tavalla. Voit myös lisätä staattisen valikon kansioon

|_+_|
parantaa käyttäjän navigointimahdollisuuksia. Mahdollisuudet ovat rajattomat.

EDIT (toukokuu 2014): Olen saanut monia pyyntöjä ladattavasta versiosta koodista, jonka rakennamme tässä opetusohjelmassa. Siksi olen päättänyt vapauttaa sen tässä (poistettu CSS: stä). Olen kuitenkin todella ei suosittele sen lataamista, koska tämä opas sisältää jokaisen askeleen, jonka sinun on rakennettava sama sovellus omin käsin, mikä on paljon hyödyllisempi ja tehokkaampi oppimisharjoitus.

Johtopäätös

Tässä opetusohjelmassa olemme käsitelleet kaiken, mitä tarvitset yksinkertaisen sovelluksen kirjoittamiseen (kuten Formula 1 -syöttölaite). Kaikilla live-esittelyn jäljellä olevilla sivuilla (esim. Rakentajan mestaruuspöytä, joukkueen tiedot, kalenteri) on sama perusrakenne ja -käsitteet, jotka olemme tarkastelleet täällä.

Lopuksi, pidä mielessä, että Angular on erittäin tehokas kehys, ja olemme tuskin raaputtaneet pintaa kaikessa, mitä se tarjoaa. Sisään Osa 2 Tästä oppaasta annamme esimerkkejä siitä, miksi Angular erottuu vertaistulostimen MVC-kehyksistä: testattavuus. Tarkistamme yksikötestien kirjoittamisen ja suorittamisen Karma , jatkuvan integraation saavuttaminen Yeomen , Maa ja Bower ja muut tämän vahvan käyttöliittymän vahvuudet.

Perustietojen ymmärtäminen

Mikä on AngularJS?

AngularJS on Googlen kehittämä JavaScript MVC -kehys, jonka avulla voit rakentaa hyvin jäsenneltyjä, helposti testattavia ja ylläpidettäviä käyttöliittymäsovelluksia.

Miksi AngularJS?

AngularJS laajentaa HTML-koodia tarjoamalla direktiivejä, jotka lisäävät toiminnot merkinnöihisi ja antavat sinun luoda tehokkaita dynaamisia malleja. Voit myös luoda omia direktiivejä, laatia uudelleenkäytettäviä komponentteja, jotka täyttävät tarpeesi ja tiivistävät kaikki DOM-manipulointilogiikat.

Palvelimettomien Node.js-toimintojen toteuttaminen Google Cloudin avulla

Taustaa

Palvelimettomien Node.js-toimintojen toteuttaminen Google Cloudin avulla
10 UX-toimitettavaa huippusuunnittelijaa käyttää

10 UX-toimitettavaa huippusuunnittelijaa käyttää

Ux-Suunnittelu

Suosittu Viestiä
Ultimate Guide to Processing Language Osa II: Yksinkertaisen pelin rakentaminen
Ultimate Guide to Processing Language Osa II: Yksinkertaisen pelin rakentaminen
Kuinka tehdä valokuvakollaasi iPhonessa: sovelluksia ja ideoita
Kuinka tehdä valokuvakollaasi iPhonessa: sovelluksia ja ideoita
Stork, osa 3: Lausekkeiden ja muuttujien toteuttaminen
Stork, osa 3: Lausekkeiden ja muuttujien toteuttaminen
3D-grafiikka: WebGL-opetusohjelma
3D-grafiikka: WebGL-opetusohjelma
Kulttuurienvälinen suunnittelu ja UX: n rooli
Kulttuurienvälinen suunnittelu ja UX: n rooli
 
Yrityksen lähtökohta: datatiede ja tekoäly
Yrityksen lähtökohta: datatiede ja tekoäly
Parhaat pilvitallennusvaihtoehdot iPhone-valokuvaajille
Parhaat pilvitallennusvaihtoehdot iPhone-valokuvaajille
Visuaalinen ohjelmointi Node-Red: llä: Internet-yhteyden muodostaminen helposti
Visuaalinen ohjelmointi Node-Red: llä: Internet-yhteyden muodostaminen helposti
Termilomakemallit - lausekkeet, joihin kannattaa kiinnittää huomiota neuvottelujen aikana
Termilomakemallit - lausekkeet, joihin kannattaa kiinnittää huomiota neuvottelujen aikana
Pythonin monisäikeinen ja moniprosessointiopetus
Pythonin monisäikeinen ja moniprosessointiopetus
Luokat
Tuote-Ihmiset Ja JoukkueetSuunnittelijan ElämäUi-SuunnitteluTaustaaKaukosäätimen NousuAmmuntaiOS-vinkkejäVarastointiMuuLiikevaihdon Kasvu

© 2023 | Kaikki Oikeudet Pidätetään

socialgekon.com